1,214 views
首页 > 技术心得 > 手工加载未注册的 COM 组件

手工加载未注册的 COM 组件

2010年2月14日

有时候我们运行程序的时候, 可能某个组件在系统内并不存在, 所以我们自己将这个组件打包在我们的安装包内, 但并不将其注册进系统. 这样做的目的就是尽量少的影响原有的系统. 组件只是在我们的应用程序需要的时候才加载进去. 不用的时候将其卸载.

用这种途径创建组件实例的主要思路就是, 调用组件 DLL 导出的 DllGetClassObject 函数, 创建类厂 IClassFactory 的实例, 然后用 IClassFactory::CreateInstance 函数创建目标接口指针的实例.

以下是用法:

  1. 将某个组件加载进程序

    	TCHAR szXmlPath[MAX_PATH] = { 0 };
    	GetModuleFileName(NULL, szXmlPath, _countof(szXmlPath));
    	PathRemoveFileSpec(szXmlPath);
    	PathAppend(szXmlPath, _T("msxml4.dll"));
    
    	TinyInitComLibrary(szXmlPath, __uuidof(DOMDocument40));
    
  2. 使用组件
    	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;
    		}
    	}
    
  3. 将所有强制加载的组件卸载
    	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

技术心得

  1. 2010年2月21日16:20 | #1

    如果组件是一个控件呢?

  2. 2010年2月21日16:22 | #2

    这倒没试过, 应该更复杂一点, 可以参考 ATL 的 atlhost.h 文件相关内容.

  1. 目前还没有任何 trackbacks 和 pingbacks.