從Win服務啟動UI程式
- 2019 年 10 月 4 日
- 筆記
# 從Win服務啟動UI程式
從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 = @"winsta0default"; 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; }