Basic ATL Object

This section presents an overview of a complete ATL project. An ATL project usually consists of the following files:

IDL

An  IDL is used to produce the following files-

  1. C++ headers for the declared interfaces.
  2. Type library for the server.
  3. Marshaling source code.

A type library is the binary form of an IDL; it describes the COM object's properties and its methods. An application uses a type library to determine the interfaces supported by the COM object.

// GENERIC.IDL

/* 'imports' provide access to all typedefs, declarations, and interface 
definitions */
import "oaidl.idl";
import "ocidl.idl";

/* This code adds another interface on this object. Extra interfaces need to 
be added manually */
[
    object,
    uuid(9D5ACE00-F934-11d3-8EBC-000000000000),
    dual,
    helpstring("IEngine Interface"),
    pointer_default(unique)
]
interface IEngine : IDispatch
{
};

/* interface attributes:
object    : Identifies this interface as a COM interface (by default, header 
            files and proxy/stub code will be generated. if 'local' attribute
            is specified, only the interface file is generated.
uuid()    : The IID of the interface.
dual      : The interface exposes its methods and properties through IDispatch 
            and directly through the VTBL.
pointer_default(unique) : TODO
*/
[
    object,
    uuid(AECA53E2-F3A6-11D3-8EB4-000000000000),
    dual,
    helpstring("IObject1 Interface"),
    pointer_default(unique)
]
interface IObject1 : IDispatch
{
    [propget, id(1), helpstring("property Number")]
    HRESULT Number([out, retval] long *pVal);

    [propput, id(1), helpstring("property Number")]
    HRESULT Number([in] long newVal);

    [id(2), helpstring("method SomeFunc")]
    HRESULT SomeFunc([in] long nNumber);

    [id(3), helpstring("method GetEngine")]
    HRESULT GetEngine([out, retval] IEngine** ppEngine);

    [id(4), helpstring("method GetString")]
    HRESULT GetString([out, retval] BSTR* pbstrString);

    [id(5), helpstring("method SetString")]
    HRESULT SetString([in] BSTR bstrString);
};

/* Items declared or referenced in the library section will be compiled into
the type library. */
[
    uuid(AECA53D1-F3A6-11D3-8EB4-000000000000),
    version(1.0),
    helpstring("Generic 1.0 Type Library")
]
library GENERICLib
{
    /* importlib is used to bring in the standard COM libraries
    Note: 'import' is used to import IDL files, whereas 'importlib' is used
    when you don't have the IDL but its associated type library. Therefore, 
    any libraries referenced in 'importlib' must be distributed with the 
    application or be present on the target machine */
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    [
        uuid(AECA53E3-F3A6-11D3-8EB4-000000000000),
        helpstring("Object1 Class")
    ]
    coclass Object1
    {
        [default] interface IObject1;
        interface IEngine;
    };
};

Server Implementation

The following code implements the server part of the ATL project in which a COM object resides.

//GENERIC.CPP

#include "stdafx.h"
#include "resource.h"
#include <initguid.h>

/* 'Generic.h' and 'Generic _i.c' are MIDL-generated files
MIDL-generated GUID constants have the prefixes of IID_, CLSID_, LIBID_ for
interfaces, coclasses, and type libraries, respectively and must be included
in the project only once.*/

#include "Generic.h"     // MIDL-generated: Contains interface declarations in 
                         // C++ classes and extern definitions of IID and CLSIDs
#include "Generic_i.c"   // MIDL-generatied:Contains non-extern definitions of 
                         // IIDs and CLSIDs
#include "Object1.h"     // COM object declaration

/* _Module is used to implement the basic services of the server such as 
registrations, object map, etc... */

CComModule _Module;

/* The object map lists all COM objects offered by this module*/
BEGIN_OBJECT_MAP(ObjectMap)
    OBJECT_ENTRY(CLSID_Object1, CObject1)
END_OBJECT_MAP()

/* DLL Entry point
extern "C": allows the functions to be called from other languages by dropping 
the C++ naming decoration.

WINAPI : defined as __stdcall. Use WINAPI when writing a DLL with exported API 
entry points

DisableThreadLibraryCalls() : Disables calls that the OS would normally make
to DLLMain() when threads attach to or detach from the DLL (optimization code)
*/

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        _Module.Init(ObjectMap, hInstance, &LIBID_GENERICLib);
        DisableThreadLibraryCalls(hInstance);
    }
    else if (dwReason == DLL_PROCESS_DETACH)
        _Module.Term();

    return TRUE; // ok
}

/* Used to determine whether the DLL can be unloaded by OLE 
typedef     LONG              HRESULT;
#define     EXTERN_C          extern "C"
#define     STDAPICALLTYPE    __stdcall
#define     STDAPI            EXTERN_C HRESULT STDAPICALLTYPE
Therefore, STDAPI = extern "C" long __stdcall
*/
STDAPI DllCanUnloadNow(void)
{
    return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}


/* Returns a class factory to create an object of the requested type */
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return _Module.GetClassObject(rclsid, riid, ppv);
}


/* DllRegisterServer - Adds entries to the system registry 
_Module.RegisterServer(TRUE, NULL) registers the type library and ALL
objects in the object map */

STDAPI DllRegisterServer(void)
{
    return _Module.RegisterServer(TRUE);
}


/* DllUnregisterServer - Removes entries from the system registry */
STDAPI DllUnregisterServer(void)
{
    return _Module.UnregisterServer(TRUE);
}

COM objects

COM Object Declaration

A COM object is implemented using multiple inheritance as follows:

#include "resource.h" // main symbols

class ATL_NO_VTABLE CObject1 : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CObject1, &CLSID_Object1>,
    public IDispatchImpl<IObject1, &IID_IObject1, &LIBID_GENERICLib>,
    public IDispatchImpl<IEngine, &IID_IEngine, &LIBID_GENERICLib>
{
public:
    CObject1() {}

    // All initialization must be performed in FinalConstruct()
    HRESULT FinalConstruct()
    {
        m_lNum = 0;
        return S_OK;
    }

/* Implements script-based registry support. IDR_OBECT1 is the resource ID
of the registry script */
DECLARE_REGISTRY_RESOURCEID(IDR_OBJECT1)
DECLARE_PROTECT_FINAL_CONSTRUCT()

/* All interfaces supported by this object (obtainable via QueryInterface()
must be in the COM_MAP. Note that when exposing TWO or more interfaces from 
the same object, it will be necessary to define which inheritance branch is 
used to obtain IDispatch (recall that both interfaces inherit from IDispatch
resulting in a diamond-shape inheritance scheme*/
BEGIN_COM_MAP(CObject1)
    COM_INTERFACE_ENTRY(IEngine)
    COM_INTERFACE_ENTRY(IObject1)
    COM_INTERFACE_ENTRY2(IDispatch, IObject1)
END_COM_MAP()

// IObject1 interface
/* All methods defined in the IDL will have the same name in C++. Properties
will have put_, or get_ prefixed to the property name */
public:
    STDMETHOD(SetString)(/*[in]*/ BSTR bstrString);
    STDMETHOD(GetString)(/*[out, retval]*/ BSTR* pbstrString);
    STDMETHOD(GetEngine)(/*[out, retval]*/ IEngine** ppEngine);
    STDMETHOD(SomeFunc)(/*[in]*/ long nNumber);
    STDMETHOD(get_Number)(/*[out, retval]*/ long *pVal);
    STDMETHOD(put_Number)(/*[in]*/ long newVal);
private:
    long m_lNum;
};

COM Object Implementation

// Object1.cpp

#include "stdafx.h"
#include "Generic.h"    // MIDL-generated
#include "Object1.h"    // COM object declaration


STDMETHODIMP CObject1::get_Number(long *pVal)
{
    *pVal = m_lNum;
    return S_OK;
}

STDMETHODIMP CObject1::put_Number(long newVal)
{
    m_lNum = newVal;
    return S_OK;
}

STDMETHODIMP CObject1::SomeFunc(long nNumber)
{
    m_lNum = nNumber;
    return S_OK;
}

STDMETHODIMP CObject1::GetEngine(IEngine **ppEngine)
{
    IEngine *pItf = NULL;
    QueryInterface( __uuidof( IEngine), reinterpret_cast<void**>(&pItf));

    *ppEngine = pItf;
    return S_OK;
}

STDMETHODIMP CObject1::GetString(BSTR *pbstrString)
{
    *pbstrString = SysAllocString( OLESTR("This is a BSTR COM object") );

    return S_OK;
}

STDMETHODIMP CObject1::SetString(BSTR bstrString)
{
    // TODO
    return S_OK;
}

Object / Server Registration

TODO: Refer to another section which details type library and rgs registration.