cómo retrasar el apagado y ejecutar un proceso en el servicio de ventana

Tengo que ejecutar un proceso, es decir, una aplicación en el cierre de Windows, ¿hay algún método para retrasar el cierre de Windows y ejecutar la aplicación en el servicio de Windows …

protected override void OnShutdown() { // Add your save code here // Add your save code here StreamWriter str = new StreamWriter("D:\\Log.txt", true); str.WriteLine("Service stoped due to on" + DateTime.Now.ToString()); str.Close(); base.OnShutdown(); } 

He utilizado la función anterior que anula el apagado y pude escribir una entrada de registro en un archivo de texto, pero no pude ejecutar una aplicación después de eso Al buscar encontré que la demora estaba debajo de algunos segundos después de que el usuario se apaga

this.RequestAdditionalTime (250000);

esto proporciona un retraso de tiempo adicional de 25 segundos en el evento de apagado, pero no pude ejecutar la aplicación. ¿Alguien puede sugerir un método o ideas para ejecutar la aplicación al apagarla?

La capacidad de las aplicaciones para bloquear un cierre de sistema pendiente fue severamente restringida en Windows Vista. Los detalles se resumen en dos artículos útiles en MSDN: Cambios de apagado para Windows Vista y cambios de apagado de la aplicación en Windows Vista .

Como indica esa página, no debe confiar en la capacidad de bloquear el apagado por más de 5 segundos. Si desea bloquear un evento de apagado pendiente, su aplicación debe usar la nueva función ShutdownBlockReasonCreate , que le permite registrar una cadena que explica al usuario el motivo por el cual cree que debería bloquearse el apagado. El usuario se reserva la capacidad de seguir sus consejos y cancelar el apagado, o de lanzar precaución al viento y cancelar de todos modos.

Tan pronto como su aplicación termine de hacer lo que sea que no deba ser interrumpido por un apagado, debe llamar a la función correspondiente ShutdownBlockReasonDestroy , que libera la cadena de razones e indica que el sistema ahora puede apagarse.

Recuerde también que los Servicios de Windows ahora se ejecutan en una sesión aislada y tienen prohibido interactuar con el usuario. Mi respuesta aquí también proporciona más detalles, así como un bonito diagtwig.

Básicamente, esto es imposible. Windows luchará con uñas y dientes por iniciar un proceso separado de su Servicio, así como cualquier bash que realice para bloquear un cierre pendiente. En última instancia, el usuario tiene el poder de anular todo lo que intente extraer. Esto suena como algo que debería resolver usando políticas de seguridad, en lugar de una aplicación. Haga preguntas sobre eso en Server Fault .

En Windows Vista SP1 y superior, el nuevo SERVICE_CONTROL_PRESHUTDOWN está disponible. Desafortunadamente, todavía no es compatible con .NET framework, pero aquí está la solución mediante la reflexión. Simplemente herede su clase de servicio de ServicePreshutdownBase , anule OnStop y periódicamente RequestAdditionalTime() . Tenga en cuenta que CanShutdown debe establecerse en false .

 public class ServicePreshutdownBase : ServiceBase { public bool Preshutdown { get; private set; } public ServicePreshutdownBase() { Version versionWinVistaSp1 = new Version(6, 0, 6001); if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= versionWinVistaSp1) { var acceptedCommandsField = typeof (ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic); if (acceptedCommandsField == null) throw new InvalidOperationException("Private field acceptedCommands not found on ServiceBase"); int acceptedCommands = (int) acceptedCommandsField.GetValue(this); acceptedCommands |= 0x00000100; //SERVICE_ACCEPT_PRESHUTDOWN; acceptedCommandsField.SetValue(this, acceptedCommands); } } protected override void OnCustomCommand(int command) { // command is SERVICE_CONTROL_PRESHUTDOWN if (command == 0x0000000F) { var baseCallback = typeof(ServiceBase).GetMethod("ServiceCommandCallback", BindingFlags.Instance | BindingFlags.NonPublic); if (baseCallback == null) throw new InvalidOperationException("Private method ServiceCommandCallback not found on ServiceBase"); try { Preshutdown = true; //now pretend stop was called 0x00000001 baseCallback.Invoke(this, new object[] {0x00000001}); } finally { Preshutdown = false; } } } } 

Aquí hay un ejemplo de uso:

 public partial class Service1 : ServicePreshutdownBase { public Service1() { InitializeComponent(); this.CanShutdown = false; } protected override void OnStop() { WriteLog(Preshutdown ? "Service OnPreshutdown" : "Service OnStop"); for (int i = 0; i < 180; i++) { Thread.Sleep(1000); WriteLog("Service stop in progress..."); RequestAdditionalTime(2000); } WriteLog(Preshutdown ? "Service preshutdown completed" : "Service stop completed"); } } 

Esto funcionará durante 3 minutos y 20 segundos. Si necesita más tiempo, debe configurar el servicio. El mejor lugar para hacerlo es durante la instalación. Simplemente use ServicePreshutdownInstaller lugar de ServiceInstaller y configure PreshutdownTimeout al tiempo máximo que necesite.

 public class ServicePreshutdownInstaller : ServiceInstaller { private int _preshutdownTimeout = 200000; ///  /// Gets or sets the preshutdown timeout for the service. ///  /// ///  /// The preshutdown timeout of the service. The default is 200000ms (200s). ///  [DefaultValue(200000)] [ServiceProcessDescription("ServiceInstallerPreshutdownTimeout")] public int PreshutdownTimeout { get { return _preshutdownTimeout; } set { _preshutdownTimeout = value; } } public override void Install(System.Collections.IDictionary stateSaver) { base.Install(stateSaver); Version versionWinVistaSp1 = new Version(6, 0, 6001); if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version < versionWinVistaSp1) { //Preshutdown is not supported return; } Context.LogMessage(string.Format("Setting preshutdown timeout {0}ms to service {1}", PreshutdownTimeout, ServiceName)); IntPtr service = IntPtr.Zero; IntPtr sCManager = IntPtr.Zero; try { // Open the service control manager sCManager = OpenSCManager(null, null, ServiceControlAccessRights.SC_MANAGER_CONNECT); if (sCManager == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to open Service Control Manager."); // Open the service service = OpenService(sCManager, ServiceName, ServiceAccessRights.SERVICE_CHANGE_CONFIG); if (service == IntPtr.Zero) throw new Win32Exception(); // Set up the preshutdown timeout structure SERVICE_PRESHUTDOWN_INFO preshutdownInfo = new SERVICE_PRESHUTDOWN_INFO(); preshutdownInfo.dwPreshutdownTimeout = (uint)_preshutdownTimeout; // Make the change int changeResult = ChangeServiceConfig2( service, ServiceConfig2InfoLevel.SERVICE_CONFIG_PRESHUTDOWN_INFO, ref preshutdownInfo); // Check that the change occurred if (changeResult == 0) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to change the Service configuration."); } Context.LogMessage(string.Format("Preshutdown timeout {0}ms set to service {1}", PreshutdownTimeout, ServiceName)); } finally { // Clean up if (service != IntPtr.Zero)CloseServiceHandle(service); if (sCManager != IntPtr.Zero)Marshal.FreeHGlobal(sCManager); } } [StructLayout(LayoutKind.Sequential)] public struct SERVICE_PRESHUTDOWN_INFO { public UInt32 dwPreshutdownTimeout; } [Flags] public enum ServiceControlAccessRights : int { SC_MANAGER_CONNECT = 0x0001, // Required to connect to the service control manager. SC_MANAGER_CREATE_SERVICE = 0x0002, // Required to call the CreateService function to create a service object and add it to the database. SC_MANAGER_ENUMERATE_SERVICE = 0x0004, // Required to call the EnumServicesStatusEx function to list the services that are in the database. SC_MANAGER_LOCK = 0x0008, // Required to call the LockServiceDatabase function to acquire a lock on the database. SC_MANAGER_QUERY_LOCK_STATUS = 0x0010, // Required to call the QueryServiceLockStatus function to retrieve the lock status information for the database SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020, // Required to call the NotifyBootConfigStatus function. SC_MANAGER_ALL_ACCESS = 0xF003F // Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table. } [Flags] public enum ServiceAccessRights : int { SERVICE_QUERY_CONFIG = 0x0001, // Required to call the QueryServiceConfig and QueryServiceConfig2 functions to query the service configuration. SERVICE_CHANGE_CONFIG = 0x0002, // Required to call the ChangeServiceConfig or ChangeServiceConfig2 function to change the service configuration. Because this grants the caller the right to change the executable file that the system runs, it should be granted only to administrators. SERVICE_QUERY_STATUS = 0x0004, // Required to call the QueryServiceStatusEx function to ask the service control manager about the status of the service. SERVICE_ENUMERATE_DEPENDENTS = 0x0008, // Required to call the EnumDependentServices function to enumerate all the services dependent on the service. SERVICE_START = 0x0010, // Required to call the StartService function to start the service. SERVICE_STOP = 0x0020, // Required to call the ControlService function to stop the service. SERVICE_PAUSE_CONTINUE = 0x0040, // Required to call the ControlService function to pause or continue the service. SERVICE_INTERROGATE = 0x0080, // Required to call the ControlService function to ask the service to report its status immediately. SERVICE_USER_DEFINED_CONTROL = 0x0100, // Required to call the ControlService function to specify a user-defined control code. SERVICE_ALL_ACCESS = 0xF01FF // Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table. } public enum ServiceConfig2InfoLevel : int { SERVICE_CONFIG_DESCRIPTION = 0x00000001, // The lpBuffer parameter is a pointer to a SERVICE_DESCRIPTION structure. SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002, // The lpBuffer parameter is a pointer to a SERVICE_FAILURE_ACTIONS structure. SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007 // The lpBuffer parameter is a pointer to a SERVICE_PRESHUTDOWN_INFO structure. } [DllImport("advapi32.dll", EntryPoint = "OpenSCManager")] public static extern IntPtr OpenSCManager( string machineName, string databaseName, ServiceControlAccessRights desiredAccess); [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")] public static extern int CloseServiceHandle(IntPtr hSCObject); [DllImport("advapi32.dll", EntryPoint = "OpenService")] public static extern IntPtr OpenService( IntPtr hSCManager, string serviceName, ServiceAccessRights desiredAccess); [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")] public static extern int ChangeServiceConfig2( IntPtr hService, ServiceConfig2InfoLevel dwInfoLevel, ref SERVICE_PRESHUTDOWN_INFO lpInfo); } 

Tengo un problema similar , y hay un truco que podría funcionar en tu caso. Puede iniciar la aplicación en cuestión antes de que se inicie el apagado con el indicador CREATE_SUSPENDED (vea esto ). Esto asegurará que el proceso se creará, pero nunca se ejecutará. Al apagar, puedes reanudar el proceso y continuará con la ejecución.

Tenga en cuenta que es posible que el proceso no se pueda inicializar y ejecutar de todos modos, ya que durante el apagado fallarán algunas funciones del sistema operativo.

Otra implicación es: el proceso que se supone que se ejecutará al apagar se mostrará en el administrador de tareas. Sería posible matar ese proceso.

Aquí hay un artículo sobre Shutdown Event Tracker. Puedes activarlo en Windows XP. Solicita al usuario una razón para el apagado.

espacio de nombres WindowsService1 {[StructLayout (LayoutKind.Sequential)] public struct SERVICE_STATUS {public int serviceType; public int currentState; controles int públicos aceptados; public int win32ExitCode; public int serviceSpecificExitCode; public int checkPoint; public int waitHint; }

 public enum SERVICE_STATE : uint { SERVICE_STOPPED = 0x00000001, SERVICE_START_PENDING = 0x00000002, SERVICE_STOP_PENDING = 0x00000003, SERVICE_RUNNING = 0x00000004, SERVICE_CONTINUE_PENDING = 0x00000005, SERVICE_PAUSE_PENDING = 0x00000006, SERVICE_PAUSED = 0x00000007 } public enum ControlsAccepted { ACCEPT_STOP = 1, ACCEPT_PAUSE_CONTINUE = 2, ACCEPT_SHUTDOWN = 4, ACCEPT_PRESHUTDOWN = 0xf, ACCEPT_POWER_EVENT = 64, ACCEPT_SESSION_CHANGE = 128 } [Flags] public enum SERVICE_CONTROL : uint { STOP = 0x00000001, PAUSE = 0x00000002, CONTINUE = 0x00000003, INTERROGATE = 0x00000004, SHUTDOWN = 0x00000005, PARAMCHANGE = 0x00000006, NETBINDADD = 0x00000007, NETBINDREMOVE = 0x00000008, NETBINDENABLE = 0x00000009, NETBINDDISABLE = 0x0000000A, DEVICEEVENT = 0x0000000B, HARDWAREPROFILECHANGE = 0x0000000C, POWEREVENT = 0x0000000D, SESSIONCHANGE = 0x0000000E } public enum INFO_LEVEL : uint { SERVICE_CONFIG_DESCRIPTION = 0x00000001, SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002, SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 0x00000003, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 0x00000004, SERVICE_CONFIG_SERVICE_SID_INFO = 0x00000005, SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 0x00000006, SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007, SERVICE_CONFIG_TRIGGER_INFO = 0x00000008, SERVICE_CONFIG_PREFERRED_NODE = 0x00000009 } [StructLayout(LayoutKind.Sequential)] public struct SERVICE_PRESHUTDOWN_INFO { public UInt32 dwPreshutdownTimeout; } [Flags] public enum SERVICE_ACCESS : uint { STANDARD_RIGHTS_REQUIRED = 0xF0000, SERVICE_QUERY_CONFIG = 0x00001, SERVICE_CHANGE_CONFIG = 0x00002, SERVICE_QUERY_STATUS = 0x00004, SERVICE_ENUMERATE_DEPENDENTS = 0x00008, SERVICE_START = 0x00010, SERVICE_STOP = 0x00020, SERVICE_PAUSE_CONTINUE = 0x00040, SERVICE_INTERROGATE = 0x00080, SERVICE_USER_DEFINED_CONTROL = 0x00100, SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL) } [Flags] public enum SCM_ACCESS : uint { STANDARD_RIGHTS_REQUIRED = 0xF0000, SC_MANAGER_CONNECT = 0x00001, SC_MANAGER_CREATE_SERVICE = 0x00002, SC_MANAGER_ENUMERATE_SERVICE = 0x00004, SC_MANAGER_LOCK = 0x00008, SC_MANAGER_QUERY_LOCK_STATUS = 0x00010, SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00020, SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS | SC_MANAGER_MODIFY_BOOT_CONFIG } public partial class Service1 : ServiceBase { [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess); [DllImport("advapi32.dll")] internal static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo); [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess); const int SERVICE_ACCEPT_PRESHUTDOWN = 0x100; const int SERVICE_CONTROL_PRESHUTDOWN = 0xf; public Service1() { InitializeComponent(); CanShutdown = true; tim = new Timer(); tim.Interval = 5000; tim.Elapsed += tim_Elapsed; FieldInfo acceptedCommandsFieldInfo = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic); int value = (int)acceptedCommandsFieldInfo.GetValue(this); acceptedCommandsFieldInfo.SetValue(this, value | SERVICE_ACCEPT_PRESHUTDOWN); StreamWriter writer = new StreamWriter("D:\\LogConst.txt", true); try { IntPtr hMngr = OpenSCManager("localhost", null, (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS); IntPtr hSvc = OpenService(hMngr, "WindowsService1", (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS); SERVICE_PRESHUTDOWN_INFO spi = new SERVICE_PRESHUTDOWN_INFO(); spi.dwPreshutdownTimeout = 5000; IntPtr lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(spi)); if (lpInfo == IntPtr.Zero) { writer.WriteLine(String.Format("Unable to allocate memory for service action, error was: 0x{0:X} -- {1}", Marshal.GetLastWin32Error(), DateTime.Now.ToLongTimeString())); } Marshal.StructureToPtr(spi, lpInfo, false); // apply the new timeout value if (!ChangeServiceConfig2(hSvc, (int)INFO_LEVEL.SERVICE_CONFIG_PRESHUTDOWN_INFO, lpInfo)) writer.WriteLine(DateTime.Now.ToLongTimeString() + " Failed to change service timeout"); else writer.WriteLine(DateTime.Now.ToLongTimeString() + " change service timeout : " + spi.dwPreshutdownTimeout); } catch (Exception ex) { writer.WriteLine(DateTime.Now.ToLongTimeString() + " " + ex.Message); } writer.Close(); } void tim_Elapsed(object sender, ElapsedEventArgs e) { result = false; StreamWriter writer = new StreamWriter("D:\\hede.txt", true); writer.WriteLine(DateTime.Now.ToLongTimeString()); //System.Threading.Thread.Sleep(5000); writer.Close(); result = true; tim.Stop(); } Timer tim; bool result = false; protected override void OnStart(string[] args) { RequestAdditionalTime(1000); tim.Start(); } protected override void OnStop() { } protected override void OnCustomCommand(int command) { StreamWriter writer = new StreamWriter("D:\\Log.txt", true); try { if (command == SERVICE_CONTROL_PRESHUTDOWN) { int checkpoint = 1; writer.WriteLine(DateTime.Now.ToLongTimeString()); while (!result) { SERVICE_STATUS myServiceStatus = new SERVICE_STATUS(); myServiceStatus.currentState = (int)SERVICE_STATE.SERVICE_STOP_PENDING; myServiceStatus.serviceType = 16; myServiceStatus.serviceSpecificExitCode = 0; myServiceStatus.checkPoint = checkpoint; SetServiceStatus(this.ServiceHandle, ref myServiceStatus); checkpoint++; } writer.WriteLine(DateTime.Now.ToLongTimeString()); } } catch (Exception ex) { writer.WriteLine(DateTime.Now.ToLongTimeString() + " " + ex.Message); } writer.Close(); base.OnCustomCommand(command); } } 

}