ふにゃるんv2

もとは、http://d.hatena.ne.jp/Wacky/

.NETから統合アーカイバ仕様DLLを呼び出すなら、x86でビルドしよう

今回は、ちょっとした気づきです。

で提供されている.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;
}

ここまで来て、ふと以前読んだ、↓の記事を思い出しました。

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」だった為、以下の動きになっていたと思われます。

  1. hoge.exeを動かす。
    「Any CPU」なので、EXEは64ビットだよ〜と、マークされる。
  2. ExistDLLのSearchPath APIを呼び出す。
    検索パスはnull指定なので、API仕様に書かれている通り、実行パスのフォルダ→カレントフォルダ→Windowsのシステムフォルダ→Windowsディレクトリ→環境変数PATHの設定 の順番に検索をかけていく。
  3. ↑で、Windowsのシステムフォルダを検索する時、64ビットモードで動いているので、検索フォルダは「C:\Windows\System32」となる。
  4. 「C:\Windows\System32」フォルダは、純粋に64ビットなDLLしか存在しないので、unlha32.dllなどの32ビットDLLは存在しない。存在しないので、ExistDLLはFALSEになって変える。


対して、「x86」でEXEをビルドしておく事で、3.番目の挙動が変わります。


3.↑で、Windowsのシステムフォルダを検索する時、32ビットモードで動いているので、検索フォルダは「C:\Windows\SysWow64」となる。


多分、すべてを.NETで記述するか、64ビット対応のDLLを用意していれば、問題なく動作するんでしょうけど、この場合はしょうがないですね。