如何延迟关机并在窗口服务中运行进程
我必须运行一个进程,即在Windowsclosures应用程序,有没有什么办法来延迟closuresWindows窗口,并在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(); }
我已经使用上面的函数覆盖关机,我能够写入文本文件的日志条目,但我不能运行一个应用程序之后,在search我发现延迟低于用户closures后几秒钟关机
this.RequestAdditionalTime(250000);
这给关机事件增加了25秒的时间延迟,但我无法运行应用程序。 任何人都可以build议方法或想法来closures运行应用程序。
在Windows Vista中,应用程序阻止挂起的系统closures的能力受到严重限制。 详细信息汇总在MSDN的两个方便的文章中: Windows Vista的关机更改和Windows Vista中的 应用程序关机更改 。
如该页面所示,您不应该依赖阻止closures超过5秒的能力。 如果您希望尝试阻止未决的closures事件,则应用程序应使用新的ShutdownBlockReasonCreate
函数 ,该函数允许您注册一个string,向用户解释为什么您认为应closures关机的原因。 用户有能力听取您的build议,并取消关机,或警惕风和取消。
一旦应用程序完成了不应该被关机中断的任何事情,您应该调用相应的ShutdownBlockReasonDestroy
函数 ,该函数释放原因string,并指示系统现在可以closures。
另外请记住, Windows服务现在在隔离的会话中运行,并禁止与用户交互。 我在这里的答案也提供了更多的细节,以及一个漂亮的图表。
基本上这是不可能的。 Windows将打击你和你的服务开始一个单独的进程,以及任何你尝试阻止挂起关机。 最终,用户有权覆盖任何你想拉的东西。 这听起来像是应该使用安全策略而不是应用程序来解决的问题 – 在服务器故障上提出问题。
在Windows Vista SP1和更高版本上,新的SERVICE_CONTROL_PRESHUTDOWN可用。 不幸的是,它不支持.NET框架,但是这里是使用reflection的解决方法。 只需从ServicePreshutdownBase
inheritance您的服务类,重写OnStop
并定期调用RequestAdditionalTime()
。 请注意, CanShutdown
应设置为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; } } } }
以下是示例用法:
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"); } }
这将工作3分20秒,如果你需要更多的时间,那么你需要configuration服务。 最好的地方是在安装过程中。 只需使用ServicePreshutdownInstaller
而不是ServiceInstaller
,并将PreshutdownTimeout
设置为您将需要的最长时间。
public class ServicePreshutdownInstaller : ServiceInstaller { private int _preshutdownTimeout = 200000; /// <summary> /// Gets or sets the preshutdown timeout for the service. /// </summary> /// /// <returns> /// The preshutdown timeout of the service. The default is 200000ms (200s). /// </returns> [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); }
我有一个类似的问题 ,有一个技巧可以在你的情况下工作。 您可以在使用CREATE_SUSPENDED标志启动closures之前启动应用程序(请参阅此 )。 这将确保该过程将被创build,但从不运行。 在关机时,您可以处理ResumeThread ,并继续执行。
请注意,该进程可能无法初始化并运行,因为在closures期间,一些OSfunction将失败。
另一个含义是:应该在关机时运行的进程将显示在任务pipe理器中。 杀死这个过程是可能的。
这里是closures事件跟踪器的文章 。 您可以在Windows XP中激活它。 它会提示用户closures的原因。
命名空间WindowsService1 {[StructLayout(LayoutKind.Sequential)] public struct SERVICE_STATUS {public int serviceType; public int currentState; public int controlsAccepted; 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); } }
}