.NETから統合アーカイバ仕様DLLを呼び出すなら、x86でビルドしよう
今回は、ちょっとした気づきです。
- 動的PInvokeによる統合アーカイバ仕様DLLを使用した書庫の展開と作成(1/4):CodeZine
http://codezine.jp/article/detail/444
で提供されている.NET向けのライブラリを使って、オレオレアプリを作っていたんですね。
この都度、Win7(64bit)環境で動くかな?と思って動かしたら、動かない。
エラーログを見たら、「DLLが無ぇよ」とおっしゃる。
そんなまさか!?と思いつつ、システムフォルダを検索すると、unlha32.dllなどの統合アーカイバ仕様DLLは、どうやら「C:\Windows\SysWow64」フォルダに在る模様。
ちゃんとDLLは存在するのに、ライブラリは見つけられない。
コードを追いかけていくと、DLLの存在チェックは、CommonArchiver.csのExistDLL関数内の SearchPath というWin32 APIを呼び出して実現している模様。
/// <summary> /// DLLが存在するか調べる /// </summary> /// <returns></returns> public bool ExistsDll() { IntPtr pathPtr; return Win32.NativeMethods.SearchPath( null, this.DllFileName, null, 0, null, out pathPtr) != 0; }
ここまで来て、ふと以前読んだ、↓の記事を思い出しました。
- EXE を作るプロジェクトのデフォルトが Any CPU から x86 に変わった理由 - または Any CPU の本当の意味 - 当面C#と.NETな記録
http://d.hatena.ne.jp/siokoshou/20091102/p1
Win64(64ビットバージョンの Windows)によって PE ファイル(EXE と DLL)は32ビット「または」64ビットとマークできるようになりました。32ビット EXE は Win64 上では、プロセスに32ビットオペレーティングシステムの幻想を見せる「WOW(Windows32 on Windows64)」の中で走ります。通常、32ビット DLL は32ビットプロセスだけにロードでき、64ビット DLL は64ビットプロセスだけにロードできます。
ここまで来て、プロジェクトの設定を確かめてみると、プラットフォームターゲットが「Any CPU」になっています。
これを「x86」に変えた所、思ったとおり、DLLを参照できるようになりました。
よかった、よかった。
解説
という程のものでも無いですが。
要するに、自分のアプリが「Any CPU」だった為、以下の動きになっていたと思われます。
- hoge.exeを動かす。
「Any CPU」なので、EXEは64ビットだよ〜と、マークされる。 - ExistDLLのSearchPath APIを呼び出す。
検索パスはnull指定なので、API仕様に書かれている通り、実行パスのフォルダ→カレントフォルダ→Windowsのシステムフォルダ→Windowsディレクトリ→環境変数PATHの設定 の順番に検索をかけていく。 - ↑で、Windowsのシステムフォルダを検索する時、64ビットモードで動いているので、検索フォルダは「C:\Windows\System32」となる。
- 「C:\Windows\System32」フォルダは、純粋に64ビットなDLLしか存在しないので、unlha32.dllなどの32ビットDLLは存在しない。存在しないので、ExistDLLはFALSEになって変える。
対して、「x86」でEXEをビルドしておく事で、3.番目の挙動が変わります。
3.↑で、Windowsのシステムフォルダを検索する時、32ビットモードで動いているので、検索フォルダは「C:\Windows\SysWow64」となる。
多分、すべてを.NETで記述するか、64ビット対応のDLLを用意していれば、問題なく動作するんでしょうけど、この場合はしょうがないですね。