All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning, from version 1.3.0 on.
- New Windows Installer API.
- New Windows Installer Cmdlets:
Get-MsiSummaryInfo
,Get-MsiTableInfo
,Get-MsiTableDump
, andInvoke-MsiQuery
. - Added methods
CompareTo
,ToUpper
,ToLower
toWuBaseString
. - Added an unmanaged class called
NtFunctions
to handle dynamic linking to 'ntdll.dll'. Until we figure out a better way.
- Rebuilt
Get-MsiProperties
to use the new Installer API and to conform to the Installer Cmdlets. - The managed class
CmdletNativeContext
was serving no purpose other than adding extra steps, and was removed. TheCoreCommandBase
class now hosts an instance of the oldCmdletContextBase
, which was renamed toCmdletContextProxy
. This change removes extra steps and avoid calling unnecessary methods and operators. - Functionalities in the 'WuCore' were reorganized together with the header / source files. The Cmdlet class files were also reorganized.
Get-ObjectHandle
now lists all handles opened for a process, given its process ID or theSystem.Diagnostics.Process
object. Similar to the option '-p' from 'handle.exe'.Get-ObjectHandle
was re-written to account for exception handling, optimization, and other issues, like when told to close a handle the function gets process information anyway, despite the results being discarted.
Get-ObjectHandle
did not try to close some handles when told to, such as owned by the System process.Get-ObjectHandle
couldn't deal with long paths (bigger than MAX_PATH). This was fixed by prepending\\?\
before big paths.
Suspend-Process
(suspend).Resume-Process
(resume).Get-ErrorInformation
(err).
WuCore
is now C++20!! (no modules in C++/CLI yet though)- The C++/CLI core was completely rewritten to refactor, organize, and conform to C++ programming guidelines.
- Writing information from unmanaged code to PowerShell was changed to use a system of proxy delegates.
These delegates do all the marshalling, and call the
Write*
functions with managed objects. This system allowed us to get rid of shared memory reading/writing. It's all done using wrappers. - All Cmdlet classes were given their own file. The main
Commands.cs
was almost 3000 lines long.
- Tied up the error handling in
Close-NetworkFile
, who was throwing from an external component instead of wrapping in aNativeException
.
Get-NetworkFile
Close-NetworkFile
New-Cabinet
Test-Port
Get-ProcessModule
- New alias
getdotnet
forGet-InstalledDotnet
- Implemented
NativeWriteError
for writing errors from unmanaged land. - Implemented
EnumerateFileSystemInfo
for future Cmdlets. - Implemented
AbstractPathTree
for future Cmdlets.
- Pester tests for the following commands:
Get-ComputerSession
Get-ErrorString
Get-MsiProperties
(created an installer to get the properties from).Get-ObjectHandle
(for files and registry!).Get-ResourceMessageTable
(created a DLL with a single message for testing).Remove-Service
(we have a test service with delayed stop).
PSObjectFactory
helper class. Implemented to help creatingPSObjects
seamlessly throughout the code.SafeHandle.h.
This header file and implementation contains wrapper classes to apply RAII to native system handles. The first one isScmHandle
for service related handles. TheRemove-Service
Cmdlet now runs with this class.Remove-Service
- Implemented the
NoWait
switch parameter for when stopping services prior to deletion.
- Implemented the
Remove-Service
- Cmdlet now uses the new
ScmHandle
class to manage Service Control Manager handles.
- Cmdlet now uses the new
Get-ErrorString
now usesWuStdException
to generate messages. It's more reliable and complete.Get-ObjectHandle
- The helper function to close handles was changed to mimic the
GetObjectUsingKey
function, on the way it usesNtQueryObject
.NtQueryObject
hangs when querying asynchronous objects like pipes, and these objects have pending operations. To mitigate that we were using threads, but this way is too slow and unreliable. Now we useCreateFileMapping
on the handle, and check the result for errorERROR_BAD_EXE_FORMAT
to check if the handle is a file handle. Idea from this question.
- The helper function to close handles was changed to mimic the
Get-ObjectHandle
was not closing file handles because it was not finding the handles listed. Fixed that with the new helper function to close external handles.Remove-Service
- Had a broken string when writing warning. That happened because I was using the
WWuString
instead of its buffer in the format - A corrupted heap exception was being thrown because we were trying to close a handle opened in managed code, wrapped in the
ServiceController
object. - Still in the handle department, there were a couple of leaks with handles that were fixed by the new
ScmHandle
wrapper class.
- Had a broken string when writing warning. That happened because I was using the
Get-MsiProperties
- Saved the best for last. This was one of the first Cmdlets I built, and oh boy that thing was crude. We were closing NONE of the handles, leaving the file opened blocked in the current process.
That was fixed switching
MSIHANDLE
byPMSIHANDLE
, which was what inspired me to create theSafeHandle
wrapper classes. - The Cmdlet had ZERO provider awareness, and I consider this a bug. Now we have all the path shenanigans in it. We also check to see if the current provider is
FileSystem
, which is the only one supported.
- Saved the best for last. This was one of the first Cmdlets I built, and oh boy that thing was crude. We were closing NONE of the handles, leaving the file opened blocked in the current process.
That was fixed switching
- Get-ObjectHandle
- The condition to see if the open handle is a handle to our key was wrong, thus returning processes that does not have handles opened to our object.
Fixing that caused performance issues, because we are querying every single object opened, and using threads to avoid freeze in
NtQueryObject
. Turns outNtQueryObject
freezes in certain asynchronous objects with blocking primitives. So we added a check to make sure it's a registry key before callingNtQueryObject
.
- The condition to see if the open handle is a handle to our key was wrong, thus returning processes that does not have handles opened to our object.
Fixing that caused performance issues, because we are querying every single object opened, and using threads to avoid freeze in
- Start-Tcping.
- When canceling via Ctrl + C while probing an unreachable port, sometimes the last result is a false positive. Also, the cancel took forever.
Implemented a worker-thread approach to cancel more rapidly, and a pooled queue system for notification, since
WriteObject
cannot be called from a different thread. Fine tunned the results to avoid false positives.
- When canceling via Ctrl + C while probing an unreachable port, sometimes the last result is a false positive. Also, the cancel took forever.
Implemented a worker-thread approach to cancel more rapidly, and a pooled queue system for notification, since
- Get-ObjectHandle.
- Checking if the user is running as administrator required the use of
WindowsIdentity
, which is not supported anymore, so everything was moved to C++.
- Checking if the user is running as administrator required the use of
- Get-ObjectHandle.
Proudly announce that
Get-ObjectHandle
now returns handles opened for registry keys, with full provider awareness.
- Get-ObjectHandle. Improved the provider awareness both to support registry keys, and handle unsupported PS providers.
- Start-Tcping. Added alias 'tcping'.
- Start-Tcping.
- New cmdlet that 'pings' a destination server(s) in the specified port(s).
- WuStdException.
- Implementing
Start-Tcping
brought a lot of challenges, the most noticeable one was the performance while returning from functions.WuStdException
is a C++std::exception
based class to slowly shift from return codes to proper error handling.
- Implementing
- NATIVE_CONTEXT -> WuNativeContext.
WuNativeContext
is an improved version of the oldNATIVE_CONTEXT
, again to make the whole process more efficient and reliable.
- Shared Memory.
- The shared memory scheme changed from using memory mapped IO to using
System.IO.UnmanagedMemoryStream
, which is basically a memory space to be used between Managed/Unmanaged code.
- The shared memory scheme changed from using memory mapped IO to using
The version 1.7.0 marks a turning point into this project. A lot of important things were added, and changed. From the changes, these deserve to be highlighted.
-
WindowsUtils string.
This feature was one of many that came to support the
Expand-Cabinet
Cmdlet. It is a simple template string class based on the C++std::basic_string
, and the .NETSystem.String
. I know, why create yet another string class with so many more reliable options? Because I wanted to, plus I got the chance to optimize it for this project. Since most allocations in the project are for strings, this class allows me to apply RAII to them, and manipulate them more easily. With this in mind, this is probably not a good string implementation, nor does it have a reason to exist. -
Memory allocation.
A lot changed in memory allocation. With the new string class we don't need to explicitly allocate memory for strings nomo. The
WuMemoryManager
is also gone, all explicit allocations (where possible) where replaced with smart pointers (about damn time eh?). A new template expression was implemented, so we can create smart pointers with custom allocation sizes. With this I hope to reduce memory-related bugs from now to new versions. -
WuResult
Up to version 1.6*, almost all unmanaged functions returned a
DWORD
value, much like Windows API functions. This was replaced (where applicable) by a new object calledWuResult
. I've implemented something similar in another module LibSnitcher (that you DEFINITELY need to check out). I was reluctant about this kind of implementation, but the debugging benefits, and exception handling made it worth it. -
Native PSCmdlet method support.
Alright, this new thing is perty damn cool. Using a system of delegates, Cmdlet context, and memory mapped IO I've implemented a way of calling
PSCmdlet.WriteWarning
andPSCmdlet.WriteProgress
from 100% unmanaged code. This is also expansible for the other methods likePSCmdlet.WriteError
.A custom command base class, inheriting from
PSCmdlet
implements native versions ofWriteWarning
andWriteProgress
. This abstract class wraps a Core class which exposes the delegates that will serve as a bridge between both worlds. Data is exchanged between these worlds using a memory mapped file. This idea came from a video from the great Pavel Yosifovich on the subject. This approach solved all issues with string marshaling I was having before. You can find the video here.Unmanaged code calls a native function that maps a view of the file, writes the data, and calls a function pointer. This function pointer is the delegate from the Cmdlet context base class. When we wrap the command base into the C++/CLI context base, we pass our methods "casted" as these delegates. These methods also creates a view of the file, reads the data and calls the actual PSCmdlet methods.
- New
Expand-Cabinet
Cmdlet. - New
Get-InstalledDotnet
function. - Implemented
WindowsUtils.MemoryMappedShare
to exchange information between managed .NET and unmanaged C++. - Implemented new system for writing information from unmanaged code.
- A system using a delegate define in the Wrapper, defined in .NET, and cast to a function pointer in unmanaged code enables calling 'PSCmdlet' methods.
- A new
CoreCommandBase
class, with aCmdletContext
class will be responsible for carrying the context to unmanaged code. This is a very common practice in Microsoft's modules.
- Added
constexpr
routing to vector, map, and smart pointers to make the code cleaner. - Added custom freer for smart pointers, allowing to allocate arbitrary sizes.
- New string template class
WuBaseString
, includingWWuString
for wide strings, andWuString
for narrow strings. This is probably not a good string implementation, nor does it have a reason to exist, but I had fun. Plus it brings some functionalities from .NET'sSystem.String
. - The new
WuResult
, andNativeException
have a new property calledCompactTrace
exposed only in the Debug version. This contains the file name, and line where the error occurred.
Remove-Service
- When
Stop
is used, the Cmdlet doesn't time out anymore. Instead, it writes warnings, much like when usingStop-Service.
- When
Get-ObjectHandle
- The Cmdlet shows a warning when the process running is not elevated. If the process is not elevated, some handle information might be missing due lack of privileges to query certain processes.
- Replaced all explicit memory allocations with smart pointers. It was about time.
- Replaced all C-style strings with the new
WuString
, and its utility methods. Get-FormattedMessage
changed toGet-ErrorText
.NativeException
moved to C++/CLI completely. This allowed me from removing theNativeExceptionBase
, and creating exceptions from the newWuResult
object.
- Calling
Get-ObjectHandle
with insufficient privileges to an object caused access violation. The function was not parsing theNTSTATUS
from the subroutine. Get-ServiceSecurity
failing to get the SDDL from a "cross-function" SECURITY_DESCRIPTOR pointer. Moved the SDDL formation to the native function.
Get-ServiceSecurity
- Parameter
Name
accepts multiple names. - Added parameter
DisplayName
. Accepts multiple display names. - Added support for wildcard on
Name
andDisplayName
parameters.
- Parameter
- Added tab completion for
Get-ServiceSecurity
,Set-ServiceSecurity
, andRemove-Service
, both onName
andDisplayName
parameters.
- Fixed issue with ownership change when using the
Sddl
parameter withSet-ServiceSecurity
.
- Fixing assembly loading and dependency issues.
- WtsSession:
- From a static class to an IDisposable class. Not only safer, but allows us to manage multiple sessions with multiple servers at the same time.
Get-FormattedError
renamed toGet-ErrorString
.
Remove-Service
Cmdlet.Get-ServiceSecurity
Cmdlet.New-ServiceAccessRule
Cmdlet.New-ServiceAuditRule
Cmdlet.Set-ServiceSecurity
Cmdlet.NativeException
exception on both core, and main libraries to wrap Win32 errors.- Implemented
WindowsUtils.AccessControl.WindowsUtilsObjectSecurity
for current and future object ACL abstraction. - Implemented
WindowsUtils.AccessControl.ServiceSecurity
to manage Windows service security. - Added help message on all parameters.
- Added the
ValidateFileExists
attribute to validate if single files exists. - Added
Format.ps1xml
for the new typeWindowsUtils.AccessControl.ServiceSecurity
. - Added type support on
Types.ps1xml
forWindowsUtils.AccessControl.ServiceSecurity
.
- Get-ResourceMessageTable:
- "s" was being used as a format specifier, which was breaking the message strings. Replaced with "S".
- Trimmed output string on
Message
to remove the extra line feed.
- Structure:
- Rewriting the functions, removing stupid mistakes from when I was beginning with MS C++.
LocalAlloc
.LocalAlloc
everywhere. Replacing with stack andHeapAlloc
allocation.
Get-ObjectHandle
- Making getting process image version info more efficient.
- Abstract processes like System, Secure System and Registry now point to
ntoskrnl.exe
, like on the Task Manager. - Column 'Name' added, 'Application' changed to 'Description', and property layout changed to mimic the Task Manager.
Disconnect-Session
- Cannot use session ID 0 when disconnecting a session on a remote computer.
Get-ObjectHandle
:- Implement
CloseHandle
. - Implemented
NtQuerySystemInformation
withSystemProcessInformation
to cover information missing fromQueryFullProcessImageName
. - New Switch Parameter
Force
to be used withCloseHandle
. Avoids confirmation.
- Implement
-
Get-ComputerSession
- When ran on the local computer,
IdleTime
should be zero for the current session. It was returning a randomfiletime
. Now ifhsession = WTS_CURRENT_SERVER_HANDLE
, and the user is the running the Cmdlet, it returnsTimeSpan.Zero
. - For system sessions, the logon time was
filetime
0, which is 12/31/1600 10:00:00 PM. Now whenLogonTime
isfiletime
0, it returns null.
- When ran on the local computer,
-
Get-ObjectHandle
- Removing the error handling caused the protected processes not to return.
- Using
PROCESS_QUERY_LIMITED_INFORMATION
to get version info, and image path from protected processes. - Processes who image doesn't have a
FileDescription
, using the image base name instead. - No more process missing main information, like Name and Description.
- Some module functionalities depend on VC++ libraries. To avoid having to install the redistributable runtime, these assemblies are provided with the module.
Invoke-RemoteMessage
:- Sessions who doesn't support message box was returning the same response of the last session.
- When 'Wait' was called with 'Timeout' equals to zero, and there are more than one valid session on the target computer, the console would wait all session's response.
This causes the console to hang if there were missing responses.
Now, if Wait is specified, Timeout is mandatory, and an error is thrown if the user inputs zero.
StageComputerSession
method added toWtsSession
to manage persistent WTS computer sessions.Get-ObjectHandle
new aliasgethandle
.- New output object for
Invoke-RemoteMessage
.MessageResponse
, with Session ID and Response.
- Release notes on the module manifest, thus also on PowerShell Gallery now points to this document.
WtsSession
as a static class to use it as a 'Global Object'.Send-Click
now is called from unmanaged code usingSendInput
. Previously was called using explicit P/Invoke.Disconnect-Session
parameterSessionId
not mandatory when disconnecting a session on the local computer. It will log off the current session.- All output objects are now .NET wrapper classes for the CLR ref class. This helps with cyclical dependencies, and further module expansion.
Get-ObjectHandle
parameterPath
not mandatory anymore. It will query objects in the current directory.Invoke-RemoteMessage
:- Now supports should process. Previously confirmation was managed by the
UtilitiesLibrary
. - Converting
SessionId
andResponse
vectors to smart pointer. Previously they were statically allocated / deallocated on the heap. - Unmanaged function now returns a DWORD and receives arguments as reference. This is part of the unmanaged code, and error handling standardization.
- Ignoring response type 0. This is returned when the session does not support message boxes.
- Now supports should process. Previously confirmation was managed by the
UtilitiesLibrary
was completely removed from the project. All functionalities were migrated to the module itself. The 'Wrapper' library now is called 'Core', contained in its own namespace.
This solves cyclical dependency issues, and helps standardize the output objects.
Version 1.3.4 improves interoperability between existing WTS Cmdlets.
Get-ComputerSession
had an additional property on its output object.ComputerName
returns a value when the Cmdlet is run for a remote computer.
This allows the output to be passed to Disconnect-Session.Disconnect-Session
parametersComputerName
andSessionId
now accepts pipeline input by property name.
Get-FileHandle
changed toGet-ObjectHandle
. This Cmdlet also works with directories, and allow future expansion to other system objects.Get-ObjectHandle
FileName
property changed toInputObject
to comply with the Cmdlet scope.
Get-ObjectHandle
was not returning a considerable number of image properties. This was due the Cmdlet using a Shell interface to get properties from the files themselves.
This implementation was replaced by VerQueryValue.
Besides increasing performance, the only cases where properties are not shown is when the image does not contain a resource section, or access denied to the process.
Version 1.3.2 fixes bugs with Get-FileHandle
Cmdlet
- Get-FileHandle accepts wildcard.
Get-FileHandle
crashed when receiving pipeline input fromGet-ChildItem
, only on Windows PowerShell.
Cmdlet was adapted to be "Provider-Aware". Recommendations found on this question.
Version 1.3.1 fixes bugs with the PS help engine.
- Using XmlDoc2CmdletDoc for creating and managing help files.
- Help result not showing proper parameter information, and examples in the wrong category.
Version 1.3.0 fixes major bugs and changes features.
- This project now have a Readme, and a Changelog!
- Adhering to Semantic Versioning.
Get-FileHandle
engine migrated from using the Restart Manager, to using NtQueryInformationFile.- I
nvoke-RemoteMessage
parameter Wait was changed from bool to SwitchParameter.
Get-LastWinSockError
Cmdlet was removed. It is effectively the same as Get-LastWin32Error.