Chinaunix首页 | 论坛 | 博客
  • 博客访问: 124339
  • 博文数量: 32
  • 博客积分: 2011
  • 博客等级: 大尉
  • 技术积分: 375
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-03 17:57
文章分类

全部博文(32)

文章存档

2011年(1)

2009年(8)

2008年(23)

我的朋友

分类: C/C++

2009-05-03 10:39:24

 
   在学习AntiIELeak的源代码时看到一些使用STDMETHOD的代码,不懂,google了一篇文章,看了也不是很懂,先硬着头皮翻下来,期待着有一天能顿悟   
 
   声明COM接口有两种方式,一种难一种简单。
   简单的方法是使用IDL文件,让MIDL编译器为你生成COM接口。如果使用MIDL,你会无条件得到uuidof支持。难的方法是手动完成。如果你选择该方法,你的接口看起来如下
#undef  INTEFERCE
#define INTERFACE ISample2
DECLARE_INTERFACE_(ISample2, ISample)
{
     BEGIN_INTERFACE
     //**IUnknown methods **
     STDMETHOD(QueryInterface)(THIS_REFIID riid, void **ppv)PURE;
     STDMETHOD_(ULONG, AddRef)(THIS) PURE;
     STDMETHOD_(ULONG, Release)(THIS) PURE;
     //** ISample methods **
     STDMETHOD(Method1)(THIS) PURE;
     STDMETHOD_(int, Methods) (THIS) PURE;
 
     //** ISample2 methods
     STDMETHOD(Method3)(THIS_ int iParameter) PURE;
     STDMETHOD_(int, Method4)(THIS_ int iParameter) PURE;
     END_INTERFACE
};
 
规则:
    1.必须将INTERFACE宏设置为你正在定义的接口的名字。注意在#define新值之前
你需要#undef以前的值。
    2.必须使用DECLARE_INTERFACE和DECLARE_INTERFACE_宏为借口生成preliminary bookkeeping. 没有基类的接口使用DECLARE_INTERFACE,从其他接口继承而来的接口使用DECLARE_INTERFACE_。在上面的例子中,ISample2是从ISample继承而来的。注意:在实践中,你不会找到DECLARE_INTERFACE宏,因为如果没有其它的,所有接口都从
IUnknown继承来的。
   3.所有基接口中的方法都必须按照其在基接口中的顺序列出;你自己加的方法要
在其后列出。
   4.必须使用STDMETHOD或者STDMETHOD_宏来定义方法。如果返回值是HRESULT使用
STEMTHOD,如果返回值是其他类型则使用STDMETHOD_。
   5.如果某个方法没有参数,参数列表必须是(THIS)。否则你必须把THIS_放在参
数列表的左括号后面。
   6.参数列表后,分号前必须有PURE。
   7.在大括号内必须有BEGIN_INTERFACE 和 END_INTERFACE。
 
以下是上述每条规则的理由
   1.使用INTERFACE宏的原因是——它的值将被THIS和THIS_使用。
   2.DECLARE_InTERFACE*宏的使用保证了为C和C++生成正确的prologue。对于C,
会声明一个vtable结构体,对于C++来说编译器会自动不理vtable。另一方面,
因为C++有继承机制,所以该宏必须指明基类保证upcasting正常工作。
   3.基类方法必须按照原来的顺序列出,(so that the C vtable structure for
your derived class mathches the structure for the base class for the
extent that they overlap)因此C中继承类的vtable结构体与基类的vtable匹配
,其余的可以覆盖(不知是否翻对)。这一条是为了保留COM原则让继承接口还
可以继续做基接口。
   4.是用STDMETHOD 和STDMETHOD_宏是为了保证为函数原型定义正确的calling
converntion(调用习俗??)。对于C该宏创建了一个指向vtable的指针;对于
C++该宏创建了一个虚函数。
   5.THIS和THIS_宏的使用使得C声明中显式地声明this参数,该参数在C++中是隐
含。参数个数的不用要求的版本(THIS, _THIS )也不同,这个主要决定是否要产
生一个逗号。
   6.PURE保证了C++虚函数是纯虚函数。COM接口的一个固定的属性是:所有的方法
都是纯虚函数。
   7.BEGIN_INTERFACE和END_INTERFACE宏生成与编译器有关的goo(什么是goo),它
是由编译器厂商提供的用来保证生成的接口与COM的vtable布局原则匹配。尽管对goo的需求随着时间慢慢消失,但是不同的编译器由于历史原因还是需要不同
的goo。
 
 
下面是原文——

The macros for declaring and implementing COM interfaces

There are two ways of declaring COM interfaces, the hard way and the easy way.

The easy way is to use an IDL file and let the MIDL compiler generate your COM interface for you. If you let MIDL do the work, then you also get support at no extra charge, which is a very nice bonus.

The hard way is to do it all by hand. If you choose this route, your interface will look something like this:

#undef  INTERFACE
#define INTERFACE   ISample2

DECLARE_INTERFACE_(ISample2, ISample)
{
    BEGIN_INTERFACE

    // *** IUnknown methods ***
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppv) PURE;
    STDMETHOD_(ULONG,AddRef)(THIS) PURE;
    STDMETHOD_(ULONG,Release)(THIS) PURE;

    // ** ISample methods ***
    STDMETHOD(Method1)(THIS) PURE;
    STDMETHOD_(int, Method2)(THIS) PURE;

    // *** ISample2 methods ***
    STDMETHOD(Method3)(THIS_ int iParameter) PURE;
    STDMETHOD_(int, Method4)(THIS_ int iParameter) PURE;

    END_INTERFACE
};

What are the rules?

  • You must set the INTERFACE macro to the name of the interface being declared. Note that you need to #undef any previous value before you #define the new one.
  • You must use the DECLARE_INTERFACE and DECLARE_INTERFACE_ macros to generate the preliminary bookkeeping for an interface. Use DECLARE_INTERFACE for interfaces that have no base class and DECLARE_INTERFACE_ for interfaces that derive from some other interface. In our example, we derive the ISample2 interface from ISample. Note: In practice, you will never find the plain DECLARE_INTERFACE macro because all interfaces derive from IUnknown if nothing else.
  • You must list all the methods of the base interfaces in exactly the same order that they are listed by that base interface; the methods that you are adding in the new interface must go last.
  • You must use the STDMETHOD or STDMETHOD_ macros to declare the methods. Use STDMETHOD if the return value is HRESULT and STDMETHOD_ if the return value is some other type.
  • If your method has no parameters, then the argument list must be (THIS). Otherwise, you must insert THIS_ immediately after the open-parenthesis of the parameter list.
  • After the parameter list and before the semicolon, you must say PURE.
  • Inside the curly braces, you must say BEGIN_INTERFACE and END_INTERFACE.

There is a reason for each of these rules. They have to do with being able to use the same header for both C and C++ declarations and with interoperability with different compilers and platforms.

  • You must set the INTERFACE macro because its value is used by the THIS and THIS_ macros later.
  • You must use one of the DECLARE_INTERFACE* macros to ensure that the correct prologue is emitted for both C and C++. For C, a vtable structure is declared, whereas for C++ the compiler handles the vtable automatically; on the other hand, since C++ has inheritance, the macros need to specify the base class so that upcasting will work.
  • You must list the base class methods in exactly the same order as in the original declarations so that the C vtable structure for your derived class matches the structure for the base class for the extent that they overlap. This is required to preserve the COM rule that a derived interface can be used as a base interface.
  • You must use the STDMETHOD and STDMETHOD_ macros to ensure that the correct calling conventions are declared for the function prototypes. For C, the macro creates a function pointer in the vtable; for C++, the macro creates a virtual function.
  • The THIS and THIS_ macros are used so that the C declaration explicitly declares the "this" parameter which in C++ is implied. Different versions are needed depending on the number of parameters so that a spurious trailing comma is not generated in the zero-parameter case.
  • The word PURE ensures that the C++ virtual function is pure, because one of the defining characteristics of COM interfaces is that all methods are pure virtual.
  • The BEGIN_INTERFACE and END_INTERFACE macros emit compiler-specific goo which the compiler vendor provides in order to ensure that the generated interface matches the COM vtable layout rules. Different compilers have historically required different goo, though the need for goo is gradually disappearing over time.

And you wonder why I called it "the hard way".

Similar rules apply when you are implementing an interface. Use the STDMETHODIMP and STDMETHODIMP_ macros to declare your implementations so that they get the proper calling convention attached to them. We'll see examples of this next time.

阅读(1396) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~