CoCreateInstance没有启动或连接atlcom服务

CoCreateInstance does not start or connect to ATL COM service

本文关键字:连接 atlcom 服务 启动 CoCreateInstance      更新时间:2023-10-16

我有一个ATL COM服务exe (MyService.exe),它编译和运行良好。如果我安装这个服务(通过MyService.exe/service),它就成功地安装到SCM中了。我可以通过SCM启动服务,它在LOCALSYSTEM帐户下运行良好。

当我尝试创建由服务定义的COM类的实例时,问题出现了。我的测试工具应用程序(MyServiceTest.exe)调用以下内容:

::CoInitialize(NULL);
::CoInitializeSecurity(NULL, 
                        NULL, 
                        NULL, 
                        NULL, 
                        RPC_C_AUTHN_LEVEL_PKT, 
                        RPC_C_IMP_LEVEL_IMPERSONATE, 
                        NULL, 
                        EOAC_NONE, 
                        NULL);
ATL::CComPtr<IMyServiceInterface> pInterface;
HRESULT hr = CoCreateInstance(CLSID_MyServiceInterface, NULL, CLSCTX_LOCAL_SERVER, IID_IMyServiceInterface, reinterpret_cast<void**>(&pInterface));

在调用CoCreateInstance时,根据MyService.exe的安装方式,会发生一些不同的事情:

  1. MyService.exe使用/Service命令行安装:
    MyServiceTest.exe调用CoCreateInstance, MyService WinMain被调用。然后调用CAtlServiceModuleT::Start,它确定可执行文件已通过命令行选项"-Embedding"启动。它确定它作为服务安装,并以此调用::StartServiceCtrlDispatcher()。此调用失败,错误码为1063 (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)。根据MS:

"如果程序作为控制台应用程序而不是作为服务运行,则返回此错误。如果程序将作为控制台应用程序运行以进行调试,则对其进行结构调整,以便在返回此错误时不调用特定于服务的代码。"

调用失败,MyService.exe退出,CoCreateInstance调用超时。

  1. MyService.exe不是作为服务安装的,而是通过/RegServer
    注册的MyServiceTest.exe调用CoCreateInstance, MyService WinMain被调用。MyService.exe在登录的用户帐户(不是LOCALSYSTEM)下实例化。可执行文件成功运行,但不是作为服务运行,这不是期望的行为。尽管没有作为服务运行,CoCreateInstance()调用成功,我得到一个有效的接口指针,通过它我可以调用MyService COM函数。

  2. MyService.exe不是作为服务安装的,是通过/RegServer注册的,并且已经在运行(例如在场景2中成功启动后)
    MyServiceTest.exe调用CoCreateInstance, MyServiceTest.exe的一个新实例被实例化,再次在登录的用户帐户下。

我想要的行为是,我可以安装MyService.exe作为一个服务,CoCreateInstance将启动服务器,或连接到当前的MyService.exe实例,如果服务已经运行。据我所知,上面的代码应该是这样的。我错过了什么?

服务在LOCALSYSTEM下运行,而简单的RegServer在本地用户下运行的事实似乎是相关的,但我不确定这是否是问题所在。

服务端对coinitializessecurity的调用是:

HRESULT hr = CoInitializeSecurity(0,
                                    -1,
                                    0,
                                    0,
                                    RPC_C_AUTHN_LEVEL_PKT,
                                    RPC_C_IMP_LEVEL_IMPERSONATE,
                                    0,
                                    EOAC_NONE,
                                    0);

我做错了什么?

注:MyService.exe一旦启动就不应该退出,因为它在Run()函数中包含WaitForSingleObject(),该函数正在等待外部信号(也在OnStop()中设置,因此SCM可以停止服务)。这就是为什么MyServiceTest.exe完成后MyService.exe仍然存在的原因。这是期望的行为(对于服务来说,它应该是这样运行的)。

如果你已经讲过了,请原谅我:

DCOMCNFG.EXE运行。

钻取组件服务 Computers My Computer DCOM Config.

找到你的组件,右键单击并激活"属性"。

在Identity选项卡中,确保将COM组件配置为在服务运行的同一身份下运行。

事实证明,罪魁祸首是服务的注册方式。为了使一个类作为服务启动它的控制应用程序,控制应用程序需要在注册表中添加条目,以便将其识别为本地服务器,即:

(MyService.rgs)
HKCR
{
    NoRemove AppID
    {
        ForceRemove {6E5B1E7E-3340-4553-A356-76F1C3543452} = s 'MyService'
        {
            val LocalService = s 'MyService'
            val ServiceParameters = s '-Service'
        }
        'MyService.EXE'
        {
            val AppID = s {6E5B1E7E-3340-4553-A356-76F1C3543452}
        }
    }
}

AppID在MyService.rgs中指定。

这会导致注册表中的以下布局:

HKCR
    AppID
        {6E5B1E7E-3340-4553-A356-76F1C3543452} (Contains LocalService, ServiceParameters REG_SZ's)
        MyService.EXE (Contains AppID REG_SZ)
    CLSID
        {MyServiceInterface GUID} (Contains MyService.EXE AppID)

相关链接:
LocalService价值
在CoClass CLSID中重载LocalServer32
为CLSID指定AppID