声明自定义的接口继承于IUnknown。需要一个GUID与该接口对应。
定义一个类继承于该接口用来实现。
定义一个类Factory继承于IClassFactory,用来创建实现类。需要一个GUID与该factory对应。
实现类除了需要实现自定义的接口外,还要声明一个用来计数的成员,用来统计多少用户引用了自己,并实现IUnknown中声明的三个纯虚函数:
AddRef:计数加1。
Release:计数减一,当计数为0的时候,delete自己。
QueryInterface:根据传入的GUID,把自己的this指针转换成GUID对应的接口类型,传给用户,调用一次AddRef(因为又多了一个使用该对象的用户)。用户得到的是接口类型指针,可以调用函数。
Factory类除了实现上面三个纯虚函数外,还需要实现另外两个纯虚函数:
CreateInstance:创建一个实现类对象,调用一次该对象的QueryInterface(对象计数+1,表示对象已被创建);将对象以IUnknown类型传给用户(用户不需要知道对象具体类型,但需要得到对象)。
LockServer:--
该com组件以DLL形式存在,需要导出以下四个函数:
DllGetClassObject:com库在载入DLL后,会调用此函数,在此函数中创建Factory类对象,调用一次QueryInterface(对象计数+1)。
DllCanUnloadNow:判断DLL是否可以被卸载。
DllRegisterServer: 当执行RegSvr32命令加载DLL时被调用,用来写注册表。
DllUnregisterServer :当执行RegSvr32 -u命令卸载DLL时被调用。
自定义的COM组建在被COM库使用前必须在HKEY_CLASSES_ROOT\CLSID键值下注册。用factory的GUID 作为
键名字,这个键值必须包含两个子键,一是DLL的位置,二是DLL的线程模型。
客户端使用时按如下步骤:
1、调用CoInitialize初始化COM库。
2、调用CoCreateInstance指定factory的GUID,获得IUnknown类型的对象指针(不需要知道对象类型)。
3、调用QueryInterface指定接口的GUID,获得接口指针(实际指向实现类对象)。
4、用该接口指针调用自定义的方法。
5、调用Release释放接口、对象,CoUninitialize释放com库资源。
过程如下图所示:
自定义接口头文件 IMyInterface.h
#ifndef __MY_INTERFACE__
#define __MY_INTERFACE__
#include "Unknwn.h"
//自己的接口
interface IMyInterface: public IUnknown
{
public:
virtual void MyTest() = 0;
};
#endif
实现类头文件 MyClass.h
#ifndef __MY_CLASS__
#define __MY_CLASS__
#include "IMyInterface.h"
//实现接口的类
class MyClass: public IMyInterface
{
public:
MyClass();
~MyClass();
//from IUnknown
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppvObject);
//from IMyInterface
void MyTest();
private:
int m_Ref ;
};
#endif
类工厂头文件 MyFactory.h
#ifndef __MY_FACTORY__
#define __MY_FACTORY__
#include "Unknwn.h"
//创建类的类工厂
class MyFactory: public IClassFactory
{
public:
MyFactory ();
~MyFactory ();
//from IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void **ppv);
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
//from IClassFactory
HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown* , const IID& iid, void **ppv);
HRESULT STDMETHODCALLTYPE LockServer(BOOL);
private:
ULONG m_Ref;
};
#endif
类的实现 MyClass.cpp
#include "MyClass.h"
#include <stdio.h>
//接口的GUID
extern "C" const GUID IID_IMyInterface =
{ 0x54bf6569, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;
ULONG g_ClassNum = 0; //统计类对象
ULONG g_LockNumber = 0;
MyClass::MyClass()
:m_Ref(0)
{
++g_ClassNum;
}
MyClass::~MyClass()
{
--g_ClassNum;
}
ULONG MyClass::AddRef()
{
++m_Ref;
return (ULONG)m_Ref;
}
ULONG MyClass::Release()
{
--m_Ref;
if (m_Ref == 0 ) {
delete this;
return 0;
}
return (ULONG) m_Ref;
}
HRESULT MyClass::QueryInterface(const IID& iid, void** ppvObject)
{
if ( iid == IID_IUnknown )
{
*ppvObject = (IMyInterface *) this ;
((IMyInterface *)(*ppvObject))->AddRef() ;
}
else if ( iid == IID_IMyInterface )
{
*ppvObject = (IMyInterface *) this ;
((IMyInterface *)(*ppvObject))->AddRef() ;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE ;
}
return S_OK;
}
void MyClass::MyTest()
{
printf("MyTest called\n");
}
类工厂的实现 MyFactory.cpp
#include "MyFactory.h"
#include "MyClass.h"
extern ULONG g_LockNumber;
extern ULONG g_ClassNum;
MyFactory::MyFactory()
:m_Ref(0)
{
}
MyFactory::~MyFactory()
{
}
HRESULT MyFactory::QueryInterface(const IID& iid, void **ppv)
{
if ( iid == IID_IUnknown )
{
*ppv = (IUnknown *) this ;
((IUnknown *)(*ppv))->AddRef() ;
}
else if ( iid == IID_IClassFactory)
{
*ppv = (IClassFactory *) this ;
((IClassFactory *)(*ppv))->AddRef() ;
}
else
{
*ppv = NULL;
return E_NOINTERFACE ;
}
return S_OK;
}
ULONG MyFactory::AddRef()
{
++m_Ref;
return (ULONG)m_Ref;
}
ULONG MyFactory::Release()
{
--m_Ref;
if (m_Ref == 0)
{
delete this;
return 0;
}
return (ULONG)m_Ref;
}
HRESULT MyFactory::CreateInstance(IUnknown *pUnknownOuter,
const IID& iid, void **ppv)
{
MyClass* pObj;
HRESULT hr;
*ppv=NULL;
hr=E_OUTOFMEMORY;
if (NULL != pUnknownOuter)
return CLASS_E_NOAGGREGATION;
//Create the object
pObj = new MyClass();
if (NULL==pObj)
return hr;
//Obtain the first interface pointer (which does an AddRef)
hr=pObj->QueryInterface(iid, ppv);
if (hr != S_OK) {
delete pObj;
}
return hr;
}
HRESULT MyFactory::LockServer(BOOL bLock)
{
if (bLock)
g_LockNumber ++;
else
g_LockNumber --;
return NOERROR;
}
需要导出的函数
MyCom.cpp
#include "MyFactory.h"
extern ULONG g_LockNumber;
extern ULONG g_ClassNum;
HANDLE g_hModule = NULL; //用来获得DLL文件名
//对应该com组件
extern "C" const GUID CLSID_Facotry =
{ 0x54bf6567, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;
//DLL 入口
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
g_hModule = hModule;
return TRUE;
}
//写注册表
//先创建,在写值
BOOL SetKey(const WCHAR* key, const WCHAR* value)
{
HKEY hKey;
//创建KEY
long lResult = ::RegCreateKeyEx(HKEY_CLASSES_ROOT ,
key,
0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL,
&hKey, NULL) ;
if (lResult != ERROR_SUCCESS)
{
return FALSE ;
}
//写value
lResult = ::RegSetValueEx(hKey, NULL, 0, REG_SZ, (const BYTE*)value, (DWORD)(wcslen(value)+1)*2);
if (lResult != ERROR_SUCCESS)
{
::RegCloseKey(hKey);
return FALSE ;
}
::RegCloseKey(hKey);
return TRUE;
}
//删除注册表
//因为::RegDeleteKey只能删除没有子key的key,
//所以提供该函数,删除一个key时,先删除其所有子key
BOOL DelKey(HKEY key, const WCHAR* subkey)
{
HKEY hKeyChild ;
//open the key
LONG lRes = RegOpenKeyEx(key, subkey, 0,
KEY_ALL_ACCESS, &hKeyChild) ;
if (lRes != ERROR_SUCCESS)
{
return FALSE ;
}
WCHAR name[1024] ;
DWORD size = 2048;
while (::RegEnumKeyEx(hKeyChild, 0, name, &size, NULL, NULL, NULL, NULL) == S_OK)
{
//若还有子key,则递归调用该函数
BOOL bRes = DelKey(hKeyChild, name) ;
if (FALSE == bRes)
{
// 出错,Close key
::RegCloseKey(hKeyChild) ;
return FALSE;
}
size = 2048 ;
}
//该key没有子key,关闭该key,删除该key
::RegCloseKey(hKeyChild) ;
if(ERROR_SUCCESS != ::RegDeleteKey(key, subkey))
return FALSE;
return TRUE;
}
//供com库调用,创建factory类对象
extern "C" HRESULT __stdcall DllGetClassObject(const CLSID& clsid, const IID& iid, void **ppv)
{
if (clsid == CLSID_Facotry )
{
MyFactory *pFactory = new MyFactory;
if (pFactory == NULL) {
return E_OUTOFMEMORY ;
}
HRESULT result = pFactory->QueryInterface(iid, ppv);
return result;
} else {
return CLASS_E_CLASSNOTAVAILABLE;
}
}
//判断dll是否可以被卸载
extern "C" HRESULT __stdcall DllCanUnloadNow(void)
{
if ((g_ClassNum == 0) && (g_LockNumber == 0))
return S_OK;
else
return S_FALSE;
}
//使用regsvr32注册DLL时会被调用
//向注册表中写信息:1、DLL位置;2、线程模型
extern "C" HRESULT __stdcall DllRegisterServer()
{
//Get DLL name
WCHAR szName[1024];
if(0 == ::GetModuleFileName((HMODULE)g_hModule, szName, 2048))
return S_FALSE;
//Get key
LPOLESTR sKey = NULL;
//由GUID获得字符串
if(S_OK != ::StringFromCLSID(CLSID_Facotry, &sKey))
return S_FALSE;
WCHAR wKey_1[1024] = L"CLSID\\";
wcscat_s(wKey_1, sizeof(wKey_1), sKey);
wcscat_s(wKey_1, sizeof(wKey_1), L"\\InprocServer32");
//Set first key and value
SetKey(wKey_1, szName);
WCHAR wKey_2[1024] = L"CLSID\\";
wcscat_s(wKey_2, sizeof(wKey_2), sKey);
wcscat_s(wKey_2, sizeof(wKey_2), L"\\ProgID");
//Set second key and value
SetKey(wKey_2, L"MyCom");
::CoTaskMemFree(sKey);
return S_OK;
}
//使用regsvr32 -u卸载DLL时会被调用
//删除之前在注册表中写入的信息
extern "C" HRESULT __stdcall DllUnregisterServer()
{
//Get key
LPOLESTR sKey = NULL;
if(S_OK != ::StringFromCLSID(CLSID_Facotry, &sKey))
return S_FALSE;
WCHAR wKey[1024] = L"CLSID\\";
wcscat_s(wKey, sizeof(wKey), sKey);
if(FALSE == DelKey(HKEY_CLASSES_ROOT, wKey))
return S_FALSE;
return S_OK;
}
导出声明 Export.def
LIBRARY "TestCom"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
以上文件在同一工程中,生成一个DLL,在注册表中注册后,客户端可以使用自己定义的接口。
客户端代码:
#include <iostream>
#include <comdef.h>
#include "IMyInterface.h"
using namespace std;
//Interface ID
extern "C" const GUID IID_IMyInterface =
{ 0x54bf6569, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;
//Class Factory ID
extern "C" const GUID CLSID_Facotry =
{ 0x54bf6567, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;
int main()
{
IUnknown *pUnknown; //指向对象,不需要知道对象是谁
IMyInterface *pInterface; //接口指针
HRESULT hResult;
//初始化com库
if (CoInitialize(NULL) != S_OK) {
printf("Initialize COM library failed!\n");
return -1;
}
//创建类对象
hResult = CoCreateInstance(CLSID_Facotry, NULL,
CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pUnknown);
if (hResult != S_OK)
{
printf("Create object failed!\n");
return -2;
}
//获得接口
hResult = pUnknown->QueryInterface(IID_IMyInterface, (void **)&pInterface);
if (hResult != S_OK) {
pUnknown->Release();
printf("QueryInterface IDictionary failed!\n");
return -4;
}
//调用函数
pInterface->MyTest();
//释放
pInterface->Release();
pUnknown->Release();
//释放com库资源
CoUninitialize();
return 0;
}