This section presents an overview of a complete ATL project. An ATL project usually consists of the following files:
An IDL is used to produce the following files-
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;
};
};
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);
}
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;
};
// 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;
}
TODO: Refer to another section which details type library and rgs registration.