我正在编写一些代码,这些代码涉及使用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
参数的指导,我会完全一无所知。