手工加载未注册的 COM 组件
有时候我们运行程序的时候, 可能某个组件在系统内并不存在, 所以我们自己将这个组件打包在我们的安装包内, 但并不将其注册进系统. 这样做的目的就是尽量少的影响原有的系统. 组件只是在我们的应用程序需要的时候才加载进去. 不用的时候将其卸载.
用这种途径创建组件实例的主要思路就是, 调用组件 DLL 导出的 DllGetClassObject 函数, 创建类厂 IClassFactory 的实例, 然后用 IClassFactory::CreateInstance 函数创建目标接口指针的实例.
以下是用法:
- 将某个组件加载进程序
TCHAR szXmlPath[MAX_PATH] = { 0 }; GetModuleFileName(NULL, szXmlPath, _countof(szXmlPath)); PathRemoveFileSpec(szXmlPath); PathAppend(szXmlPath, _T("msxml4.dll")); TinyInitComLibrary(szXmlPath, __uuidof(DOMDocument40)); - 使用组件
CComPtr<IXMLDOMDocument2> spXMLDoc; hr = spXMLDoc.CoCreateInstance(__uuidof(DOMDocument40)); if (FAILED(hr)) { hr = TinyCoCreateInstance(__uuidof(DOMDocument40), NULL, CLSCTX_ALL, __uuidof(IXMLDOMDocument2), (void**)&spXMLDoc); if (FAILED(hr)) { return E_FAIL; } } - 将所有强制加载的组件卸载
TinyReleaseAllComLibrary();
以下是实现的源代码:
#ifndef __TINY_CREATE_COM_OBJ_H__
#define __TINY_CREATE_COM_OBJ_H__ 1
typedef HRESULT (WINAPI * PFN_DllCanUnloadNow)(void);
typedef HRESULT (WINAPI * PFN_DllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
#include <vector>
#include <algorithm>
typedef struct COM_OBJ_DLL_CONTAINER
{
HINSTANCE hDllInst;
CLSID clsObject;
} COM_OBJ_DLL_CONTAINER ;
typedef std::vector<COM_OBJ_DLL_CONTAINER> COM_LIB_ARR_TYPE;
__declspec(selectany) COM_LIB_ARR_TYPE * g_vecComLib = NULL;
__inline HRESULT WINAPI TinyInitComLibrary(LPCTSTR lpszObjHostPath, REFCLSID rclsid)
{
if (NULL == g_vecComLib) {
g_vecComLib = new COM_LIB_ARR_TYPE();
}
HRESULT hr = E_FAIL;
COM_OBJ_DLL_CONTAINER tmp = { 0 };
tmp.hDllInst = LoadLibrary(lpszObjHostPath);
if (tmp.hDllInst)
{
tmp.clsObject = rclsid;
g_vecComLib->push_back(tmp);
hr = S_OK;
}
return hr;
}
__inline HRESULT WINAPI TinyReleaseComLibrary(REFCLSID rclsid)
{
HRESULT hr = E_FAIL;
if (NULL == g_vecComLib) {
return hr;
}
COM_LIB_ARR_TYPE::iterator it;
for (it=g_vecComLib->begin(); it!=g_vecComLib->end(); it++)
{
if (IsEqualCLSID(it->clsObject, rclsid))
{
BOOL bCanUnload = TRUE;
PFN_DllCanUnloadNow pfn_DllCanUnloadNow = NULL;
pfn_DllCanUnloadNow = (PFN_DllCanUnloadNow) GetProcAddress(it->hDllInst, "DllCanUnloadNow");
if (pfn_DllCanUnloadNow && S_OK != pfn_DllCanUnloadNow()) {
bCanUnload = FALSE;
}
if (bCanUnload) {
FreeLibrary(it->hDllInst);
g_vecComLib->erase(it);
hr = S_OK;
}
break;
}
}
if (0 == g_vecComLib->size()) {
delete g_vecComLib;
g_vecComLib = NULL;
}
return hr;
}
__inline HRESULT WINAPI TinyReleaseAllComLibrary(void)
{
HRESULT hr = S_OK;
if (NULL == g_vecComLib) {
return hr;
}
COM_LIB_ARR_TYPE::iterator it;
for (it=g_vecComLib->begin(); it!=g_vecComLib->end(); it++)
{
FreeLibrary(it->hDllInst);
}
g_vecComLib->clear();
delete g_vecComLib;
g_vecComLib = NULL;
return hr;
}
__inline HRESULT WINAPI TinyCoCreateInstance(IN REFCLSID rclsid, IN LPUNKNOWN pUnkOuter,
IN DWORD dwClsContext, IN REFIID riid, OUT LPVOID FAR* ppv)
{
HRESULT hr = E_FAIL;
if (g_vecComLib==NULL)
{
__asm int 3;
return hr;
}
COM_LIB_ARR_TYPE::iterator it;
for (it=g_vecComLib->begin(); it!=g_vecComLib->end(); it++)
{
if (IsEqualCLSID(it->clsObject, rclsid))
{
PFN_DllGetClassObject pfn_DllGetClassObject = NULL;
pfn_DllGetClassObject = (PFN_DllGetClassObject) GetProcAddress(it->hDllInst, "DllGetClassObject");
if (pfn_DllGetClassObject) {
IClassFactory * spClsFact = NULL;
hr = pfn_DllGetClassObject(rclsid, __uuidof(IClassFactory), (void **)&spClsFact);
if (SUCCEEDED(hr))
{
hr = spClsFact->CreateInstance(NULL, riid, ppv);
spClsFact->Release();
}
}
break;
}
}
return hr;
}
#endif // __TINY_CREATE_COM_OBJ_H__
延伸阅读:
用 SDK 玩转 ActiveX

如果组件是一个控件呢?
这倒没试过, 应该更复杂一点, 可以参考 ATL 的 atlhost.h 文件相关内容.