二、COM组件的信息隐藏采用IUnknown接口来实现: 1、IUnknown接口功能简介: IUnknown意思是未知,即未知的接口。 采用这个名字是为了简单起见,所有的COM接口都需要继承IUnknown,因此若某个客户拥有一个IUnknown接口的指针,也就不知道接口到底是什么类型的,只需要知道此接口可以用来查询其他接口。 IUnknown接口有一个用来查询接口的函数QueryInterface。 <1>、QueryInterface返回IUnknown接口时,总是返回同一IUnknown指针。 每个组件实例只有一个IUnknown接口,因此当查询组件实例的IUnknown接口时,不论通过那个接口查询,得到的均将是同一指针值。 总结:遵循这一规则能确定两个接口是否指向同一组件。
QueryInterface 接口查询 IUnknown: 所有的COM接口均需要继承IUnknown接口。 这使得所有的COM接口都被当成是IUnknown接口来处理。由于所有的接口指针也将是IUnknown指针,客户并不需要单独维护一个代表组件的指针,它所关心的将仅仅是接口的指针。 一个有趣的例子是返回IUnknown指针的情形: *ppv=static_cast<IUnknown* >(this);// Ambiguous. 含糊不清的 但是将this指针转化成IUnknown* 是不明确的。这是由于IX和IY都是从IUnknown继承得到的。 *pI1=NULL; IUnknown *pI2=NULL; //Get IUnknown pointer from pIX pIX->QueryInterface(IID_IUnknown,(
IUnknown:未直接口 一个特殊的接口,所有COM接口都继承IUnKnown这个接口。 interface IUnknown{ HRESULT QueryInterface( // 查询com对象的其他接口指针 [in] REFIID iid, // 接口ID [out] __interface IAntiCheat:public IUnknown{ // 接口虚函数 virtual BOOL IAntiCheat_PUBG(string& Tag); }; // 定义一个类 Inherit 继承 IAntiCheat 接口 class Inherit:public IAntiCheat{ public: // 声明 IUnknown 成员 virtual Inherit 重载的 IUnknown 指针 IUnknown* pI = static_cast<IAntiCheat*>(new Inherit); // 增加计数 pI->AddRef
《COM技术内幕》 §3 —— QueryInterface函数 §3 —— IUnknown.CPP代码清单: // //IUnkown.cpp // // #include <iostream.h include <objbase.h> void trace(const char * msg) {cout << msg << endl ;} //Interface interface IX : IUnknown { virtual void __stdcall Fx() = 0; }; interface IY : IUnknown { virtual void __stdcall Fy() = 0; }; interface IZ : IUnknown { virtual void __stdcall from IY.”); IUnknown* pIUknownFromIY = NULL; hr = pIY->QueryInterface(IID_IUnknown
接口 IUnknown 的定义在 Win32 SDK 的头文件 1 见UNKNWN . H 中。 第二部分是组件的实现。类 CA 实现了一个支持 IX 和 IY 接口的组件。 客户可以使用此函数来创建类 CA 所代表的组件并返回一个指向其 IUnknown 接口的指针。 在定义好 CreateInstance函数之后,下面定义的是各接口的 IID 结构。 { virtual void _stdcall Fx() = 0 ; }; interface IY:IUnknown { virtual void _stdcall Fy() = 0 ; }; interface IZ:IUnknown { virtual void _stdcall Fz() = 0 ) { trace( “ QueryInterface:return pointer to IUnknown “ ); * ppv = static_cast
对于同一个对象的不同的接口指针,查询得到的 IUnknown 接口必须完全相同。 也就是说,每个对象的 IUnknown 接口指针是唯一的,因此,对两个接口指针,我们可以通过判断其查询到的 IUnknown 接口是否相等来判断它们是否指向同一个对象。 对象 A 也需要为支持聚合做一些事情——实现一个委托 IUnknown 接口和一个非委托 IUnknown 接口。 按照通常使用方式实现的 IUnknown 为非委托 IUnknown,而委托 IUnknown 在不同的情况下有不同的行为:当对象被正常使用时,委托 IUnknown 把调用传递给对象的非委托 IUnknown ;当对象被聚合使用时,委托 IUnknown 把调用传递到外部对象的 IUnknown 接口,即对象被创建时传递进来的 pUnknownOuter 参数,并且,这时外部对象通过非委托 IUnknown
具体来讲,这些规则是: QueryInterface返回的总是同一 IUnknown指针。 若客户曾经获取过某个接口,那么它将总能获取此接口。 客户可以再次获取已经拥有的接口。 // 组件的实现只有一个IUnknown接口 BOOL SameComponents(IX * pIX,IY * pIY) { IUnknown * pI1 = NULL; IUnknown * pI2 = NULL; // 从PIX得到IUnknown接口 pIX -> QueryInterface(IID_IUnknown ,( void ** ) & pI1); // 从pIY得到IUnknown接口 pIY -> QueryInterface(IID_IUnknown,(
STDMETHODIMP CMathFactory::QueryInterface(REFIID riid,void ** ppv) { *ppv=NULL; if(riid==IID_IUnknown //if(riid==IID_IUnknown||riid==IID_IClassFactory || riid == IID_IDispatch)//改变 { *ppv=static_cast< IClassFactory *>(this); reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; } else return else if(riid == IID_IAdvancedMath) *ppv = static_cast<IAdvancedMath *>(this); else if(riid == IID_IUnknown else { *ppv = 0; return E_NOINTERFACE; } reinterpret_cast<IUnknown *>(*ppv)->AddRef(); //这里要这样是因为引用计数是针对组件的
STDMETHODIMP CMathFactory::QueryInterface(REFIID riid,void ** ppv) { *ppv=NULL; if(riid==IID_IUnknown //if(riid==IID_IUnknown||riid==IID_IClassFactory || riid == IID_IDispatch)//改变 { *ppv=static_cast< IClassFactory *>(this); reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; } else return else if(riid == IID_IAdvancedMath) *ppv = static_cast<IAdvancedMath *>(this); else if(riid == IID_IUnknown else { *ppv = 0; return E_NOINTERFACE; } reinterpret_cast<IUnknown *>(*ppv)->AddRef(); //这里要这样是因为引用计数是针对组件的
在查询接口之前,先检查查询的接口IID,如果请求的是IUnknown,从表中取出第一个表项立即返回,不需要偏历表的剩余部分。 接口 { IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw); pUnk bBlind); IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw); >pFunc(pv, __uuidof(IUnknown), (void**)pp); if (*pp ! * p = *(IUnknown**)((DWORD_PTR)pv + dw); if (p !
interface IClassFactory : public IUnknown{ virtual HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID IUnknown接口IUnknown接口是所有COM接口的基接口,它定义了三个基本方法:QueryInterface、AddRef和Release。 interface IUnknown{ virtual HRESULT QueryInterface(REFIID riid, void **ppvObject) = 0; virtual 示例代码// 客户端代码IClassFactory *pClassFactory = nullptr;IUnknown *pUnknown = nullptr;// 获取类厂HRESULT hr = CoGetClassObject *)&pClassFactory);if (SUCCEEDED(hr)){ // 创建对象 hr = pClassFactory->CreateInstance(nullptr, IID_IUnknown
使用方法: 假如知道一个指向IUnknown接口的指针pI,传给它一个接口标志符即可 例如: void Foo(IUnknown * pI) QueryInterface函数有关QueryInterface IID & iid, void ** ppv) QueryInterface函数有关QueryInterface函数 { QueryInterface函数 if(iid == IID_IUnknown *ppv = static_cast<IY *>(this); QueryInterface函数 } QueryInterface函数 else if(iid == IID_IUnknown return E_NOINTERFACE; QueryInterface函数 } QueryInterface函数 QueryInterface函数 static_cast <IUnknown
1.2枚举器组件的实现步骤 即实现枚举器组件的IUnknown接口的方法,IUnknown接口的实现需要两个步骤。 * CComObject、CComAggObject 或 CComPolyObject 实现 IUnknown 方法。 * CComObjectRoot 或 CComObjectRootEx 维护管理IUnknown的引用计数和外部指针。 因此要枚举器组件的实现同样需要从CComObjectRootEx派生实现IUnknown接口的引用计数,然后将派生类作为CComObject或CComAggObject等得模版参数,实现IUnknown } std::cout << std::endl; CComPtr<IEnumNums> spEnum; CComPtr<IUnknown
HRESULT __stdcall CA::NondelegatingQueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown ,查询IUnknown接口时对CA的this指针进行了强制转换,转换成了非委托未知接口。 书中特意强调“通过这一转换,我们可以保证返回的是一个非委托的未知接口指针,当向委托接口指针查询IID_IUnknown时,他返回的将总是一个指向其自身的指针”。 如果没有,那么再看下IUnknown的数据结构:(注意这不是系统中的定义,而是对IUnknown的示意,不过也差不多就是了) interface IUnknown { virtual HRESULT 外部组件CB创建CA时需要获取内部组件CA的IUnknown指针,创建过程中使用NondelegatingQueryInterface进行IUnknown的获取,该函数中将指向CA组件自己的指针强制转换成了非委托未知接口的指针
IClassFactory 接口IClassFactory 接口是所有类厂必须实现的接口,它的定义如下:interface IClassFactory : public IUnknown{ virtual HRESULT STDMETHODCALLTYPE CreateInstance( _In_opt_ IUnknown *pUnkOuter, _In_ REFIID DllGetClassObject 实现示例:#include <objbase.h>class CMyClassFactory : public IClassFactory{public: // IUnknown STDMETHOD_(ULONG, Release)() override; // IClassFactory methods STDMETHOD(CreateInstance)(IUnknown
,大部分接口直接继承自 IUnknown,还有部分通过继承 IDispatch 或其他接口间接的继承自 IUnknown。 继承在内存布局上实际上就是在父类的内存结构基础上进行新增,所以不继承直接将 IUnknown 中的方法搬过来也行。 C 里面有 IUnknown,Rust 里也不需要我们从 IUnknown 开始实现,实际上在 windows-rs 和 winapi 这两个 crate 中都有实现,但是实现方式上有所不同。 : *mut IUnknown = ptr::null_mut(); windows-rs let p: IUnknown; 可以看出来 winapi 的接口定义方式更符合 c 的接口调用风格,而 windows-rs >); transparent 可以理解为透传,相当于:pub type IUnknown = std::ptr::NonNull<std::ffi::c_void>,所以 let p: IUnknown
内部组件的IUnknown接口的实现 tear-off派生类不是CComObject,而是CComTearOffObject,CComTearOffObject知道基类的m_pOwner成员,并在构造函数中初始化 } }; 拥有缓存的tear-off组件的实现 template <class contained> class CComCachedTearOffObject : public IUnknown HRESULT hRes = S_OK; if (InlineIsEqualUnknown(iid)) { *ppvObject = (void*)(IUnknown AddRef(); #ifdef _ATL_DEBUG_INTERFACES _AtlDebugInterfacesModule.AddThunk((IUnknown STDMETHOD(PrintA)(){ cout<<"执行PrintA函数"<<endl; return 0; }; CComPtr<IUnknown
0x4EAE, { 0xB1,0x72,0x5F,0xEC,0x52,0xA2,0xA4,0xFD } } }; DECLARE_INTERFACE_(IFlexibleTaskbarPinnedList, IUnknown UNICODE) setlocale(LC_CTYPE, ".ACP"); #endif HRESULT hr = CoInitialize(NULL); if (hr == S_OK) { IUnknown *punkn = NULL; hr = CoCreateInstance(CLSID_TaskbandPin, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown,
OpenRowset函数原型如下: HRESULT OpenRowset( IUnknown *pUnkOuter, DBID *pTableID, //打开表时使用该结构 ULONG cPropertySets, //给对应返回对象设置的属性集的个数 DBPROPSET rgPropertySets[], //给对应对象设置的属性集 IUnknown ; HRESULT hRes = pIOpenRowset->OpenRowset(NULL, &dbId, NULL, IID_IRowset, 1, dbRowsetPropset, (IUnknown
IUnknown接口和QueryInterface在COM中,每个接口都继承自IUnknown接口,IUnknown提供了三个基本方法:QueryInterface、AddRef和Release。