分类: C/C++
2009-05-03 10:39:24
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?
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.
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.
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.
(THIS)
. Otherwise, you must insert THIS_
immediately after the open-parenthesis of the parameter list.
PURE
.
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.
INTERFACE
macro because its value is used by the THIS
and THIS_
macros later.
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.
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.
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.
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.
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.