为 32 位 DLL 编写 COM/ATL Wrapper 的正确方式

Posted

技术标签:

【中文标题】为 32 位 DLL 编写 COM/ATL Wrapper 的正确方式【英文标题】:Proper Manner for Writing COM/ATL Wrapper for 32-bit DLL 【发布时间】:2013-06-17 18:37:47 【问题描述】:

我有一个关于为 32 位 DLL 正确编写 COM/ATL 包装器的问题。

这就是问题所在。正如我的其他一些问题所暗示的那样,我经常需要在大型代码库上执行编码工作,其中包括从无数不同供应商链接的库。我们的软件可以在各种机器、各种架构上运行。此代码主要用 C# 编写,C++ DLL 被链接或编译/链接,然后在必要时从 C# 中 P/Invoked(在我看来,比托管 C++ 更容易实现)。

无论如何,一个这样的程序包含必须与几个 64 位 DLL 接口的 C# 代码,而这些 DLL 的源代码不可用。该软件的目标主机都是 64 位的。没问题。因此,在 AnyCPU 模式下运行的 C# 会自动生成为 64 位(没有 WoW),并且能够无缝 P/Invoke 这些预编译的第 3 方 64 位 DLL。

有一个这样的 DLL,我只能使用 32 位版本。无法获得此 DLL 的 64 位版本。我有 1.) 32 位 DLL,2.) 包含函数导出的头文件(即 #define EXP __declspec(dllexport)、EXP BOOL WINAPI DLL_Function(IN int myInt,OUT int *result); 和 3.)对应的 32 位 lib 文件。

经过大量研究,我了解到 COM/ATL 显然是从 64 位进程访问 32 位 DLL 的正确方法。显然,我在 WoW 下生成了一个 32 位 COM 进程,并与之交互。从架构的角度来看听起来不错,但我找不到任何关于如何具体做到这一点的教程或描述。我尝试过使用 Visual Studio 2010 的 ATL 向导,但仍然不知道如何正确执行此操作。有没有自动化工具?有模板或教程吗?我保证我已经尽我所能搜索了网络,只是想知道 1.)我是否以正确的方式解决这个问题,或者 2.)你知道任何资源吗?或者 3.)确实有人为此提供某种通用 IPC 模板吗?

从我在 SO 上读到的内容来看,像这样的图像不匹配似乎是程序员的常见问题,但除了“使用 COM/ATL”。我之前的一篇文章产生了一个回复,其中 P/Invoke 签名调用的问题被详细地充实了,这太棒了——我想知道我们是否可以开始一个“确定的”64/32 位跨架构讨论到这里!

谢谢!

-卡达杰

编辑:我已经知道http://blog.mattmags.com/2007/06/30/accessing-32-bit-dlls-from-64-bit-code/,并且了解架构,但我想知道实现的细节。

【问题讨论】:

SO 不是讨论的正确场所。 COM 可以做到这一点,但它不是 Windows 上唯一可用的进程外执行选项。使用 WCF、Remoting、命名管道或套接字是 .NET 中的简单解决方案,无需陡峭的学习曲线。 OK... COM 不是个好主意,那么?也许我用“讨论”这个词是不正确的。例如,汉斯,你的评论已经给我指明了不同的方向......我所读到的关于这个问题的几乎所有内容似乎都是针对使用 COM 的——我没有考虑过为这种通信寻找套接字。 【参考方案1】:

您可以在几分钟内获得简约的 ATL 解决方案。

分步说明:

    创建空解决方案 使用 Visual Studio 向导添加 ATL 项目。这将是 C++、ATL 项目、可执行 EXE - 您将需要 EXE,因为这将是托管您的 32 位代码的独立 32 位进程 在项目中添加类、ATL 简单对象、所有默认值 - 您将拥有一个类(例如 CFoo)以及 IDispatch 派生接口 IFoo 你添加一个方法到IFoo 接口,以及CFoo 类中的相应实现,例如CFoo::Bar C++ 部分已完成,构建并确保它是 COM 注册的(可执行文件使用 /regserver 参数运行一次) 添加 C# 项目,强制其位数为 x64,将 COM 引用添加到您在上述步骤 5 中构建的 ATL COM 服务器 将 [STAThread] 添加到您的 C# 代码并从 C# 调用 Bar - 大功告成!

C#:

class Program

    [STAThread]
    static void Main(string[] args)
    
        HostLib.Foo foo = new HostLib.Foo();
        foo.Bar();
    

C++ IDL:

[
    // ...
]
interface IFoo : IDispatch

    [id(1)] HRESULT Bar();
;

C++:

// IFoo
    STDMETHOD(Bar)()
    
        MessageBox(GetActiveWindow(), _T("Hello, 32-bit World!"),
            _T("Information"), MB_OK);
        return S_OK;
    

64 位 C# 代码启动 32 位 COM EXE 并通过调用 Bar 方法。

来源:Trac/SVN。

【讨论】:

【参考方案2】:

最终,您需要将 32 位 dll 托管在 32 位进程中,并将数据编组到 64 位进程中和从 64 位进程中输出。

首先,我应该解释一下COM和DCOM是什么:

COM

在 C/C++/VB6/etc 之上提供一个约定,通过定义的接口标准化调用和与对象交换数据。它还有一个注册表,用于将对象的相关 dll 加载到您的进程中。

DCOM

引入了编组和代理的概念,您可以在其中调用接口,但它是一个将参数包装起来的代理,将它们通过命名管道推送到不同的进程,等待结果并解包响应。该进程可以在不同的计算机上,甚至具有不同的处理器架构。

因此您可以使用 DCOM 来解决您的问题,但是学习曲线非常庞大,而且对于您想要的东西来说它几乎太强大了。

您需要查看应用程序的所需架构以确定正确的解决方案,但我将介绍一些高级原理。

如果您创建一个 32 位 .Net 进程/服务来托管 DLL,那么您可以研究在 64 位和 32 位进程之间执行 IPC 的多种技术之一。您需要决定是要为每个 64 位进程创建一个还是每个系统一个,以及它的生命周期应该是多长。 然后你需要找到一种方法来交换数据,并编组调用。

WCF 如果您希望每台机器有一个主机进程并且具有相对复杂的接口,那么可能是一个不错的(但复杂的)选择。

JSON RPC/HTTP 可能是另一种更轻量级的解决方案,您需要注意这里的安全性。

STDIO 如果您能侥幸成功,请编写一个 32 位命令行应用程序,然后通过 STDIO 管道传输数据,这很容易保护并且具有一定的 unix 般的优雅。

这些解决方案的好处是您可以尽可能多地留在 .Net 领域,这将减少学习曲线。

【讨论】:

以上是关于为 32 位 DLL 编写 COM/ATL Wrapper 的正确方式的主要内容,如果未能解决你的问题,请参考以下文章

在 64 位机器上注册 C++ DLL

将 32 位 COM DLL 注册到 64 位 Windows 7

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

如何测试 Windows DLL 文件以确定它是 32 位还是 64 位? [复制]

使用 C++ 从 32 位进程访问 64 位 dll

找不到指定的模块 - 64 位 dll