
Unfortunately, I was hoping to come up with something purdy, but like my face…. Ugly will just have to do 😛
So the challenge is while we are executing something like a Script from UEM, whilst running as system, how do we send some kind of notification to the user?
This used to be a lot easier pre Vista.. Maybe Vista, can anyone remember that far? not me… Luckily Microsoft didnt remove all methods of doing this, we can still use the WTSSendMessage API to do this.
No I didnt come up with this, I’m not even sure who to credit with the initial work. Googling it will come up with a few sites with similar info.
So my adapted and stolen function looks something like:
Function Send-MessageBox { param([int]$sessionId = 1, [string]$title = $ScriptName, [string]$message = "Message", [int]$buttonSet = 0x00240010L, [int]$timeout = 0, [bool]$waitResponse = $false) $signature = @" [DllImport("wtsapi32.dll", SetLastError = true)] public static extern bool WTSSendMessage( IntPtr hServer, [MarshalAs(UnmanagedType.I4)] int SessionId, String pTitle, [MarshalAs(UnmanagedType.U4)] int TitleLength, String pMessage, [MarshalAs(UnmanagedType.U4)] int MessageLength, [MarshalAs(UnmanagedType.U4)] int Style, [MarshalAs(UnmanagedType.U4)] int Timeout, [MarshalAs(UnmanagedType.U4)] out int pResponse, bool bWait); "@ [int]$titleLength = $title.Length; [int]$messagelength = $message.Length; [int]$response = 0; $MessageBox = Add-Type -memberDefinition $signature -name "WTSAPISendMessage" -namespace WTSAPI -passThru $MessageBox::WTSSendMessage(0, $sessionId, $title, $titleLength, $message, $messageLength, $buttonSet, $timeout, [ref] $response, $waitResponse) }
Example: Send-MessageBox -Title “wsnone.mom” -Message “A bloggity blog” -buttonset 0x00000010L
In my testing, the above function will work when your script is setup like:

You can see there are a bunch of defaults for the parameters of the function:
SessionID: I’ll be honest.. 1 worked for me, I didnt bother looking to much in to it. From what I read, this function can be used on Terminal Services servers, So I guess you would have to determine the SessionID if you want to bug a specific user with a message.
Title & Message: These are obvious…
ButtonSet: This is where things get obvious that I appropriated the code… I wouldnt have called it that (or laid out parameters that way), but here we are… 😀
This is where your button and dialog formatting happens.
You can check out all the different options here: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
From here we have to do Math to get what we want.
In my example dialog above I have an Error Dialog box with only an OK button.
So from the Microsoft Doco:
MB_OK 0x00000000L gives me the OK Button
MB_ICONERROR 0x00000010L gives me the error icon.
0x00000000L
+ 0x00000010L
========
0x00000010L
Not the best example, but you get the idea. If you wanted OK and Cancel with an Exclamation Mark, you would add MB_OKCANCEL and MB_ICONEXCLAMATION toghether to get 0x00000031L
Timeout: How long the box shows before it goes bye bye
waitReponse: Does the script halt until the user chooses an option, this can be handy if you want to do things based on the users button click.
Hopefully this saves someone some time finding a solution.