Figure 2

Figure 2 C++ Attributed Code

  // in manager.h
[coclass]
class ATL_NO_VTABLE CManager :
   public IEmployee
{
public: 
   STDMETHODIMP DoWork(BSTR bstrTask);
};

Figure 3 CManager Class in ATL 3.0

  class ATL_NO_VTABLE CManager : 
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass<CManager, &CLSID_Manager>,
   public IDispatchImpl<IEmployee, &IID_IEmployee>
{
public:
DECLARE_REGISTRY_RESOURCEID(IDR_MANAGER)

BEGIN_COM_MAP(CManager)
   COM_INTERFACE_ENTRY(IEmployee)
   COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

   STDMETHOD(DoWork)(BSTR bstrTask);
};

Figure 5 Calling the CManager Object

  IDispatch* pDisp;
CoCreateInstance(__uuidof(CManager), NULL, 
   CLSCTX_INPROC_SERVER, __uuidof(IDispatch), 
   (void**)&pDisp);

BSTR bstr = SysAllocString(L"call meeting");
VARIANT var;
VariantInit(&var);
V_BSTR(&var) = bstr;
V_VT(&var) = VT_BSTR;
DISPPARAMS params = {&var, NULL, 1, 0};
pDisp->Invoke(1, IID_NULL, 0, DISPATCH_METHOD, 
   &params, NULL, NULL, NULL);
VariantClear(&var);
pDisp->Release();

Figure 6 Injected Code Created for IDispatch::Invoke

  [coclass]
class CManager : public IEmployee,
    /*+++ Added Baseclass */ 
    public CComCoClass<CManager, &__uuidof(CManager)>,
    /*+++ Added Baseclass */ public 
    CComObjectRootEx<CComSingleThreadModel>,
    /*+++ Added Baseclass */ 
    public IProvideClassInfoImpl<&__uuidof(CManager)>
{
public:    
   STDMETHODIMP DoWork(BSTR bstrTask);
//+++ Start Injected Code
#injected_line 17 "d:\\development\\AttributeTest\\manager.h"
    virtual HRESULT STDMETHODCALLTYPE IEmployee::Invoke(
                /* [in] */ DISPID dispIdMember,
                /* [in] */ REFIID riid,
                /* [in] */ LCID lcid,
                /* [in] */ WORD wFlags,
                /* [out][in] */ DISPPARAMS *pDispParams,
                /* [out] */ VARIANT *pVarResult,
                /* [out] */ EXCEPINFO *pExcepInfo,
                /* [out] */ UINT *puArgErr) 
    {
        (void) riid;
        (void) dispIdMember;
        (void) lcid;
        (void) wFlags;
        (void) pExcepInfo;
        (void) puArgErr;
        HRESULT hr = S_OK;
        if (pDispParams == 0) {
            return DISP_E_BADVARTYPE;
        }
        if (pVarResult != 0) {
            VariantInit(pVarResult);
        }
        switch (dispIdMember) {
        case 1:
            {
                BSTR i1 = (BSTR) V_BSTR(&pDispParams->rgvarg[0]);
                hr = ((IEmployee*)this)->DoWork(i1);
                if (pVarResult != 0) {
                    V_VT(pVarResult) = VT_ERROR;
                    V_ERROR(pVarResult) = hr;
                }
                break;
            }
        default:
            return DISP_E_MEMBERNOTFOUND;
        }
        return hr;
    }
    virtual HRESULT STDMETHODCALLTYPE IEmployee::GetIDsOfNames(
                /* [in] */ REFIID riid,
                /* [size_is][in] */ LPOLESTR *rgszNames,
                /* [in] */ UINT cNames,
                /* [in] */ LCID lcid,
                /* [size_is][out] */ DISPID *rgDispId) 
    {
        (void) riid;
        (void) rgszNames;
        (void) cNames;
        (void) lcid;
        (void) rgDispId;
        static LPOLESTR names[] = { L"DoWork" };
        static DISPID dids[] = { 1 };
        for (unsigned int i = 0; i < cNames; ++i) {
            int fFoundIt = 0;
            for (unsigned int j = 0; 
                 j < sizeof(names)/sizeof(LPOLESTR); ++j) {
                if (lstrcmpW(rgszNames[i], names[j]) == 0) {
                    fFoundIt = 1;
                    rgDispId[i] = dids[j];
                }
            }
            if (fFoundIt == 0) {
                return DISP_E_UNKNOWNNAME;
            }
        }
        return S_OK;
    }
    HRESULT TypeInfoHelper(REFIID iidDisp, LCID /*lcid*/, 
                           ITypeInfo** ppTypeInfo) 
    {
        if (ppTypeInfo == NULL) {
            return E_POINTER;
        }
        *ppTypeInfo = NULL;
        TCHAR szModule1[_MAX_PATH];
        ::GetModuleFileName(_AtlBaseModule.GetModuleInstance(), 
            szModule1, _MAX_PATH);
        USES_CONVERSION;
        CComPtr<ITypeLib> spTypeLib;
        HRESULT hr = LoadTypeLib(T2OLE(szModule1), &spTypeLib);
        if (SUCCEEDED(hr)) {
            CComPtr<ITypeInfo> spTypeInfo;
            hr = spTypeLib->GetTypeInfoOfGuid(iidDisp, &spTypeInfo);
            if (SUCCEEDED(hr)) {
                *ppTypeInfo = spTypeInfo.Detach();
            }
        }
        return hr;
    }
    virtual HRESULT STDMETHODCALLTYPE IEmployee::GetTypeInfoCount(
        unsigned int*  pctinfo) 
    {
        if (pctinfo == NULL) {
            return E_POINTER;
        }
        CComPtr<ITypeInfo> spTypeInfo;
        *pctinfo = 
            (SUCCEEDED(TypeInfoHelper(
                __uuidof(IEmployee), 0, &spTypeInfo))) ? 1 : 0;
        return S_OK;
    }
    virtual HRESULT STDMETHODCALLTYPE IEmployee::GetTypeInfo(
        unsigned int iTInfo, LCID lcid, ITypeInfo** ppTInfo) 
    {
        if (iTInfo != 0) {
            return DISP_E_BADINDEX;
        }
        return TypeInfoHelper(__uuidof(IEmployee), lcid, ppTInfo);
    }
    BEGIN_COM_MAP(CManager)
        COM_INTERFACE_ENTRY(IEmployee)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(IProvideClassInfo)
    END_COM_MAP()
   //— End Injected Code
   // other code omitted
};

Figure 7 General C++ Attributes for Generating Type Information

Attribute
IDL Equivalent
Description
cpp_quote
cpp_quote statement
Adds a cpp-quote statement to the generated IDL file. The quoted string will be added to the header file generated from the IDL
emitidl

Used to turn on and off IDL generation
export

Used to add a union, typedef, enumeration, or struct to the generated IDL
idl_quote

Adds an IDL statement to the generated IDL file
import
import directive
Inserts an IDL file into the generated IDL file outside of the generated library block
importidl

Merges an IDL file into the generated file so that the statements in the imported file's library block are placed in the resultant file's library block, but the statements outside the imported file's library block placed outside of the resultant file's library block
importlib
importlib directive
Gives access to types defined in a compiled type library
include
include directive
Inserts a header file into the generated IDL file outside the library block
includelib

Inserts an IDL or header file into the generated IDL file after the importlib statement inside the library block
library_block

Places the item inside the library block of the generated IDL file

Figure 8 Compiler-generated Files

File
Description
vc70.idl
The generated IDL file
vc70.h
C/C++ bindings header file
vc70_i.c
Constants declaration file
vc70_p.c
Proxy-stub implementation file
dlldata.c
Entry points for the proxy-stub file
employee.dll, employee.obj
Empty DLL code generated for employee.cpp

Figure 9 [rdx] Attribute Applied to m_dwCount Data

  [coclass, progid("MySvr.Counted.1")]
class CCounted : public ICount
{ 
public:
   [rdx(key="HKCR\\MySvr.Counted.1", 
          valuename="usagecount", regtype="dword")] DWORD m_dwCount;
   CCounted() : m_dwCount(0){}
   HRESULT FinalConstruct()
   {
       RegistryDataExchange(eReadFromReg);
       m_dwCount++;
       return S_OK;
   }
   void FinalRelease()
   {
       RegistryDataExchange(eWriteToReg);
   }
/* other code */ 
};

Figure 10 A One File Implementation of a COM Server

  // manager.cpp
// compile with cl /LD manager.cpp
#define _ATL_ATTRIBUTES
#include <atlbase.h>
#include <atlcom.h>
using namespace ATL;

[module(dll, name="OneFileServer")];

[dual]
__interface IEmployee : IDispatch
{
   [id(1)] HRESULT DoWork(BSTR bstrTask);
};

[coclass]
class CManager : public IEmployee
{
public:
   HRESULT DoWork(BSTR bstrTask)
   {
      ATLTRACE(_T("I am about to %S\n"), bstrTask);
      return S_OK;
   }
};

Figure 11 Code Generated for the [module()] Parameter


dll
exe
service and service_name
Entry point
DllMain
_tWinMain
_tWinMain
_Module
CAtlDllModuleT<>
CAtlExeModule<>
CAtlServiceModuleT<>
Other exported functions
DllGetClassObject, DllRegisterServer, DllUnregisterServer, DllCanUnloadNow


Figure 12 Event Generation Code

  [dispinterface, hidden, uuid("72AB1227-E965-4D66-B09D-CB9C5F282A0F")]
__interface _IEventSink : IDispatch
{
   [id(0)] void SomethingHappened([in]BSTR strDesc);
}

[object, dual, uuid("F65FFE64-8B16-4018-A6C0-2CBE633364E4")]
__interface IEventSource : IDispatch
{
   [id(0)] HRESULT DoSomething();
}

[coclass, event_source("com") , threading("free"), progid("Svr.MyObject.1")]
class CMyObject : public IMyInterface
{
public:
   __event __interface _IEventSink;
   HRESULT DoSomething();
/* other code */
};

Figure 13 Event Receiver Code

  #define _ATL_ATTRIBUTES
#define _WIN32_DCOM
#include <atlbase.h>
#include <atlcom.h>
#include <stdio.h>
using namespace ATL;

[module(name="MyHandler")];
 
[dispinterface, hidden, uuid("72AB1227-E965-4D66-B09D-CB9C5F282A0F")]
__interface _IEventSink : IDispatch
{
   [id(0)] void SomethingHappened([in]BSTR strDesc);
}

[object, dual, uuid("F65FFE64-8B16-4018-A6C0-2CBE633364E4")]
__interface IEventSource : IDispatch
{
   [id(0)] void DoSomething();
}

[event_receiver(com)]
class CMyHandler
{
public:
   void Advise(IEventSource* pSrc)
   {
      __hook(IEventSink::SomethingHappened, pSrc, Handler);
   }
   void Unadvise(IEventSource* pSrc)
   {
      __unhook(IEventSink::SomethingHappened, pSrc, Handler);
   }
   void __stdcall Handler(BSTR strDesc)
   {
      _putws(L"event handled");
      _putws(strDesc);
   }
};

void main()
{
   HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
   if (SUCCEEDED(hr))
   {
      CComPtr<IEventSource> pSrc;
      pSrc.CoCreateInstance(L" Svr.MyObject.1");

      CMyHandler handler;
      handler.Advise(pSrc);
      pSrc->DoSomething();
      Sleep(5000); // kludge to allow the event to be handled
      handler.Unadvise(pSrc);
   }
}