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

标签 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”。

有什么想法吗?我对互操作代码比较陌生,但我认为这可能与 IntPtr 变量如何发送到非托管代码或从非托管代码发送有关?但为什么它与 x86 和 x64 不同?

最佳答案

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

最初,我想要一个 x64 构建以防万一这是必要的……结果就是这样。

事实证明,these comments是正确的。在 x64 构建中,未记录的 ___arglist 关键字无法按预期运行。

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

不管怎样,回到正题:

我将 gdk_pixbuf_saveDllImport 更改为如下所示:

[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 参数的指示,我会完全一无所知。

关于c# - 如何使用 P/Invoke 从 64 位 C# 应用程序调用 64 位 C++ DLL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20108090/

相关文章:

c# - Prism 如何将模块 View 绑定(bind)到应用程序范围的属性

c# - 尝试序列化异常时忽略自定义解析器

c++ - 为什么有时不需要在 lambda 中捕获 const 变量?

c# - .net core - PInvoke C 共享库函数依赖于另一个共享库

xamarin - 如何在 OSX 中获取显示器 EDID?

c# - 如何在 C# 中调用需要证书的 Web 服务?

c# - 从带有外键的表中获取数据 C# Entity Framework

c++ - 模板类 C++ - 排除某些类型

php - swig 和 libcurl 在一起

c# - 如何用TWAIN获取扫描仪的序列号?