我正在研究一些涉及使用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?
答案 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
参数的指示,我就完全无能了。