Convert c++ (DLL) project to COM DLL project [closed]

蹲街弑〆低调 提交于 2019-12-07 16:47:33

问题


Hi I have a pure C++ project (DLL) which I would like to convert to COM project, e.g. all public interfaces defined in headers, will now be exposed as COM interfaces (IDL etc...). And the final product should be COM Dll.

How do I start? How do I define Any high level guidelines? good articles?


回答1:


There are at least two parts to this problem:

First, COM objects are created using CoCreateInstance. CoCreateInstance looks for COM registrations in memory (via CoRegisterClassObject), in the applications manifest as a zero reg COM object, and finally, in the registry.

For zero reg, create an assembly manifest describing your dll so that consumers of your object can add a dependantAssembly reference to their application manifest.

Then, a COM dll needs at least two entry points: DllGetClassObject and DllCanUnloadNow

You implement DllGetClassObject by creating an instance of a factory object - that supports IClassFactory - that can be used to make instances of your actual object.

So, to summarize - a kind of TDD driven approach to implementing a COM dll:

  1. Create a DLL with 'DllGetClassObject' and 'DllCanUnloadNow' entry points.
  2. Create a new GUID to represent your object, and create a assembly manifest describing the COM object your dll (will) contain.
  3. Create a test application, that calls CoCreateInstance with that GUID.
  4. Calling CoCreateInstance should now land up in your DllGetClassObject call. Implement the class factory object.
  5. Implement the CreateInstance method to create a new instance of your c++ class. Ensure that all your interfaces derive from (at least) IUnknown. Call QueryInterface on your own newly created object to get and return the desired interface.

Assuming Visual Studio (Express is ok) is your build environment:

Create a test 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();
}

Create the manifest for reg free activation of the 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>

Create a dll project

// 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;
  }
};

And finally, define your own class. You could use IDL to do this but, for other cpp consumers, you can just define it in a header file.

// 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;
};

Implement the class that implements IMyInterface in a cpp file in much the same way that the ClassFactory was implemented (in terms of the IUnknown methods) - swapping out references to IClassFactory with your own interface.




回答2:


THe canonical gentle introduction is Inside COM by Dale Rogenson. For real depth, try Essential COM by Don Box

The three big areas to worry about are:

  • Threading
  • Memory management and memory ownership conventions]
  • Types passed across COM interface boundaries - particularly not using STL

THe Box book also covers some of the really nasty things people do with COM - like the free threaded marshaller - which you might be applicable if your library is already thread safe and you want to avoid marshalling penalties.



来源:https://stackoverflow.com/questions/11947870/convert-c-dll-project-to-com-dll-project

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!