Захват события входа в систему на локальном компьютере без прав администратора. Есть ли API?
Как пользователь без прав администратора, я хочу обнаружить событие, когда другой пользователь входит в систему. Я не могу использовать службу уведомлений о системных событиях (SensLogon2), поскольку для этого требуется, чтобы пользователь входил в группу администраторов. Есть ли другой API или есть определенные разрешения / привилегии, которые я могу предоставить текущему пользователю?
Нам нужно обнаружить, что другой пользователь входит в терминал через RDP, чтобы мы могли изменить состояние приложения, в котором находится текущий пользователь.
1 ответ
Вы можете сделать следующие шаги, чтобы получить информацию об изменениях сессии:
- Вызовите уведомление WTSRegisterSessionNotification с NOTIFY_FOR_ALL_SESSIONS в своей форме, чтобы получить сообщение WM_WTSSESSION_CHANGE
- переопределить void WndProc(ref Message m) формы и выполнить фильтрацию по WM_WTSSESSION_CHANGE (0x2b1)
- Извлечение идентификатора сеанса из LPARAM и события изменения состояния сеанса из WPARAM
- Вызовите WTSQuerySessionInformation с идентификатором сеанса, чтобы получить имя пользователя.
Вот рабочий пример с pInvoke. У меня есть Form1 (WinForm) в моем проекте. Это:
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);
}
}
}
Вот 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;
}
}
}
И последний файл 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);
}
}