As a non-admin user, I want to detect the event when another user logs in. I cannot use System Event Notification Service (SensLogon2) since it requires the user to be part of the Administrators group. Is there another API or are there certain permission/privileges that I can grant to the current user?
We need to detect another user logging on to the terminal via RDP so that we can change the application state the current user is in.
You can do next steps to get info about session changes:
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);
}
}