使用进程外COM服务在64位应用中调用32位DLL

最近做一个项目,需要在64位的VS2022 c++项目中调用32位的动态库,实现导出docx文件的功能,开始在网上找了一些解决方案,基本都是:创建32位COM组件 -- 注册32位COM组件 -- 64位程序调用32位COM组件,但是按照这些方法封装COM组件后,发现不能调用,客户端使用CoCreateInstance创建对象提示创建COM服务器接口的实例时出现类未注册错误(REGDB_E_CLASSNOTREG Class not registered,查看注册表,计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID\{**********},发现COM组件成功注册,但就是不能调用,百思不得其解!最后经过几天的摸索,找到解决办法,在这里分享一下。

一、COM组件有in-process和out-of-process两种

运行in-process的COM客户端和COM服务器需要以相同的位元运行,因此在x86中都是如此,或者在x64中都是这样。因此:

  • 如果COM客户端以x86运行,它会与注册表的x86侧进行通信,因此COM服务器必须在注册表的x86侧注册(regsvr32、regasm、自定义reg代码等必须以x86方式运行)。
  • 如果COM客户端以x64运行,它将与注册表的x64侧进行通信,因此COM服务器必须在注册表的x64侧注册(regsvr32、regasm、自定义reg代码等必须以x64方式运行)。

对于out-of-process通信,x86客户端或服务器可以与x64服务器或客户端进行通信。这是COM的一个好处。也是解决64位软件调用32位DLL的关键所在!笔者的问题就是封装了in-process的COM组件,导致无法在64位进程内调用32位DLL。后面创建out-of-process类型的COM组件,问题得到解决!

二、创建out-of-process类型的COM组件的流程

1.建一个ATL项目

2.选择可执行文件,注意:应用程序类型这里选择exe,表示创建out-of-process COM组件;如果选择DLL,则是in-process,切记不要选错

3.添加ATL简单对象

4.在IDL文件中添加 show([in] BSTR a);接口

import "oaidl.idl";
import "ocidl.idl";

[
	object,
	uuid(93e14b15-e9de-44c0-9fa1-12f5ba1265fd),
	dual,
	nonextensible,
	pointer_default(unique)
]
interface IATLSimpleObject : IDispatch
{
	[id(1)] HRESULT show([in] BSTR a);
};
[
	uuid(74fa1274-a76d-4392-b2b1-f841db7be581),
	version(1.0),
]
library ATLProject1Lib
{
	importlib("stdole2.tlb");
	[
		uuid(d9049425-0435-4727-85bb-853d81368b19)
	]
	coclass ATLSimpleObject
	{
		[default] interface IATLSimpleObject;
	};
};

import "shobjidl.idl";

5.在实现类中添加C++函数,实现在IDL新增的接口,此时可以调用x86的DLL,实现自己想要的功能

char* wchartoGBK(LPCWSTR wideStr) {
    int wideStrLen = wcslen(wideStr);

    int multiByteLen = WideCharToMultiByte(CP_ACP, 0, wideStr, wideStrLen, NULL, 0, NULL, NULL);
    if (multiByteLen == 0) {
        //std::cerr << "Error converting wide char to multi byte: " << GetLastError() << std::endl;
        return "";
    }

    char* multiByteStr = new char[multiByteLen + 1];
    if (!WideCharToMultiByte(CP_ACP, 0, wideStr, wideStrLen, multiByteStr, multiByteLen, NULL, NULL)) {
       // std::cerr << "Error converting wide char to multi byte: " << GetLastError() << std::endl;
        delete[] multiByteStr;
        return "";
    }
    multiByteStr[multiByteLen] = '\0';


    //delete[] multiByteStr;
    return multiByteStr;
}

STDMETHODIMP_(HRESULT __stdcall) CATLSimpleObject::show(BSTR a)
{
    auto str = wchartoGBK(a);
    ::MessageBoxA(NULL, str, "test", MB_OK);
    delete[] str;
    return S_OK;
}
 

6.编译选项选X86,编译,生成ATLProject1.exe文件

7.打开cmd窗口,cd生成目录,注册COM组件:ATLProject1.exe /regserver(反注册:ATLProject1.exe /unregserver),VS编译后会自动注册,次步可以省略

8.64位客户端调用:创建测试项目,Project1,编译选项选择x64,包含ATLProject1_i.h和ATLProject1_i.c文件,CoCreateInstance的第三个测试填CLSCTX_LOCAL_SERVER,运行弹出测试窗口,成功!收工下班(2024.7.26 18:29)

#include "../../ATLProject1/ATLProject1/ATLProject1_i.h"

#include <atlbase.h>

using namespace ATL;

int main() {
	HRESULT hr_ini = ::CoInitialize(NULL);

	IATLSimpleObject* Isimple = nullptr;
	HRESULT hr1 = S_OK;
	hr1 = ::CoCreateInstance(CLSID_ATLSimpleObject, NULL, CLSCTX_LOCAL_SERVER, IID_IATLSimpleObject, (LPVOID*)&Isimple);
	if (SUCCEEDED(hr1)) {
		BSTR bstr = ::SysAllocString(L"进程外调用!");
		Isimple->show(bstr);
		Isimple->Release();
	}

	::CoUninitialize();
}

输出结果:

参考连接:

VS2022使用进程外Com服务让64位应用调用32位DLL

裸写一个进程外 COM 组件

创建COM服务器接口的实例时出现类未注册错误

要在 C# 64 位应用程序调用 32 位 DLL 方法,你需要使用.NET Framework 的“可移植性”功能。这个功能允许你在 64 位进程加载 32 位 DLL,并使用的方法。 以下是一个简单的示例: 1. 首先,你需要在 C# 代码声明你想调用的 32 位 DLL 方法。这需要使用 DllImport 属性。例如,如果你的 DLL 包含名为 "MyFunction" 的方法,你可以在 C# 这样声明: ```csharp [DllImport("My32BitDll.dll", EntryPoint="MyFunction")] public static extern int MyFunction32(int param1, int param2); ``` 这告诉 C# 编译器在 32 位 DLL 查找一个名为 "MyFunction" 的方法,并将其导入到 C# 代码。 2. 然后,你需要在 C# 应用程序的 app.config 文件添加一个“可移植性”选项: ```xml <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration> ``` 这将告诉应用程序使用“可移植性”功能来加载 32 位 DLL。 3. 最后,你可以像调用任何其他 C# 方法一样调用该函数: ```csharp int result = MyFunction32(1, 2); ``` 这将调用 32 位 DLL 的 "MyFunction" 方法,并将参数 1 和 2 传递给它。该方法的返回值将存储在 result 变量。 注意,在使用“可移植性”功能时,你需要确保所有使用DLL 都是 32 位的,并且你的应用程序必须是 64 位的。如果你的 DLL 是 64 位的,你需要在 64 位应用程序使用常规的平台调用方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值