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

时间:2013-11-20 22:09:19

标签: c# c++ pinvoke gdkpixbuf

我正在研究一些涉及使用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”。

有什么想法吗?我对interop代码比较陌生,但我认为它可能与IntPtr变量如何发送到非托管代码/从非托管代码发送?但为什么它不同于x86到x64?

1 个答案:

答案 0 :(得分:0)

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

最初,我希望有一个x64 build ,以防万一它是必要的...而它最终只是那个。

事实证明,these comments是正确的。在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为空(还有其他可选参数可以指定),documentation告诉我它应该为空终止。为实现这一目标,我会传递IntPtr.Zero(或UIntPtr.Zero,就我而言)。

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

再次感谢那些对我的帖子发表评论的人 - 如果没有你对___arglist参数的指示,我就完全无能了。