1

P/Invoke を使用して、いくつかの C++ DLL からアンマネージ関数を呼び出すコードに取り組んでいます。アプリケーションを 32 ビットまたは 64 ビットとしてビルドできるようにしたいと考えています。

現在、x86 としてのみ動作します。

参照されている C++ DLL のそれぞれの 32 ビットと 64 ビットのコピーがあり、次のコードを使用して、アプリが x86 または x64 としてビルドされているかどうかに応じて DllDirectory を変更しています (/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には呼び出し規約が1つしかありませんか?

とにかく、要点に戻ります。

DllImportforgdk_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すべて に切り替えたので、 as に渡しています。IntPtrUIntPtr

そうは言っても、関数を呼び出すと、次のようになります。

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

my___arglistは空であるため (指定できるオプションのパラメーターは他にもあります)、ドキュメントには null で終了する必要があることが記載されています。これを実現するために、IntPtr.Zero(またはUIntPtr.Zero、私の場合は ) を渡します。

これで、コードがコンパイルされて実行され、(さらに重要なことに) 64 ビット相当のメモリにアクセスできるようになりました。

私の投稿にコメントしてくれた人たちにもう一度感謝します -___arglistパラメータへのポインタがなければ、私は完全に無知だったでしょう.

于 2013-11-27T07:20:43.883 に答える