0

** 主要な更新 ** 小さな間違いを犯しましたが、正確に何が起こっているのかまだ知りたいです。

私が呼び出している関数は、実際には「fooV」であり、このシグネチャを持つ関数です。

foo(const char *, const char *, EnumType, va_list)

これにより、取得していた AccessViolationExceptions がクリアされますが、マルチバイト ANSI 文字に変換する必要がある文字列を除いて、params パラメーターが他のすべての .NET タイプで機能する理由は説明されていません。DLL の開発者に、実際に使用しているバージョンを公開してもらいます...パラメーター リストで、va_list がパラメーターである場合の PInvoking のヒントはありますか?

** 古い投稿 **

これは関連していますが、私が尋ねた最近の質問とは異なります。

次のシグネチャを持つ C ライブラリ関数を呼び出すには、PInvoke を使用する必要があります。

foo(const char *, const char *, EnumType, ...)

この関数は、StructType によって異なる方法でリソースを構成します。私が興味を持っているケースでは、varargs が単一の ANSI マルチバイト文字列であることを期待するものを構成します。この PInvoke シグネチャを使用して関数を呼び出しました。

    [DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
    public static extern int Foo(string s1, string s2, EnumType st1, params string[] s3);

params string[]私には奇妙に思えましたが、この関数を呼び出す以前の署名は、この関数を他の型などに使用していたのでbool、パターンに従いました。

これをより使いやすい .NET メソッドでラップします。

public void Foo(string s1, string s2, string s3)
{
    int error = Dll.Foo(s1, s2, EnumType.Foo, s3);
    // handle errors
}

最近、署名を変更して、FxCop の提案に準拠するために、DLLImport 属性に「BestFitMapping = false, ThrowOnUnmappableChar = true」を含めました。後で説明するように、これは赤いニシンです。

この変更を行うことで私が期待しているのは、Unicode のサポートが制限されていることです。たとえば、英語のコード ページを持つマシンでは、日本語の文字を含む文字列を渡すと、例外がスローされます。日本語のマシンでは、日本語の文字列を関数に渡すことができると思います。英語のテストは期待どおりに機能しましたが、日本語のテストは HRESULT 0x8007007A で System.Runtime.InteropServices.COMException をスローします。これは、BestFitMapping と ThrowOnUnmappableChar の設定がなくても発生します。

少し調べてみたところ、通常の引数を指定するだけで varargs を PInvoke できることを示唆するサイトをいくつか見たので、次の署名を試しました。

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
public static extern int Foo(string s1, string s2, EnumType st1, string s3);

これを英語または日本語のマシンで使用すると、AccessViolationException がスローされます。

この C ライブラリはマルチバイト ANSI のみを想定して処理するため、UTF-8 または別の Unicode エンコーディングを使用できません。この関数に CharSet.Unicode を指定すると機能することがわかりましたが、これは単なる偶然であり、信頼できるものではないのではないかと心配しています。システム コード ページを使用して文字列を byte[] に変換することを考えましたが、varargs パラメーターにバイト配列を渡したいのですが、指定方法がわかりません。

どうしたの?英語のマシンでは、英語の文字は問題なく動作し、日本語の文字は予想どおり ArgumentException をスローします。日本語のマシンでは、英語の文字は正常に機能し、日本語の文字は COMException をスローします。私の PInvoke 署名に何か問題がありますか? 属性を使用してMarshalAsLPArray のタイプと LPStr のサブタイプを指定しようとしましたが、これも同じように失敗します。UnmanagedType.LPStr は、1 バイトの ANSI 文字列であることを示します。マルチバイト ANSI 文字列を指定する方法はありますか?

** 更新 ** これは、現在のコメントを考慮した内容の追加です。

CDecl の呼び出し規約を指定し、次のような通常のパラメーターを使用する場合:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

この仕様を使用すると、AccessViolationException が発生します。だから私はこれを試しました:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.CDecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

これは英語で機能し、日本語の文字を使用すると COMException がスローされます。

一貫して機能することがわかった唯一のことはこれです:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int Foo(string s1, string s2, EnumType e1, params IntPtr[] s3)

これを機能させるために、Marshal.StringToHGlobalAnsi() を使用してそのポインターを渡します。これは、英語と日本語で機能します。なぜこれが解決策なのか理解できないのは不快ですが、うまくいきます。

4

1 に答える 1

2

CallingConvention=CallingConvention.Cdeclvarargs 関数は呼び出し規則を使用するため、おそらく指定する必要もあります_cdecl(デフォルトは Winapi で、デフォルトは StdCall になります)。

他に何か必要かもしれませんが、少なくともそれが必要になると確信しています。

于 2009-05-05T16:54:40.420 に答える