随笔 - 51  文章 - 1  评论 - 453  0

将VS创建的Windows服务项目编译生成的程序,通过命令行 “服务.exe -Service”注册为Windows服务后,就可以通过服务管理器进行管理了。

问题

通过服务管理器进行启动的时候,发现服务无法启动,提示错误

无Com接口服务启动失败

由于程序代码是由VS的项目模板生成的,生成后直接编译为Debug版,然后通过命令行进行注册,期间并没有修改过任何代码。

难道是只有Release版本才能通过服务管理器启动吗。于是将Release版注册为服务,通过服务管理器启动,仍然会报同样的错误。

由于服务比较特殊,应该会在Windows的事件管理器中留下日志,经查看,日志为

服务启动失败系统事件

可以看到系统日志中并无异常。

问题还不只如此,当Debug版本通过 RegServer 注册后,程序也是启动后立即停止。

既然如此,那就开始使用神器——单步调试。

启动失败原因

Window服务程序启动后,会在WinMain方法中调用一个Run方法

HRESULT Run(_In_ int nShowCmd = SW_HIDE) throw()
{
    HRESULT hr = S_OK;

    T* pT = static_cast<T*>(this);
    hr = pT->PreMessageLoop(nShowCmd);

    // Call RunMessageLoop only if PreMessageLoop returns S_OK.
    if (hr == S_OK)
    {
        pT->RunMessageLoop();
    }

    // Call PostMessageLoop if PreMessageLoop returns success.
    if (SUCCEEDED(hr))
    {
        hr = pT->PostMessageLoop();
    }

    ATLASSERT(SUCCEEDED(hr));
    return hr;
}

而Run方法中会调用pT->RunMessageLoop()方法,来进行一个While循环,从而阻止WinMain方法返回,造成程序结束。

经过调试,发现执行时,pT->PreMessageLoop 方法返回了S_FALSE,由于RunMessageLoop方法前有一个判断,所以此方法没有执行,从而造成程序启动便立即停止。

而真正造成PreMessageLoop方法返回S_FALSE的原因是

hr = pT->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);

调用RegisterClassObjects方法,返回了S_FALSE,而RegisterClassObjects方法又调用了AtlComModuleRegisterClassObjects方法,实际上是在注册Com相关的东西,而由于程序是由VS创建的默认代码编译的,并未添加任何Com接口,所以就会返回S_FALSE,即注册失败。

解决方案

找到了问题的原因了,那么也就相对好解决了。

既然程序中,并没有使用到Com相关的东西,那么就干脆跳过RegisterClassObjects方法的调用。

由于PreMessageLoop方法中通过宏_ATL_NO_COM_SUPPORT,可以决定是否跳过RegisterClassObjects的调用,所以只需要在预处理器中或者代码中定义这个宏即可。

代码如下

#define _ATL_NO_COM_SUPPORT

或者通过预处理器来定义

无Com接口的服务项目预处理器

系列链接

玩转Windows服务系列——创建Windows服务

玩转Windows服务系列——Debug、Release版本的注册和卸载,及其原理

玩转Windows服务系列——无COM接口Windows服务启动失败原因及解决方案

玩转Windows服务系列——服务运行、停止流程浅析

玩转Windows服务系列——Windows服务小技巧

玩转Windows服务系列——命令行管理Windows服务

玩转Windows服务系列——Windows服务启动超时时间

玩转Windows服务系列——使用Boost.Application快速构建Windows服务

玩转Windows服务系列——给Windows服务添加COM接口

posted on 2013-12-24 00:28 缘生梦 阅读(...) 评论(...) 编辑 收藏