CoCreateInstance 无法启动或连接到 ATL COM 服务

发布于 2024-11-03 22:13:50 字数 2772 浏览 1 评论 0原文

我有一个 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,并在登录的用户帐户下再次实例化 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);

我做错了什么?

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

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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

音盲 2024-11-10 22:13:50

如果您已经介绍过这一点,请原谅我:

运行 DCOMCNFG.EXE。

深入查看组件服务\计算机\我的电脑\DCOM 配置。

找到您的组件,右键单击并激活“属性”。

在“标识”选项卡中,确保 COM 组件配置为在与服务运行时相同的标识下运行。

Forgive me if you have covered this already:

Run DCOMCNFG.EXE.

Drill down through Component Services \ Computers \ My Computer \ DCOM Config.

Find your component, right-click and activate "Properties".

In the Identity tab, ensure that the COM component is configured to run under the same identity that the service is running under.

我不咬妳我踢妳 2024-11-10 22:13:50

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

(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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文