1

我正在实现我的第一个进程外 COM 服务器(我的第一个 COM 服务器,就此而言)。我已按照步骤编写 IDL 文件、为代理/存根 DLL 生成代码、编译 DLL 并注册它。

当我检查注册表项时,我有

  • 一个名为 的键HKEY_CLASSES_ROOT/Interface/<GUID>,其值是 (say)IMyApp
  • 一个名为 的键HKEY_CLASSES_ROOT/Interface/<GUID>/ProxyStubClsid32,其值为<GUID>,即与键名中的值相同

我不明白第二个键的值如何与键<GUID>名中的值相同,因为我目前的理解是

  • HKEY_CLASSES_ROOT/Interface/<GUID>, GUID 是一个接口ID
  • 不是ProxyStubClsid32接口ID,而是ID,指实现上述接口的组件
  • HKEY_CLASSES_ROOT/CLSID/<GUID>/InprocServer32(其中 GUID 是上面的ID)的值指向代理 DLL

那么,HKEY_CLASSES_ROOT/Interface/<GUID>/ProxyStubClsid32如果一个是接口 ID,另一个是类 ID,那么值如何保持相同的值 GUID?

编辑:我仍然希望得到这个答案。简而言之:既然组件和接口是两个不同的东西,那么相同的 ID 怎么能同时用于两者呢?

4

3 回答 3

7

您对在 COM 中使用 Guid 的方式的基本理解是正确的。首先值得注意的是,具有相同 guid 的接口和 coclass 不是问题。它们存在于不同的注册表项中,HKCR\Interface 与 HKCR\CLSID 并且在 COM 中始终清楚您是在查找 IID 还是 CLSID。

其次是您编写的 IDL。请注意,那里没有指定代理的 CLSID 的地方,只能在那里声明代理和存根支持的 IID。

接下来,您需要通过代理/存根自动生成的方式进行疯狂追逐。核心 Windows SDK 头文件是 RpcProxy.h,在文本编辑器中打开它来查看。宏汤很重,但确实有一些不错的评论来描述正在发生的事情。重要的 RPC 帮助函数是 NdrDllRegisterProxy(),它注册代理并在您使用 Regsvr32.exe 时调用。它的第三个参数指定代理的 CLSID。我会让您阅读并仅引用 .h 文件中的重要部分:

编译器开关:

-DPROXY_CLSID=clsid
    Specifies a class ID to be used by the proxy DLL.

您可以使用 Project + Properties、C/C++、Preprocessor、Preprocessor Definitions 设置指定此选项。请注意,您的项目不会指定它。

追逐汤然后让你找到这个:

// if the user specified an override for the class id, it is
// PROXY_CLSID at this point

#ifndef PROXY_CLSID
#define GET_DLL_CLSID   \
    ( aProxyFileList[0]->pStubVtblList[0] != 0 ? \
    aProxyFileList[0]->pStubVtblList[0]->header.piid : 0)
#else  //PROXY_CLSID
#define GET_DLL_CLSID   &PROXY_CLSID
#endif //PROXY_CLSID

换句话说,如果您自己没有指定 CLSID(您没有),那么它将使用存根表中的第一个IID

这使得 ProxyStubClsid32 guid 与您的第一个接口的 IID 相同。功能,而不是错误。

于 2012-10-27T14:53:34.230 回答
0

初学者的困惑 (tm) 案例。通过调用注册的类regsrv32不是我的CLSID 的类。它是专门为代理/存根 DLL 生成的(友好名称 PSFactory 也表明了这一点)。因此,正如 Roman R. 所怀疑的那样,有两个班级,我认为只有一个班级。/Embedding我自己的 CLSID 在使用交换机调用时由 EXE 服务器注册。

于 2012-10-29T18:40:17.260 回答
0

据我所知,所有代理/存根混乱现在都由 MIDL 管理(继承自 IDispatch 而不是 IUnknown,您可能已经这样做了,因为您拥有 ProxyStubClsid32 reg 密钥)。

唯一需要的是正确注册服务器(只是构建它或做 /RegServer 没有为我们正确注册它......以及许多其他人),这样做只需要调用 LoadTypeLibEx (在构建服务器或安装它)。

因此,只需使用此代码创建一个小 exe,并在构建和安装后调用它:

String^ l_TLB = l_Path + "\\MyServer.tlb";
IntPtr  l_TLBP = System::Runtime::InteropServices::Marshal::StringToBSTR(l_TLB);
ITypeLib *pTypeLib;
HRESULT hr = LoadTypeLibEx(static_cast<LPCOLESTR>(l_TLBP.ToPointer()), REGKIND_REGISTER, &pTypeLib);
if(SUCCEEDED(hr))
    pTypeLib->Release();
于 2013-09-16T06:47:02.023 回答