1

我正在处理一些涉及使用 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 不同呢?

4

1 回答 1

0

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

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

事实证明,这些评论是正确的。在 x64 版本上,未记录的___arglist关键字无法按预期工作。

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

不管怎样,回到正题:

我改变了我DllImport的 forgdk_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);

这里的关键是我传递了最后一个参数 ,arglistIntPtr不是___arglist

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

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

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

由于 my___arglist是空的(可以指定其他可选参数),文档告诉我它应该是空终止的。为了做到这一点,我传入IntPtr.Zero(或者UIntPtr.Zero,在我的情况下)。

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

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

于 2013-11-27T07:20:43.883 回答