【问题标题】:How can I use P/Invoke to call a 64-bit C++ DLL from a 64-bit C# Application?如何使用 P/Invoke 从 64 位 C# 应用程序调用 64 位 C++ DLL?
【发布时间】:2013-11-20 22:09:18
【问题描述】:

我正在处理一些涉及使用 P/Invoke 从几个 C++ DLL 调用非托管函数的代码。我希望能够将应用程序构建为 32 位或 64 位。

目前,它仅适用于 x86。

我有每个引用的 C++ DLL 的 32 位和 64 位副本,我使用以下代码更改 DllDirectory,具体取决于应用程序是构建为 x86 还是 x64(/lib/x64 包含 64 位 dll, /lib/x86 保存 32 位的):

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern bool SetDllDirectory(string lpPathName);

string libPath = Path.Combine(Environment.CurrentDirectory, "lib", (Environment.Is64BitProcess == true ? "x64" : "x86"));
SetDllDirectory(libPath);  

我的其余非托管 C++ 函数定义如下:

[DllImport("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void g_type_init();
[DllImport("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void g_object_unref(IntPtr pixbuf);
[DllImport("librsvg-2-2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr rsvg_pixbuf_from_file_at_size(string file_name, int width, int height, out IntPtr error);
[DllImport("libgdk_pixbuf-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern bool gdk_pixbuf_save(IntPtr pixbuf, string filename, string type, out IntPtr error, __arglist);

实际使用这些函数的代码如下所示:

g_type_init();
IntPtr ptrError;
IntPtr ptrPixbuf = rsvg_pixbuf_from_file_at_size(filePath, width, height, out ptrError);
if (ptrError == IntPtr.Zero)
{
    bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputPath, outputFormat, out ptrError, __arglist(null));  //this line fails when compiled as x64!
    if (isSaved && File.Exists(outputPath))
    {
        return outputPath;
    }
}
g_object_unref(ptrPixbuf);

正如我所提到的,在我的本地计算机(Windows 7 x64)上以 x86 运行应用程序时一切正常。但是,当我将其编译为 x64 应用程序时,在调用 gdk_pixbuf_save() 时会收到“AccessViolationException”。

有什么想法吗?我对互操作代码比较陌生,但我认为这可能与 IntPtr 变量如何发送到非托管代码或从非托管代码发送有关?但是为什么从 x86 到 x64 不一样呢?

【问题讨论】:

  • 您将不得不对其进行调试,以便您至少知道哪个参数是错误的。在本机 C++ 代码上设置断点并从那里获取。
  • __arglist 没有记录,所以也许它根本不起作用......
  • 您是否尝试过明确定义额外参数而不是使用 __arglist?
  • 是的,我也在看__arglist。这需要 C++/CLI。
  • 我只使用了___arglist,因为这是我发现的一个例子所使用的。话虽如此,我尝试从函数调用中删除它,我得到了相同的结果:它在 32 位构建上成功,在 64 位构建上失败。此外,我真的无法访问本机 C++ 代码。我的意思是,它是一个 Gnome 库,所以它是可用的,但我根本没有考虑在 Windows 中调试它。

标签: c# c++ pinvoke gdkpixbuf


【解决方案1】:

非常感谢所有评论的人——你们让我走上了正确的道路,并帮助我解决了一个潜在的问题。

最初,我想构建一个 x64 版本以防万一有必要……结果就是这样。

事实证明,these comments 是正确的。在 x64 构建中,未记录的 ___arglist 关键字无法按预期工作。

我无法评论具体出了什么问题。我链接的评论提到了调用约定设置不正确的可能性。我不确定这是如何工作的……x64 不是只有 one 调用约定吗?

不管怎样,回到正题:

我将DllImport 更改为gdk_pixbuf_save,如下所示:

[DllImport("libgdk_pixbuf-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool gdk_pixbuf_save(UIntPtr pixbuf, string filename, string type, out UIntPtr error, UIntPtr arglist);

这里的关键是我将最后一个参数arglist 传递为IntPtr 而不是___arglist

实际上,我将其作为UIntPtr 传递,因为我将所有原始IntPtr 对象都切换为UIntPtr

话虽如此,当我调用该函数时,它看起来像这样:

bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputFilePath, outputFileFormat, out ptrError, UIntPtr.Zero);

由于我的___arglist 为空(还有其他可选参数可以指定),documentation 告诉我它应该以空值终止。为此,我传入IntPtr.Zero(或UIntPtr.Zero,就我而言)。

现在,我的代码编译、运行并且我(更重要的是)可以访问 64 位的内存。

再次感谢那些对我的帖子发表评论的人 - 如果没有您对 ___arglist 参数的指点,我会完全一无所知。

【讨论】:

  • 干得好!另外:您可以在调用任何其他 pinvokes 之前使用 DLL 的完整路径调用 LoadLibrary,而不是使用 SetDllDirectory。只是另一种方法。
猜你喜欢
  • 2011-09-14
  • 2013-04-11
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 2021-03-06
  • 2011-12-25
  • 2017-09-17
相关资源
最近更新 更多