はじめに
手元にある太古のCで書かれたWin32なスタティック ライブラリを、Visual Studio 2019でASP.NET 5.0を使ってWeb API化しようと思い立ち挑戦したところ、スタティック ライブラリをラップした C++/CLI のアセンブリを使うところで
System.BadImageFormatException: Could not load file or assembly 'HogeWrapper, Version=1.0.7796.41445, Culture=neutral, PublicKeyToken=null'. 間違ったフォーマットのプログラムを読み込もうとしました。
のエラーでハマってしまったため、解決方法をメモしておきます。
背景
10年以上前のVisual StudioでビルドされたWin32(x86)のスタティック ライブラリ hoge.lib
を.Net 5.0で使うために、まず C++/CLI でラッパーのクラスライブラリのDLLHogeWrapper.dll
を作成しました。元のLIBが64bit対応していないので、このDLLも32bit縛りです。
次にDLL単独の動作確認のため、同じソリューション内に.Net 5.0 C#のコンソール アプリのプロジェクトを作って、ソリューション内のDLLのプロジェクトと依存関係を結んで実行したところ、うまく使うことができました。
そして最後に、別のソリューションでASP.NET 5.0 C#のWeb APIプロジェクトを作り、HogeWrapper.dll
を参照設定しました。これで間接的にhoge.lib
の機能が呼び出せるようになります。ビルドまでは順調でした。
しかし、実行時にハマりました。以下、Debug実行時の出力ウィンドウの途中部分なのですが、せっかくロードしたHogeWrapper.dll
がすぐにアンロードされてしまい、例外System.BadImageFormatException
がスローされてしまいます。
'Hoge.exe' (Win32): 'C:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\HogeWrapper.dll' が読み込まれました。シンボルが読み込まれました。
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\msvcp140d.dll' が読み込まれました。
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\ucrtbased.dll' が読み込まれました。
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\vcruntime140d.dll' が読み込まれました。
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\vcruntime140d.dll' がアンロードされました
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\ucrtbased.dll' がアンロードされました
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\msvcp140d.dll' がアンロードされました
'Hoge.exe' (Win32): 'C:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\HogeWrapper.dll' がアンロードされました
'Hoge.exe' (Win32): 'C:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\HogeWrapper.dll' が読み込まれました。シンボルが読み込まれました。
0x7519C602 で例外がスローされました (Hoge.exe 内): Microsoft C++ の例外: HRException (メモリの場所 0x065ACAD0)。
'Hoge.exe' (Win32): 'C:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\HogeWrapper.dll' がアンロードされました
0x7519C602 で例外がスローされました (Hoge.exe 内): Microsoft C++ の例外: [rethrow] (メモリの場所 0x00000000)。
0x7519C602 で例外がスローされました (Hoge.exe 内): Microsoft C++ の例外: EEFileLoadException (メモリの場所 0x065AD048)。
0x7519C602 で例外がスローされました (Hoge.exe 内): Microsoft C++ の例外: [rethrow] (メモリの場所 0x00000000)。
0x7519C602 で例外がスローされました (Hoge.exe 内): Microsoft C++ の例外: [rethrow] (メモリの場所 0x00000000)。
0x7519C602 で例外がスローされました (Hoge.exe 内): Microsoft C++ の例外: [rethrow] (メモリの場所 0x00000000)。
'Hoge.exe' (Win32): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\Microsoft.DiaSymReader.Native.x86.dll' が読み込まれました。
'Hoge.exe' (Win32): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.Diagnostics.StackTrace.dll' が読み込まれました。
'Hoge.exe' (CoreCLR: clrhost): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.Diagnostics.StackTrace.dll' が読み込まれました。シンボルの読み込みをスキップしました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'Hoge.exe' (Win32): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.Reflection.Metadata.dll' が読み込まれました。
'Hoge.exe' (CoreCLR: clrhost): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.Reflection.Metadata.dll' が読み込まれました。シンボルの読み込みをスキップしました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'Hoge.exe' (Win32): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.Collections.Immutable.dll' が読み込まれました。
'Hoge.exe' (CoreCLR: clrhost): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.Collections.Immutable.dll' が読み込まれました。シンボルの読み込みをスキップしました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'Hoge.exe' (Win32): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.IO.MemoryMappedFiles.dll' が読み込まれました。
'Hoge.exe' (CoreCLR: clrhost): 'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.IO.MemoryMappedFiles.dll' が読み込まれました。シンボルの読み込みをスキップしました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware: Error: An unhandled exception has occurred while executing the request.
System.BadImageFormatException: Could not load file or assembly 'HogeWrapper, Version=1.0.7796.41445, Culture=neutral, PublicKeyToken=null'. 間違ったフォーマットのプログラムを読み込もうとしました。
File name: 'HogeWrapper, Version=1.0.7796.41445, Culture=neutral, PublicKeyToken=null'
at Hoge.Controllers.KeywordItemsController.Get(String data)
at lambda_method2(Closure , Object , Object[] )
解決方法
このSystem.BadImageFormatException
は、x86(Win32)とx64を混在させた時などに起こるそうなのですが、ASP.NET 5.0のWeb API側もきちんとx86に縛っていて混在はしていません。
ググってもうまく日本語の情報が見つからず、仕方がないので苦手な英語のサイトを追い始めたところ、Stack OverflowでそのものズバリのQAがありました。
Bad image format when running managed C++/CLI assembly in .NET Core 3.1
これは .NET Core 3.1時代のものですが .NET 5.0も同様でした。C++/CLIのDLLを使う場合、DLLだけ持ってきてもダメで、同じ位置に出力されるIjwhost.dll
もDLLと同じ位置に持っていかないとダメだそうです。今回 .NET Core系のC++/CLIを初めて使ったので、この辺りの常識を全く知りませんでした。
DLLはC:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\HogeWrapper.dll
にあるので、同じ位置にIjwhost.dll
を持っていったところ、無事にエラーが解消しました。以下、正常時の出力ウィンドウの抜粋です。DLLと同じ位置に置いたIjwhost.dll
が読み込まれているのがわかります。
'Hoge.exe' (Win32): 'C:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\HogeWrapper.dll' が読み込まれました。シンボルが読み込まれました。
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\vcruntime140d.dll' が読み込まれました。
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\ucrtbased.dll' が読み込まれました。
'Hoge.exe' (Win32): 'C:\Windows\SysWOW64\msvcp140d.dll' が読み込まれました。
'Hoge.exe' (Win32): 'C:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\Ijwhost.dll' が読み込まれました。
'Hoge.exe' (CoreCLR: clrhost): 'C:\Users\Segavvy\source\repos\Hoge\Hoge\bin\x86\Debug\net5.0\HogeWrapper.dll' が読み込まれました。シンボルが読み込まれました。
なお、最初にHoge.dll
の動作確認を簡単なC#のコンソール アプリで実施した時につまずかなかったのは、同一ソリューション内にDLLのプロジェクトも入れて、プロジェクトの依存関係を結んで使ったためです。これならDLLの位置にIjwhost.dll
もある形が維持されます。
別ソリューションからDLLの参照設定で使う場合は、今回のような注意が必要です。Debug/Releaseの切り替えなども考えると、手元にプロジェクト ファイルがある場合は同一ソリューションに入れてしまうのが正解みたいです。
おわりに
.NET Core系を初めて使っているので、誤りがあるかも知れません。正しい参照設定の方法がありそうな気もします。何かわかったら更新します。識者の方のアドバイスもお待ちしております。