0

PInvoke を使用して、C# コードから C++ ライブラリにアクセスします。しかし、次の機能の使用に問題があります。

UINT32 myfunc(IN char * var1, IN UINT32 var2, INOUT UINT16 * var3, OUT UINT8 * var4)

ドキュメンテーションは言う:

戻りパラメータはエラーコードです

var1 は文字列です

var2 は整数です

var3 は整数へのポインタです (値は var4 のサイズになり、値は関数によって変更されます)

var4 は、関数によって埋められるバッファへのポインタです

私が使うとき

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int myfunc(string var1, int var2, out int var3, 
    out byte[] var4);

関数は次の例外を与えます。

マネージ デバッグ アシスタント 'FatalExecutionEngineError' は、'C:\Users...\Documents\Projects...\bin\Debug...vshost.exe' で問題を検出しました。

追加情報: ランタイムで致命的なエラーが発生しました。エラーのアドレスは、スレッド 0x2edc の 0x74292e44 でした。エラー コードは 0xc0000005 です。このエラーは、CLR のバグ、またはユーザー コードの安全でない部分または検証不可能な部分のバグである可能性があります。このバグの一般的な原因には、COM 相互運用機能または PInvoke のユーザー マーシャリング エラーが含まれ、スタックが破損する可能性があります。

この例外のハンドラがあれば、プログラムは安全に続行できます。

パラメータを操作して、何かを取得できるかどうかを確認しようとしましたが、次のように文字セットを変更しました。

[DllImport("mydll.dll, CallingConvention = CallingConvention.Cdecl", CharSet = CharSet.Unicode)]
public static extern int myfunc(string var1, int var2, out int var3, out byte[] var4);

この場合、FatalExecutionEngineError は返されず、関数はエラーなしで戻ります。しかし、戻り値は、関数で内部エラーが発生したことを示しています。(文字セットの変更によって FatalExecutionEngineError が防止される理由はわかりません。)

何が間違っているのかわかりません。助けてください。

4

2 に答える 2

4

p/invoke宣言は次のようになります。

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int myfunc(
    string var1, 
    uint var2, 
    ref ushort var3, 
    byte[] var4
);

そして、あなたはそれをこのように呼びます:

string var1 = "...";
uint var2 = ...;
ushort var3 = 1024;//use whatever length buffer you want to pass
byte[] var4 = new byte[var3];
int retval = myfunc(var1, var2, ref var3, var4);
Array.Resize(ref var4, var3);

ネイティブコードが機能する方法は、呼び出し元のコードがバッファを割り当てることを期待することですvar4。呼び出し元のコードは、バッファの長さを示していvar3ます。

次に、ネイティブコードが戻るvar3と、バッファの実際の長さが含まれます。または、おそらくコピーされたバイト数。前者の方が便利です。前者を想定すると、おそらく次のように書くことができます。

string var1 = "...";
uint var2 = ...;
ushort var3 = 0;
int retval = myfunc(var1, var2, ref var3, null);
byte[] var4 = new byte[var3];
int retval = myfunc(var1, var2, ref var3, var4);

戻り値などを確認する必要があります。

質問に十分な情報がないため、関数の呼び出し方法を100%正確に把握することはできません。少なくともp/invokeシグネチャがC++コードと一致するようになりました。

于 2013-01-16T11:47:54.853 に答える
1

これは非常に危険な関数である可能性があり、4 番目の引数を介して勝手にジャンクをメモリにスプレーする可能性があります。適切に設計されている (指が交差している) 場合は、最初に必要なバッファー サイズを測定できるはずです。任意の推測:

[DllImport("mydll.dll, CallingConvention = CallingConvention.Cdecl", CharSet = CharSet.Ansi)]
public static extern int myfunc(string name, ref int var2, ref int buflen, IntPtr buf);

string name = "foo";
int var2 = 42; //???
int len = 0;
int error = myfunc(name, ref var2, ref len, IntPtr.Zero);
if (error == BUFFER_TOO_SMALL) {
    IntPtr buf = Marshal.AllocCoTaskMem(len);
    try {
       error = myfunc(name, ref var2, ref len, buf);
       if (error == OK) {
           var bytes = new bytes[len];
           Marshal.Copy(buf, bytes, 0, len);
           //...
       }
    }
    finally {
       Marshal.FreeCoTaskMem(buf);
    }
}
if (error != OK) handleError(error);

これはかなり重い推測でした。関数が実際に最初に必要なバッファー サイズを要求できない場合は、このように機能するように C コードを変更する必要があります。安全に呼び出す唯一の方法。

于 2013-01-16T12:07:18.753 に答える