从32位dll从64位应用程序调用函数

时间:2018-10-02 03:05:20

标签: c++ dll com 32bit-64bit

我有一个32位dll(无源代码),需要从64位C#应用程序访问。我已阅读this文章,并研究了here中的相应代码。我也阅读了this帖子。 我不确定我要问的是正确的问题,所以请帮助我。

有3个项目:dotnetclientx86Libraryx86x64x86x64的{​​{1}}加载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

如果我将所有项目都构建为x86或x64,则所有这些都适用。我得到的温度结果为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..."); 。但是,整个想法是使用32位20。这意味着x86x64Lib.dll应该被构建为x64,dotnetclientx86Library被构建为x86,对吗?如果这样做,我将得到x86x64

我应该将-1x86Library构建为x86,将x86x64构建为x64吗?如果这样做,我得到dotnetclient会是什么问题?

澄清 似乎提供的示例仅在客户端和服务器均以32位或64位构建时才有效。但是,当客户端以64位构建而服务器以32位构建时,则不是。有人可以看看吗?

2 个答案:

答案 0 :(得分:1)

恕我直言,最简单的方法是使用COM+ (Component Services),它是Windows的一部分,已有大约20年的历史(以前的版本以前称为MTS ...)。它为您提供代理基础结构,包括工具,UI和所需的一切。 但这意味着您必须使用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 +代理过程中。

enter image description here

完成此操作后,浏览“组件”,右键单击并创建一个新组件。确保选择“ 32位注册表”。您应该看到对象的ProgId。在我创建ATL项目的情况下,我将“ MyObject”添加为Progid,但否则可以将其命名为“ x86Library.MyObject”或“ x86LibraryLib.MyObject” ...如果不存在,则说明您犯了一些错误较早。

enter image description here

就是这样。现在,此.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
    }
}

您可以使用Component Services UI配置代理(激活,关闭等)。它还具有一个API,因此您可以创建COM+ apps programmatically

答案 1 :(得分:0)

您将无法直接从64位代码中调用32位代码(或相反),这简直不会发生。

还有其他选择,例如创建32位COM主机程序,然后将调用转发到DLL。再加上您使用DCOM标准编组,以便您的64位进程可以连接到32位主机。

但是,如果要重新编译32位DLL,几乎可以肯定是最好的选择。