《COM技术内幕》 学习二

上一部分内容介绍了COM最核心的概念接口,当然那不是真正意义上的COM接口,真正的COM接口都是从IUnknown接口继承的,并且其vtbl指针的前三个函数必须是:QueryInterface()AddRef()Release()

1IUnknown接口

       首先一切接口都要继承自IUnknown接口,其在Unknwn.h中的定义摘录如下:

interface Iunknown{

virtualHRESULT _stdcall QueryInterface(const IID& iid,void** pv);

virtualULONG _stdcall AddRef(){return 0;}

virtualULONG _stdcall Release(){return 0;}

}

        第一个函数实现了接口间的查询也就是切换工作,第二个函数与第三个函数作用相反,一个增加组件对象的引用计数一个减少组件对象的引用计数。

创建一个Iunknow接口的方法如下:

Iunknown* CreateInstace();

2QueryInterface

        Returns a pointer to a specifiedinterface on an object to which a client currently holds an interface pointer.

        客户可以通过此函数来查询某个组件是否支持某个特定的接口,如果支持则返回该接口的指针。函数原型如下:
 

HRESULT QueryInterface(
  REFIID
iid,       //Identifier of the requestedinterface
  void ** ppvObject //Addressof output variable that receives the
                     //interface pointerrequested in iid
);

     参数

iid

[in]Identifier of the interface being requested.

输入参数,所请求的接口的IDIID=interface ID

ppvObject

[out]Address of pointer variable that receives the interface pointer requested inriid. Upon successful return, *ppvObject contains the requested interfacepointer to the object. If the object does not support the interface specifiediniid, *ppvObjectis set to NULL.

输出参数:所请求接口的指针地址

    使用举例:

voidfoo(IUnknown* pI)

{

//所请求的接口

IX* pIX=NULL;

HRESULThr=pIX->QueryInterface(IID_IX,(void**)&pIX);

//检查返回值

if(SUCCEEDED(hr)){

pIX->Fx();

}

}

        可以卡到在使用QI前我们要先声明一个所请求的接口指针并将其赋值为NULL。另外检查时候成功QI时一定要使用SUCCEEDED宏和FAILED宏,而不用直接hr==true这种方式。原因在于,首先这里的HRESULTS_OK  NOERROR S_FALSE E_NEXPECTED等多个值,此外S_FALSE被定义成1S_OK被定义成0,这和C++中的编译方法恰好是相反的。故HRESULT的返回值判断一定要采用上述两个宏。

 

下面我们给出一个完成的例子来更好的理解QI


#include <iostream>
#include <ObjBase.h>
using namespace std;
void trace(const char* pMsg)
{
	cout<<pMsg<<endl;
}
// 接口 [3/13/2014 pan]
interface IX:IUnknown
{
	virtual void _stdcall Fx()=0;
};
interface IY:IUnknown
{
	virtual void _stdcall Fy()=0;
};
interface IZ:IUnknown
{
	virtual void _stdcall Fz()=0;
};
//GUID
extern const IID IID_IX;
extern const IID IID_IY;
extern const IID IID_IZ;
//组件
class CA:public IX,public IY
{
	//IUnkonw 实现部分
	virtual HRESULT _stdcall QueryInterface(const IID& iid,void** pv);
	virtual ULONG _stdcall AddRef(){return 0;}
	virtual ULONG _stdcall Release(){return 0;}
	//IX 接口实现
	virtual void _stdcall Fx(){cout<<"Fx"<<endl;}
	//IY 接口实现
	virtual void _stdcall Fy(){cout<<"Fy"<<endl;}
};
//QI的实现细节
HRESULT _stdcall CA::QueryInterface(const IID& iid,void** ppv)
{
	if(iid==IID_IUnknown){
		trace("接口查询,返回IUnkonw接口");
		*ppv=static_cast<IX*>(this);
	}
	else if(iid==IID_IX){
		trace("接口查询,返回IX接口");
		*ppv=static_cast<IX*>(this);
	}
	else if(iid==IID_IY){
		trace("接口查询,返回IY接口");
		*ppv=static_cast<IY*>(this);
	}
	else{
		trace("接口查询,不支持该接口");
		*ppv=NULL;
		return E_NOINTERFACE;
	}
	reinterpret_cast<IUnknown*>(*ppv)->AddRef();
	return S_OK;
}
//////////////////////////////////////////////////////////////////////////
//主函数
IUnknown* CreateInstance()
{
	IUnknown* pI=static_cast<IX*>(new CA);
	pI->AddRef();
	return pI;
}
//////////////////////////////////////////////////////////////////////////
//IID
static const IID IID_IX={0x32bb8320,0xb41b,0x11cf,
{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};
static const IID IID_IY={0x32bb8321,0xb41b,0x11cf,
{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};
static const IID IID_IZ={0x32bb8322,0xb41b,0x11cf,
{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};
//////////////////////////////////////////////////////////////////////////
//客户
int _tmain(int argc, _TCHAR* argv[])
{
	HRESULT hr;
	trace("Client:   得到IUnknown接口");
	IUnknown* pIUnknown=CreateInstance();
	trace("Client:   得到IX接口");
	IX* pIX=NULL;
	hr=pIUnknown->QueryInterface(IID_IX,(void**)&pIX);
	if(SUCCEEDED(hr)){
		trace("Client:   成功得到IX接口");
		pIX->Fx();  
	}
	trace("Client:   得到IY接口");
	IY* pIY=NULL;
	hr=pIUnknown->QueryInterface(IID_IY,(void**)&pIY);
	if(SUCCEEDED(hr)){
		trace("Client:   成功得到IY接口");
		pIY->Fy();
	}
	trace("Client:    请求不支持的接口");
	IZ* pIZ=NULL;
	hr=pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);
	if(SUCCEEDED(hr)){
		trace("Clietn:    Succeed in getting interface IZ");
		pIZ->Fz();
	}
	else{
		trace("Client:   Could not get interface IZ");
	}
	trace("Clietn:   Get interface IY  from IX");
	IY* pIYfromIX=NULL;
	hr=pIX->QueryInterface(IID_IY,(void**)&pIYfromIX);
	if(SUCCEEDED(hr)){
		trace("Client:    Succeed getting IY");
		pIYfromIX->Fy();
	}
	trace("Client:    get interface IUnknow  from IY");
	IUnknown* pIUnknowfromIY=NULL;
	hr=pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknowfromIY);
	if(SUCCEEDED(hr)){
		
	}
	return 0;
}

         QI的细节中我们可以观察得到这个结论——QueryInterface定义了组件。

         从上面的具体实现细节可以看到,组件所支持的接口集就是QueryInterface能够为之返回的接口指针的那些接口。(原因在于,除了QI之外我们没有其他方法去获得组件接口的指针,也就无法使用QI不支持的接口所对应的函数方法)这一定是由QueryInterface的实现细节所决定的,而不是有实现组件的C++类(上面例子中的class CA)所决定的。由于客户并不知道QueryInterface的实现,因此,它将无法知道一个组件所支持的所有接口。客户了解组件所支持的接口的唯一方法就是进行查询。这一点同C++有着很大的不同(C++可以同头文件来查看类的所有成员)。

         结论:一个组件仅仅是由QueryInterface的实现决定的。


你可能感兴趣的:(C++,com,arcgis)