Comience un servicio de Windows y ejecute cmd

¿Debo habilitar Interactt desktp para que funcione y cuál es el código correcto para iniciar una ventana EXE o cmd? Todavía no puedo iniciar el servicio incluso cuando tuve habilitado para interactuar con el escritorio.

Utilizaría un motor de chat para que sea más fácil administrarlo como un servicio de Windows.

¿Qué mal con mi código?

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 } } } 

He pasado todo el dolor de hacer esto.

En Windows 7 / Vista / 2008 no es posible cargar ningún proceso interactivo desde un servicio, sin llamar a una cantidad de Win API. = MAGIA NEGRA

Echa un vistazo aquí y aquí .

El código a continuación es el truco, úsalo bajo tu propio riesgo:

 public static class ProcessAsCurrentUser { ///  /// Connection state of a session. ///  public enum ConnectionState { ///  /// A user is logged on to the session. ///  Active, ///  /// A client is connected to the session. ///  Connected, ///  /// The session is in the process of connecting to a client. ///  ConnectQuery, ///  /// This session is shadowing another session. ///  Shadowing, ///  /// The session is active, but the client has disconnected from it. ///  Disconnected, ///  /// The session is waiting for a client to connect. ///  Idle, ///  /// The session is listening for connections. ///  Listening, ///  /// The session is being reset. ///  Reset, ///  /// The session is down due to an error. ///  Down, ///  /// The session is initializing. ///  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; ///  /// 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)! ///  ///  ///  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 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 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 ListSessions() { IntPtr server = IntPtr.Zero; List ret = new List(); 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; } } 

Cuando se ejecuta como un servicio, no podrá iniciar nada que necesite interactuar con el escritorio O generará sus propias ventanas.

Como dijo Aliostad, debe llamar a Win API calls para CreateProcessAsUser y emular al usuario para que funcione. Esto implica emular al usuario conectado y usar sus credenciales para “elevar” su proceso al nivel de aislamiento de proceso 1 (que le da acceso al sistema de ventanas y cosas como la GPU).

Estoy haciendo esto en una aplicación que escribí y funciona, pero estoy de acuerdo con Aliostad porque está pasando un poco de magia negra y generalmente apesta

Habiendo dicho todo eso, puede engendrar procesos de trabajo dentro de un servicio siempre y cuando no requieran cosas que estén en proceso de aislamiento de nivel 1 (ventanas, GPU, etc.)

cmd.exe por defecto intentará crear una ventana, esta es la razón por la cual su ejemplo está fallando. Puede establecer las siguientes propiedades ProcessStartInfo para que funcione.

CreateNoWindow WindowStyle

Escribí un servicio de vigilancia de aplicaciones que simplemente reinicia una aplicación (en mi caso, una aplicación de ventana de consola).

  1. Encontré un muy buen tutorial práctico de laboratorio (en C ++) que probé en el que funcionaba para el aislamiento de la sesión 0 en: http://msdn.microsoft.com/en-us/Windows7TrainingCourse_Win7Session0Isolation

  2. Convertí esa muestra de C ++ en C #. Después de algunas pruebas, funcionó. Mientras permanezca conectado y no cierre la sesión y vuelva a iniciar sesión, el código funciona perfectamente. Tengo que hacer un poco para atrapar la sesión Logout / Login. Pero para un simple inicio de sesión y condición de trabajo en Windows, el watchdog funciona como se esperaba.

  3. Aquí está el código de PInvoke requerido:

     [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. Aquí está el método encapsulado:

      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); } } 

Espero que sea útil !

La siguiente función lanzará un ejecutable como usuario activo de un servicio de 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(&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); }