如何将 PreShutdown 事件添加到 ATL 服务?

How to add PreShutdown event to ATL service?

我继承了基于旧 ATL 的服务的 C++ 代码,其中包含旧 ATL 向导的基本结构(_tWinMain()、运行()、运行MessageLoop() 等).

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

我添加了 SERVICE_ACCEPT_PRESHUTDOWN 选项..

m_status.dwControlsAccepted |= SERVICE_ACCEPT_PRESHUTDOWN

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

任何人都可以指出一些允许我向现有代码添加 OnPreShutdown 的东西吗?

ATL Services. To handle control requests that a default ATL-generated service wouldn't, you need to extend the default-generated Handler 实施中介绍了使用 ATL 实施服务。

这是您需要做的事情的粗略描述(除了注册您已经注册的控制请求):

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

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

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

(1) 更新接受的控制消息列表以包含 SERVICE_ACCEPTED_PRESHUTDOWN 消息。您可以删除 SERVICE_ACCEPTED_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();

不要包括对基class的调用(因为这将包括对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) 调用。