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

3

我需要加载不同的硬件驱动程序,这些驱动程序是以 .dll 文件形式提供的。问题似乎在于一个设备的驱动程序采用了64位的 dll,而另一个设备(比较老)显然依赖于32位的 dll 驱动程序。我想通过一个用 C# 编写的程序来控制它们,这个程序将通过一个 Python 包装器运行。

很明显我无法直接从一个程序中运行两个设备,但我需要一种根据彼此情况对它们进行地址分配的方法 - 例如:设备1等待设备2完成某个作业。是否有任何方法可以绕过这个问题,或者我需要在两个独立的程序中运行它们,并通过 Python 包装器来管理彼此之间的操作?


1
是的,您需要两个不同可执行文件运行的独立进程。让一个64位进程启动一个线程,并在其中执行jmp far到32位代码段可能在技术上是可能的,但这是疯狂的,并且可能被所有东西非常差地支持。即使您手写汇编语言,也可能无法稳定地执行该操作,这取决于Windows内核如何为它认为应该是64位的线程返回用户空间之后中断/系统调用。 - Peter Cordes
那绝对不像是我会做的事情。最终需要通过两个独立的过程来解决这个问题其实也并不算太糟糕,因为功能已经在不同的类中提供了。 - Zi1mann
1
当Windows从16位迁移到32位时,微软提供了一个thunking层,以允许从16位世界过渡到32位世界。当64位出现时,微软采取了“永不再次”的立场,并强制执行“每个进程单一位数”的规则。您需要两个进程和它们之间的某种RPC机制。 - Flydog57
@Flydog57:Win32s(以及thunking层)的美好时光。 - Michael Petch
2个回答

6
在64位的Windows系统中,64位的进程无法使用32位的DLL文件,而32位的进程也无法使用64位的DLL文件。Microsoft已经对此进行了记录

在64位的Windows系统中,64位进程不能加载32位动态链接库(DLL)文件。另外,32位进程也不能加载64位的DLL文件。

你需要一个32位进程来与32位DLL文件通信,以及一个64位进程来与64位DLL文件通信。Microsoft说过这样一句话:

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

问题变成了如何让Python与这些进程进行通信。需要一种形式的进程间通信(IPC)。几十年前,Microsoft就创建了一种可以做到这一点的技术 - 使用进程外COM服务器(out-of-proc)的COM接口。
一般的想法是:
  • 创建一个64位的out-of-proc COM服务器,封装(并公开)64位DLL所需的方法和数据。
  • 创建一个32位的out-of-proc COM服务器,封装(并公开)32位DLL所需的方法和数据。
  • 编写32位或64位客户端代码,实例化COM对象并调用它们的接口。Python可以通过win32com作为COM客户端使用。

COM提供了IPC机制,使64位客户端可以访问64位out-of-proc COM服务器,并且64位客户端可以访问32位out-of-proc服务器。甚至可以让32位客户端与32位和64位out-of-proc COM服务器进行通信。

我没有使用较新的MS语言进行低级Windows工作。当我需要在您的问题中做到这一点时,使编写COM服务器和COM接口变得容易的两个主要技术是:

我更喜欢ATL,因为它不需要MFC库,并且开销较小。preference


感谢分享您的见解。不幸的是,任何针对此问题的解决方法都过于深入,超出了我的目的。我只需要坚持我的另一种方法,为每个进程使用单独的过程。 - Zi1mann

4
您需要两个单独的进程,分别从不同的可执行文件中运行。只有32位可执行文件可以加载32位DLL(参见@MichaelPetch的答案,了解如何使用远程过程调用机制实现其中一个与另一个进行通信,可以模拟从64位代码调用32位函数或反之亦然)。
x86 32位和x86-64是两个独立的体系结构,只是以不同的模式由相同的CPU执行。它们的机器代码非常相似但不兼容,并且其他许多事情也不同,包括目标文件格式和ABI细节,例如指针宽度为8字节 vs. 4字节。
启动64位进程启动一个线程,该线程对32位代码段执行jmp far在技术上是可能的(因为GDT有32位和64位代码段条目),但这是疯狂的并且得到很差的支持,包括DLL动态加载/符号解析代码。(甚至包括内核,所以这甚至不安全:如果尝试了这个,中断或系统调用可能会返回到64位模式中的32位代码,因为内核知道您的线程/进程是在64位模式下启动的 )。
即使您在可以“安全”这样做的操作系统上使用C语言,也无法说服编译器生成32位代码并将其链接到64位代码。即使使用手写的汇编码,在更高级别的托管语言中也更难以使用。
我提到这一点只是为了让您了解实现这一点需要什么技术,而不是因为有人应该这样做。
但是,如果它是安全的,理论上您可以为32位DLL中的每个函数编写(在asm中手动)包装器函数,以便在调用函数之前切换到32位模式。
显然,早期的32位Windows曾经有过这种东西;您可以通过操作系统提供的“thunk”包装器从32位代码调用16位DLL。但是对于32位DLL和64位代码之间或反之亦然,没有类似的支持。

我认为这是一个否定的答复,因为解决这个问题需要深入研究,而这并不符合我的目的。我将坚持使用两个单独的进程,并通过包装器来管理任何依赖关系。感谢澄清。 - Zi1mann
@Zi1mann:是的,这并不是一个真正的建议,更多的是“这只是一个多么恶心的想法,以及为什么你不想这样做”。主要的观点是它可能从字面上讲是无法安全地完成的,就像我最后一段所说的那样。 - Peter Cordes
1
在Windows中,即使您有编译器生成代码,也不可能拥有跳转到32位(兼容模式)段的64位线程。此外,这样的代码不是编译器相关的,而是GDT相关的。 - Michael Chourdakis
@Michael:为什么不行?是因为32位代码段没有GDT条目而导致jmp本身出错吗?还是像我在最后一段中想的那样,内核会在下一个中断或系统调用时终止你的线程?如果是后者,那么我的答案至少在技术上是正确的,只是表述顺序不好。 - Peter Cordes
@Michael:我更新了我的回答,强调这不是一个真正的建议,并且很可能会在下一个中断时崩溃。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接