4

C# Windows フォームアプリケーションで使用したいベンダーの API 呼び出しがあります。問題は、どのP/Invokeを試しても、呼び出しがスタックのバランスを崩したことを示す行でクラッシュが発生することです。私はベンダーと協力してみましたが、彼らは Visual Studio 2012 を持っておらず、C# や.NETの経験もありません。数回メールをやり取りした後、彼らは基本的に問題を解決しました。

公式の C/C++ 宣言は次のとおりです。

const char *<dll name here>(int Param1,
                            const char *Param2,
                            const char *Param3,
                            unsigned long Param4,
                            unsigned short Param5,
                            unsigned short Param6,
                            unsigned short Param7,
                            unsigned short Param8,
                            unsigned short Param9);

これは、私が試した最近の P/Invoke DLLImport 行です。

[System.Runtime.InteropServices.DllImport(
    "<dll name here>", EntryPoint = "<AnsiFunctionName>", ExactSpelling = true,
    CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern String <AnsiFunctionName>(int Param1,
                                               String Param2,
                                               String Param3,
                                               ulong Param4,
                                               Int16 Param5,
                                               Int16 Param6,
                                               Int16 Param7,
                                               Int16 Param8,
                                               Int16 Param9);

実際のエラー メッセージは次のとおりです。

件名: PInvokeStackImbalance が検出されました

P/Invoke 関数 '' の呼び出しにより、スタックのバランスが崩れています。これは、マネージド PInvoke 署名がアンマネージド ターゲット シグネチャと一致しないことが原因である可能性があります。P/Invoke シグネチャの呼び出し規約とパラメーターがターゲットのアンマネージド シグネチャと一致することを確認します。

void 戻り型を入れても、いろいろ試しました。戻り値の型を使用しないようにしました。Strings、StringBuilders、IntPtrs の代わりに試してみました。何を試しても、何も機能しません。

ここでスタック オーバーフローに関する記事を読んだ後、文字列を返すことは危険であり、決して行うべきではないことをベンダーに伝えました。彼らの反応は次のとおりです。

「そのページに記載されている文字列を返す問題は、私たちの関数には当てはまりません。それらは静的に割り当てられた文字列へのポインターを返すため、解放する必要はありません (また、解放しようとしてはいけません)。次のようになります。 「UnmanagedType.LPStr」は機能するはずです (ただし、IntPtr も適切な変換またはキャストで機能するはずです)。

更新 #1: コメントで述べたように、この投稿まで ulong ではなく uint を使用していました。比較の見栄えを良くしただけだと思いました。uint と ulong の両方が機能しません。

更新 #2: 実際の関数名を指定すると役立つ場合があります。アルマジロのCreateCodeShort3Aです。今後は、純粋な .NET を使用しますが、まだそれとのインターフェイスが必要です。現時点では、それは実際には不可能です。それ以上に、この問題は、P/Invoke のように一見単純に見えるものを解決できない理由に興味をそそられました。果たして、バリエーションはいくつあるのだろうか。(深刻な質問ではありません)。

4

1 に答える 1

2

私が見ることができる可能性は次のとおりです。

  1. 戻り値は であってはなりませんString。を使用IntPtrして文字列に変換しますMarshal.PtrToStringAnsi
  2. C++ 型unsigned longは と一致する必要がありuintます。ulong64 ビット型の C# で使用しました。Windows 上の C++ ではunsigned long、符号なしの 32 ビット型です。
  3. C++ 型unsigned shortは と一致する必要がありushortます。
  4. C++ DLL で使用される呼び出し規約は、おそらくcdecl. P/Invokeに追加CallingConvention = CallingConvention.Cdeclします。
  5. SetLastErrorそのような機能には使用しないでください。これは、 を通じてエラー状態を返す Win32 API 関数で使用されますGetLastError。この DLL ファイルはほぼ確実にそうではありません。

したがって、P/Invoke は次のようになります。

[DllImport(@"mydll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr FunctionName(
    int Param1, 
    string Param2, 
    string Param3, 
    uint Param4, 
    ushort Param5, 
    ushort Param6, 
    ushort Param7, 
    ushort Param8, 
    ushort Param9
);
于 2012-10-31T16:15:44.947 に答える