免注册调用COM组件的两种办法

        最近放假了,闲来没事,总结一下免注册调用COM组件的两种办法。虽然是老古董的东西,但是对经常需要用VB/VC做一些小工具的我来说还是很有用的。

        一、调用DLL的DllGetClassObject函数

        下面是VC++实现的代码,原理很简单,直接看代码就明白了,代码面前毫无秘密。

HRESULT WINAPI GetClassIID(LPCTSTR lpszDllFileName, LPCTSTR lpszClassName, REFIID classIID)
{
	ITypeLib* lpTypeLib = NULL;

	HRESULT hr = LoadTypeLibEx(lpszDllFileName, REGKIND_NONE, &lpTypeLib);

	if (FAILED(hr))return hr;

	CComPtr<ITypeLib> pTypeLib(lpTypeLib);

	UINT nTypeInfoCount = pTypeLib->GetTypeInfoCount();

	for (UINT i = 0 ; i < nTypeInfoCount; i++)
	{
		ITypeInfo* lpTypeInfo = NULL;

		hr = pTypeLib->GetTypeInfo(i, &lpTypeInfo);

		if (SUCCEEDED(hr))
		{	
			CComPtr<ITypeInfo> pTypeInfo(lpTypeInfo);

			TYPEATTR* lpTypeAttr = NULL;

			hr = lpTypeInfo->GetTypeAttr(&lpTypeAttr);

			if (SUCCEEDED(hr))
			{
				if (lpTypeAttr->typekind == TKIND_COCLASS)
				{			
					_bstr_t strTmp;
					hr = pTypeLib->GetDocumentation(i, strTmp.GetAddress(), NULL, NULL, NULL);

					if (SUCCEEDED(hr))
					{
						if (strTmp == _bstr_t(lpszClassName))
						{
							CopyMemory((void*)&classIID, &(lpTypeAttr->guid), sizeof IID);

							pTypeInfo->ReleaseTypeAttr(lpTypeAttr);

							return S_OK;
						}
					}
				}

				pTypeInfo->ReleaseTypeAttr(lpTypeAttr);
			}
		}

	}

	return S_FALSE;
}


HRESULT WINAPI CreateDllObjectW(LPCTSTR lpszDllFileName, LPCTSTR lpszClassName, IDispatch **ppvObject)
{
	IID classIID = {0};

	HRESULT hr = GetClassIID(lpszDllFileName, lpszClassName, classIID);

	if (FAILED(hr)) return hr;

	HINSTANCE hDllInst = GetModuleHandle(lpszDllFileName);

	if (!hDllInst) hDllInst = LoadLibrary(lpszDllFileName);
		
	if (!hDllInst) return S_FALSE;

	typedef HRESULT (__stdcall* pfnDllGetClassObject)(REFCLSID, REFIID, void**);
	pfnDllGetClassObject fnDllGetClassObject = (pfnDllGetClassObject)GetProcAddress(hDllInst, "DllGetClassObject");

	if (!fnDllGetClassObject)
	{
		FreeLibrary(hDllInst);
		return S_FALSE;
	}

	IClassFactoryPtr lpClassFactory = NULL;

	hr = (fnDllGetClassObject)(classIID, IID_IClassFactory, (void**)&lpClassFactory);

	if (SUCCEEDED(hr)) 
	{ 
		hr = lpClassFactory->CreateInstance(NULL, IID_IDispatch, (void**)ppvObject);

		if (SUCCEEDED(hr)) return S_OK;
	}

	FreeLibrary(hDllInst);

	return S_FALSE;
};


HRESULT WINAPI CreateDllObjectA(LPCSTR lpszDllFileName, LPCSTR lpszClassName, IDispatch **ppvObject)
{
	return CreateDllObjectW(CA2T(lpszDllFileName), CA2T(lpszClassName), ppvObject);
}

        我把上面代码编译成一个DLL,方便给VB使用,文章最后有源码的下载链接。如果要用纯VB代码实现上面的功能,就比较费劲。但也是可以实现的,有兴趣的可以自己去试试,这样就不用多带一个DLL文件。

        在VB中还有一种变通的办法,详细可以看我之前写的另外一个例子。

        VB调用JMail发邮件(无需注册JMail.dll)

二、利用Manifest清单文件

        关于Manifest清单文件的详细介绍,可以到微软的网站看一下。

        https://learn.microsoft.com/zh-cn/windows/win32/sbscs/manifest-files-reference

        下面是一个免注册调用COMDLG32.OCX和TLBINF32.DL的清单文件,里面的每一项在微软的网站都有详细的介绍。

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32" name="client" version="1.0.0.0" />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*" />
    </dependentAssembly>
  </dependency>
  <file name="COMDLG32.OCX">
    <comClass description="CommonDialog" clsid="{F9043C85-F6F2-101A-A3C9-08002B2F49FB}" />
  </file>
  <comInterfaceExternalProxyStub name="ICommonDialog" iid="{083039C2-13F4-11D1-8B7E-0000F8754DA1}" />
  <file name="TLBINF32.DLL">
    <comClass description="SearchHelper" clsid="{8B217752-717D-11CE-AB5B-D41203C10000}" />
    <comClass description="TypeLibInfo" clsid="{8B217746-717D-11CE-AB5B-D41203C10000}" />
    <comClass description="TLIApplication" clsid="{8B21775E-717D-11CE-AB5B-D41203C10000}" />
  </file>
  <comInterfaceExternalProxyStub name="_SearchHelper" iid="{8B217751-717D-11CE-AB5B-D41203C10000}" />
  <comInterfaceExternalProxyStub name="_TypeLibInfo" iid="{8B217745-717D-11CE-AB5B-D41203C10000}" />
  <comInterfaceExternalProxyStub name="_TLIApplication" iid="{8B21775D-717D-11CE-AB5B-D41203C10000}" />
</assembly>

        我做了一个小工具用来生成Manifest清单文件,这个小工具同时也是一个Manifest清单文件应用的例子。文章最后有源码的下载链接。

三、两种方法的比较

        方法一:

        一般用于调用自己开发的,可能经常需要更改的COM类库,也可以用于调用一些通用的类库,但是函数参数中不能含有自定义类型的参数,比如结构体类型的参数。

        方法二:

        一般用于调用接口稳定的DLL类库,或者OCX等需要处理事件的组件。用这个办法,开发软件和以前一样,只是发布的时候增加一个清单文件即可,或者直接把清单文件嵌入资源里面。

        两种方法可以结合在一起同时使用,怎么方便怎么来。

四、源码下载以及学习资源

免注册调用COM组件方法一-DllGetClassObject

https://download.csdn.net/download/Zezese/87394537

免注册调用COM组件方法二-AssemblyManifests

https://download.csdn.net/download/Zezese/12269960

清单文件参考 - Win32 apps | Microsoft Learn

https://learn.microsoft.com/zh-cn/windows/win32/sbscs/manifest-files-reference

Isolated Applications and Side-by-side Assemblies - Win32 apps | Microsoft Learn

https://learn.microsoft.com/en-us/windows/win32/sbscs/isolated-applications-and-side-by-side-assemblies-portal

Building C/C++ Isolated Applications and Side-by-side Assemblies | Microsoft Learn

https://learn.microsoft.com/zh-tw/previous-versions/visualstudio/visual-studio-2013/ms235532(v=vs.120)

Registration-Free Activation of COM Components: A Walkthrough | Microsoft Learn

https://learn.microsoft.com/en-us/previous-versions/dotnet/articles/ms973913(v=msdn.10)

调用 COM 组件的方法有多种方法,下面是其中的几种示例代码: 1. 使用 CreateInstance 函数创建 COM 对象并调用其方法: ``` CoInitialize(NULL); // 初始化 COM 组件 ISomeInterface* pSomeInterface = NULL; HRESULT hr = CoCreateInstance(CLSID_SomeComponent, NULL, CLSCTX_ALL, IID_ISomeInterface, (void**)&pSomeInterface); if (SUCCEEDED(hr)) { pSomeInterface->SomeMethod(); pSomeInterface->Release(); } CoUninitialize(); // 释放 COM 组件 ``` 2. 使用 IDispatch 接口调用 COM 对象的方法: ``` CoInitialize(NULL); // 初始化 COM 组件 IDispatch* pDispatch = NULL; HRESULT hr = CoCreateInstance(CLSID_SomeComponent, NULL, CLSCTX_ALL, IID_IDispatch, (void**)&pDispatch); if (SUCCEEDED(hr)) { VARIANTARG varg1, varg2, varg3; VariantInit(&varg1); VariantInit(&varg2); VariantInit(&varg3); // 设置参数 varg1.vt = VT_I4; varg1.lVal = 1; varg2.vt = VT_BSTR; varg2.bstrVal = SysAllocString(L"Hello"); varg3.vt = VT_BOOL; varg3.boolVal = VARIANT_TRUE; DISPPARAMS dispparams = { &varg1, &varg2, &varg3, 3, 0 }; // 调用方法 VARIANT variantResult; VariantInit(&variantResult); hr = pDispatch->Invoke(DISPID_SomeMethod, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &variantResult, NULL, NULL); // 处理返回值 if (SUCCEEDED(hr)) { // do something with variantResult VariantClear(&variantResult); } VariantClear(&varg1); VariantClear(&varg2); VariantClear(&varg3); pDispatch->Release(); } CoUninitialize(); // 释放 COM 组件 ``` 3. 使用 ATL 的 CComPtr 类模板调用 COM 对象的方法: ``` CoInitialize(NULL); // 初始化 COM 组件 CComPtr<ISomeInterface> pSomeInterface; HRESULT hr = pSomeInterface.CoCreateInstance(CLSID_SomeComponent); if (SUCCEEDED(hr)) { pSomeInterface->SomeMethod(); } CoUninitialize(); // 释放 COM 组件 ``` 以上示例仅供参考,具体实现方式可能因 COM 组件的实现方式和语言而有所不同。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值