在一个程序中结合 32 位和 64 位 DLL

Posted

技术标签:

【中文标题】在一个程序中结合 32 位和 64 位 DLL【英文标题】:Combine 32- and 64bit DLLs in one program 【发布时间】:2019-08-03 02:51:03 【问题描述】:

我需要加载 .dll 文件中提供的不同硬件驱动程序。 问题似乎是一个设备的驱动程序是在 64 位 dll 中给出的,而另一个设备(相当旧的)显然依赖于 32 位 dll 中给出的驱动程序。我想通过一个用 C# 编写的程序来控制它们,该程序将通过 python 包装器运行。

显然我不能直接从一个程序运行这两个设备,但我需要一种方法来解决它们相互依赖的问题 - 例如:设备 1 等待设备 2 完成某些工作。有什么办法可以规避这个问题,还是我需要在两个单独的程序中运行它们并通过 python 包装器管理相互依赖的操作?

【问题讨论】:

是的,您将需要 2 个独立的进程,从不同的可执行文件运行。让一个 64 位进程启动一个对 32 位代码段执行jmp farthread 在技术上可能是可行的,但这太疯狂了,而且可能 very一切。即使您在 asm 中手写,也可能无法稳定地做到这一点,这取决于 Windows 内核在中断/系统调用它认为应该是 64 位的线程后如何返回用户空间。跨度> 这听起来绝对不像我会做的事情。毕竟,必须通过两个单独的进程来解决这个问题甚至还不错,因为已经在不同的类中提供了功能。 当 Windows 从 16 位迁移到 32 位时,Microsoft 提供了一个 thunking 层以允许从 16 位世界过渡到 32 位世界。当 64 位到达时,然后采取“永不重复”的立场并强制执行“每个进程一个位”规则。您需要两个进程以及它们之间的某种 RPC 机制。 @Flydog57 : Win32s(和 thunking 层)的美好时光。 【参考方案1】:

在 64 位 Windows 上,64 位进程不能使用 32 位 DLL,32 位进程不能使用 64 位 DLL。微软有documented这个:

在 64 位 Windows 上,64 位进程无法加载 32 位动态链接库 (DLL)。此外,32 位进程无法加载 64 位 DLL。

您需要一个与 32 位 DLL 通信的 32 位进程和一个与 64 位 DLL 通信的 64 位进程。微软这样说:

但是,64 位 Windows 支持 64 位和 32 位进程之间的远程过程调用 (RPC)(在同一台计算机上和跨计算机)。

然后问题就变成了如何让 Python 与这些进程通信的问题之一。需要某种形式的进程间通信(IPC)。微软几十年前创造了一项技术,可以做到这一点 - 使用进程外 COM 服务器(进程外)的 COM 接口。

大体思路是:

创建一个 64 位进程外 COM 服务器,用于包装(并公开)64 位 DLL 所需的方法和数据。 创建一个 32 位进程外 COM 服务器,用于包装(并公开)32 位 DLL 所需的方法和数据。 编写实例化 COM 对象并调用其接口的 32 位或 64 位客户端代码。 Python 可以通过win32com 用作 COM 客户端

COM 在底层提供了一种 IPC 机制,允许 64 位客户端访问 64 位进程外 COM 服务器并允许 64 位客户端访问 32 位进程外服务器.您甚至可以让 32 位客户端与 32 位和 64 位进程外 COM 服务器进行通信。

我没有使用较新的 MS 语言完成低级 Windows 工作。当我必须在您的问题中做您需要的事情时,使编写 COM 服务器和 COM 接口变得容易的两种主要技术是:

MSVC/C++ 使用Microsoft Foundation Classes (MFC) MSVC/C++ 使用Active Template Library (ATL)。

我有一个用于 ATL 的 preference,因为它不需要 MFC 库并且开销较小。

【讨论】:

感谢您分享您的见解。不幸的是,任何解决这个问题的方法都是为了我的目的而深入,我只会坚持我的替代方法并为每个方法使用单独的过程。【参考方案2】:

是的,您将需要 2 个独立的进程,从不同的可执行文件运行。只有 32 位可执行文件可以加载 32 位 DLL。 (请参阅@MichaelPetch 的回答,了解有关如何使用远程过程调用机制与另一方通信的有用建议,该机制可以模拟从 64 位代码调用 32 位函数,反之亦然。)

x86 32 位和 x86-64 是两个独立的架构,只是碰巧都可以由同一个 CPU 以不同的模式执行。它们的机器代码是非常 相似但不兼容,还有许多其他方面也不同,包括目标文件格式,以及指针宽度为 8 字节和 4 字节等 ABI 细节。


让一个 64 位进程启动一个 线程 对 32 位代码段执行jmp far 在技术上是可行的(因为 GDT 有 32 位和 64 位代码段条目),但这太疯狂了,而且 非常 受到包括 DLL 动态加载/符号解析代码在内的所有内容的不良支持。 (还包括内核,所以这甚至不安全:如果您尝试这样做,中断或系统调用可能会返回到 64 位模式下的 32 位代码,因为内核知道您的线程/进程以 64 位模式启动。)

您无法说服编译器生成 32 位代码并将其与 64 位代码链接,即使您在“安全”的操作系统上使用 C 也是如此。更高级别的托管语言使其更加无法使用,即使它对于手写 asm 是“安全的”。

我提到这一点是为了以防你对实现这一点的技术要求感到好奇,不是,因为任何人都应该这样做。

但如果它是安全的,理论上你可以为 32 位 DLL 中的每个函数编写(在 asm 中手动)包装函数,在调用函数之前更改为 32 位模式。

显然这是早期 32 位 Windows 的事情;您可以通过操作系统提供的“thunk”包装器从 32 位代码调用 16 位 DLL。 但从 64 位代码到 32 位 DLL 没有类似的支持,反之亦然。

【讨论】:

我认为这是一个否,因为解决这个问题对于我的目的来说是非常深入的。我只会坚持两个独立的进程并通过包装器管理任何依赖项。感谢您的澄清 @Zi1mann:是的,这并不是一个真正的建议,更多的是“这就是它会多么令人讨厌,以及为什么你不想这样做”。主要的一点是,它可能真的不能安全地完成,就像我最后一段所说的那样。 即使你有编译器来生成代码,在 Windows 中也不可能有一个 64 位线程跳转到 32 位(兼容模式)段。此外,此类代码不依赖于编译器,而是依赖于 GDT。 @Michael:为什么不呢? jmp 本身是否因为没有 32 位代码段的 GDT 条目而出错?或者就像我在上一段中想知道的那样,内核会在下一次中断或系统调用时中断你的线程?如果是后者,那么我的回答至少在技术上是正确的,只是顺序很差。 @Michael:更新了我的回答,强调这不是一个真正的建议,它可能会在下一次中断时崩溃。

以上是关于在一个程序中结合 32 位和 64 位 DLL的主要内容,如果未能解决你的问题,请参考以下文章

32位和64位dll判断

关于 64 位和 32 位 JVM

使用 mingw-w64 和 cmake 构建 32 位和 64 位应用程序

无趣的 BHO dll 64 位和 32 位使用 regsvr32 导致 0x8002801c 错误

Java 程序文件 (x86) 和 64 位和 32 位程序文件

64位系统下注册32位dll文件