【问题标题】:how to delay shutdown and run a process in window service如何延迟关闭并在窗口服务中运行进程
【发布时间】:2011-03-07 08:02:01
【问题描述】:

我必须在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();
}

我使用了上面的函数来覆盖关机,并且我能够将日志条目写入文本文件,但之后我无法运行应用程序在搜索时我发现延迟仅在用户之后几秒钟以下触发关机

this.RequestAdditionalTime(250000);

这会在关闭事件时增加 25 秒的时间延迟,但我无法运行该应用程序。任何人都可以建议在关闭时运行应用程序的方法或想法。

【问题讨论】:

  • 当用户确认他的关机请求后延迟关机通常不是一个好主意。此外,Windows 在关机期间拒绝各种操作。其中一项操作是启动 Windows 服务。要让您的流程真正开始,您可能需要大量的工程和黑客攻击。
  • 您需要重新设计您的系统 - 在 Windows 想要关闭时尝试启动新进程似乎是一个相当糟糕的设计 - 如果 Windows 崩溃并重新启动,您的系统处于什么状态?
  • 那么我可以在窗口服务中中止关闭并在应用程序退出时打开应用程序关闭系统..
  • @deepu:不,你不能。没有可靠的方法来中止关机,并且 Windows 服务无法生成用户模式应用程序。我的回答提供了更多细节,但总结是您需要找到不同的方法。也许如果您解释了您要完成的工作,而不是您提出的解决方案,我们可以给您一些更好的建议。
  • @CodyGray:感谢您在下面的回答......我将简要描述我想要完成的工作......我建议制作一个应用程序来记录用户登录和关闭系统的时间视窗XP。我能够在启动时运行应用程序,我可以在其中获取系统运行时间的详细信息到 Db,但我必须在关闭时执行应用程序,以记录系统何时关闭以及关闭的原因是什么。我在 c# 中完成了一个 .net 窗口应用程序,并使用窗口服务调用了该应用程序。请提出一些建议以使其锻炼

标签: c# .net winforms windows-services


【解决方案1】:

应用程序阻止挂起的系统关闭的能力在 Windows Vista 中受到严格限制。详细信息总结在 MSDN 上的两篇方便的文章中:Shutdown Changes for Windows VistaApplication Shutdown Changes in Windows Vista

正如该页面所示,您不应依赖阻止关机超过 5 秒的功能。如果您希望尝试阻止挂起的关闭事件,您的应用程序应该使用新的ShutdownBlockReasonCreate function,它允许您注册一个字符串,向用户解释您认为应该阻止关闭的原因。用户保留听从您的建议并取消关闭的能力,或者抛开警告并无论如何取消的能力。

一旦你的应用程序完成任何不应该被关机中断的事情,你应该调用相应的ShutdownBlockReasonDestroy function,它会释放原因字符串并表明系统现在可以被关闭。

还要记住Windows Services now run in an isolated session 和被禁止与用户交互。 My answer here 还提供了更多细节,以及漂亮的图表。

基本上,这是不可能的。 Windows 将竭尽全力为您启动与您的服务不同的进程,以及您为阻止挂起的关闭所做的任何尝试。最终,用户有权覆盖您尝试拉取的任何内容。这听起来像是您应该使用安全策略而不是应用程序来解决的问题 - 请在 Server Fault 上提问。

【讨论】:

  • +1 当用户说他们想关机时,你现在需要尊重这个愿望。
  • @David:如果我们允许用户控制他们的机器,那么作为程序员的意义何在?无聊。
【解决方案2】:

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

这里是示例用法:

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 秒,如果您需要更多时间,则需要配置服务。最好的地方是在安装过程中。只需使用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);
}

【讨论】:

    【解决方案3】:

    我有一个similar problem,有一个技巧可能适用于您的情况。您可以在之前使用 CREATE_SUSPENDED 标志启动有问题的应用程序(请参阅this)。这将确保该进程将被创建,但永远不会运行。关闭时您可以ResumeThread该进程,它将继续执行。

    请注意,该进程可能无法初始化和运行,因为在关闭期间某些操作系统功能会失败。

    另一个含义是:应该在关机时运行的进程将显示在任务管理器中。有可能杀死该进程。

    【讨论】:

    • 在这个场景中,你如何在 C# 中恢复那个线程?因为创建的进程我看不到对该线程的访问。
    • @StevenHernandez 我认为纯粹在 c# 中是不可能的。如果您想从 .NET 执行此操作,您将需要使用 c++ sn-p 或使用 p-invoke。看这里的例子:pinvoke.net/default.aspx/kernel32/ResumeThread.html
    • 是的,通过 p/invoke 我的意思是因为我知道完全通过 .Net 是不可能的。
    • 但是谢谢你忘了提到我让它与 p/invoke 一起工作。然而它并没有解决我的问题。我需要能够停止关机/重新启动/会话结束,以停止我的进程的操作并恢复关机/重新启动/会话结束。
    • my problem... 也许你能帮帮我。
    【解决方案4】:

    这是关机事件跟踪器上的article。您可以在 Windows XP 中激活它。它会提示用户关闭的原因。

    【讨论】:

    • @MPelletier :我不想知道是什么错误导致系统关闭。实际上我的意思是当用户正常关闭系统时,我的应用程序将在用户给出原因的地方运行关闭系统,例如“我必须参加会议”、“午休”、“去办公室完成家庭作业”等希望你明白。
    • 我认为这就是 Shutdown Event Tracker 的用途,不是错误,而是记录任何关闭。
    • 话虽如此,我永远不想在有这样一个系统的地方工作。
    • @MPelletier: 是的,它报告了所有的关机事件。但我的意图是在关机时加载一个应用程序.. :)
    • @MPelletier:是的,我完全同意你的评论......但必须看到这些事情是否可能在关闭之前运行应用程序。
    【解决方案5】:
    namespace 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);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-09-23
      • 1970-01-01
      • 1970-01-01
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      • 2021-08-25
      • 1970-01-01
      相关资源
      最近更新 更多