启动一个Windows服务并启动cmd

我是否需要启用交互式desktp才能正常工作,以及启动EXE或cmd窗口的正确代码是什么? 即使启用它与桌面交互,我仍然无法启动服务。

我会使用一个聊天引擎,所以作为一个Windows服务来pipe理更容易。

我的代码有什么问题?

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceProcess; using System.Diagnostics; using System.ComponentModel; using System.Threading; namespace MyNewService { class Program : ServiceBase { static void Main(string[] args) { } public Program() { this.ServiceName = "Chatter"; } protected override void OnStart(string[] args) { base.OnStart(args); //TODO: place your start code here ThreadStart starter = new ThreadStart(bw_DoWork); Thread t = new Thread(starter); t.Start(); } private void bw_DoWork() { Process p = new Process(); p.StartInfo = new ProcessStartInfo(@"C:\Windows\system32\cmd.exe"); p.Start(); p.WaitForExit(); base.Stop(); } protected override void OnStop() { base.OnStop(); //TODO: clean up any variables and stop any threads } } } 

我已经经历了这一切的痛苦。

在Windows 7 / Vista / 2008下,无法从服务中加载任何交互式进程 – 无需调用多个Win API。 =黑魔法

看看这里和这里 。

下面的代码可以做到这一点,使用它有你自己的风险:

 public static class ProcessAsCurrentUser { /// <summary> /// Connection state of a session. /// </summary> public enum ConnectionState { /// <summary> /// A user is logged on to the session. /// </summary> Active, /// <summary> /// A client is connected to the session. /// </summary> Connected, /// <summary> /// The session is in the process of connecting to a client. /// </summary> ConnectQuery, /// <summary> /// This session is shadowing another session. /// </summary> Shadowing, /// <summary> /// The session is active, but the client has disconnected from it. /// </summary> Disconnected, /// <summary> /// The session is waiting for a client to connect. /// </summary> Idle, /// <summary> /// The session is listening for connections. /// </summary> Listening, /// <summary> /// The session is being reset. /// </summary> Reset, /// <summary> /// The session is down due to an error. /// </summary> Down, /// <summary> /// The session is initializing. /// </summary> Initializing } [StructLayout(LayoutKind.Sequential)] class SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] internal struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } enum LOGON_TYPE { LOGON32_LOGON_INTERACTIVE = 2, LOGON32_LOGON_NETWORK, LOGON32_LOGON_BATCH, LOGON32_LOGON_SERVICE, LOGON32_LOGON_UNLOCK = 7, LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_LOGON_NEW_CREDENTIALS } enum LOGON_PROVIDER { LOGON32_PROVIDER_DEFAULT, LOGON32_PROVIDER_WINNT35, LOGON32_PROVIDER_WINNT40, LOGON32_PROVIDER_WINNT50 } [Flags] enum CreateProcessFlags : uint { CREATE_BREAKAWAY_FROM_JOB = 0x01000000, CREATE_DEFAULT_ERROR_MODE = 0x04000000, CREATE_NEW_CONSOLE = 0x00000010, CREATE_NEW_PROCESS_GROUP = 0x00000200, CREATE_NO_WINDOW = 0x08000000, CREATE_PROTECTED_PROCESS = 0x00040000, CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, CREATE_SEPARATE_WOW_VDM = 0x00000800, CREATE_SHARED_WOW_VDM = 0x00001000, CREATE_SUSPENDED = 0x00000004, CREATE_UNICODE_ENVIRONMENT = 0x00000400, DEBUG_ONLY_THIS_PROCESS = 0x00000002, DEBUG_PROCESS = 0x00000001, DETACHED_PROCESS = 0x00000008, EXTENDED_STARTUPINFO_PRESENT = 0x00080000, INHERIT_PARENT_AFFINITY = 0x00010000 } [StructLayout(LayoutKind.Sequential)] public struct WTS_SESSION_INFO { public int SessionID; [MarshalAs(UnmanagedType.LPTStr)] public string WinStationName; public ConnectionState State; } [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern Int32 WTSEnumerateSessions(IntPtr hServer, int reserved, int version, ref IntPtr sessionInfo, ref int count); [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserW", SetLastError = true, CharSet = CharSet.Auto)] static extern bool CreateProcessAsUser( IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, UInt32 dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("wtsapi32.dll")] public static extern void WTSFreeMemory(IntPtr memory); [DllImport("kernel32.dll")] private static extern UInt32 WTSGetActiveConsoleSessionId(); [DllImport("wtsapi32.dll", SetLastError = true)] static extern int WTSQueryUserToken(UInt32 sessionId, out IntPtr Token); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public extern static bool DuplicateTokenEx( IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, int ImpersonationLevel, int TokenType, out IntPtr phNewToken); private const int TokenImpersonation = 2; private const int SecurityIdentification = 1; private const int MAXIMUM_ALLOWED = 0x2000000; private const int TOKEN_DUPLICATE = 0x2; private const int TOKEN_QUERY = 0x00000008; /// <summary> /// Launches a process for the current logged on user if there are any. /// If none, return false as well as in case of /// /// ##### !!! BEWARE !!! #### ------------------------------------------ /// This code will only work when running in a windows service (where it is really needed) /// so in case you need to test it, it needs to run in the service. Reason /// is a security privileg which only services have (SE_??? something, cant remember)! /// </summary> /// <param name="processExe"></param> /// <returns></returns> public static bool CreateProcessAsCurrentUser(string processExe) { IntPtr duplicate = new IntPtr(); STARTUPINFO info = new STARTUPINFO(); PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION(); Debug.WriteLine(string.Format("CreateProcessAsCurrentUser. processExe: " + processExe)); IntPtr p = GetCurrentUserToken(); bool result = DuplicateTokenEx(p, MAXIMUM_ALLOWED | TOKEN_QUERY | TOKEN_DUPLICATE, IntPtr.Zero, SecurityIdentification, SecurityIdentification, out duplicate); Debug.WriteLine(string.Format("DuplicateTokenEx result: {0}", result)); Debug.WriteLine(string.Format("duplicate: {0}", duplicate)); if (result) { result = CreateProcessAsUser(duplicate, processExe, null, IntPtr.Zero, IntPtr.Zero, false, (UInt32)CreateProcessFlags.CREATE_NEW_CONSOLE, IntPtr.Zero, null, ref info, out procInfo); Debug.WriteLine(string.Format("CreateProcessAsUser result: {0}", result)); } if (p.ToInt32() != 0) { Marshal.Release(p); Debug.WriteLine(string.Format("Released handle p: {0}", p)); } if (duplicate.ToInt32() != 0) { Marshal.Release(duplicate); Debug.WriteLine(string.Format("Released handle duplicate: {0}", duplicate)); } return result; } public static int GetCurrentSessionId() { uint sessionId = WTSGetActiveConsoleSessionId(); Debug.WriteLine(string.Format("sessionId: {0}", sessionId)); if (sessionId == 0xFFFFFFFF) return -1; else return (int)sessionId; } public static bool IsUserLoggedOn() { List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions(); Debug.WriteLine(string.Format("Number of sessions: {0}", wtsSessionInfos.Count)); return wtsSessionInfos.Where(x => x.State == ConnectionState.Active).Count() > 0; } private static IntPtr GetCurrentUserToken() { List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions(); int sessionId = wtsSessionInfos.Where(x => x.State == ConnectionState.Active).FirstOrDefault().SessionID; //int sessionId = GetCurrentSessionId(); Debug.WriteLine(string.Format("sessionId: {0}", sessionId)); if (sessionId == int.MaxValue) { return IntPtr.Zero; } else { IntPtr p = new IntPtr(); int result = WTSQueryUserToken((UInt32)sessionId, out p); Debug.WriteLine(string.Format("WTSQueryUserToken result: {0}", result)); Debug.WriteLine(string.Format("WTSQueryUserToken p: {0}", p)); return p; } } public static List<WTS_SESSION_INFO> ListSessions() { IntPtr server = IntPtr.Zero; List<WTS_SESSION_INFO> ret = new List<WTS_SESSION_INFO>(); try { IntPtr ppSessionInfo = IntPtr.Zero; Int32 count = 0; Int32 retval = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref ppSessionInfo, ref count); Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); Int64 current = (int)ppSessionInfo; if (retval != 0) { for (int i = 0; i < count; i++) { WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO)); current += dataSize; ret.Add(si); } WTSFreeMemory(ppSessionInfo); } } catch (Exception exception) { Debug.WriteLine(exception.ToString()); } return ret; } } 

当作为服务运行时,您将无法启动任何需要与桌面交互的东西,或者会产生自己的窗口。

正如Aliostad所说,你需要调用Win API调用CreateProcessAsUser并模拟用户才能使其工作。 这包括模拟已login的用户并使用他们的凭据将您的进程“升级”为进程隔离级别1(可让您访问窗口系统以及像GPU这样的东西)。

我在一个我写的应用程序中这样做,它确实有效,但我同意Aliostad有一些黑魔法正在进行,它通常很烂

说了这么多,你可以从一个服务中产生工作进程,因为他们不需要进程隔离级别1(窗口,GPU等)的东西。

cmd.exe默认会尝试创build一个窗口,这就是为什么你的例子失败。 您可以设置下面的ProcessStartInfo属性来使其工作。

CreateNoWindow WindowStyle

我写了一个应用程序看门狗服务,它只是重新启动应用程序(在我的情况下是一个控制台窗口应用程序)

  1. 我发现了一个非常好的动手实验教程(用C ++),我尝试了它在Session 0 Isolation上工作: http : //msdn.microsoft.com/en-us/Windows7TrainingCourse_Win7Session0Isolation

  2. 我将该C ++示例转换为C#。 经过几次testing,它的工作。 只要我保持login状态,不要再注销,并重新login,代码工作完美。 我必须做一些赶上会议注销/login。 但是,对于Windows中的简单login和工作条件,看门狗按预期工作。

  3. 这是所需的PInvoke代码:

     [StructLayout(LayoutKind.Sequential)] public struct STARTUPINFO { public int 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 short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } public enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } public enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public extern static bool CloseHandle(IntPtr handle); [DllImport("kernel32.dll")] public static extern uint WTSGetActiveConsoleSessionId(); [DllImport("wtsapi32.dll", SetLastError = true)] public static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token); [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment, String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public extern static bool DuplicateTokenEx( IntPtr hExistingToken, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, out IntPtr phNewToken); 
  4. 这是封装的方法:

      private void CreateUserProcess() { bool ret; SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); uint dwSessionID = WTSGetActiveConsoleSessionId(); this.EventLog.WriteEntry("WTSGetActiveConsoleSessionId: " + dwSessionID, EventLogEntryType.FailureAudit); IntPtr Token = new IntPtr(); ret = WTSQueryUserToken((UInt32)dwSessionID, out Token); if (ret == false) { this.EventLog.WriteEntry("WTSQueryUserToken failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit); } const uint MAXIMUM_ALLOWED = 0x02000000; IntPtr DupedToken = IntPtr.Zero; ret = DuplicateTokenEx(Token, MAXIMUM_ALLOWED, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out DupedToken); if (ret == false) { this.EventLog.WriteEntry("DuplicateTokenEx failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit); } else { this.EventLog.WriteEntry("DuplicateTokenEx SUCCESS", EventLogEntryType.SuccessAudit); } STARTUPINFO si = new STARTUPINFO(); si.cb = Marshal.SizeOf(si); //si.lpDesktop = ""; string commandLinePath; // commandLinePath example: "c:\myapp.exe c:\myconfig.xml" . cmdLineArgs can be ommited commandLinePath = AppPath + " " + CmdLineArgs; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); //CreateProcessAsUser(hDuplicatedToken, NULL, lpszClientPath, NULL, NULL, FALSE, // 0, // NULL, NULL, &si, &pi) ret = CreateProcessAsUser(DupedToken, null, commandLinePath, ref sa, ref sa, false, 0, (IntPtr)0, null, ref si, out pi); if (ret == false) { this.EventLog.WriteEntry("CreateProcessAsUser failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit); } else { this.EventLog.WriteEntry("CreateProcessAsUser SUCCESS. The child PID is" + pi.dwProcessId, EventLogEntryType.SuccessAudit); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } ret = CloseHandle(DupedToken); if (ret == false) { this.EventLog.WriteEntry("CloseHandle LastError: " + Marshal.GetLastWin32Error(), EventLogEntryType.Error); } else { this.EventLog.WriteEntry("CloseHandle SUCCESS", EventLogEntryType.Information); } } 

我希望它是有用的!

下面的函数将从Windows服务启动一个可执行文件作为活动用户。

 //Function to run a process as active user from windows service void ImpersonateActiveUserAndRun(WCHAR* path, WCHAR* args) { DWORD session_id = -1; DWORD session_count = 0; WTS_SESSION_INFOA *pSession = NULL; if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count)) { //log success } else { //log error return; } for (int i = 0; i < session_count; i++) { session_id = pSession[i].SessionId; WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected; WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL; DWORD bytes_returned = 0; if (::WTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, session_id, WTSConnectState, reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state), &bytes_returned)) { wts_connect_state = *ptr_wts_connect_state; ::WTSFreeMemory(ptr_wts_connect_state); if (wts_connect_state != WTSActive) continue; } else { //log error continue; } HANDLE hImpersonationToken; if (!WTSQueryUserToken(session_id, &hImpersonationToken)) { //log error continue; } //Get real token from impersonation token DWORD neededSize1 = 0; HANDLE *realToken = new HANDLE; if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1)) { CloseHandle(hImpersonationToken); hImpersonationToken = *realToken; } else { //log error continue; } HANDLE hUserToken; if (!DuplicateTokenEx(hImpersonationToken, //0, //MAXIMUM_ALLOWED, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hUserToken)) { //log error continue; } // Get user name of this process //LPTSTR pUserName = NULL; WCHAR* pUserName; DWORD user_name_len = 0; if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len)) { //log username contained in pUserName WCHAR string } //Free memory if (pUserName) WTSFreeMemory(pUserName); ImpersonateLoggedOnUser(hUserToken); STARTUPINFOW StartupInfo; GetStartupInfoW(&StartupInfo); StartupInfo.cb = sizeof(STARTUPINFOW); //StartupInfo.lpDesktop = "winsta0\\default"; PROCESS_INFORMATION processInfo; SECURITY_ATTRIBUTES Security1; Security1.nLength = sizeof SECURITY_ATTRIBUTES; SECURITY_ATTRIBUTES Security2; Security2.nLength = sizeof SECURITY_ATTRIBUTES; void* lpEnvironment = NULL; // Get all necessary environment variables of logged in user // to pass them to the new process BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE); if (!resultEnv) { //log error continue; } WCHAR PP[1024]; //path and parameters ZeroMemory(PP, 1024 * sizeof WCHAR); wcscpy(PP, path); wcscat(PP, L" "); wcscat(PP, args); // Start the process on behalf of the current user BOOL result = CreateProcessAsUserW(hUserToken, NULL, PP, //&Security1, //&Security2, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, //lpEnvironment, NULL, //"C:\\ProgramData\\some_dir", NULL, &StartupInfo, &processInfo); if (!result) { //log error } else { //log success } DestroyEnvironmentBlock(lpEnvironment); CloseHandle(hImpersonationToken); CloseHandle(hUserToken); CloseHandle(realToken); RevertToSelf(); } WTSFreeMemory(pSession); }