主要问题是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);
但您还需要将对 pT 和 T 的引用更改为 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) 调用。