Introduction
A while ago I wrote a small utility that converts DOT syntax into an image using
the WinGraphViz.DLL COM component.
However it is not very usual to have the user register this component before
running the tool, thus I started looking around for a way to use COM component
as if they were normal DLL.
The research yielded that there are many techniques to accomplish that:
- Registration-Free COM (for XP and above)
- Emulating the CoCreateInstance()
To learn more about the Registration-Free COM please check the references at
the end of this article.
Emulating CoCreateInstance()
Normally, to create an instance you would do something like:
hr = CoCreateInstance(CLSID_DOT, NULL, CLSCTX_ALL, IID_IDOT, (LPVOID *)&pIDOT);
This will cause OLE to fetch the associated DLL from the registry and call its
DllGetClassObject() method to get a class factory then from the class factory
an instance of your required IID will be created.
For that reason we may emulate the CoCreateInstance() by the following code:
HRESULT __stdcall MyCoCreateInstance( LPCTSTR szDllName, IN REFCLSID rclsid, IUnknown* pUnkOuter, IN REFIID riid, OUT LPVOID FAR* ppv) { HRESULT hr = REGDB_E_KEYMISSING; HMODULE hDll = ::LoadLibrary(szDllName); if (hDll == 0) return hr; typedef HRESULT (__stdcall *pDllGetClassObject)(IN REFCLSID rclsid, IN REFIID riid, OUT LPVOID FAR* ppv); pDllGetClassObject GetClassObject = (pDllGetClassObject)::GetProcAddress(hDll, "DllGetClassObject"); if (GetClassObject == 0) { ::FreeLibrary(hDll); return hr; } IClassFactory *pIFactory; hr = GetClassObject(rclsid, IID_IClassFactory, (LPVOID *)&pIFactory); if (!SUCCEEDED(hr)) return hr; hr = pIFactory->CreateInstance(pUnkOuter, riid, ppv); pIFactory->Release(); return hr; }
Notice that how this function takes a parameter holding the DLL’s name.
Using the code
You need your application to run first if the COM is registered, if not you
would resort to emulating the CoCreateInstance(). Your code could look like
this:
hr = CoCreateInstance(CLSID_DOT, NULL, CLSCTX_ALL, IID_IDOT, (LPVOID *)&pIDOT); if (hr == REGDB_E_CLASSNOTREG) { hr = MyCoCreateInstance(_T("WinGraphViz.dll"), CLSID_DOT, NULL, IID_IDOT, (LPVOID *)&pIDOT); } if (FAILED(hr)) { cout << "CoCreateInstance Failed: " << hr << "nn"; return -1; }
Reference
The following links were helpful during the building of this simple snippet:
- Mikhail Arkhipov WebLog: Registration-free COM
http://blogs.msdn.com/mikhailarkhipov/archive/2005/04/01/404433.aspx - Dave Templin: Simplify App Deployment with ClickOnce and Registration-Free
COM
http://msdn.microsoft.com//msdnmag/issues/05/04/RegFreeCOM/default.aspx
- Jeff Glatt: COM in Plain C
http://www.codeproject.com/com/com_in_c1.asp - Registration-Free Activation of COM Components: A Walkthrough
http://msdn2.microsoft.com/en-us/library/ms973913.aspx
Hi Lallous,
Great stuff, I only wonder now how I can catch the events that are fired from a COM object.
Regards,
Martin
Sorry Martin, I don’t know. Only worked with events one time and with the help of VC’s wizard.
Hmmm….. Nice workaround. Thanx for this topic.