第一讲:COM接口与QueryInterface

来自:http://www.vckbase.com/index.php/video/listview/fid/2/sid/12

本节内容:

1、现实中的组件与接口

2、把现实中的思想融入到软件中

3、C++程序中的组件与接口

4、COM组件与COM接口

5、QueryInterface函数,HRESULT类型,IID类型,数据类型转换


1、现实中的组件与接口

组件的定义:

含有可独立性,可通用性,可组合性,可替换性的事物,我们把它称作组件。

现实世界中存在各种各样的具备组件概念的设备。如:电脑主机内的设备: CPU、内存条、硬盘、光驱。还比如:5号电池、7号电池、插座。

为什么说这些设备具备组件概念呢?因为这些设备都具有可独立性,可通用性,可组合性,可替换性。

 接口的定义:

是组件与组件之间,组件与外部事物之间进行交互的协议。

组件与组件,组件与其它设备的交互工作是通过接口进行的。 CPU跟主板之间有固定的接口,内存条跟主板之间也有固定的接口。如果CPU要更换,被更换的CPU必须与旧的CPU拥有相同的使用接口。


* 主板不直接认识CPU,只认识CPU的接口。主板也不直接认识内存条,只认识内存条的接口。

*所以接口的约定是很重要的。在组件的开发之前,必须先约定组件与外界交互的接口协议。

* 接口协议只要确定后,往往是不能再改变的,比如5号电池不能再做得大一点或再小一点,然后去替换旧的5号电池。


采用组件与接口的思想来开发设备,体现了社会分工的一个现象,也是社会生产力发展的必然过程,做CPU的厂商只管做CPU,不用告诉主板关于CPU本身的实现细节,也不用去了解主板的实现细节。


2、把现实中的思想融入到软件中

面向对象思想来源于大自然,让我们面向着是一个一个的对象,不是面向一个一个的过程(面向过程思想)。

黑格尔说存在的就是合理的,大自然存在各种各样的对象,每类对象具有自己的特性,对象存在继承关系。这些存在是合理的。最后把这些合理的思想演变成面向对象思想,所以面向对象思想是合理的。

社会的发展促使人类在劳动上的分工,分工又以约定的接口协议来交互。社会的发展产生了这种组件-接口的开发思想,这种思想又是长期没有被替换过的思想,这是一种好的思想,合理的思想。

若我们以组件方式架构我们的软件。我们软件中的组件将具有可独立性,可通用性,可组合性,可替换性;我们的软件也将具有更好的灵活性,可拓展性和可维护性。我们软件的开发过程也会变得更加的简单,更好的分工,更加的规范。


3、C++程序中的组件与接口

接口,是一种约定,一种协议。它是抽象的,指明了具体含义,但却没有实现这个定义。

  我们看一下C++的纯虚函数:求最大公约数,virtual int GreatestCommonDivisor(int a,int b) = 0; //求a与b的最大公约数。

这个函数的定义很明确,但没有实现这个含义的具体方法,所以,是抽象的。

我们一般采用interface这个英文单词表示 C ++中的接口,它在Microsoft Visual Studio安装目录\VC\PlatformSDK\include\objbase.h中被预定义。

    #define  interface   struct

在其它开发平台下,也可以自己编写预定义代码。

//接口IX

interface IX

  virtual void Fx1() = 0;

  virtual void Fx2() = 0;

};

//接口IY

interface IY

  virtual void Fy1() = 0;

  virtual void Fy2() = 0;

};

组件是派生于接口的,现在我们定义一个组件CA,它派生于IXIY

//组件CA

class CA: public IX,public IY

{

public:

  virtual void Fx1() { cout <<"Fx1" << endl; }

  virtual void Fx2(){ cout <<"Fx2" << endl; }

  virtual void Fy1() {cout <<"Fy1" << endl; }

  virtual void Fy2() { cout <<"Fy2" << endl; }

};

客户端的调用查看Section1Demo1

//预定义interface
#define interface struct

//接口IX
interface IX
{
  virtual void Fx1() = 0;
  virtual void Fx2() = 0;
};

//接口IY
interface IY
{
  virtual void Fy1() = 0;
  virtual void Fy2() = 0;
};

//组件CA
class CA: public IX,public IY
{
public:
  virtual void Fx1() { cout <<"Fx1" << endl; }
  virtual void Fx2() { cout <<"Fx2" << endl; }
  virtual void Fy1() { cout <<"Fy1" << endl; }
  virtual void Fy2() { cout <<"Fy2" << endl; }
};

int main()
{
    //创建组件的实例
    CA *pCA = new CA;

    //获取IX的接口
    IX *pIX = pCA;
    //使用IX的接口
    pIX->Fx1();
    pIX->Fx2();
    
    //获取IY的接口
    IY *pIY = pCA;
    //使用IY的接口
    pIY->Fy1();
    pIY->Fy2();

    //销毁组件
    delete pCA;
    pCA = NULL;
    
    return 0;
}


4、COM组件与COM接口

COM 的定义:是 Component Object Model 组件对象模型 )的缩写
COM组件是可以以二进制的形式发布,具有指定规则的二进制结构;
COM组件是可以被其它应用程序来调用,以实现二进制代码的共享(跨应用);
COM组件是完全与编程语言无关的。(跨语言);
COM 组件只能被运行在 Windows 操作系统平台上面, Linux Mac 不能适用。
COM组件的内存结构和 C ++编译器为抽象基类所生成的内存结构是相同的。因此可以用 C ++的抽象基类来定义COM接口。
COM组件必须继承于最基本的COM接口:I U nknow。
IUnknow有三个函数,为别是QueryInterface,AddRef, Release。

interface IUnknown

{

  virtual HRESULT QueryInterface(const IID &iid, void **ppv) = 0;

  virtual ULONG AddRef() = 0;

  virtual ULONG Release() = 0;

};

5、QueryInterface函数,HRESULT类型,IID类型,数据类型转换
QueryInterface

      可以通过QueryInterface函数来查询某个组件是否支持某个特定的接口。若支持,QueryInterface返回一个指向此接口的指针。

      这里我们看到函数返回类型为HRESULT,参数其中一个的类型是constIID&。

      HRESULT跟IID是什么呢?

      HRESULT



IID
IID,接口标识符,每个接口都可以设置一个IID,用于标志该接口,若标志了某个接口后,IID的值不能再修改。
IID其实是:

  typedefGUID IID;

GUID

typedef struct _GUID

{

  DWORD  Data1;  //随机数        4字节

  WORD  Data2;  //和时间相关    2字节

  WORD  Data3;  //和时间相关    2字节

  BYTE  Data4[8];    //和网卡MAC相关  8字节

} GUID;  //一共16字节


GUID有16个字节,共128位二进制数。
GUID的生成方法,可以采用Windows SDKv6.0A的Tools文件夹下的GUID生成器生成。
从理论上讲,它是不能保证唯一,但由于重复的可能性非常非常非常。。。非常小。有句夸张的说法是: 在每秒钟产生一万亿个GUID的情况下,即使太阳变成白矮星的时候,它仍是唯一的
GUID生成器的界面截图

GUID的表示方法:

//{0E04C466-6CE9-4513-B306-43E8F7025EB9}

static const GUIDguid =

{ 0xe04c466, 0x6ce9,0x4513, { 0xb3, 0x6, 0x43, 0xe8, 0xf7, 0x2, 0x5e, 0xb9 } };



查看Section1Demo2例子,讲解QueryInterface的实现方法。
#include "stdafx.h"
#include <iostream>
#include <Unknwn.h>

using namespace std;

// {A348FBDD-E765-4b41-8477-6D8B7038FCC6}
static const IID IID_IX =
{ 0xa348fbdd, 0xe765, 0x4b41, { 0x84, 0x77, 0x6d, 0x8b, 0x70, 0x38, 0xfc, 0xc6 } };

// {10A90ED2-FCDE-4067-92DA-ABA38F5C1B12}
static const IID IID_IY =
{ 0x10a90ed2, 0xfcde, 0x4067, { 0x92, 0xda, 0xab, 0xa3, 0x8f, 0x5c, 0x1b, 0x12 } };


//接口IX
interface IX : public IUnknown
{    
    virtual void Fx1() = 0;
    virtual void Fx2() = 0;
};

//接口IY
interface IY : public IUnknown
{    
    virtual void Fy1() = 0;
    virtual void Fy2() = 0;
};

//组件CA
class CA: public IX, public IY
{
//实现
public:
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppv)
    {
        //查看PPT中CA的内存结构讲解如下的转换过程

        if (iid == IID_IUnknown)
        {
            //即使CA继承了两个IUnknown接口,其中一个来自于IX,另一个来自于IY。我们一般返回第一个被继承的IX接口。
            *ppv = static_cast<IX*>(this);        
        }
        else if (iid == IID_IX)
        {
            //返回IX接口
            *ppv = static_cast<IX*>(this);        
        }
        else if (iid == IID_IY)
        {
            //返回IY接口
            *ppv = static_cast<IY*>(this);
        }
        else
        {
            //查询不到IID,*ppv返回NULL。
            *ppv = NULL;
            return E_NOINTERFACE;    //函数返回值返回E_NOINTERFACE,表示组件不支持iid的接口。
        }

        AddRef();        //这个第二讲会讲

        return S_OK;    //返回S_OK
    }

    virtual ULONG STDMETHODCALLTYPE AddRef()
    {
        //暂时不实现,暂时返回0
        return 0;
    }

    virtual ULONG STDMETHODCALLTYPE Release()
    {
        //暂时不实现,暂时返回0
        return 0;
    }

    virtual void Fx1()    { cout << "Fx1" << endl;    }
    virtual void Fx2()    { cout << "Fx2" << endl;    }
    virtual void Fy1()    { cout << "Fy1" << endl;    }
    virtual void Fy2()    { cout << "Fy2" << endl;    }

//数据
public:
    long m_lAA;
    long m_lAB;
    long m_lAC;
};


int main( )
{
    HRESULT hr;

    //创建组件
    CA *pA = new CA();

    //从组件查询IUnknown接口
    IUnknown *pIUnknown = NULL;
    hr = pA->QueryInterface(IID_IUnknown, (void**)&pIUnknown);
    if (SUCCEEDED(hr))        //对HRESULT返回值的判断,一般采用SUCCEEDED
    {
        //从IUnknown查询IX接口
        IX *pIX = NULL;
        hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX);
        if (SUCCEEDED(hr))
        {
            //调用IX接口的方法
            pIX->Fx1();
            pIX->Fx2();
        }

        //从IUnknown查询IY接口
        IY *pIY = NULL;
        hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY);
        if (SUCCEEDED(hr))
        {
            //调用IY接口的方法
            pIY->Fy1();
            pIY->Fy1();
        }

        if ((void*)pIX != (void*)pIY)
        {
            cout << "pIX != pIY" <<endl;
        }
        
        if ((void*)pIUnknown != (void*)pIY)
        {
            cout << "pIUnknown != pIY" <<endl;
        }

        if ((void*)pIUnknown == (void*)pIX)
        {
            cout << "pIUnknown == pIX" <<endl;
        }

        //从IX查询IY
        IY *pIY2 = NULL;
        hr = pIX->QueryInterface(IID_IY, (void**)&pIY2);
        if (SUCCEEDED(hr))
        {
            pIY2->Fy1();
            pIY2->Fy2();
        }

        //从IX也可以查询到IUnknown
        //从IY也可以查询到IX
        //从IY也可以查询到IUnknown
        //从CA也可以查询到IX
        //从CA也可以查询到IY
        //总结:
            //只要是CA所继承的接口,从CA的组件里都可以查询到;
            //只要是CA所继承的接口,从CA所继承的其它接口里都可以查询到。
    }

    //释放组件
    delete pA;

    return 0;
}















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值