2009-05-05 10 views
0

**主な更新** 私は小さな間違いをしましたが、私はまだ何が起こっているのかについてはまだ不思議です。multibyte ANSIをvarargsにPInvokeできますか?私は間違って何をしていますか?

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

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

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

**古いポスト**

これはに関連しますが、recent question I asked異なっています。

Iは、次のシグネチャを有する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 =」を含めるように署名を変更しました。私が後で説明するように、これは赤いニシンです。

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

は、私の周り見て少し行って、あなたは可能性の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署名に何か問題がありますか?私はMarshalAs属性を使用してLPArrayのタイプとLPStrの​​サブタイプを指定しようとしましたが、これは同じ方法で失敗します。 UnmanagedType.LPStrは、それがシングルバイトの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を使用)し、そのポインタを渡します。これは英語と日本語で動作します。私はこれが解決策である理由を理解していないのは苦ではありませんが、うまくいきます。

答えて

2

また、varargs関数が_cdecl呼び出し規約を使用するため、CallingConvention=CallingConvention.Cdeclを指定する必要があります(デフォルトはWinapiで、デフォルトはStdCallです)。

あなたは別のものが必要かもしれませんが、私はあなたが少なくともそれを必要とすると確信しています。

+0

私はこれを試しましたが、何らかの理由でその機能を無効にしました。 (これはGet/Setペアの "Set"であり、Setをこのように呼び出すとGetは何も返しません)私はこれが正しかったと確信しています。 – OwenP

+0

実際に日本のマシンでは、Cdeclを使用するとAccessViolationExceptionが発生します。好奇心。 – OwenP

関連する問題