当前位置:天才代写 > tutorial > C语言/C++ 教程 > 用C实现的一个根基COM接口IFoo(一)

用C实现的一个根基COM接口IFoo(一)

2017-11-02 08:00 星期四 所属: C语言/C++ 教程 浏览:607

副标题#e#

把该文中实现的代码整理汇总到一个项目中。今朝只是实现到一其中间阶段,重点在说明COM接口的实现道理,还没有包括类厂的部门。今后还需连续添加类厂等高级成果。

文件构成:

ifoo.h    COM接口IFoo,接口ID IID_IFoo 声明文件。

outside.c   COM接话柄现。这里实现IFoo的是一个布局体COutside.

util.h    一些宏界说、全局函数、变量声明文件。

main.c    笔者为实现项目添加的文件。提供main函数、内存打点函数Alloc,Free的实现(封装C运行库函数malloc和free.)、接口ID界说。

COM接口到底是什么?

COM接口是一个指向虚函数表的指针。通过这个指针可以会见内存中某处的各个成果块,执行预界说的成果,完成用户的任务。这些成果块以函数的形式存在(想不出尚有其他形式:))并被挪用。它们有一个配合点:都包括一个指针参数,指向这些成果要操纵的数据地点。在C++中,这个地点就是工具的首地点,也就是类成员函数中隐含的this指针。在C函数中并没有这种现成的便利,因此代码实现中在接口界说时仍利用了接口指针(HRESULT (__stdcall * QueryInterface)   (IFoo * This,  const IID * const, void **)),而在接口函数实现时按照布局体机关布局,从这个接口指针推算获得工具实例指针。

typedef struct IFoo
{
 struct IFooVtbl * lpVtbl;
} IFoo;
typedef struct IFooVtbl IFooVtbl;
struct IFooVtbl
{

 HRESULT (__stdcall * QueryInterface)   (IFoo * This,  const IID * const, void **) ;
 ULONG (__stdcall * AddRef)    (IFoo * This) ;
 ULONG (__stdcall * Release)   (IFoo * This) ;

 HRESULT (__stdcall * SetValue)         (IFoo * This,  int) ;
 HRESULT (__stdcall * GetValue)         (IFoo * This,  int *) ;
};

COM接口的要求:

每一个COM接口(指向的虚函数表)的头三个函数必需是IUnknown接口的函数:QueryInterface,AddRef和Release.在C++中,称为从IUnknown接口担任。

对付挪用QueryInterface响应查询IID_IUnknwon获得的接口指针值,同一个工具实现的所有接口必需沟通。这是判定两个COM工具是否是同一个工具的尺度。

宏界说“#define IUNK_VTABLE_OF(x) ((IUnknownVtbl *)((x)->lpVtbl))”说明


#p#副标题#e#

在预处理惩罚输出文件main.i中可以找到IUnknownVtbl和IFooVtbl的声明:

typedef struct IUnknownVtbl
    {

        HRESULT ( __stdcall *QueryInterface )(
            IUnknown * This,
             const IID * const riid,
             void **ppvObject);

        ULONG ( __stdcall *AddRef )(
            IUnknown * This);

        ULONG ( __stdcall *Release )(
            IUnknown * This);

    } IUnknownVtbl;

    struct IUnknown
    {
         struct IUnknownVtbl *lpVtbl;
    };

struct IFooVtbl
{

 HRESULT (__stdcall * QueryInterface)   (IFoo * This,  const IID * const, void **) ;
 ULONG (__stdcall * AddRef)    (IFoo * This) ;
 ULONG (__stdcall * Release)   (IFoo * This) ;

 HRESULT (__stdcall * SetValue)         (IFoo * This,  int) ;
 HRESULT (__stdcall * GetValue)         (IFoo * This,  int *) ;
};

该宏界说的浸染就是把IFoo接口中的IFooVtbl范例的指针拿出来((x)->lpVtbl)),并强制转换((IUnknownVtbl *))成IUnknownVtbl.

“强制转换”的功效是什么呢?是怎么做到的呢?

很明明,功效就是获得的指针不再是IFooVtbl *范例,而是酿成了IUnknownVtbl *范例。至于做法,系统应该记录每一个变量、表达式的范例。当举办强制范例转换时,就(姑且地)修改其范例为转换到的范例。

同理,QueryInterface, AddRef, Release宏界说中的(IUnknown *)也是这种用法。

可以看到,宏“IUNK_VTABLE_OF”的浸染是供宏QueryInterface,宏AddRef,宏Release引用,把IFooVtbl *范例转换为IUnknownVtbl *范例,最终到达挪用IUnknownVtbl中界说的三个QueryInterface,AddRef,Release函数。

那么,这种大费周章的目标是什么呢?为什么不以IFooVtbl中三个函数的界说形式(不通过强制转换来转换成必需的范例),直接挪用IFooVtbl中界说的函数呢?固然强制转换在参数值上并不会造成改变,最终挪用的也是IFooVtbl界说的函数(FooQueryInterface,FooAddRef,FooRelease)。

为什么必然要通过IUnknown接口指针挪用这三个函数呢?修改QueryInterface宏界说如下:

#define QueryInterface(pif, iid, pintf) \

(((pif)->lpVtbl)->QueryInterface(pif, iid, (void **)(pintf)))

即通过IFoo接口指针来挪用由IUnknown引入的函数,有什么差池的处所吗?

试验表白,将QueryInterface宏界说如下也可以编译通过,执行起来也没有呈现任何异常。

#define QueryInterface(pif, iid, pintf) \

(((pif)->lpVtbl)->QueryInterface(pif, iid, (void **)(pintf)))

#p#分页标题#e#

对付IUnknown接口的三个函数,挪用时通报的参数是IUnknown *范例(见QueryInterface, AddRef, Release宏界说),而函数界说中(FooQueryInterface, FooAddRef, FooRelease)声明的参数是IFoo *范例,这种纷歧致的环境是怎么呈现的?这种纷歧致不会有问题吗?

这种纷歧致的发生是由于从差异的角度对待引起的。假如从IUnknown接口来看,那么接口函数中的第一个参数范例就是IUnknown *;假如从IFoo来看,那么第一个参数的范例就是IFoo *.

这种纷歧致性只是针对付编译器对付范例的编译要求有意义的,在接话柄现及利用时,通报给lpVtbl->QueryInterface, lpVtbl->AddRef,lpVtbl->Release的第一个参数在值上都是沟通的,都是实现该接口的内存地点(在本例中是COutside工具的首地点)。

#p#副标题#e#

一些语法现象回首

函数指针变量界说、赋值及挪用。

HRESULT (__stdcall * pQI)   (IFoo * This,  const IID * const, void **) ;

界说一个函数指针变量pQI,该变量指向“返回HRESULT,取3个参数别离为范例IFoo *,const IID * const, void **”的函数。

typedef HRESULT (__stdcall * QIType)   (IFoo * This,  const IID * const, void **) ;

界说一个函数指针范例,该范例的指针指向“返回HRESULT,取3个参数别离为范例IFoo *,const IID * const, void **”的函数。

HRESULT __stdcall QueryInterface(IFoo * This,  const IID * const, void **) ;//函数声昭示例
pQI = 0; // 函数指针赋值,0暗示不指向任何函数。
pQI = QueryInterface; // 函数指针赋值,pQI指向QueryInterface。
pQI = &QueryInterface; // 与上面等价。

QueryInterface(&this->ifoo, riid, ppv); // 利用函数名直接挪用
pQI(&this->ifoo, riid, ppv); // 函数指针挪用
(*pQI)(&this->ifoo, riid, ppv); // 第二种函数指针挪用方法

宏界说、展开法则

对付宏,一直有一种雾里看花的感受,好像很随意,怎么来都行,好比:

#define AddRef(pif) \

(IUNK_VTABLE_OF(pif)->AddRef((IUnknown *)(pif)))

宏界说应该是可以嵌套的,即宏界说的“内容”中还可以包括(嵌套)宏,如本例,“IUNK_VTABLE_OF”就是嵌套宏。在展开的时候,将嵌套的宏也一并展开(替换成界说的内容),直到不再有宏为止。

那么就有两个疑问:

1.假如被嵌套的宏包括(直接或间接)界说的宏,那么展开就没完没了,死轮回了。

2.假如界说的内容中有跟界说的宏同名的字符串(好比上面的例子IUNK_VTABLE_OF),那么怎么区分这同名的东东是嵌套的宏(需要展开),照旧一般的字符串(不需要展开)?

函数挪用类型约定、main函数挪用类型。

一开始把几个文件汇总到项目里时,编译通不外,错误提示大抵意思是,不能把一种挪用类型的函数指针转换成另一种挪用类型的函数指针。厥后把挪用类型改为   /Gz(__stdcall),编译为(Compile As)改为/TC(Compile As C Code)就好了。

想来是对付。c文件,编译器缺省利用的是__cdecl,而IFoo中的接口宏界说在win32下展开成了__stdcall,所以呈现了抵牾。而利用/Gz强制未声明挪用类型的函数利用__stdcall,实现就与声明一致了。

(size_t)&(((s *)0)->m)

c++措施员也许都知道,会见解点“0”处的成员是一大忌,会造成GP.然而,取地点“0”处的成员的地点,却是个正当的操纵。固然地点“0”处并没有什么内容,可是,假如在地点0处存放一个内容,那么该内容中的成员也是有地点的。本例中正是巧妙地操作这种要领,从接口地点计较得出实现该接口的实例地点,进而会见实例的内部变量。

 

    关键字:

天才代写-代写联系方式