Suppose we want to define the interface IStack, which provides facilities for pushing and popping integers. One technique is to define the COM interface as a C++ abstract base class. #include "wtypes.h" /* {FC3B3F61-BCEC-11D1-91FE-E1CBED988F66} */ DEFINE_GUID(IID_IStack, 0xFC3B3F61, 0xBCEC, 0x11D1, 0x91, 0xFE, 0xE1, 0xCB, 0xED, 0x98, 0x8F, 0x66); class IStack : public IUnknown { public: virtual HRESULT Push(long value) = 0; virtual HRESULT Pop(long* value) = 0; virtual HRESULT Empty(long* flag) = 0; }; This technique would be the most familiar to C++ programmers. Notice how IStack is derived from IUnknown (all COM interfaces must be derived either directly or indirectly from IUnknown). The problem with this definition is that it is language specific and can't be used by callers written in other languages. Also you'd have to provide code for marshaling the parameters if the caller using IStack and the COM object implementing IStack were running in different processes. Another technique is to define the interface using a set of COM-defined macros. This technique defines the interface in a manner that hides the differences between programming languages (e.g., C and C++) and operating systems (e.g., Windows and Macintosh). #include "wtypes.h" /* {FC3B3F61-BCEC-11D1-91FE-E1CBED988F66} */ DEFINE_GUID(IID_IStack, 0xFC3B3F61, 0xBCEC, 0x11D1, 0x91, 0xFE, 0xE1, 0xCB, 0xED, 0x98, 0x8F, 0x66); DECLARE_INTERFACE_(IStack, IUnknown) { // *** IStack methods *** // STDMETHOD(Push) (THIS_ long value) PURE; STDMETHOD(Pop) (THIS_ long* value) PURE; STDMETHOD(Empty) (THIS_ long* flag) PURE; }; The tags such as DECLARE_INTERFACE_, STDMETHOD, THIS_, and PURE are COM macros that expand differently based on the operating system and programming language. This is a better approach than the pure C++ approach because it allows the same interface definition to be used in multiple environments without changes. But you'd still have to provide code for marshaling the parameters if the caller using IStack and the COM object implementing IStack were running in different processes. A third technique is to define the interface using the Microsoft Interface Definition Language (MIDL). MIDL allows interfaces to be defined in a language-independent manner. MIDL is based on DCE's IDL syntax and includes extensions to support COM programming. MIDL is used for defining COM interfaces, defining what interfaces a COM class implements (see FAQ 34.12), defining dispatch interfaces (see FAQ 34.25), and generating type libraries (see FAQ 34.29). Here is the MIDL definition of the IStack interface. [ object, uuid(FC3B3F61-BCEC-11D1-91FE-E1CBED988F66) ] interface IStack : IUnknown { import "unknwn.idl"; HRESULT Push([in] long value); HRESULT Pop([out, retval] long* pVal); HRESULT Empty([out, retval] boolean* pVal); }; MDL has some major advantages.
MIDL also has some limitations, including the fact that it is relatively complex, all out parameters must be pointers (which is an issue only for programmers and programming languages who are pointer challenged), function name overloading is not supported, and the return type for methods in object interfaces must be an HRESULT (although methods can return any number of results by defining one or more parameters as out parameters or in/out parameters). Define interfaces using MIDL when possible: it is the most general and the easiest to work with. |