[DllImport("wtsapi32.dll", SetLastError=true)]
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);
// This is typically used within a windows service where
// you cannot use a normal call to MessageBox.Show().
MS Docs here: https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtssendmessagew
[DllImport("wtsapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool WTSSendMessage(IntPtr hServer,
uint SessionId,
string pTitle,
uint TitleLength,
string pMessage,
uint MessageLength,
uint Style,
uint Timeout,
out uint pResponse,
bool bWait);
// Useful constants
static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
const uint WTS_CURRENT_SESSION = unchecked((uint)-1);
private enum MsgBox : uint
{
OK = 0x00_00_00_00U,
OKCANCEL = 0x00_00_00_01U,
YESNOCANCEL = 0x00_00_00_03U,
YESNO = 0x00_00_00_04U,
ICONSTOP = 0x00_00_00_10U,
ICONINFORMATION = 0x00_00_00_40U,
SERVICE_NOTIFICATION = 0x00_20_00_00U // interessant ?!
}
private enum ResponseID : uint
{
OK = 1U, // OK
CANCEL = 2U, // Cancel
ABORT = 3U, // Abort
RETRY = 4U, // Retry
IGNORE = 5U, // Ignore
YES = 6U, // Yes
NO = 7U, // No
TRYAGAIN = 10U, // Try Again
CONTINUE = 11U, // Continue
ASYNC = 32001U, // 0x7D01 The bWait parameter was FALSE, so the function returned without waiting for a response
TIMEOUT = 32000U // 0x7D00 Time Out
}
public static string SendMessageToActiveUser(uint sessionId)
{
string strRet;
try
{
const string TITLE = "🕰 👁 🗨 ATTENTION 👋 🗣 👂 IS NEEDED!";
const MsgBox STYLE = MsgBox.YESNO | MsgBox.ICONINFORMATION;
const uint TIMEOUT = 30U; // time to wait in seconds
const bool WAIT = true;
string msg = $"I am going to reboot your machine."
+ $"{Environment.NewLine}"
+ $"Would you like to reboot your system now?"
+ $"{Environment.NewLine}"
+ $"{Environment.NewLine}"
+ $"This message disappears automatically after 30 seconds.";
bool bSucceeded = WTSSendMessage(WTS_CURRENT_SERVER_HANDLE,
sessionId,
TITLE,
(uint)(TITLE.Length * 2), // it needs the number of BYTES
msg,
(uint)(msg.Length * 2), // it needs the number of BYTES
(uint)STYLE,
TIMEOUT,
out uint resp,
WAIT);
if (bSucceeded) // user responded the message box or it was timed out
{
var respAsEnum = (ResponseID)resp;
switch (respAsEnum) // user clicked "No"
{
case ResponseID.NO:
strRet = $"User selected {respAsEnum}";
// write "No" functionality here
Debug.WriteLine(strRet);
break;
case ResponseID.YES:
strRet = $"User selected {respAsEnum}";
// write "Yes" functionality here
Debug.WriteLine(strRet);
break;
case ResponseID.TIMEOUT:
strRet = "User did not select anything (timed out).";
// write the functionality for time out here
Debug.WriteLine(strRet);
break;
default:
strRet = $"Unknown or not properly treated response: {respAsEnum}";
Debug.WriteLine(strRet);
break;
}
strRet += $" USER_SESSION:{sessionId} resp:{respAsEnum}";
Debug.WriteLine(strRet);
}
else
{
int err = Marshal.GetLastWin32Error();
// there was an error, typically 5 (ERROR_ACCESS_DENIED)
strRet = $"There was an error: {err}";
Debug.WriteLine(strRet);
}
}
catch (Exception ex)
{
strRet = $"Unexpected error occurred, {ex}";
Debug.WriteLine(strRet);
// watch out! it can crash the whole service after all
// throw;
}
return strRet;
}
<DllImport("wtsapi32.dll", SetLastError:=True)> _
Private Shared Function WTSSendMessage(ByVal hServer As IntPtr, ByVal SessionId As Int32, ByVal title As String, ByVal titleLength As UInt32, ByVal message As String, ByVal messageLength As UInt32, ByVal style As UInt32, ByVal timeout As UInt32, ByRef pResponse As UInt32, ByVal bWait As Boolean) As Boolean
End Function
$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);
"@
$MessageBox = Add-Type memberDefinition $signature -name "WTSAPISendMessage" -namespace WTSAPI passThru
None.
// Useful constants
public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
public static int WTS_CURRENT_SESSION = -1;
Please add some!
public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
public static int WTS_CURRENT_SESSION = -1;
bool result = false;
String title = "Hello";
int tlen = title.Length;
String msg = "Terminal Service!";
int mlen = msg.Length;
int resp = 0;
result = WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, title, tlen, msg, mlen, 0, 0, out resp, false);
int err = Marshal.GetLastWin32Error();
System.Console.WriteLine("result:{0}, errorCode:{1}, response:{2}", result, err, resp);
<DllImport("wtsapi32.dll", SetLastError:=True)>
Private Shared Function WTSSendMessage(ByVal hServer As IntPtr, ByVal SessionId As Int32, ByVal title As String, ByVal titleLength As UInt32, ByVal message As String, ByVal messageLength As UInt32, ByVal style As UInt32, ByVal timeout As UInt32, ByRef pResponse As UInt32, ByVal bWait As Boolean) As Boolean
End Function
Public Shared WTS_CURRENT_SERVER_HANDLE As IntPtr = IntPtr.Zero
Public Shared WTS_CURRENT_SESSION As Integer = -1
Dim title As String = "MessageBox Title"
Dim content As String = "Hello World!"
''In a Sub/Function, shows MessageBox with exclamation icon.
WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, title, title.Length, content, content.Length, MessageBoxIcon.Exclamation, Nothing, Nothing, False)
#http://technet.microsoft.com/en-us/query/aa383842
#http://technet.microsoft.com/en-us/query/aa383488
Function Send-TSMessageBox
{
param([int]$sessionId = 1, [string]$title = "Title", [string]$message = "Message", [int]$buttonSet = 4, [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)
$response
}
#ABORT 3
#CANCEL 2
#IGNORE 5
#NO 7
#OK 1
#RETRY 4
#YES 6
#ASYNC 32001 (0x7D01)
#The bWait parameter was FALSE, so the function returned without waiting for a response.
#TIMEOUT 32000 (0x7D00)
#The bWait parameter was TRUE and the time-out interval elapsed.
Do you know one? Please contribute it!