将C++(DLL)项目转换为COM DLL项目

3

您好,我有一个纯C ++项目(DLL),我想将其转换为COM项目,例如所有在头文件中定义的公共接口现在将作为COM接口(IDL等)公开。最终产品应该是COM Dll。

我该如何开始?如何定义任何高层指南?有好的文章可以参考吗?

2个回答

3
这个问题至少有两个部分:
首先,COM对象是使用CoCreateInstance创建的。CoCreateInstance通过在内存中查找COM注册表(通过CoRegisterClassObject),在应用程序清单中作为零注册COM对象以及最后在注册表中进行查找。
对于零注册,需要创建一个程序集清单来描述您的dll,以便对象的使用者可以将dependantAssembly引用添加到他们的应用程序清单中。

然后,COM dll 需要至少两个入口点: DllGetClassObjectDllCanUnloadNow

您通过创建一个支持 IClassFactory 的工厂对象的实例来实现 DllGetClassObject,该工厂对象可用于创建您实际对象的实例。

因此,总结一下 - 一种基于 TDD 的实现 COM dll 的方法:

  1. 创建一个带有 'DllGetClassObject' 和 'DllCanUnloadNow' 入口点的 DLL。
  2. 创建一个新的 GUID 代表您的对象,并创建一个程序集清单,描述 COM 对象您的 dll (将) 包含。
  3. 创建一个测试应用程序,调用 CoCreateInstance 使用该 GUID。
  4. 调用 CoCreateInstance 现在应该会进入您的 DllGetClassObject 调用。实现类工厂对象。
  5. 实现 CreateInstance 方法来创建一个新的 c++ 类的实例。确保所有接口都派生自 (至少) IUnknown。在您自己新创建的对象上调用 QueryInterface 来获取并返回所需的接口。

假设您的编程环境是Visual Studio(Express也可以):

创建一个测试exe:

// main.cpp
#include <windows.h>
#include <objbase.h>
#include <initguid.h>
DEFINE_GUID(CLSID_RegFreeOcx,0x00000000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
#if defined _MSC_VER 
#if !defined _WINDLL && !defined (_CONSOLE)
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
#endif
#pragma comment(linker, "/manifestDependency:\"name='acme.RegFreeOcx' processorArchitecture='*' version='1.0.0.0' type='win32' \"")
int main(){
  CoInitialize(NULL);
  IUnknown* pUnk;
  CoCreateInstance(CLSID_RegFreeOcx,NULL,CLSCTX_ALL,IID_IUnknown,(void**)&pUnk);
  if(pUnk)
    pUnk->Release();
}

创建用于注册自由激活COM dll的清单:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity name="Acme.RegFreeOcx" processorArchitecture="x86" version="1.0.0.0" type="win32" />
  <file name = "RegFreeOcx.dll">
    <comClass clsid="{00000000-0000-0000-0000-000000000000}" threadingModel="Apartment" />
  </file>
</assembly>

创建一个dll项目
// dllmain.cpp
#include <windows.h>
#pragma comment(linker,"/export:DllGetClassObject=_DllGetClassObject@12,PRIVATE")
#pragma comment(linker,"/export:DllCanUnloadNow=_DllCanUnloadNow@0,PRIVATE")
#include <objbase.h>
#include <initguid.h>
DEFINE_GUID(CLSID_RegFreeOcx,0x00000000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
#include "MyClassFactory.hpp"

STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid,void** ppvObj)
{
  if(CLSID_RegFreeOcx == clsid){
    MyClassFactory* pFactory = new MyClassFactory();
    HRESULT result = pFactory->QueryInterface(riid,ppvObj);
    pFactory->Release();
    return result;
  }
 return E_FAIL;
}
STDAPI DllCanUnloadNow()
{
  return E_FAIL;
}

//MyClassFactory.hpp
#include <MyClass.hpp>
class MyClassFactory : public IClassFactory {
  volatile ULONG _cRef;
public:
  MyClassFactory():_cRef(1){}
  virtual ~MyClassFactory(){}
public: // IUnknown
  STDMETHODIMP_(ULONG)AddRef(){
    return InterlockedIncrement(&_cRef);
  }
  STDMETHODIMP_(ULONG)Release(){
    ULONG result = InterlockedDecrement(&_cRef);
    if(!result) delete this;
    return result;
  }
  STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj){
    if(riid == IID_IUnknown || riid == IID_IClassFactory)
      *ppvObj = (IClassFactory*)this;
    else { 
      *ppvObj=0;
       return E_NOINTERFACE;
    }
    AddRef();
    return S_OK;
  }
public: // IClassFactory
  STDMETHODIMP CreateInstance(IUnknown* pUnkOuter,REFIID riid,void** ppvObj){
    if(pUnkOuter)
      return E_INVALIDARG;
    MyClass* pClass = new MyClass();
    HRESULT result = pClass->QueryInterface(riid,ppvObj);
    pClass->Release();
    return result;
  }
  STDMETHODIMP LockServer(BOOL fLock){
    return E_NOTIMPL;
  }
};

最后,定义您自己的类。您可以使用IDL来完成此操作,但对于其他cpp消费者,您只需在头文件中定义即可。

// IMyClass.h
#include <objbase.h>
DEFINE_GUID(IID_MyInterface,0x00000000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
interface IMyInterface : IUnknown {
  STDMETHOD(MyMethod)(void)PURE;
};

在一个cpp文件中实现实现IMyInterface的类,方法与ClassFactory相似(涉及IUnknown方法),将IClassFactory引用替换为您自己的接口。

为了确保,假设我有一个类X{ foo();bar();}。如何更改类,以便客户端应用程序可以找到其接口?定义IDL?将其包装在某个ATL模板中?假设您可以更改类本身。 - Boris
非常感谢您提供完整的答案。 - Boris

1

COM 的经典入门书是 Dale Rogenson 的 Inside COM。如果想要深入了解,可以尝试 Don Box 的 Essential COM

需要关注的三个主要领域是:

  • 线程控制
  • 内存管理和内存所有权规则
  • 在 COM 接口边界传递的类型 - 特别是不使用 STL

Box 的书还涵盖了一些人们在 COM 中做的一些非常恶心的事情 - 比如自由线程封送程序 - 如果您的库已经是线程安全的,并且想要避免封送惩罚,那么这可能会适用于您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接