You can do next steps to get info about session changes:
- Call WTSRegisterSessionNotification with NOTIFY_FOR_ALL_SESSIONS on your form to receive WM_WTSSESSION_CHANGE message
- override void WndProc(ref Message m) of the Form and filtering by WM_WTSSESSION_CHANGE (0x2b1)
- Extract session id from LPARAM and session state change event from WPARAM
- Call WTSQuerySessionInformation with session id to get username
Here is working example with pInvoke. I have Form1 (WinForm) in my project. It is:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MessageLoop
{
public partial class Form1 : Form
{
/// <summary>
/// WM_WTSSESSION_CHANGE message number for filtering in WndProc
/// </summary>
private const int WM_WTSSESSION_CHANGE = 0x2b1;
public Form1()
{
InitializeComponent();
NativeWrapper.WTSRegisterSessionNotification(this, SessionNotificationType.NOTIFY_FOR_ALL_SESSIONS);
}
protected override void OnClosing(CancelEventArgs e)
{
NativeWrapper.WTSUnRegisterSessionNotification(this);
base.OnClosing(e);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_WTSSESSION_CHANGE)
{
int eventType = m.WParam.ToInt32();
int sessionId = m.LParam.ToInt32();
WtsSessionChange reason = (WtsSessionChange)eventType;
Trace.WriteLine(string.Format("SessionId: {0}, Username: {1}, EventType: {2}",
sessionId, NativeWrapper.GetUsernameBySessionId(sessionId), reason));
}
base.WndProc(ref m);
}
}
}
Here is NativeWrapper.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MessageLoop
{
public enum WtsSessionChange
{
WTS_CONSOLE_CONNECT = 1,
WTS_CONSOLE_DISCONNECT = 2,
WTS_REMOTE_CONNECT = 3,
WTS_REMOTE_DISCONNECT = 4,
WTS_SESSION_LOGON = 5,
WTS_SESSION_LOGOFF = 6,
WTS_SESSION_LOCK = 7,
WTS_SESSION_UNLOCK = 8,
WTS_SESSION_REMOTE_CONTROL = 9,
WTS_SESSION_CREATE = 0xA,
WTS_SESSION_TERMINATE = 0xB
}
public enum SessionNotificationType
{
NOTIFY_FOR_THIS_SESSION = 0,
NOTIFY_FOR_ALL_SESSIONS = 1
}
public static class NativeWrapper
{
public static void WTSRegisterSessionNotification(Control control, SessionNotificationType sessionNotificationType)
{
if (!Native.WTSRegisterSessionNotification(control.Handle, (int)sessionNotificationType))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public static void WTSUnRegisterSessionNotification(Control control)
{
if (!Native.WTSUnRegisterSessionNotification(control.Handle))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public static string GetUsernameBySessionId(int sessionId)
{
IntPtr buffer;
int strLen;
var username = "SYSTEM"; // assume SYSTEM as this will return "\0" below
if (Native.WTSQuerySessionInformation(IntPtr.Zero, sessionId, Native.WTS_INFO_CLASS.WTSUserName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer); // don't need length as these are null terminated strings
Native.WTSFreeMemory(buffer);
if (Native.WTSQuerySessionInformation(IntPtr.Zero, sessionId, Native.WTS_INFO_CLASS.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer) + "\\" + username; // prepend domain name
Native.WTSFreeMemory(buffer);
}
}
return username;
}
}
}
And the last file is Native.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace MessageLoop
{
public static class Native
{
public enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType,
WTSIdleTime,
WTSLogonTime,
WTSIncomingBytes,
WTSOutgoingBytes,
WTSIncomingFrames,
WTSOutgoingFrames,
WTSClientInfo,
WTSSessionInfo
}
[DllImport("wtsapi32.dll", SetLastError = true)]
internal static extern bool WTSRegisterSessionNotification(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] int dwFlags);
[DllImport("wtsapi32.dll", SetLastError = true)]
internal static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);
[DllImport("Wtsapi32.dll")]
internal static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
internal static extern void WTSFreeMemory(IntPtr pointer);
}
}