COM初始化
if (CoInitialize(NULL) != S_OK)CoUninitialize();
COM库的内存管理
由于COM组件与客户程序,COM库三者之间内存交互(分配和释放可能不在同一个模块),所以必须使用一致的内在管理器。COM提供的内在管理器标准:
class IMalloc:public IUnknow
{
void* Alloc(ULONG cb) = 0;
void* Realloc(void *pv, ULONG cb) = 0;
void* Free(void *pv) = 0;
ULONG GetSize(void *pv) = 0;
int DidAlloc(void *pv) = 0;
void HeapMinimize() = 0;
}
ANSI与UNICODE字符转换:
#include <comutil.h>
#pragma comment(lib, "comsuppw.lib")
namespace _com_util
{
char* __stdcall ConvertBSTRToString(
BSTR pSrc
BSTR __stdcall ConvertStringToBSTR(
const char* pSrc
)
);
}
进程内组件的装载与卸载:
客户程序调用COM库的CoCreateInstance或者CoGetClassObject函数创建COM对象,在 CoGetClassObject函数中,COM库根据系统注册表中的信息,找到类标识符CLSID对应的组件程序(DLL文件)的全路径,然后调用 LoadLibrary函数(CoLoadLibrary),并调用组件程序中的DllGetClassObject引出函 数,DllGetClassObject函数创建相应的类厂对象,并返回类厂对象的IClassFactory接口。客户程序继续调用类厂对象的 CreateInstance成员函数,由它负责COM对象的创建工作。
客户程序调用CoFreeUnusedLibraryes函数卸载组件,检测当前进程中的所有组件程序,当发现某个组件的DllCanUnloadNow函数返回TRUE时,就调用FreeLibrary(CoFreeLibrary)函数把组件从内存中卸出。
COM对象的接口原则
为了规范COM的接口机制,微软向COM开发者发布了COM对象的接口原则。
(1)IUnknown接口的等价性
同一个对象的Queryinterface的IID_IUnknown查询出来的IUnknown指针值应当相等。也就是说,每个对象的IUnknown指是唯一的。我们可以通过判断IUnknown指针是否相等来判断它们是否指向同一个对象。
(2)接口自反性,对一个接口来说,查询它本身应该是允许的。
设pPsychics是已赋值IPsychics的接口。那么pPsychics->QueryInterface(IID_IPsychics,(void **) &XXX);应当成功。
(3)接口对称性,当我们从一个接口查询到另一个接口时,那么我们再从结果接口还可以查询到原来的接口。
如 果pSrcPsychics->QueryInterface(IID_IDynamics,(void **) &pDynamics);成功的话。那么pDynamics->QueryInterface(IID_IPsychics,(void **) &pTarget);也相当成功。
(4)接口传递性。如果我们从第一个接口查询到了第二个接口,又从第二个接口查询到了第三接口。则我们应该能够从第三个接口查询到第一个接口。其它依此类推。
(5)接口查询时间无关性。当我们在某时查询到一个接口,那么在任意时刻也应该查询到这个接口。
HRESULT:
类别码(30~31位):00 调用成功,01 包含一些信息,10 警告,11错误。
自定义标志位(第29位):1 自定义类型。
操作码(16~28位):标识结果操作来源。
操作结果码(0~15位):反应操作状态。
S_OK,函数执行成功,S_FALSE,执行成功,返回FALSE,E_FAIL,执行失败,原因未知,E_NOMEMORY,内在分配失败,E_NOTIMPL,没有实现成员函数,E_NOTINTERFACE,未实现指定接口,用于QueryInterface.
使用FormatMessage函数可以获得该结果的说明字符。
使用SUCESSED或FAILED宏来对HRESULT类型的结果值作出成功或失败判断。
COM库的重用
1.包容
1. Class CB //保存包容组件接口
{ private:ISomeInterface *m_pSomeInterface; }
2. HRESULT result = ::CoCreateInstance(CLSID_CompA, NULL, //在Init()中初始化组件A
CLSCTX_INPROC_SERVER,
IID_ISomeInterface, (void **)& m_pSomeInterface) ;
3.HRESULT __stdcall CB::SomeFunction() { return m_pSomeInterface->SomeFunction();
4.HRESULT CBFactory::CreateInterface(IUnknow *pUnknowOuter, const IID& iid, void **pv)
{ CB *pObj = new CB(); HRESULT hr = pObj->Init(); hr = pObj->QueryInterface(iid, pv);}
2.聚合
1. Class CB //保存包容组件IUnknown接口
{ private:IUnknown *m_pUnknownInner; }
2. HRESULT CB::QueryInterface(const IID& iid, void **ppv)
{ if ( iid == IID_SomeInterface ) { return m_pUnknownInner->QueryInterface(iid, ppv) ; } //转给内部对象A的QueryInterface
3.HRESULT CA::NondelegationQueryInterface(const IID& iid, void **ppv){//非委托IUnknown查询接口,COM不是通过类名来识别接口,而是通过vtable来调用接口成员函数。
if ( iid == IID_IUnknown ) { *ppv = (INondelegatingUnknown *) this ; ((IUnknown *)(*ppv))->AddRef() ; }
else if ( iid == IID_SomeInterface ) { *ppv = (ISomeInterface *) this ; ((ISomeInterface *)(*ppv))->AddRef() ; }
else { *ppv = NULL; return E_NOINTERFACE ; } return S_OK;}
4.HRESULT CA::QueryInterface(const IID& iid, void **ppv){ //委托IUnknown查询接口
if ( m_pUnknownOuter != NULL ) return m_pUnknownOuter->QueryInterface(iid, ppv); //如果被聚合,返回外部对象的接口。
else return NondelegationQueryInterface(iid, ppv);}
5.HRESULT CB::Init(){ //创建对象A
IUnknown *pUnknownOuter = (IUnknown *)this;
HRESULT result = ::CoCreateInstance(CLSID_CompA, pUnknownOuter, //传递IUnknown说明被聚合
CLSCTX_INPROC_SERVER,
IID_IUnknown, (void **)& m_pUnknownInner) ;//得到组件A的非委托IUnknown
result = m_pUnknownInner->QueryInterface(IID_SomeInterface, (void **)&m_pSomeInterface); //检测是否有接口SomeInterface,调用的其实是NondelegationQueryInterface()函数
if (FAILED(result)) { m_pUnknownInner->Release(); return E_FAIL; }
6. CA::CA (IUnknown *pUnknownOuter){ m_pUnknownOuter = pUnknownOuter; } //构造时保存B的IUnknown接口
7. HRESULT CAFactory::CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv){
if ( ( pUnknownOuter != NULL ) && ( iid != IID_IUnknown ) ) { return CLASS_E_NOAGGREGATION; } //如果被聚合,IID必须是IUnknown.防止暴露其它接口。
CA *pObj=new CA (pUnknownOuter);
HRESULT hr = pObj->NondelegationQueryInterface(iid, ppv); if (hr != S_OK) { delete pObj; } return hr; } //必须返回非委托IUnknown接口
CB *pObj = new CB (); if (NULL==pObj) return hr;
pObj->AddRef(); // The Reference count of pObj is 1
HRESULT hr = pObj->Init();
hr=pObj->QueryInterface(iid, ppv);
pObj->Release(); // The Reference count of pObj is 1
return hr;}