Last part of summary of great book about COM+ATL

ATL

Handles COM infrastructure, implements all common stuff.

ATL is set of headers, is VS type of project.

Provides a class “CComModule” and global instance “_Module”, it initialized/uninitialized within DllMain (attach/detach).

It provides set of operations – register/unregister COM dll, creating class object.

BEGIN_OBJECT_MAP(ObjectMap)/END_OBJECT_MAP() – embraces all coclasses defined within module by OBJECT_ENTRY(<coclass name>) macro.

Registration for COM servers: for inrpoc – “regsvr32” utility, for local servers – “register/unregister” parameter for WinMain.

CoClass template

class ATL_NO_VTABLE CCoHexagon :

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<CCoHexagon, &CLSID_CoHexagon>,

public IDraw

{

public: CCoHexagon() { }

DECLARE_REGISTRY_RESOURCEID(IDR_COHEXAGON) DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CCoHexagon)

COM_INTERFACE_ENTRY(IDraw)

END_COM_MAP()

// IDraw

public:

STDMETHOD(<method name>)();

};

CComObjectRootEx<> provides IUnknown implementation

BEGIN_COM_MAP(<coclass name>)/END_COM_MAP() embraces interfaces declaration by COM_INTERFACE_ENTRY(<interface name>)

CComCoClass<CCoHexagon, &CLSID_CoHexagon> – defines coclass factory, handles aggregation

rgs-files – registry scripting

Properties – for VB, for C++ just a syntax sugar: methods get_<prop name>, set_<prop name>

IDL definition:[propget], function parameter specification: [out, retval]

COM string: CComBSTR – wrapper over raw BSTR

Text conversion macroses: converts between C (const C++ string), A (ANSI char*), BSTR, W, T, OLE strings.

ATLTRACE – trace macro for ATL debugging.

Apartments – it is an idea, it is like a “room” where some coclasses are placed, there is two kind of “room”:

– STA provides sync via invisible window’s messages, so all methods of all coclasses within the same “room” will be called sequentially.

– MTA – does not provide any sync.

Process can contain

– 0 or 1 MTA

– 0 or 1 STA as an addition to 1 MTA, first STA – is called “main STA”.

For local server:

CoInitialize() turns current thread into separate STA.

CoInitializeEx(NULL, COINIT_MULTITHREADED) turns current thread into MTA.

For inproc server:

HKCR\CLSID\{guid}\InprocServer32, key “ThreadingModel”, can hold values:

– (none) – each object will join main STA

– Apartment – each object loaded into separate STA

– Free – all objects loaded into MTA

– Both – apartement model is defined by client setting

Proxy/stubs are used: different STAs communicate, MTA communicates to STA

Proxy/stubs are NOT used: STA communicates to itself, MTA communicates to itself.

ATL support classes for thread models: CComSingleThreadModel and CComMultiThreadModel.

CComObject<> class family is parametrized by final ATL CoClass (which is abstract itself), it provides final implementation for IUnknown.

ATL_NO_VTABLE – macros prevents vtable creating for all at inheritance hierarchy  besides final object (object size and object creation speed optimization)

FinalConstruct(), FinalRelease() – should be used instead constructor/destructor in order to avoid issues during object creation/destruction (there are some hacks with NO_VTABLE object infrastructure)

CComObjectRootBase – contains reference count variable and aggregation support

CComObjectRootEx<> – apartments support

CComCoClass<> – class factories, aggregation and error handling

CComObject<> – provides implementation for actual IUnknown interface

Public section of coclass should contains COM_MAP:

BEGIN_COM_MAP(<coclass name>)

COM_INTERFACE_ENTRY(<coclass public specific interface name>)

END_COM_MAP()

COM error handling (COM exceptions)

Errors processing

Interface ISupportErrorInfo, one method InterfaceSupportsErrorInfo – defines whether some interface supports possibility to get an error.

Actions of COM server object in case error happens:

1. ICreateErrorInfo * p1;

2. CreateErrorInfo(p1);

3. IErrorInfo * p2;

4. p1->QueryInterface(, p2);

5. SetErrorInfo(NULL, p2);

ICreateErrorInfo – provides an ability to set error’s description, GUID, Help, Source

IErrorInfo – interface of error passed into client’s thread

SetErrorInfo – function actually sets an error for some thread

Actions of COM client’s code in case error happens:

1. Got a bad HRESULT.

2. Try to get ISupportErrorInfo.

3. Define whether the failed interface can provide an error information (by InterfaceSupportsErrorInfo call)

4. Call GetErrorInfo() to get IErrorInfo interface and call its methods to get the information about the error.

Nested classes is an alternative way how the COM class can be constructed. Parent class is inherited only from IUknown. Internal classes get a pointer at parent class – it allows nested classes to access the parents, methods and also IUknown interface. This way is used in case some interfaces has the methods with equal names, so it is impossible to use multiple inheritance. It is possible to mix these two approaches.

Tear-off interfaces

Some COM interface is implemented by nested internal class of a friend class in order to avoid vtp-table (object construction time) and objects size reduce.

COM reuse

Wrapped object should have an IUnknow pointer of wrapper object

FinalConstruct and FinalRelease are good places to deal with nested object

Containment  – mean wrapper object is inherited from the interface of wrapped object, provides an implementation of each it method, but just redirects calls to wrapped object.

Aggregation – when the wrapper object exposes an interface of wrapped object itself. Not all COM classes can be aggregated, special infrastructure should be supported by wrapped object, in should be INPROC server and be at the same apartment as the its wrapper.

CComModule – represents body of COM class – dll or exe.

All classes are registered at OBJECT_MAP, it is easy to add some COM class by OBJECT_ENTRY macro, these classes will be created automatically. It is possible to specify some coclasses which will not be created automatically – OBJECT_ENTRY_NON_CREATABLE.

Creator class – auxiliary class that helps to construct an object.

GetObjectDescription() – coclass method defines description of some class

Category (identified by CATID) – describes some set of interface grouped by logical meaning.

In case we say that class supports some category this means that class supports all interfaces from the category.

ObjectMain() coclass method of ATL’s coclass is called when server is started or stopped. It is good place to acquire and release resources.

IDispatch – for lang that doesn’t support v-table. All params, that are passed through the interface should be variants.

IDispatch : IUknown, methods:

1. GetTypeInfo

2. GetTypeInfoCount

3. GetIDsOfNames – translate method or property name into DISPID.

4. Invoke – call the specified method of property.

Methods can be implemented manually or with a support of type libraries.

DISPID – identificator, describes some interface method of property, it is long, not GUID.

Work with raw variant:

VARIANT myOtherVar;

VariantInit(&myOtherVar);

myotherVar.vt = VT_I4;

myotherVar.lVal = 5000;

VariantClear(&myotherVar);

another methods: VariantCopy(), VariantChangeType().

Variant object wrapper: _variant_t

DISPPARAMS – structure-container that stores all parameters that passed into dispatch-method.

SAFEARRAY – arrays, that handled through special APIs

Dual interface – supports both v-table and dispatch interface.

IDispatchImpl<> – template class that helps to implement IDispatch interface.

Multiple dual interfaces is not good idea – since the script clients can get only one default dispatch interface and on script clients can get v-table interfaces without dispatch.

Enumerations

COM enumerations are non editable containers, like C-arrays.

IEnumUnknown

IEnumVariant

IEnumString

IEnumConnectionPoints

IEnumConnections

Methods, that provides each interface:

Next()

Skip()

Reset()

Clone()

It is required to declare such interface for particular data type in class that implements it. ATL provides template CComIEnum<>, CComEnum<> and CComIEnumImpl<>.

Collections

Editable COM container.

Exposes its functionality through dispatch

Expected methods:

1. Item()

2. Add()

3. Remove()

4. Count()

COM collection class just inherits IDispatch and implements a container for business COM classes.

Callback interfaces and connectable objects

IConnectionPointContainer – is inherited by some class that provides one or more event’s sources. Provides methods EnumConnectionPoints(**p) and FindConnectionPoint(guid, **p)

IConnectionPoint – interface is implemented by event source object

Next post regarding COM and ATL

ATL

Handles COM infrastructure, implements all common stuff.

ATL is set of headers, is VS type of project.

Provides a class “CComModule” and global instance “_Module”, it initialized/uninitialized within DllMain (attach/detach).

It provides set of operations – register/unregister COM dll, creating class object.

BEGIN_OBJECT_MAP(ObjectMap)/END_OBJECT_MAP() – embraces all coclasses defined within module by OBJECT_ENTRY(<coclass name>) macro.

Registration for COM servers: for inrpoc – “regsvr32” utility, for local servers – “register/unregister” parameter for WinMain.

CoClass template

class ATL_NO_VTABLE CCoHexagon :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCoHexagon, &CLSID_CoHexagon>,
public IDraw
{
public: CCoHexagon() { }
DECLARE_REGISTRY_RESOURCEID(IDR_COHEXAGON) DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CCoHexagon)
COM_INTERFACE_ENTRY(IDraw)
END_COM_MAP()
// IDraw
public:
STDMETHOD(<method name>)();
};

CComObjectRootEx<> provides IUnknown implementation

BEGIN_COM_MAP(<coclass name>)/END_COM_MAP() embraces interfaces declaration by COM_INTERFACE_ENTRY(<interface name>)

CComCoClass<CCoHexagon, &CLSID_CoHexagon> – defines coclass factory, handles aggregation

rgs-files – registry scripting

Properties – for VB, for C++ just a syntax sugar: methods get_<prop name>, set_<prop name>

IDL definition:[propget], function parameter specification: [out, retval]

COM string: CComBSTR – wrapper over raw BSTR

Text conversion macroses: converts between C (const C++ string), A (ANSI char*), BSTR, W, T, OLE strings.

ATLTRACE – trace macro for ATL debugging.

COM book extract

IUnknown (QueryInterface, AddRef, Release)

InProc
OutProc
Remote

Contained (nested wrapped by container), Aggregated (nested is exposed by container)

GUID – identificator (IID, CLSID, LIBID)

AddRef/Release rules
1. AddRef internally by CoClass if interface is passed to client
2. Release when client finish working with interface
3. AddRef if pointer on interface is copied
4. AddRef at begging at function which got interface as parameter, Release at function exit

Almost all COM methods returns HRESULT (error lookup can explain it)

IDL -interface definition lang (MIDL- compiler)

Macros: STDMETHOD, DECLARE_INTERFACE, …

Strings: OLECHAR (just char or wchar_t – only C/C++ lang) and BSTR (Sys* – family of functions), …

IClassFactory (CreateInstance, LockServer), IClassFactory2 = IClassFactory + Licensing

CoClassFactory is one to one with CoClass

Dll storage should provide mandatory export functions
DllGetClassObject
CanUnloadNow
RegisterServer
UnRegisterServer

DllGetClassObject – creates and returns factory for CoClass specified by CLSID. Factory doesn’t have CLSID

CanUnloadNow operates with two counters (common for all CoClasses contained within dll): manual (operated by LockServer), automatic (constructor/destructor of each COM object), returns TRUE if both are 0

Registry

ProgID – test represented CLSID, should be uniq (“servername.coclassname.version”). ProgID and CLSID can be pragmatically converted.

Min changes:
HKCR – contains all ProgIDs (subkeys: CLSID, CurVer)
HKCR\CLSID – contains all CLSIDs (subkeys: ProgID, VersionIdependentProgID, InprocServer32- path to dll, LocalServer32- path to exe)

ole32.dll – COM system dll, connects client and rpcss.exe – service manager

Client’s code:
1. each thread should call CoInitialize() before using COM and CoUnitialize() after it using.
2. CoGetClassObject() (or CoCreateInstance(), it is wrapper, it calls CoGetClassObject() internally)
3. Release interface pointer.

IDL data types: boolean, byte, char, small, wchar_t, short, long, void*, float, double, hyper.

Variant – compatible with non C++ lang (VB), if we have ONLY variants – stub/proxy can be automatically generated.

Variant compatible datatypes: VARIANT_BOOL, double, float, long, short, BSTR, DATE, IDispatsh*, *IUnknown, VARIANT, CY/CURRENCY, SAFEARRAY

IDL
typedef – like C++ typedef
import – like C++ #incluse, (wtypes.idl – winodw’s types, unknwn.idl – IUnknown + IClassFactory, objidl.idl – more COM stuff, oleidl.idl – OLE stuff, oaidl.idl – autoamation)
[object+uuid()] – define interface
interface method parameter’s attributes – in, out, in out
cpp_quote + helpstring attribute defines text hint
library – defines library (specified by LIBID) where coclass (specified by CLSID) is stores.
coclass – includes interface declaration inside.
default interface – for non C++ langs
out, retval – attributes that specify return value for C++ and non C++ langs

tlb – (binary idl) needs for non C++ langs, registry: HKCR\TypeLib\LIBID\version = helpstring + subkey (natural lang selector) and update HKCR\CLSID\<CLSID>\TypeLib = <LIBID>

OLE\COM object viewer – nice tool (COM browser)

COM smart pointers

Proxy Class is inherited from IRpcProxyBuffer (connect, disconnect)

IRpcChannelBuffer – instanse is provided for IRpcProxyBuffer::connect method as parameter.

Stub class is inherited from IRpcStubBuffer (connect, disconnect, invoke, isIIDsupported, CountRefs, DebugServerQueryInterface, DebugServerRelease)

IPSFactoryBuffer – factory for Proxies and Stubs

dllhost.exe – process that loads COM-dll and makes COM-exe local server process from it.

AppID – identifies local exe-server, can be equal to one of CLSID of coclasses that it stores.

Registry key HKCR\AppID\<AppID> contains names (AccessPermitions-  acl list, AuthentificationLevel, DLLSurrogate – default dllhost.exe, LaunchPermisions, RemoteServerName), it is some kind of security.

It is also required to update HKCR\CLSID\<CLSID> with AppID value that refers to it’s exe local server container’s AppID

OLE object viewer can upgrade inproc server to be spawned as local server

Marshaling
1. Custom – need to inherit your coclass from IMarshal (GetUnmarshalClass, GetMarshalSizeMax, MarshalInterface, UnMarshalInterface, ReleaseMarshalData, DisconnectObject) and implement it.
2. Standard – MIDL defines all proxy/stub code, we need to build and register proxy and stub dll. Preproc definitions should be defined REGISTER_PROXY_DLL and _WIN32_DCOM and link with libs: rpcndr.lib, rpcns4.lib, rpdrt4.lib

Registry: HKCR\Interface\<IID> (values: BaseInterface, NumMethods, ProxyStubClsid32 (this mandatory one), ProxyStubClsid, TypeLib)

3. Universal (or type library marshaling) – use system proxy/stub dll, but all interfeaces method parameters should be variant – compatible. IDL attribute [oleautomation] specifies that some interface will be marshaled by universal marshaling.

Registry: HKCR\Interface\<IID>\ProxyStubClsid32 should be set in oleaut32.dll library’s CLSID. TypeLib should be registered at HKCR\TypeLib. HKCR\Interface\<IID> should have TypeLib value pointed at valid TypeLib for mentioned interface

LoadTypeLibEx – loads and provides ITypeLib interface (it also can register typelib at registry REGKIND_REGISTER)

Registry settings:

HCKR\TypeLib\{your LIBID}\1.0
HCKR\TypeLib\{your LIBID}\1.0\win32 = <path to your *.tlb file>
HCKR\TypeLib\{your LIBID}\Flags
HCKR\TypeLib\{your LIBID}\1.0\Helpdir
HKCR\Interface\{your IID}
HKCR\Interface\ProxyStubClsid = <CLSID of oleaut32.dll>
HKCR\Interface\ProxyStubClsid32 = <CLSID of oleaut32.dll>
HKCR\Interface\TypeLib = <LIBID of *.tlb file describing the interfaces>

Local server architecture

CoRegisterClassObject() – registers some class object of local server in some SCM structure (class object table).
CoRevokeClassObject() – removes class object from class object table.

CoRegisterClassObject (on enter of WinMain) CoRevokeClassObject (on exit of WinMain)  is called if “embedding” string is contained in command line.

Standard schema:

WinMain()
{
CoRegisterClassObject
GetMessage
CoRevokeClassObject
}

Class objects within local server doesnt use reference counting, AddRef and Release just return constant values.

Constructors and destructors of coclasses calls global lock counter, which in case of zero value sends PostQuitMethod to unregister class objects and shutdown an app.

Registry settings:
HKCR\LocalShapes.CoHexagon\CLSID = {<guid>}
HKCR\CLSID\{<guid>} = LocalShapes.CoHexagon
HKCR\CLSID\{<guid>}\LocalServer32 = C:\LocalShapes\Debug\LocalShapes.exe

DCOM
dcomcnfg.exe – utlity redirects all local calls to remote machine
It is possible to make this programmatically, it overrides dcomcnfg.exe-made settings