【问题标题】:How to add PreShutdown event to ATL service?如何将 PreShutdown 事件添加到 ATL 服务?
【发布时间】:2016-10-19 15:43:04
【问题描述】:

我继承了旧 ATL 服务的 C++ 代码,其中包含旧 ATL 向导的基本结构(_tWinMain()、Run()、RunMessageLoop() 等)。

特别是,这段代码已经处理了“关闭”和“停止”事件 - 我被要求在“PreShutdown”事件中添加一个额外的关闭项目。

我添加了 SERVICE_ACCEPT_PRESHUTDOWN 选项..

m_status.dwControlsAccepted |= SERVICE_ACCEPT_PRESHUTDOWN

..但是我看不到如何捕获结果事件 - SERVICE_CONTROL_PRESHUTDOWN - 因为 ATL 似乎提供了“OnShutdown”和“OnClose”方法,但不包含我可以看到的“OnPreShutdown”条目。

谁能指出我可以在现有代码中添加 OnPreShutdown 的内容?

【问题讨论】:

    标签: c++ winapi service atl


    【解决方案1】:

    ATL Services 介绍了使用 ATL 实现服务。要处理默认 ATL 生成的服务无法处理的控制请求,您需要扩展默认生成的 Handler 实现。

    这是你需要做的粗略草图(除了注册控制请求,你已经是):

    void CMyServiceModule::Handler(DWORD dwOpcode) throw() {
        if (SERVICE_CONTROL_PRESHUTDOWN == dwOpcode) {
            // Perform custom handling
        }
        else {
            // Pass all other control codes to default implementation
            __super::Handler(dwOpcode);
        }
    }
    

    作为替代方案,您可以在自定义实现中覆盖 OnUnknownRequest 成员。对于默认实现未处理的所有代码都会调用此成员:

    void CMyServiceModule::OnUnknownRequest(DWORD dwOpcode) {
        if (SERVICE_CONTROL_PRESHUTDOWN == dwOpcode) {
            // Perform custom handling
        }
        else {
            // Possibly handle other control codes
        }
    }
    

    【讨论】:

    • 首先,感谢您的回答。其次,我已经尝试完全按照您上面描述的方式覆盖处理程序,但从未进入“自定义处理”块。看起来使用 PRESHUTDOWN 可能还需要覆盖 ServiceMain,因为它嵌入了 RegisterServiceCtrlHandler 而不是 RegisterServiceCtrlHandlerEx (这似乎是 preshutdown 控制消息所必需的)。我将仔细查看 OnUnknownRequest 以查看是否可以在那里收到消息。
    • 我刚刚确认我也没有在 OnUnknownRequest 中收到预关机控制消息。 (可能是因为 ATL 通过 RegisterServiceCtrlHandler 使用 Handler 而不是通过 RegisterServiceCtrlHandlerEx 使用 HandlerEx,但我不是 100% 确定。)
    • @Nick:这在documentation 中并不完全清楚,但您必须在构造函数中添加m_status.dwControlsAccepted |= SERVICE_ACCEPT_PRESHUTDOWN;。在任何其他时间设置它可能为时已晚。既然您在问题中没有提及,那么您在哪里更新了m_status 成员?
    • 之前的(工作)代码在被覆盖的 PreMessageLoop() 中使用 m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,紧接在信号 SetServiceStatus(SERVICE_START_PENDING) 之前。我只是将此行更改为m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN。 (设置此项仅发生在此服务代码中的这一行中。)
    • @Nick:为了安全起见,尽量把它放在构造函数中,以免在完全初始化之前无意中使用它。
    【解决方案2】:

    主要问题是ATL使用RegisterServiceCtrlHandler而不是RegisterServiceCtrlHandlerEx;其中只有第二个实际注册了SERVICE_CONTROL_PRESHUTDOWN 消息。此外,ATL 不包含 OnPreShutdown() 的可继承模板。

    因此解决方案分为四个部分:

    (1) 更新接受的控制消息列表以包含SERVICE_ACCEPTED_PRESHUTDOWN 消息。您可以删除SERVICE_ACCEPTED_SHUTDOWN,因为一旦您处理了 Pre-Shutdown,Windows 就会认为该服务将被关闭,因此您将永远不会收到此消息。在我的情况下,更改是在覆盖的 PreMessageLoop() 方法中设置的控件列表:

    我改了:m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

    收件人:m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN;

    但是,您可能没有覆盖此方法 - 因此您应该在设置此值的任何时候简单地更改该列表。 (IInspectable(见其他答案)建议考虑构造函数。)

    (2) 覆盖ServiceMain(),通过克隆代码并将其更新为使用RegisterServiceCtrlHandlerEx 来完全替换它。关键变化是:

    更改:m_hServiceStatus = RegisterServiceCtrlHandler(m_szServiceName, _Handler);

    收件人:m_hServiceStatus = RegisterServiceCtrlHandlerEx(m_szServiceName, (LPHANDLER_FUNCTION_EX)_Handler, NULL);

    但您还需要将对 pTT 的引用更改为 this 以删除模板代码(您也可以删除定义 pT 的行);例如:

    更改:hr = T::InitializeCom();

    收件人:hr = this->InitializeCom();

    包括对基类的调用(因为这将包括对RegisterServiceCtrlHandler 的调用)。

    (3) 覆盖和扩展Handler()。这需要添加新代码,例如:

    void CServiceModule::Handler(_In_ DWORD dwOpcpde) throw()
    {
        switch (dwOpcode)
        {
            case SERVICE_CONTROL_PRESHUTDOWN:
                this->OnPreShutdown();
                break;
    
            default:
                __super::Handler(dwOpcode);
                break;
        }
    }
    

    (感谢 IInspectable(请参阅其他答案)帮助我在这一点上指明了正确的方向。)

    (4) 您现在可以实现OnPreShutdown() 方法:

    void CServiceModule::OnPreShutdown() throw()
    {
        // custom handling code
        // ...
    
        // note: service must shutdown in this handler, so finish
        //       by notifying this to SCM.
        SetServiceStatus(SERVICE_STOPPED);
    }
    

    请注意,如果您使用该服务,Windows 会在预关闭期间停止该服务,并且一旦实施上述操作,您的服务将不会收到任何SERVICE_CONTROL_SHUTDOWN 消息(因为它应该已经关闭)。因此,要允许 Windows 继续关闭其他服务,您需要在 OnPreShutdown() 方法的末尾添加最终的 SetServiceStatus(SERVICE_STOPPED) 调用。

    【讨论】:

    • 那么底线是:ATL 还没有更新到包括features,至少从 Windows XP/Server 2003 开始​​就已经存在了。这有点令人难过。不过,在分析和解决方案方面做得很好。
    猜你喜欢
    • 2021-07-17
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 1970-01-01
    • 2016-07-02
    • 2012-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多