­

从Win服务启动UI程序

  • 2019 年 10 月 4 日
  • 筆記

# 从Win服务启动UI程序

从windows服务启动一个带UI程序的界面,这个需求在xp中是很随意的,从Vista开始似乎没有那么随意了,因为Vista中加入了Session的概念,那么什么是Session,我想这篇文章介绍的应该比我权威的多。Session隔离介绍

明白了Session的概念后,我将通过Win32 API来实现从windows服务启动一个带UI的界面(从Session 0中启动Session *的程序),这个实现过程是我从C++代码翻译过来的。

实现的思路

  1. 找到一个除Session 0之外的活动Session
  2. 通过Session ID获取用户Token
  3. 通过Token来启动UI程序

涉及的Win32 API

  1. WTSGetActiveConsoleSessionId获取活动的Session ID
  2. WTSQueryUserToken根据Session ID获取用户Token
  3. CreateProcessAsUser使用用户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

  1. WTSEnumerateSessions 检索在远程桌面会话主机 (RD 会话主机) 服务器上的会话的列表。
  2. 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;  }