COM的Interface: 1)继承自IUnknown。2)vtbl中前3个函数:QuereyInterface,AddRef和Release。
interface IUnknown
{
virtual HRESULT __stdcall QueryInterface(const IID && iid, void * *ppv) = 0;
virtual ULONG __stdcall AddRef() = 0;
virtual ULONG __stdcall Release() = 0;
}
一,引用计数简介:
AddRef 和 Release实现的是一种名为引用计数器的内存管理技术。引用计数是使组件能将自身删除最简单也是效率最高的方法。
计数器使用规则:
1、返回之前调用AddRef();
2、使用完接口调用Release();
3、赋值之后调用AddRef。再将一个接口指针赋给另一个接口指针时,应调用AddRef。
二,QueryInterface函数
HRESULT __stdcall QueryInterface(const IID&iid,void **ppv);
stdcall约定:1)参数从右向左压入栈,2)函数自身修改堆栈 3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸。
iid:接口标志符。
ppv:用来存放所请求接口的地址。
返回值:S_OK 或 E_NOINTERFACE, 用SUCEEDED或者FAILED宏验证是否成功。HRESULT为一个可以分为三个域的32位值。
使用方法:假如知道一个指向IUnknown接口的指针pI,传给它一个接口标志符即可
例如:
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![None.gif](/Images/OutliningIndicators/None.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
![dot.gif](/Images/dot.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](/Images/OutliningIndicators/ContractedSubBlock.gif)
![dot.gif](/Images/dot.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockEnd.gif](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![ExpandedBlockEnd.gif](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![None.gif](/Images/OutliningIndicators/None.gif)
![None.gif](/Images/OutliningIndicators/None.gif)
![None.gif](/Images/OutliningIndicators/None.gif)
![None.gif](/Images/OutliningIndicators/None.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
![dot.gif](/Images/dot.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](/Images/OutliningIndicators/ContractedSubBlock.gif)
![dot.gif](/Images/dot.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockEnd.gif](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](/Images/OutliningIndicators/ContractedSubBlock.gif)
![dot.gif](/Images/dot.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockEnd.gif](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](/Images/OutliningIndicators/ContractedSubBlock.gif)
![dot.gif](/Images/dot.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockEnd.gif](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](/Images/OutliningIndicators/ContractedSubBlock.gif)
![dot.gif](/Images/dot.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockEnd.gif](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
![ExpandedBlockEnd.gif](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![None.gif](/Images/OutliningIndicators/None.gif)
多重继承及类型转换:通常将一种类型的指针转换为另一种类型并不会改变它的值。为了支持多重继承,某些情况下,C++必须改变类指针的值。假如一个类定义如下:
class CA: public IX,public IY{...};
下图显示了CA对象的内存结构。
GUID:是一个128位16字节的GUID结构。
MS提供两个工具生成GUID,一个是UUIDGEN。EXE 另一个是 GUIDGEN.EXE。
定义一个GUID:
extern "C" const IID IID_IX = { .....}
声明:
extern "C" congst IID IID_IX;
或者宏
DEFINE_GUID(IID_IX, .....);
将一个GUID做为组件标志符。
IUnknown * CoCreateInstance(.....);
com中用以标志组件的GUID被称为类标志符,用CLSID与IID区分。
通过引用传递GUID。
注册表的使用:
CoCreateInstance将利用CLSID作为关键字在注册表中找所需文件名。
com只用了这册表的一个分支:HKEY_CLASSES_ROOT, 其下有一个CLSID关键字,其下列有系统中安装的所有组件的CLSID。
com库
所有com组件和客户需要一些相同操作,为保证这些操作是按标准来的,com定义了一个函数库来实现所有这些操作。此函数库是在 ole32.dll中实现的。在使用静态链接时,可以使用ole32.lib。
com库的初始化,除了CoBuildVersion必须首先调用CoInitialize来初始化com库函数。当进程不再需要库函数时,必须调用CoUninitialize。
对于一个进程只需初始化一次com库,如果多次初始化,必须保证每个都有对应的CoUninitialize.
OleInitialize基于com,增添了更多功能。
类厂
创建一个组件最简单的方法
1,创建一个组件最简单的方法:用 CoCreateInstanc函数.因此CoCreateInstanc也是创建组件用的最多的一种方法.但其灵活性有限,不能满足所有组件的需求. 这就是为什么要引入类厂.
所有组件都是用类厂创建的. CoCreateInstanc也是按照一般方法通过类厂来创建组件的.
2. CoCreateInstanc
com库中包含一个创建组件的名为CoCreateInstanc的函数.
定义如下:
HRESULT __stdcall CoCreateInstanc(
const CLSID & clsid,
IUnknown * pIUnknown, //outer component
DWORD dwClsContext, //server context
const IID &iid,
void ** ppv
);
看以看到有四个输入参数一个输出参数.
第一个待创建组件的CLSID.
第二个是用来聚合组件的.
第三个是用来限定所创建组件的执行上下文.
第四个是组件待使用的接口的IID;
CoCreateInstanc将在最后一个参数中返回此接口指针.
将一个IID 传给CoCreateInstanc,客户将无需在创建组件后再调用QueryInterface;
3.CoCreateInstanc的使用
IX *pIX = NULL;
HRESULT hr = CoCreateInstanc(CLSID_component1,
NULL,
CLSCTX_INPROC_SERVER,
IID_IX,
(void **)&pIX);
if(SUCCEEDED(hr))
{
pIX->Fx();
pIX->Release();
}
该例子创建了一个由CLSID_component1标志的组件.
此处不需要聚合组件,因此第二个参数之NULL;
我们用IID_IX来表示希望得到接口IIX的指针,CoCreateInstanc将把此指针返回在pIX变量中,若成功返回则可用该接口.Release()表示对接口和组件的使用已经完成.
类上下文
CoCreateInstanc的第三个参数可以控制所创建的组件是在客户创建的进程中运行,还是在不同进程中.或者在另外一台机器上.
该参数可以由一下几个值组合:
CLSCTX_INPROC_SERVER,
CLSCTX_INPROC_HANDLER,
CLSCTX_LOCAL_SERVER,
CLSCTX_REMOTE_SERVER;
客户可以在三种不同上下文中使用某个组件: 进程中,本地,远程.
为什么用类厂
1.CoCreateInstance的不灵活.
我们知道 CoCreateInstance创建组件的过程是:传给他一个CLSID,然后它创建相应的组件.,并返回所请求接口的指针,.CoCreateInstance没有提供一种可以控制组件创建过程的的方法.
存在问题: 我们不能控制组件创建过程.
解决方案: 使用一个专门创建组件的组件,既 类厂.
2.类厂.
实际上CoCreateInstance并没有创建组件,而是创建了一个被称为类厂的组件.
类厂唯一功能就是创建其他组件.
精确点讲就是.某个特定类厂将创建某个特定CLSID相应的组件. 客户可以通过类厂所支持的接口来对类厂创建组件的过程加以控制. 创建组件的标准接口是 IClassFactory,用CoCreateInstance创建的组件实际上是通过IClassFactory来创建的.
3.CoGetClassObject
要创建一个组件,首先要创建类厂本身.
CoCreateInstance()用来创建与指定CLSID的组件,并返回指向组件某个接口的指针.
与CoCreateInstance类似,用CoGetClassObject() 来创建与指定CLSID的类厂,并返回指向类厂某个接口的指针.
函树定义如下:
HRESULT __stdcall 用CoGetClassObject(
const CLSID & clsid,
DWORD dwClsContext, //server context
COSERVERINFO *pServerInfo, //Resevred for DCOM
const IID &iid,
void ** ppv
);
客户用 CoCreateInstanc 所返回的指针来创建所需的组件,这个指针通常是一个IClassFactory指针.
3.IClassFactory
类厂所支持的用于创建组件的标准接口是 IClassFactory.大部分组件可以通过它来创建.
声明如下:
interface IClassFactory :IUnknown
{
HRESULT stdcall CreateInstace(IUnknown * pUnknownOuter,
const IID &iid,
void **ppv);
HRESULT stdcall LockServer(bool bLock);
};
4.为什么要用用CoGetClassObject
大多数情况下使用 CoCreateInstanc 创建组件,而不使用CoGetClassObject。但是在以下两种情况 下应使用CoGetClassObject而不使用 CoCreateInstanc 。
1、若想用不同于 IClassFactory 的某个创建接口来创建组件,则必须使用CoGetClassObject。
2、若需要创建同一组件的多个不同实例,使用CoGetClassObject可以取得较高效率。因为这样只需相应组件的类厂一次,而CoCreateInstanc 需要为每个实例分别创建并释放相应的类厂。
另外,CoGetClassObject可以对组件的创建过程进行更多的控制。
类厂实现
1、CoGetClassObject使用DllGetClassObject来创建类厂。声明如下:
STDAPI DllGetClassObject (
const CLSID &clsid,
const IID & iid,
void ** ppv );
2、组件的创建过程
客户调用 CoGetClassObject----〉com库调用DllGetClassObject ------>返回给客户IClassFactory------>客户调用 IClassFactory::CreateInstance------>返回给客户 IX---------〉客户调用IX::FX.