在win32里实现COM

本文档介绍了如何在Windows 32位环境下实现COM组件。重点讲述了如何创建VTable,实现IClassFactory以创建IExample接口,以及DLL中DllGetClassObject()和DllCanUnloadNow()函数的用法。此外,还提到了使用DECLARE_INTERFACE_宏定义接口,并讨论了注册DLL和设置ThreadingModel的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

终于看到一篇关于在win32里实现COM的文章了,文章很长,写的很好。

http://www.codeproject.com/Articles/13601/COM-in-plain-C#DLL

http://www.codeproject.com/Articles/3365/Embed-an-HTML-control-in-your-own-window-using-pla


1) 要实现的功能

typedef long SetStringPtr(char *);

typedef long GetStringPtr(char *, long);
typedef struct {   
    SetStringPtr * SetString;   
    GetStringPtr * GetString;   
    DWORD          count;   
    char           buffer[80];
}IExample;


long SetString(char * str)
{
   return(0);
}


long GetString(char *buffer, long length)
{
   return(0);
}



2)需要将其改为下列的形式

typedef HRESULT STDMETHODCALLTYPE QueryInterfacePtr(IExample *, REFIID, void **);
typedef ULONG STDMETHODCALLTYPE AddRefPtr(IExample *);
typedef ULONG STDMETHODCALLTYPE ReleasePtr(IExample *);
typedef HRESULT STDMETHODCALLTYPE SetStringPtr(IExample *, char *);
typedef HRESULT STDMETHODCALLTYPE GetStringPtr(IExample *, char *, long);


typedef struct {
   QueryInterfacePtr  *QueryInterface;
   AddRefPtr          *AddRef;
   ReleasePtr         *Release;
   SetStringPtr       *SetString;
   GetStringPtr       *GetString;
} IExampleVtbl;


typedef struct {
   IExampleVtbl *lpVtbl;
   DWORD         count;
   char          buffer[80];
} IExample


说明:But all COM objects begin with a pointer to their VTable, and the first three VTable pointers are to the object's QueryInterface, AddRef, and Release functions. The first argument passed to an object's function is a pointer to the object (struct) itself. That is the law. Obey it.



3)需要设定两个GUID分别指向Com Object(IExample)和COM Object's Vtbl(IExampleVtbl)。

COM Object:
// {0B5B3D8E-574C-4fa3-9010-25B8E4CE24C2}
DEFINE_GUID(CLSID_IExample, 0xb5b3d8e, 0x574c, 0x4fa3, 
      0x90, 0x10, 0x25, 0xb8, 0xe4, 0xce, 0x24, 0xc2);


COM Object's Vtbl
// {74666CAC-C2B1-4fa8-A049-97F3214802F0}
DEFINE_GUID(IID_IExample, 0x74666cac, 0xc2b1, 0x4fa8, 
      0xa0, 0x49, 0x97, 0xf3, 0x21, 0x48, 0x2, 0xf0);


说明:In conclusion, every COM object has its own GUID, which is an array of 16 bytes that are different from any other GUID. A GUID is created with the GUIDGEN.EXE utility. A COM object's VTable (i.e., interface) also has a GUID.



4)我们的COM DLL中需要有一个IClassFactory,用它来创建IExample。IClassFactory的实现如下

static const IClassFactoryVtbl IClassFactory_Vtbl = {classQueryInterface,
classAddRef,
classRelease,
classCreateInstance,
classLockServer};

static IClassFactory MyIClassFactoryObj = {&IClassFactory_Vtbl}; 

  • 实现可参照原文,(IClassFactory的实例永远是1,classAddRef和classRelease直接返回1即可。)
  • 这里主要要注意classCreateInstance,它是创建用IExample的。

HRESULT STDMETHODCALLTYPE classCreateInstance(IClassFactory *this, 
        IUnknown *punkOuter, REFIID vTableGuid, void **ppv)
{
   HRESULT          hr;
   struct IExample *thisobj;

   // Assume an error by clearing caller's handle.
   *ppv = 0;

   // We don't support aggregation in IExample.
   if (punkOuter)
      hr = CLASS_E_NOAGGREGATION;
   else
   {
      // Create our IExample object, and initialize it.
      if (!(thisobj = GlobalAlloc(GMEM_FIXED, 
                      sizeof(struct IExample))))
         hr = E_OUTOFMEMORY;
      else
      {
         thisobj->lpVtbl = &IExample_Vtbl;
         thisobj->count = 1;
         hr = IExample_Vtbl.QueryInterface(thisobj, vTableGuid, ppv);
         IExample_Vtbl.Release(thisobj);
      }
   }

   return(hr);
}


  • 另外classQueryInterface函数的实现,作用返回IClassFactory。

HRESULT STDMETHODCALLTYPE classQueryInterface(IClassFactory *this, 
                          REFIID factoryGuid, void **ppv)
{
   // Check if the GUID matches an IClassFactory or IUnknown GUID.
   if (!IsEqualIID(factoryGuid, &IID_IUnknown) && 
       !IsEqualIID(factoryGuid, &IID_IClassFactory))
   {
      // It doesn't. Clear his handle, and return E_NOINTERFACE.
      *ppv = 0;
      return(E_NOINTERFACE);
   }

   *ppv = this;

   // Call our IClassFactory's AddRef, passing the IClassFactory. 
   this->lpVtbl->AddRef(this);

   // Let him know he indeed has an IClassFactory. 
   return(NOERROR);
}


说明:The really important function is CreateInstance. The program calls our IClassFactory's CreateInstance whenever the program wants us to create one of our IExample objects, 



5)继续打包到一个DLL里

为了方便其他的程序调用我们的IClassFactory::CreateInstance, 微软要求我们必须在dll实现一个DllGetClassObject()函数。只是调用上面的classQueryInterface函数返回IClassFactory。

HRESULT PASCAL DllGetClassObject(REFCLSID objGuid, 
        REFIID factoryGuid, void **factoryHandle)
{
   HRESULT  hr;

   if (IsEqualCLSID(objGuid, &CLSID_IExample))
   {
       hr = classQueryInterface(&MyIClassFactoryObj, 
                          factoryGuid, factoryHandle);
   }
   else
   {
      *factoryHandle = 0;
      hr = CLASS_E_CLASSNOTAVAILABLE;
   }

   return(hr);
}



6)DllCanUnloadNow()函数 

这个函数是告诉操作系统是否可以卸载这个DLL了。只要确认IExample的引用个数是否为0。(文章里提到还要判断LockCount,不在这里解释了)



7)补充我们的源文件

  • 定义INTERFACE宏

说明:Instead of defining things as above, Microsoft provides a macro we can use to define our VTable and object in a way that works for both C and C++, and hides the extra data members. To use this macro, we must first define the symbol INTERFACE to the name of our object (which in this case is IExample). And prior to that, we must undef that symbol to avoid a compiler warning. Then, we use the DECLARE_INTERFACE_ macro. Inside of the macro, we list our IExample functions. Here's what it will look like:

#undef  INTERFACE
#define INTERFACE   IExample
DECLARE_INTERFACE_ (INTERFACE, IUnknown)
{
   STDMETHOD  (QueryInterface)  (THIS_ REFIID, void **) PURE;
   STDMETHOD_ (ULONG, AddRef)   (THIS) PURE;
   STDMETHOD_ (ULONG, Release)  (THIS) PURE;
   STDMETHOD  (SetString)       (THIS_ char *) PURE;
   STDMETHOD  (GetString)       (THIS_ char *, DWORD) PURE;
};


  • DEF文件
LIBRARY IExample
EXPORTS
DllCanUnloadNow   PRIVATE
DllGetClassObject PRIVATE

  • register our dll
We first need to create a key under HKEY_LOCAL_MACHINE\Software\Classes\CLSID.

Under our "GUID key", we must create a subkey named InprocServer32. This subkey's default value is then set to the full path where our DLL has been installed.

We must also set a value named ThreadingModel to the string value "both"





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值