QueryInterface详解 COM

QueryInterface详解 COMQueryInterface接口查询IUnknown:      所有的COM接口均需要继承IUnknown接口。因此,若某个用户拥有一个IUnknown接口指针,它并不需要知道它所拥有的接口指针到底是什么类型的,而只需要通过此接口就可以用来查询其他接口就行了。      由于所有的COM接口都继承了IUnknown,每个接口的vbtl的前三项都是QueryInterface,A

大家好,又见面了,我是你们的朋友全栈君。

QueryInterface 接口查询

IUnknown:

       所有的COM接口均需要继承IUnknown接口。因此,若某个用户拥有一个IUnknown接口指针,它并不需要知道它所拥有的接口指针到底是什么类型的,而只需要通过此接口就可以用来查询其他接口就行了。

       由于所有的COM接口都继承了IUnknown,每个接口的vbtl的前三项都是QueryInterface,AddRef,Release(如图1所示)。这使得所有的COM接口都被当成是IUnknown接口来处理。由于所有的接口指针也将是IUnknown指针,客户并不需要单独维护一个代表组件的指针,它所关心的将仅仅是接口的指针。

QueryInterface详解 COM

       HRESULT__stdcall QueryInterface(const IID& iid,void** ppv);

       iid:接口标示符结构。

       ppv:用来存放请求的接口指针的地址

       ex:IX *pIX=NULL;

        HRESULT hr=pI->QueryInterface(IID_IX,(void**)&pIX);

          If(SUCCEEDED(hr))

{

       pIX->Fx();

}

Else

{

       pIX=NULL;

}

在调用QueryInterface之前,我们将pIX初始化为NULL,这是一种比较好的编程习惯,但由于QueryInterface是由程序员而不是由系统实现的,因此在查询失败的时候,将此指针置为NULL.

 

QueryInterface 的实现

Interface IX: IUnknown{};

Interface IY: IUnknown{};

Class CA:public IX,public IY{};

类CA及其接口的继承关系如图(2):

QueryInterface详解 COM

QueryInterface(const IID& iid,void**ppv);

//注意由QueryInterface 返回的IUnknown指针一定要和

//CreateInstance 返回的IUnknown指针一样。

//这里均采用了static_cast<IX* >(this);

具体实现:

HRESULT __stdcall CA::QueryInterface(constIID& iid,void** ppv)

{

       If(iid==IID_IUnknown)

       {

              //TheClient wants the IUnknown interface

              *ppv=static_cast<IX*>(this);

}

Elseif(iid==IID_IX)

{    

       *ppv=static_cast<IX* >(this);

}

Elseif(iid==IID_IY)

{

       *ppv=static_cast<IY *>(this);

}

Else

{

       //we don’t support the interface theclient

//wants. Be sure to set the resulting pointer

// to NULL.

*ppv=NULL;

Return E_NOINTERFACE;

}

Static_cast<IUnknown*>(*ppv)->AddRef();

Return S_OK;

}

   在客户所查询的接口不被支持的时候,QueryInterface将*ppv置为NULL。这是COM规范所要求的,而且从其本身来讲也是一种很好的做法。

类型转换:

这里将this指针转化成为一个IX指针所得到的地址与将其转化成为一个IY指针所得到的地址是不同的,例如:

 Static_cast<IX*>(this)!=static_cast<IY* >(this);

Static_cast<void*>(this)!=static_cast<IY*>(this);

换用某些程序员更加常用的旧方式的类型转化,即使:

(IX*)this!=(IY*)this;

(void*)this!=(IY*)this;

this指针进行类型转换将会导致其值的改变,这一点主要是由于C++的实现方式。

一般在将this指针赋值给某个void指针时候,应先将其转化为合适的类型。一个有趣的例子是返回IUnknown指针的情形:

  *ppv=static_cast<IUnknown* >(this);// Ambiguous. 含糊不清的

  但是将this指针转化成IUnknown* 是不明确的。这是由于IX和IY都是从IUnknown继承得到的。因此在这种情况向下,返回值应该是static_cast<IUnknown *>(static_cast<IX*>(this))或者是

Static_cast<IUnknown*>(static_cast<IY*>(this)).使用哪一个不重要,因为他们使用的是同一个实现。但是在代码中要保持一致,这是因为这两个指针是不一样的,并且COM要求对IUnknown接口返回相同的指针。

 

多生继承及类型转换

通常将一种类型的指针转化成为另外一种类型,并不会改变它的值,但是为了支持多重继承,在某些情况下,C++必须改变类指针的值。许多C++程序员并不清楚多重继承的此种负面效果。例如假定有一个类CA定义如下:

Class CA:public IX,public IY{};

由于CA同时继承了IXIY,因此在可以使用IX或者IY指针的地方均可以使用指向CA的指针。例如可以将指向CA的指针传给接受IXIY指针的函数,这样此函数仍将能够正常工作。例如:

Void foo(IX* pIX);

Void bar(IY* pIY);

Int main()

{

   CA*pA=new CA();

   Foo(pA);

   Bar(pA);

   DeletepA;

}

Foo需要一个指向合法的IX的虚拟函数表指针,而bar则需要一个指向IY虚拟函数表的指针。当然IXIY的虚拟函数表中的内容是不一样的。因此在将一个IX vtbl传给bar时候,此函数将不能正常工作。因此编译器将同一指针传给foobar是不可能的,他必须对CA指针进行修改一遍它指向一个合适的vtbl指针。图三显示了类CA对象的内存结构:

QueryInterface详解 COM


 从图3-3来看,CAthis指针指向IX的虚拟函数表。因此可以再不改变CAthis指针的值的情况下用它来代替IX指针。但是从图中可以很明显看出,类CAthis指针并没有指向IY的虚拟函数表。因此在将指向类CA的指针传给一个接受IY指针的函数之前,其值必须修改。为了完成这种修改,编译器将把IY虚拟函数表指针的偏移量(IY)加到CAthis指针上,一次编译器把下面的代码:

 IY*pC=PA;

转化成为下面类似的代码:

 IY*pC=(char*)pA+IY;

 

一个完整的例子:

void trace(const char* msg)

{

    cout<<msg<<endl;

}

interface IX:IUnknown

{

    virtual void __stdcall Fx()=0;

};

interface IY:IUnknown

{

    virtual void __stdcall Fy()=0;

};

interface IZ:IUnknown

{

    virtual void __stdcall Fz()=0;

};

 

extern const IID IID_IX;

extern const IID IID_IY;

extern const IID IID_IZ;

 

class CA:public IX,public IY

{

    //IUnknown implementation

    virtual HRESULT __stdcall QueryInterface

        (const IID& iid,void **ppv);

    virtual ULONG __stdcall AddRef(){
return 0;}

    virtual ULONG __stdcall Release(){
return 0;}

    //Interface IX implementation

    virtual void __stdcall Fx()

    {

        cout<<“Fx”<<endl;

    }

    virtual void __stdcall Fy()

    {

        cout<<“Fy”<<endl;

    }

};

 

HRESULT __stdcallCA::QueryInterface(const IID &iid, void **ppv)

{

    if(iid==IID_IUnknown)

    {

    //  trace(“QueryInterface:Returnpointer to Iunknown.”);

        *ppv=static_cast<IX* >(this);

    }

    else if(iid==IID_IX)

    {

    //  trace(“QueryInterface:ReturnPointer to IX”);

        *ppv=static_cast<IX* >(this);

    }

    else if(iid==IID_IY)

    {

    //  trace(“QueryInterface:ReturnPointer to IY.”);

        *ppv=static_cast<IY* >(this);

    }

    else

    {

    //  trace(“QueryInterface:Interfacenot support.”);

        *ppv=NULL;

        return E_NOINTERFACE;

    }

    reinterpret_cast<IUnknown*>(*ppv)->AddRef();

    return S_OK;

}

IUnknown *CreateInstance()

{

    IUnknown *pI=static_cast<IX*>(new CA);

    //注意这里的static_cast<IX* >(new CA);

    //和static_cast<IY* >(new CA)是不一样的

    //所以说IUnknown *pI的上下文环境要一致

    pI->AddRef();

    return pI;

}

 

static const IID IID_IX=

{0x32bb8320,0xb41b,0x11fc,

{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};

static const IID IID_IY=

{0x32bb8f21,0xb41b,0x11cf,

{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};

static const IID IID_IZ=

{0x32bb8322,0xb1b,0x11cf,

{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};

 

bool SameComponents(IX* pIX,IY* pIY)

{

    IUnknown *pI1=NULL;

    IUnknown *pI2=NULL;

    //Get IUnknown pointer from pIX

    pIX->QueryInterface(IID_IUnknown,(void**)&pI1);

    pIY->QueryInterface(IID_IUnknown,(void**)&pI2);

    return PI1==PI2;

}

 

 

void f(IX* pIX)

{

    IX *pIX2=NULL;

    //Query IX for IX

    HRESULT hr=pIX->QueryInterface(IID_IX,

        (void**)&pIX2);

    assert(SUCCEEDED(hr));

    //query must succeed.

}

void fi(IX* pIX)

{

    HRESULT hr;

    IX* pIX2=NULL;

    IY* pIY=NULL;

    hr=pIX->QueryInterface(IID_IY,(void**)&pIY);

    if(SUCCEEDED(hr))

    {

        hr=pIY->QueryInterface(IID_IX,(void**)&pIX2);

        //QueryInterface must be succeeded.

        assert(SUCCEEDED(hr));

    }

}

 

 

int _tmain(int argc, _TCHAR* argv[])

{

    HRESULT hr;

    trace(“Client:Get an IUnknown pointer”);

    IUnknown *pIUnknown=CreateInstance();

    trace(“Client:Get interface IX.”);

    IX* pIX=NULL;

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

    if(SUCCEEDED(hr))

    {

        trace(“Client:Succeed getting IX.”);

        pIX->Fx();

    }

    trace(“Client:Get interface IY”);

    IY* pIY=NULL;

    hr=pIUnknown->QueryInterface(IID_IY,(void**)&pIY);

    if(SUCCEEDED(hr))

    {

        trace(“Client:Succeeded getting IY.”);

        pIY->Fy();

    }

    trace(“Client:Ask For an unsupported interface”);

    IZ *pIZ=NULL;

    hr=pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);

    if(SUCCEEDED(hr))

    {

        trace(“Client:Succeeded in getting interface IZ”);

        pIZ->Fz();

    }

    else

    {

        trace(“Could notget interface IZ.”);

    }

    trace(“Client:Get interface IY from interface IX.”);

    IY* pIYfromIX=NULL;

    hr=pIX->QueryInterface(IID_IY,(void**)&pIYfromIX);

    if(SUCCEEDED(hr))

    {

        trace(“Client:Succeeded getting IY.”);

        pIYfromIX->Fy();

    }

    trace(“Client:Getinterface IX from interface IY”);

    IX* pIXfromIY=NULL;

    hr=pIY->QueryInterface(IID_IX,(void**)&pIXfromIY);

    if(SUCCEEDED(hr))

    {

        trace(“Client:Succeeded getting IX.”);

        pIXfromIY->Fx();

    }

 

    trace(“Client:Get interface IUnknown from IY”);

    IUnknown *pIUnknownFromIY=NULL;

    hr=pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknownFromIY);

    if(SUCCEEDED(hr))

    {

        cout<<“Arethe IUnknown pointers equal?”<<endl;

        if(pIUnknownFromIY==pIUnknown)

        {

            cout<<“Yes,pIUnknownFromIY==pIUnknown.”<<endl;

        }

 

        else

        {

            cout<<“No,pIUnknownFromIY!=pIUnknown.”<<endl;

        }

    }

    delete pIUnknown;

    return 0;

}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/147857.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • pytest指定用例_pytest执行多个py文件

    pytest指定用例_pytest执行多个py文件前言测试用例在设计的时候,我们一般要求不要有先后顺序,用例是可以打乱了执行的,这样才能达到测试的效果.有些同学在写用例的时候,用例写了先后顺序,有先后顺序后,后面还会有新的问题(如:上个用例返回

  • java核心技术 – 17个重要的知识点

    java核心技术 – 17个重要的知识点1.Java中没有多继承,而是用接口来代替多继承 2.运行一个已经编译的程序时,Java解释器总是从指定类的main方法中的代码开始执行,因此,执行代码中必须有一个main函数。 

  • JVM内存结构概述

    JVM内存结构概述本节将会介绍一下JVM的内存结构,JVM运行时数据区的各个组成部分:堆,方法区,程序计数器,Java虚拟机栈,本地方法栈,还会对Java堆的分代划分做个简单的介绍。目录前言JVM是什么JVM内存结构概览运行时数据区程序计数器Java虚拟机栈本地方法栈方法区运行时常量池Java堆直接内存前言JVM是Java中比较难理解和掌握的一部分,也是面试…

  • subscriptions_promise sb to do

    subscriptions_promise sb to do1.Promise的含义Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了P

  • Java虚拟机:class类文件结构

    Java虚拟机:class类文件结构

  • mock测试概念「建议收藏」

    mock测试概念「建议收藏」mock测试概念:mock是在测试过程中,对于一些不容易构造/获取的对象,创建啊一个mock对象来模拟对象的行为mock对象使用范畴真实对象具有不可确定的行为。真实对象很难被创建。真实对象的某些行为很难触发。真实情况令程序运行速度很难。真实对象实际上并不存在。测试隔离的实现。mock有什么用?解除一些依赖关系,当测试部分接口实现,需要依赖于与其他接口与,而其他接口没完…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号