Figure 1   IFoo Interface


 /////////////////////////////////////////////////////////
 //
 // IFoo.h - interface definition
 //
 #ifndef _IFOO_H
 #ifndef _IFOO_H
 
 class IFoo : public IUnknown {
 public:
   STDMETHOD(Argument)(short arg1) PURE;
   STDMETHOD(Agreement)(short *parg1) PURE;
   STDMETHOD(Articulate)(short arg1) PURE;
   STDMETHOD(Announcement)(short arg1) PURE;
 };
 
 DEFINE_GUID(IID_IFoo,0xBABACACAL,0xDADA,0xEAEA,0xFA,0xFA,0x00,0x00,0x00,0x00,0x00,0x0B);
 
 #endif


Figure 2   Implementation of IFoo


 /////////////////////////////////////////////////////////
 //
 // CoFoo.cpp - an implementation of IFoo
 //
 
 #include <windows.h>
 #include "IFoo.h"
 
 class CoFoo : public IFoo {
   ULONG m_cRef;
 public:
 // ctor/dtor
   CoFoo() : m_cRef(0) {}
   virtual ~CoFoo() {}
 
 // IUnknown methods
   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
     if (riid = = IID_IFoo || riid = = IID_IUnknown)
       *ppv = (IFoo*)this;
     else
       *ppv = 0;
     if (*ppv)
       AddRef();
     return *ppv ? S_OK : E_NOINTERFACE;
   }
 
   STDMETHODIMP_(ULONG) AddRef() {
     return ++m_cRef;
   }
 
   STDMETHODIMP_(ULONG) Release() {
     ULONG result = --m_cRef;
     if (!result)
       delete this;
     return result;
   }
 
 // IFoo methods
   STDMETHODIMP Argument(short arg1) {
     return (arg1 = = 0) ? S_OK : S_FALSE;
   }
 
   STDMETHODIMP Agreement(short *parg1) {
     *parg1 = 0;
     return NOERROR;
   }
 
   STDMETHODIMP Articulate(short arg1) {
     return (arg1 = = 0) ? S_OK : S_FALSE;
   }
 
   STDMETHODIMP Announcement(short arg1) {
     return (arg1 = = 0) ? S_OK : S_FALSE;
   }
 };


Figure 3   IFoo Client


 /////////////////////////////////////////////////////////
 //
 // Client1.cpp - client code to access IFoo
 //
 
 #include <windows.h>
 #include "IFoo.h"
 
 void UseFoo(IUnknown *punk)
 {
   IFoo *pfoo = 0;
   if (SUCCEEDED(punk->QueryInterface(IID_IFoo, (void**)&pfoo))) {
     short s = 0;
     if (pfoo->Agreement(&s) = = S_OK)
       pfoo->Argument(s);
     pfoo->Release();
   }
 }


Figure 4   Marshaling and Interfaces

Standard Marshaling Upon initial connection to the object, the system creates and connects the standard proxy manager on the client side to the stub manager on the object side. The particular proxy and stub implementations for the interface being marshaled are then created and connected as well. Both the client and the object are oblivious to the entire infrastructure.
Custom Marshaling Upon initial connection to the object, the system asks the object to build a marshaling packet that is transmitted to the client. Once transmitted, an object-specified handler is created in the client's address space to read the packet and act as a delegate. The client is oblivious to this. The object and handler, however, must explicitly support the IMarshal interface to enable custom marshaling.
Standard Interface A Microsoft-defined interface that is part of the standard OLE distribution. A large number of standard interfaces support standard marshaling via proxy/stub implementations provided by the system DLLs.
Custom Interface An interface defined by an independent software vendor. All custom interfaces require an ISV-supplied proxy/stub DLL to participate in standard marshaling.


Figure 7   An IDL DLL

IFOO.IDL


 [uuid(BABACACA-DADA-EAEA-FAFA-00000000000B), object]
 interface IFoo : IUnknown
 {
     import "unknwn.idl";
     HRESULT Argument([in] short arg1);
     HRESULT Agreement([out] short *parg1);
     HRESULT Articulate([in] short arg1);
     HRESULT Announcement([in] short arg1);
 }
IFOOPS.DEF

 LIBRARY IFOOPS
 EXPORTS
         DllGetClassObject
         DllCanUnloadNow
         GetProxyDllInfo
IFOOPS.MAK

 !include <ntwin32.mak>
 
 !ifndef MIDL
 MIDL            = midl
 !endif
 
 MIDLFLAGS    = /ms_ext /c_ext /cpp_cmd $(CC)
 
 .cxx.obj:
     $(CC) $(cdebug) $(CFLAGS) $(cvars) /c $<
 
 .c.obj:
     $(CC) $(cdebug) $(CFLAGS) $(cvars) /c $<
 
 all:  IFooPS.dll
 
 IFoo_i.obj : IFoo_i.c
 
 IFoo_p.obj : IFoo_p.c IFoo.h
 
 dlldata.obj : dlldata.c
 
 DLLOBJS= IFoo_p.obj IFoo_i.obj dlldata.obj
 
 idirps.dll: $(DLLOBJS) IFooPS.def
         $(link) $(linkdebug) -DLL -OUT:IFooPS.dll -DEF:IFooPS.def \
         $(DLLOBJS) rpcrt4.lib $(ole2libs)
 
 IFoo.h dlldata.c IFoo_p.c IFoo_i.c : IFoo.idl
                 $(MIDL) $(MIDLFLAGS) -Oi IFoo.idl


Figure 8   IDispatch Interface


 interface IDispatch : public IUnknown {
   HRESULT GetTypeInfoCount(UINT *pctinfo);
   HRESULT GetTypeInfo(UINT itinfo, 
                       LCID lcid, 
                       ITypeInfo **pptinfo);
 
   HRESULT GetIDsOfNames(REFIID riid, 
                         LPOLESTR *rgszNames, 
                         UINT cNames, 
                         LCID lcid, 
                         DISPID *rgdispid);
     
   HRESULT Invoke( DISPID dispidMember,
                   REFIID riid,
                   LCID lcid,
                   WORD wFlags,
                   DISPPARAMS *pdispparams,
                   VARIANT *pvarResult,
                   EXCEPINFO *pexcepinfo,
                   UINT *puArgErr);
 };


Figure 9   COQUUX

COQUUX.ODL


 [   uuid(BABACACA-DADA-EAEA-FAFA-00000000000A), 
     version(1.0),
     lcid(9),
     helpstring("The Quux Type Library")
 ]
 library Quux
 {
     importlib("stdole32.tlb");
 
 // the logical interface
     [ uuid(00000001-0000-0000-FFFF-112233445566), odl]
     interface IQuuxLogical : IUnknown {
       [id(1)] void Chatter([in] short arg1);
       [id(2)] short Commentary();
       [id(3)] void Criticism([in] long arg1);
       [id(4)] void ChitChat([in] long arg1);
     }
 
 // the physical interface
     [ uuid(00000002-0000-0000-FFFF-112233445566)]
     dispinterface DQuuxPhysical {
       interface IQuuxLogical;
     }
 
 // the instantiable implementation class
     [ uuid(00000003-0000-0000-FFFF-112233445566)]
     coclass CoQuux {
       [default] dispinterface DQuuxPhysical;
     }
 }

COQUUX.CPP

 /////////////////////////////////////////////
 //
 // CoQuux.cpp - an implementation of a dispatch-interface
 //
 
 #include <windows.h>
 
 // bring in ODL generated header
 #include "CoQuux.h"
 
 LPCOLESTR szTypeLibName = OLESTR("CoQuux.tlb");
 
 class CoQuux :  public IQuuxLogical, // ->IUnknown
                 public DQuuxPhysical // ->IDispatch->IUnknown
 {
     ULONG   m_cRef;
     short   m_sum;
     static ITypeInfo *s_pTypeInfo;
 public:
 
 // ctor/dtor
     CoQuux() 
     :  m_cRef(0), m_sum(0)
     {
         if (s_pTypeInfo != 0)
             return;
 
 // load the type info to implement IDispatch members
         ITypeLib *ptl;
         if (SUCCEEDED(LoadTypeLib(szTypeLibName, &ptl))) {
             ptl->GetTypeInfoOfGuid(IID_IQuuxLogical, &s_pTypeInfo);
             ptl->Release();            
         }
     }
 
     virtual ~CoQuux() {  }
 
 // IUnknown methods
     STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppv) {
         if (riid = = IID_IUnknown || riid = = IID_IQuuxLogical)
             *ppv = (IQuuxLogical *)this;
         else if (riid = = IID_IDispatch || riid = = DIID_DQuuxPhysical)
             *ppv = (DQuuxPhysical *)this;
         else
             *ppv = 0;
         if (*ppv)
             AddRef();
         return *ppv ? S_OK : E_NOINTERFACE;
     }
 
     STDMETHODIMP_(ULONG) AddRef() {
         return ++m_cRef;
     }
 
     STDMETHODIMP_(ULONG) Release() {
         ULONG result = --m_cRef;
         if (!result)
             delete this;
         return result;
     }
 
 // IDispatch methods
     STDMETHODIMP GetTypeInfoCount(UINT FAR* pctinfo) {
         *pctinfo = s_pTypeInfo ? 1 : 0;
         return NOERROR;
     }
 
     STDMETHODIMP GetTypeInfo( UINT itinfo,  LCID lcid,
                               ITypeInfo FAR* FAR* pptinfo) {
         *pptinfo = 0;
         if (itinfo != 0)
             return E_INVALIDARG;
         else if (s_pTypeInfo = = 0)
             return E_NOTIMPL;
         
         (*pptinfo = s_pTypeInfo)->AddRef();
         return NOERROR;
     }
 
     STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR * * rgszNames,
                                UINT cNames, LCID lcid, 
                                DISPID * rgdispid) {
         if (s_pTypeInfo = = 0)
             return E_NOTIMPL;
         return s_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
     }
 
 // forward invoke to the type info object, providing an implementation
 // of the logical interface as the first parameter
     STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid,
                         WORD wFlags, DISPPARAMS FAR* pdispparams,
                         VARIANT FAR* pvarResult, 
                         EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) {
         if (s_pTypeInfo = = 0)
             return E_NOTIMPL;
         return s_pTypeInfo->Invoke((IQuuxLogical*)this, dispidMember, wFlags, 
                                    pdispparams, pvarResult, 
                                    pexcepinfo, puArgErr);
     }
 
 // IQuuxLogical methods 
     STDMETHODIMP_(void) Chatter(short arg1)  {
       m_sum += arg1;
     }
 
     STDMETHODIMP_(short) Commentary()  {
         return m_sum;
     }
 
     STDMETHODIMP_(void) Criticism(short arg1)  {
         m_sum *= arg1;
     }
 
     STDMETHODIMP_(void) ChitChat(short arg1)  {
         m_sum -= arg1;
     }
 
 };
 
 // give the type info pointer linkage
ITypeInfo *CoQuux::s_pTypeInfo;

Figure 11   Dispatch Invocation Costs

  Invocations per Second by Argument Type
  Scalars Unicode Text ANSI Text
In-process
Custom Interface 3,822,265 73,421 120,337
IDispatch::Invoke 14,333 11,494 7,500
GetIDsOfNames + Invoke 5,826 4,910 3,891
Out-of-process
Custom Interface 675 591 640
IDispatch::Invoke 555 447 468
GetIDsOfNames + Invoke 274 252 258
All figures based on a 90MHz Pentium running Windows NT 3.5


Figure 12   COQUUX2.CPP


 /////////////////////////////////////////////
 //
 // CoQuux2.cpp - an implementation of a dual-interface
 //
 
 #include <windows.h>
 
 // bring in ODL generated header
 #include "CoQuux2.h"
 
 LPCOLESTR szTypeLibName = OLESTR("CoQuux2.tlb");
 
 class CoQuux2 :  public DIQuux // ->IDispatch->IUnknown
 {
     ULONG   m_cRef;
     short   m_sum;
     static ITypeInfo *s_pTypeInfo;
 public:
 
 // ctor/dtor
     CoQuux2() 
     :  m_cRef(0), m_sum(0) 
     {
         if (s_pTypeInfo != 0)
             return;
 
 // load the type info to implement IDispatch members
         ITypeLib *ptl;
         if (SUCCEEDED(LoadTypeLib(szTypeLibName, &ptl))) {
             ptl->GetTypeInfoOfGuid(IID_IQuuxLogical, &s_pTypeInfo);
             ptl->Release();            
         }
     }
 
     virtual ~CoQuux2() {  }
 
 // IUnknown methods
     STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppv) {
         if (riid= =IID_IUnknown || riid==IID_IDispatch || riid= =IID_DIQuux)
             *ppv = (DIQuux *)this;
         else
             *ppv = 0;
         if (*ppv)
             AddRef();
         return *ppv ? S_OK : E_NOINTERFACE;
     }
 
     STDMETHODIMP_(ULONG) AddRef() {
         return ++m_cRef;
     }
 
     STDMETHODIMP_(ULONG) Release() {
         ULONG result = --m_cRef;
         if (!result)
             delete this;
         return result;
     }
 
 // IDispatch methods
     STDMETHODIMP GetTypeInfoCount(UINT FAR* pctinfo) {
         *pctinfo = s_pTypeInfo ? 1 : 0;
         return NOERROR;
     }
 
     STDMETHODIMP GetTypeInfo( UINT itinfo,  LCID lcid,
                               ITypeInfo FAR* FAR* pptinfo) {
         *pptinfo = 0;
         if (itinfo != 0)
             return E_INVALIDARG;
         else if (s_pTypeInfo = = 0)
             return E_NOTIMPL;
         
         (*pptinfo = s_pTypeInfo)->AddRef();
         return NOERROR;
     }
 
     STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR * * rgszNames,
                                UINT cNames, LCID lcid, 
                                DISPID * rgdispid) {
         if (s_pTypeInfo = = 0)
             return E_NOTIMPL;
         return s_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
     }
 
 // forward invoke to the type info object, providing an implementation
 // of the logical interface as the first parameter
     STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid,
                         WORD wFlags, DISPPARAMS FAR* pdispparams,
                         VARIANT FAR* pvarResult, 
                         EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) {
         if (s_pTypeInfo = = 0)
             return E_NOTIMPL;
         return s_pTypeInfo->Invoke((DIQuux*)this, dispidMember, wFlags, 
                                    pdispparams, pvarResult, 
                                    pexcepinfo, puArgErr);
     }
 
 // DIQuux methods 
     STDMETHODIMP Dialog(short arg1)  {
       m_sum += arg1;
       return NOERROR;
     }
 
     STDMETHODIMP Dissension(short *pResult)  {
       *pResult = m_sum;
       return NOERROR;
     }
 
     STDMETHODIMP Debate(short arg1)  {
       m_sum *= arg1;
       return NOERROR;
     }
 
     STDMETHODIMP Diatribe(short arg1)  {
       m_sum -= arg1;
       return NOERROR;
     }
 };
 
 // give the type info pointer linkage
ITypeInfo *CoQuux::s_pTypeInfo;


Figure 14   Dual Invocation Costs

  Invocations per Second by Argument Type
  Scalars Unicode Text ANSI Text
In-process
Custom Interface 3,822,265 73,421 120,337
Dual Interface 3,822,265 60,698 19,503
IDispatch::Invoke 14,333 11,494 7,500
GetIDsOfNames + Invoke 5,826 4,910 3,891
Out-of-process
Custom Interface 675 591 640
Dual Interface 613 549 549
IDispatch::Invoke 555 447 468
GetIDsOfNames + Invoke 274 252 258
All figures based on a 90MHz Pentium running Windows NT 3.51


Figure 15   Interface Tradeoffs

  Dispatch Dispatch with DISPID resolved at run time Dual Custom
Standard marshaling support Built-in   Built-in User-supplied
Physical parameter data types VARIANTARG VARIANTARG 13 VARTYPES Unlimited
Logical parameter data types 13 VARTYPES 13 VARTYPES 13 VARTYPES Unlimited
Physical result type VARIANT VARIANT HRESULT HRESULT
Logical result type 13 VARTYPES 13 VARTYPES 13 VARTYPES HRESULT
Inproc performance penalty (integral types) 275 700 None None
Inproc performance penalty (Unicode text) 7 15 1.25 None
Inproc performance penalty (ANSI text) 17 32 6.75 None
OutOfProc performance penalty (integral types) 7,300 14,500 6,600 6,000
OutOfProc performance penalty (Unicode text) 170 300 140 128
OutOfProc performance penalty (ANSI text) 275 500 240 200
Primary clients Visual Basic 3.0 and 4.0, C++ Visual Basic 3.0 and 4.0, C++ Visual Basic 3.0 and 4.0, C++ C++
MFC support Good Good None Minimal
All penalties are relative to the performance of an inproc custom interface. For example, a performance penalty of 275 means that call took 275 times as long to complete than an inproc custom interface call.