从 64 位应用程序调用 32 位 DLL 的函数
Posted
技术标签:
【中文标题】从 64 位应用程序调用 32 位 DLL 的函数【英文标题】:Calling a function of a 32-bit DLL from a 64-bit Application 【发布时间】:2019-03-07 04:11:45 【问题描述】:我有一个需要从 64 位 C# 应用程序访问的 32 位 dll(无源代码)。我已经阅读了this 的文章并查看了here 的相应代码。我也读过this 的帖子。 我不确定我问的问题是否正确,所以请帮助我。
有 3 个项目:dotnetclient
、x86Library
和 x86x64
。 x86x64
有 x86LibraryProxy.cpp
加载 x86library.dll
并调用 GetTemperature
函数:
STDMETHODIMP Cx86LibraryProxy::GetTemperature(ULONG sensorId, FLOAT* temperature)
*temperature = -1;
typedef float (__cdecl *PGETTEMPERATURE)(int);
PGETTEMPERATURE pFunc;
TCHAR buf[256];
HMODULE hLib = LoadLibrary(L"x86library.dll");
if (hLib != NULL)
pFunc = (PGETTEMPERATURE)GetProcAddress(hLib, "GetTemperature");
if (pFunc != NULL)
dotnetclient
调用 GetTemperature
函数并打印结果:
static void Main(string[] args)
float temperature = 0;
uint sensorId = 2;
var svc = new x86x64Lib.x86LibraryProxy();
temperature = svc.GetTemperature(sensorId);
Console.WriteLine($"temperature of sensorId is temperature, press any key to exit...");
如果我将所有项目构建为 x86 或 x64,这一切都有效。我得到的温度结果是20
。但是,整个想法是使用 32 位 x86x64Lib.dll
。这意味着dotnetclient
应该构建为 x64,x86Library
和 x86x64
构建为 x86,对吗?如果我这样做,我会得到-1
。
我应该将x86Library
和x86x64
构建为x86 并将dotnetclient
构建为x64 吗?如果我这样做了,那么我得到-1
的问题是什么?
澄清 似乎提供的示例仅在客户端和服务器均以 32 位或 64 位构建时才有效。但当客户端构建为 64 位而服务器构建为 32 位时则不然。有人可以看看吗?
【问题讨论】:
【参考方案1】:恕我直言,最简单的方法是使用 COM+ (Component Services),它是 Windows 的一部分,已有 20 年左右的时间了(以前的版本曾经被称为 MTS ...)。它为您提供了代理基础设施,包括工具、UI 和您需要的一切。 但这意味着您必须使用 COM,因此最好了解一些 COM。
首先创建一个 x86 COM DLL。我为此使用了ATL。创建了一个 ATL 项目,向其中添加了一个 ATL 简单对象,将方法添加到 IDL 和实现中。
.idl(注意 [out, retval] 属性,因此温度被视为包括 .NET 在内的高级语言的返回值):
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(f9988875-6bf1-4f3f-9ad4-64fa220a5c42),
dual,
nonextensible,
pointer_default(unique)
]
interface IMyObject : IDispatch
HRESULT GetTemperature(ULONG sensorId, [out, retval] FLOAT* temperature);
;
[
uuid(2de2557f-9bc2-42ef-8c58-63ba77834d0f),
version(1.0),
]
library x86LibraryLib
importlib("stdole2.tlb");
[
uuid(b20dcea2-9b8f-426d-8d96-760276fbaca9)
]
coclass MyObject
[default] interface IMyObject;
;
;
import "shobjidl.idl";
用于测试目的的方法实现:
STDMETHODIMP GetTemperature(ULONG sensorId, FLOAT* temperature)
*temperature = sizeof(void*); // should be 4 in x86 :-)
return S_OK;
现在,您必须在 32 位注册表中注册此组件(事实上,如果您在没有管理员权限的情况下运行 Visual Studio,它会在编译时抱怨该组件无法注册,这是意料之中的),所以在 64 位操作系统上,你必须以管理员权限运行类似这样的东西(注意 SysWow64):
c:\Windows\SysWOW64\regsvr32 x86Library.dll
完成后,运行“组件服务”,浏览“计算机/我的电脑/COM+ 应用程序”,右键单击并创建一个新应用程序。选择一个名称和一个“服务器应用程序”。这意味着您的组件将托管在 COM+ 代理进程中。
完成后,浏览“组件”,右键单击并创建一个新组件。确保选择“32 位注册表”。您应该看到对象的 ProgId。在我的情况下,当我创建我的 ATL 项目时,我将“MyObject”添加为 Progid,但否则它可能被命名为“x86Library.MyObject”或“x86LibraryLib.MyObject”......如果它不存在,那么你犯了一些错误早一点。
就是这样。现在,这个 .NET 程序将始终能够运行,编译为 AnyCpu 或 x86 或 x64:
class Program
static void Main(string[] args)
var type = Type.GetTypeFromProgID("MyObject"); // the same progid
dynamic o = Activator.CreateInstance(type);
Console.WriteLine(o.GetTemperature(1234)); // always displays 4
您可以使用组件服务 UI 来配置您的代理(激活、关闭等)。它还有一个 API,因此您可以创建 COM+ apps programmatically。
【讨论】:
在我的情况下,GetTemperature
的实现将通过调用LoadLibrary(...)
然后从该 dll 调用正确的函数来加载我的 32 位 dll,对吧?
另外,您使用 ATL 创建了进程内 COM dll。为什么不使用 ATL 创建进程外 COM exe?当我双击它时,它不会自动加载COM exe吗?
是的,对于 LoadLibrary,没问题,只要它们都是 32 位二进制文件。对于带有 ATL 的 COM exe,我几乎从未这样做过,因为编写 COM dll 并受益于组件服务功能(激活、安全、关闭等)要容易得多。另外,当您编写 DLL 时,您仍然可以在其他主机的进程内使用它以用于其他场景。【参考方案2】:
您将无法从 64 位代码(或相反的方式)直接调用 32 位代码,这根本不会发生。
还有其他替代方法,例如创建一个 32 位 COM 主机程序,然后将调用转发到 DLL。再加上您使用 DCOM 标准编组,因此您的 64 位进程可以连接到 32 位主机。
但如果重新编译 32 位 DLL 完全是一个选项,那几乎可以肯定是您的最佳选择。
【讨论】:
很抱歉,这如何回答我的问题?我知道您不能在 64 位空间中加载 32 位 dll。我知道您需要将该 dll 加载到 32 位进程中并使用 IPC 将函数从 64 位进程调用到 32 位进程中。这正是他们在我的帖子中提到的示例项目应该做的事情。但是,我不太明白如果汽车人将 dll 构建为 64 位,它是如何实现的。 你能提供一个简单的例子或分步说明如何做吗?以上是关于从 64 位应用程序调用 32 位 DLL 的函数的主要内容,如果未能解决你的问题,请参考以下文章