CoCreateInstance 不启动或连接到 ATL COM 服务 [英] CoCreateInstance does not start or connect to ATL COM service

查看:24
本文介绍了CoCreateInstance 不启动或连接到 ATL COM 服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 ATL COM 服务 exe (MyService.exe),它编译并运行良好.如果我安装此服务(通过 MyService.exe/Service),它会成功安装到 SCM 中.我可以通过 SCM 启动服务并且它在 LOCALSYSTEM 帐户下运行良好.

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

::CoInitialize(NULL);:: CoInitializeSecurity(NULL,空值,空值,空值,RPC_C_AUTHN_LEVEL_PKT,RPC_C_IMP_LEVEL_IMPERSONATE,空值,EOAC_NONE,空值);ATL::CComPtrp接口;HRESULT hr = CoCreateInstance(CLSID_MyServiceInterface, NULL, CLSCTX_LOCAL_SERVER, IID_IMyServiceInterface, reinterpret_cast(&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,并在登录的用户帐户下实例化 MyService.exe 的新实例.每次对 CoCreateInstance 的后续调用都会继续这种行为.

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

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

对 CoInitializeSecurity 的服务端调用是:

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 仍然存在的原因.这是期望的行为(对于服务而言,它应该以这种方式运行).

解决方案

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

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

在 MyService.rgs 中指定的 AppID.

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

HKCR应用程序ID{6E5B1E7E-3340-4553-A356-76F1C3543452}(包含LocalService、ServiceParameters REG_SZ)MyService.EXE(包含 AppID REG_SZ)CLSID{MyServiceInterface GUID}(包含 MyService.EXE AppID)

相关链接:
本地服务值
CoClass CLSID 中的 LocalServer32 过载
为 CLSID 指定 AppID

I have a ATL COM service exe (MyService.exe), which compiles and runs fine. If I install this service (via MyService.exe /Service), it is successfully installed into the SCM. I can start the service through the SCM and it runs fine, under the LOCALSYSTEM account.

My problem arises when I attempt to create an instance of a COM class defined by the service. My test harness application (MyServiceTest.exe), calls the following:

::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));

At the call to CoCreateInstance, a few different things happen, depending on how MyService.exe is installed:

  1. MyService.exe is installed using the /Service command line:
    MyServiceTest.exe calls CoCreateInstance, and MyService WinMain is called. CAtlServiceModuleT::Start is then called, which determines that the executable has been started with the command line option '-Embedding'. It determines that it's installed as a service, and as such calls ::StartServiceCtrlDispatcher(). This call fails with error code 1063 (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT). According to MS:

"This error is returned if the program is being run as a console application rather than as a service. If the program will be run as a console application for debugging purposes, structure it such that service-specific code is not called when this error is returned."

The call fails, MyService.exe exits, and the CoCreateInstance call times out.

  1. MyService.exe is NOT installed as a service, but is registered via /RegServer
    MyServiceTest.exe calls CoCreateInstance, and MyService WinMain is called. MyService.exe is instantiated under the logged in user account (not LOCALSYSTEM). The executable successfully runs, but not as a service, which is not the desired behaviour. Despite not running as a service, the CoCreateInstance() call succeeds, and I get a valid interface pointer through which I can call MyService COM functions.

  2. MyService.exe is NOT installed as a service, is registered via /RegServer, and is already running (after successfully being started in scenario 2 for example)
    MyServiceTest.exe calls CoCreateInstance, and a NEW instance of MyService.exe is instantiated, again under the logged in user account. This behaviour continues for every subsequent call to CoCreateInstance.

My desired behaviour is that I can install MyService.exe as a service, and CoCreateInstance will start the server, or connect to the current MyService.exe instance if the service is already running. As far as I was aware, the above code should behave this way. What am I missing?

It seems the fact that the service runs under LOCALSYSTEM while the simple RegServer alternative runs under the local user could be pertinent, but I'm not sure if this is the issue.

The service side call to CoInitializeSecurity is:

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

What am I doing wrong?

P.S. MyService.exe should not exit once started, as it contains a WaitForSingleObject() in the Run() function which is waiting on an external signal (which is also set in OnStop() so the SCM can halt the service). This is why MyService.exe persists after MyServiceTest.exe completes. This is desired behaviour (for a service, which is what it should be running as).

解决方案

It turns out the culprit was how the service was registered. In order for a class to launch it's controlling application as a service, the controlling application needs to add entries into the registry so it is recognised as a local server, i.e:

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

The AppID of which is specified in MyService.rgs.

This leads to the following layout in the registry:

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)

Pertinent links:
LocalService value
LocalServer32 overload in CoClass CLSID
Specifying AppID for CLSID

这篇关于CoCreateInstance 不启动或连接到 ATL COM 服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆