旧システムのリプレース作業中に↓の例外に遭遇した。
PInvokeStackImbalance が検出されました
なんのこっちゃいって感じなので調べてみた。
これは、VS2010以降でDllImport等を使用して、アンマネージドのDLLを呼び出す場合に発生する。
通常、DLLを作成する際に呼び出し規則(_stdcallなど)をつけてexportするが、その指定が 作成したDLL側と、呼び出すC#側で不一致 だということらしい。
なぜVS2008以前は起きなかったかというと、VS2008以前では↑の規則の不一致をチェックする例外「PInvokeStackImbalance」が無効(設定できない)となっており、VS2010から有効となったからだ。
『”P/Invoke”呼び出し規則のチェックの厳格化』ということらしい。
この事象はプロジェクトの「対象のフレームワーク」に応じて設定値がかわるわけではなく、Visual Studioのバージョンによって初期値がかわる。
VS2008には、例外設定内のManaged Debugging Assistantsに、「PInvokeStackImbalance」がないのがわかる。VS2008 ~ VS2010は↓の手順で確認できる。
デバッグ ⇒ 例外 で例外ウィンドウを開く。
例外ウィンドウで Managed Debugging Assistants ⇒ PInvokeStackImbalance
VS2015以降の場合は↓の手順となる。
デバッグ ⇒ ウィンドウ ⇒ 例外の設定 で例外ウィンドウを開く。
例外ウィンドウで Managed Debugging Assistants ⇒ PInvokeStackImbalance
DLLインポートの記述を↓のように修正する。
[DllImport("Hoge.dll")]
↓
[DllImport("Hoge.dll", CallingConvention = CallingConvention.XXXX)]
“XXXX”はDLLエクスポートの定義と合わせる。
CallingConventionを省略すると、StdCallが既定値(呼び出し先がStackを削除)となる。
CallingConventionとは、エントリポイントの呼び出し規則で、列挙の内容はMSDNを参照して頂きたい。
MSDN:CallingConvention列挙方メンバー
ちなみに上記のCallingConventionを正しく設定しないと、ぼくの環境では不穏な動き(再現性が一定でない不具合)をしたため、やはり設定するのが正しいやり方であろう。