从windows服务启动一个带UI程序的界面,这个需求在xp中是很随意的,从Vista开始似乎没有那么随意了,因为Vista中加入了Session的概念,那么什么是Session,我想这篇文章介绍的应该比我权威的多。Session隔离介绍
明白了Session的概念后,我将通过Win32 API来实现从windows服务启动一个带UI的界面(从Session 0中启动Session *的程序)
,这个实现过程是我从C++代码翻译过来的。
实现的思路
- 找到一个除Session 0之外的活动Session
- 通过Session ID获取用户Token
- 通过Token来启动UI程序
涉及的Win32 API
WTSGetActiveConsoleSessionId
获取活动的Session IDWTSQueryUserToken
根据Session ID获取用户TokenCreateProcessAsUser
使用用户Token来启动UI程序
public class ProcessAsUser { public struct SECURITY_ATTRIBUTES { public uint nLength; public uint lpSecurityDescriptor; public bool bInheritHandle; } public struct STARTUPINFO { public uint cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public ushort wShowWindow; public ushort cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } [DllImport("kernel32.dll")] static extern uint WTSGetActiveConsoleSessionId(); [DllImport("Wtsapi32.dll")] private static extern bool WTSQueryUserToken(uint SessionId, out uint hToken); [DllImport("Kernel32.dll")] private static extern uint GetLastError(); [DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr hSnapshot); [DllImport("advapi32.dll")] public extern static bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, uint dwCreationFlags, uint lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); public static bool StartUIProcessFromService(string exePath) { //获取Session ID var sId=WTSGetActiveConsoleSessionId(); if (sId == 0) { return false; } uint hToken; var isOk=WTSQueryUserToken(sId, out hToken); if (!isOk || hToken == 0) { return false; } var lpProcessAttr = new SECURITY_ATTRIBUTES(); lpProcessAttr.nLength = (uint)Marshal.SizeOf(lpProcessAttr); var lpThreadAttr = new SECURITY_ATTRIBUTES(); lpThreadAttr.nLength = (uint)Marshal.SizeOf(lpThreadAttr); var lpStratupInfo = new STARTUPINFO(); lpStratupInfo.cb = (uint)Marshal.SizeOf(lpStratupInfo); lpStratupInfo.lpDesktop = @"winsta0\default"; PROCESS_INFORMATION lpProcessInfo; isOk=CreateProcessAsUser((IntPtr)hToken, exePath, null, ref lpProcessAttr, ref lpThreadAttr, false, 0, 0, null, ref lpStratupInfo, out lpProcessInfo ); CloseHandle((IntPtr)hToken); return isOk; } }
枚举活动Session ID
之前我们通过WTSGetActiveConsoleSessionId
获取活动Session ID,当有多个用户登录时,Windows提供了WTSEnumerateSessions
方法枚举多个Session ID。
主要涉及API
WTSEnumerateSessions
检索在远程桌面会话主机 (RD 会话主机) 服务器上的会话的列表。WTSFreeMemory
释放由远程桌面服务函数分配的内存。
实现代码
[DllImport("Wtsapi32.dll")] private static extern void WTSFreeMemory(IntPtr pSessionInfo); [DllImport("Wtsapi32.dll")] private extern static bool WTSEnumerateSessions(IntPtr hServer, uint reserved, uint version, out IntPtr ppSessionInfo, out uint pCount); struct WTS_SESSION_INFO { public uint SessionId; public string pWinStationName; public WTS_CONNECTSTATE_CLASS State; } enum WTS_CONNECTSTATE_CLASS { WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit } private static uint EnumerateActiveSession() { uint dwSessionID = 0xFFFFFFFF; uint dwCount = 0; IntPtr intPtr = IntPtr.Zero; try { IntPtr hServer = IntPtr.Zero; if (WTSEnumerateSessions(hServer, 0, 1, out intPtr, out dwCount)) { var tmp = intPtr; for (var i = 0; i < dwCount; ++i) { var pSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(tmp, typeof(WTS_SESSION_INFO)); if (WTS_CONNECTSTATE_CLASS.WTSActive == pSessionInfo.State) { dwSessionID = pSessionInfo.SessionId; break; } if (WTS_CONNECTSTATE_CLASS.WTSConnected == pSessionInfo.State) { dwSessionID = pSessionInfo.SessionId; } tmp += Marshal.SizeOf(typeof(WTS_SESSION_INFO)); } WTSFreeMemory(intPtr); } var eCode = GetLastError(); } catch (Exception ex) { var eCode = GetLastError(); } return dwSessionID; }