Creating COM Objects

From LSDevLinux
Jump to: navigation, search

See Mark's blog post for details.

Write the IDL file

Create an IDL file that matches the header for the object. In Fieldworks, this may be done using the Fieldworks specific method.

Generate the header file

Using MIDL, a header file can be generated for the interface defined in an IDL file. MIDL can be invoked with a command like the following:

C:\> midl /env win32 /tlb “IBaz.tlb” /h “IBaz_idl.h” IBaz.idl

Basic COM methods

AddRef, Release and QueryInterface need to be implemented. In Fieldworks, this can be done by inheriting from the Unknown template class as follows:

class VwGraphicsGTK : public COMBase::Unknown<IVwGraphicsGTK>

Generic Factory

A static instance of GenericFactory must be created in the COM class implementation as follows:

static GenericFactory g_fact(
	_T("SIL.Text.VwGraphicsWin32"),
	&CLSID_VwGraphicsWin32,
	_T("SIL Graphics"),
	_T("Apartment"),
	&VwGraphics::CreateCom);

To use the generic factory, the following will have to be declared somewhere in the program:

typedef class VwGraphicsWin32 VwGraphicsWin32;
template<> GUID __uuidof(VwGraphicsWin32)("3c908d4c-93e3-420e-aeef-2749133675d1"); // < Should come from LanguageTlb
#define CLSID_VwGraphicsWin32 __uuidof(VwGraphicsWin32)

Creating an instance

A CreateCom method is required to create an instace of the COM object. The following example is along the right lines, but may be lacking in exception safety.

void VwGraphics::CreateCom(IUnknown *punkCtl, REFIID riid, void ** ppv)
{
	AssertPtr(ppv);
	Assert(!*ppv);

	if (punkCtl)
		ThrowHr(WarnHr(CLASS_E_NOAGGREGATION));

	VwGraphics* pTest = new VwGraphics(); // ref count initialy 1

	if (pTest == NULL)
		ThrowHr(WarnHr(E_OUTOFMEMORY));

	// Get the requested interface.
	HRESULT hr = pTest->QueryInterface(riid, ppv);

	// Release the IUnknown pointer.
	// (If QueryInterface failed, component will delete itself.)
	pTest->Release();

	CheckHr(WarnHr(hr));
}

Obtaining an instance

Once the headers and implementation for the COM class are created, a class factory can be used to create an instance of the COM object. Note that the object created will actually be an instance of the interface class, eg. IVwGraphicsGTK instead of VwGraphicsGTK.

DllMain must be invoked (only on Windows?):

extern "C" BOOL WINAPI DllMain(HMODULE hmod, DWORD dwReason, PVOID pvReserved);
STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, VOID ** ppv);

... Do COM stuff

DllMain(0, DLL_PROCESS_DETACH, 0) ? 0 : 1;

Note: This should be done with CoCreateInstance

try
{	
	IClassFactory* pFact = 0;
	CheckHr(DllGetClassObject(CLSID_VwGraphicsGTK, IID_IClassFactory, (void**)&pFact));
	
	CheckHr(pFact->CreateInstance(NULL, IID_IVwGraphicsGTK, (void**)&m_graphics));
		
	pFact->Release();
}
catch (Throwable& thr)
{
	std::cerr << "Failed HRESULT: " << std::hex << thr.Error() << "\n";
}
catch (std::exception& e)
{
	std::cerr << "Exception: " << e.what() << "\n";
}
catch (...)
{
	std::cerr << "Unknown Exception:\n";
}