如何使用P/Invoke从64位C#应用程序调用64位C++DLL



我正在编写一些代码,这些代码涉及使用p/Invoke从一些C++DLL调用非托管函数。我希望能够将应用程序构建为32位或64位。

目前,它只能作为x86使用。

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

[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不同呢?

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

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

事实证明,这些评论是正确的。在x64内部版本中,未记录的___arglist关键字无法正常工作。

我不能评论具体出了什么问题。我链接的评论提到了呼叫约定设置不正确的可能性。我不知道这是怎么回事。。。x64不是只有一个调用约定吗?

不管怎样,回到正题:

我将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是空的(可以指定其他可选参数),文档告诉它应该是空终止的。为了实现这一点,我传入IntPtr.Zero(在我的情况下是UIntPtr.Zero)。

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

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

相关内容

  • 没有找到相关文章

最新更新