如何延迟关闭并在窗口服务中运行进程

dee*_*epu 7 .net c# windows-services winforms

我必须运行一个进程,即Windows关闭时的应用程序,是否有任何方法来延迟Windows关闭并在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();
}
Run Code Online (Sandbox Code Playgroud)

我使用上面的函数覆盖了关闭,我能够写一个日志条目到文本文件,但我之后无法运行应用程序在搜索时我发现延迟低于用户激活关闭后的几秒钟

this.RequestAdditionalTime(250000);

这给关闭事件添加时间延迟25秒,但我无法运行应用程序.任何人都可以提出方法或想法在关机时运行应用程序.

Cod*_*ray 11

在Windows Vista中,应用程序阻止挂起的系统关闭的能力受到严格限制.详情概括在MSDN上的2篇得心应手文章:关闭更改为Windows VistaWindows Vista中的应用程序关闭的变化.

如该页面所示,您不应该依赖于阻止关机超过5秒的能力.如果您希望尝试阻止挂起的关闭事件,您的应用程序应该使用新ShutdownBlockReasonCreate功能,该功能允许您注册一个字符串,向用户解释您认为应该阻止关闭的原因.用户保留注意您的建议并取消关机的能力,或者保持警惕并取消.

一旦您的应用程序完成不应该被关闭中断的任何操作,您应该调用相应的ShutdownBlockReasonDestroy函数,该函数释放原因字符串并指示系统现在可以关闭.

还要记住,Windows服务现在在隔离的会话中运行,并且禁止与用户交互.我在这里的答案还提供了更多细节,以及一个漂亮的图表.

基本上,这是不可能的.Windows将为您在服务中启动一个单独的进程以及阻止挂起关闭的任何尝试而斗争.最终,用户可以覆盖您尝试拉动的任何内容.这听起来像是你应该使用安全策略解决的问题,而不是关于服务器故障的应用程序问题.

  • @David:如果我们允许*user*控制他们的机器,那么成为程序员有什么意义?无聊. (10认同)
  • +1当用户说要关机时,您现在需要尊重这个愿望. (3认同)

pub*_*pub 5

在 Windows Vista SP1 及更高版本上,新的 SERVICE_CONTROL_PRESHUTDOWN 可用。不幸的是,.NET 框架尚不支持它,但这里是使用反射的解决方法。只需从 继承您的服务类ServicePreshutdownBase,覆盖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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是示例用法:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这将工作 3 分 20 秒,如果您需要更多时间,则需要配置服务。最好的地方是在安装期间。只需使用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);
}
Run Code Online (Sandbox Code Playgroud)