Я работаю над кодом, который включает использование P / Invoke для вызова неуправляемых функций из нескольких библиотек C ++. Я хотел бы иметь возможность создавать приложения как 32-разрядные, так и 64-разрядные.
В настоящее время он работает только как x86.
У меня есть 32- и 64-битные копии каждой из указанных C ++ DLL, и я использую следующий код для изменения 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);
Как я уже говорил, все работает нормально при запуске приложения под x86 на моей локальной машине (Windows 7 x64). Однако, когда я компилирую его как приложение x64, я получаю «AccessViolationException» при вызове gdk_pixbuf_save ().
Есть идеи? Я относительно новичок в взаимодействии кода, но я думаю, что это может иметь какое-то отношение к тому, как переменные 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
параметр, я бы был совершенно невежествен
Других решений пока нет …