とっちゃん's Blog

WindowsInstaller に WiX はいかがですか~

目次

Blog 利用状況

ニュース

とっちゃんって?

コミュニティ

メーリングリスト

@ITの記事

CodeZineの記事

WiX チュートリアル

Windows ユーザー エクスペリエンス ガイドライン

唯一の日本語書籍

人気のほどは...

記事カテゴリ

書庫

日記カテゴリ

インストーラ関連

旧館

[Win32] LoadLibrary のサーチパス

ネタ元:WinUnitをマルチバイトコードで使うと実行時にエラー(わんくま掲示板)

WinUnit 便利っすね~。DLLになってさえいれば、さまざまなテストができます。おいらは、今のところはツールメニューに仕込んで使ってます。ビルド後イベントに入れてないのは、テストしたいDLLが、MFC拡張DLLだから...そのままじゃビルド後のイベントに使えないのよねぇw

なので、テスト専用のMFCレギュラーDLL(今は違う名前だったかな?)が、あります。<こいつを自動テストにすればいいんだが、今のところはまだやってないw

ちなみに、テスト関数にブレークポイントはっておけば、デバッグもできます。実はこっちの方が便利だったりするw<おい!

 

さて、DLLといえば、厄介なのがDLL同士の従属関係。古くからこのエラーで悩まされている人が後を絶たないのが現実です。

見たことありませんか?

「Hoge.dll が見つからなかったため、このアプリケーションを開始できませんでした。アプリケーションをインストールし直すとこの問題は解決される場合があります。 」

「指定されたモジュールが見つかりません。」

というメッセージ、もしくは 126 や 0x8007007E というエラー番号。

これらのエラーはすべて同じものです。WinError.h(SDKのヘッダーです)に、ERROR_MOD_NOT_FOUND という値で定義されているエラーです(上のパターンは異なる表現方法なので、全部挙げてみましたw)。

なぜこれらのエラーが発生するのでしょうか?

最初のメッセージは、親切にもDLL名を挙げていますので、わかりやすいですね。「Hoge.dll が見つからなかった」だからロードできなかったと。こちらは、エンドユーザー向けのメッセージで、カーネルがロード失敗時に出してくれるメッセージです。まだ、前世紀のころこのメッセージボックスが中身からっぽで出てくるというOSがあったんですがねw

ちなみに、ネタ元は、2個目のメッセージが表示されていました。

 

では、なぜこのようなエラーが出るんでしょうか?

今回の場合は、WinUnit に指定したDLL(仮にTest.dll)が、Hoge.dll を参照していたために発生しています。

Test.dll と Hoge.dll は同じフォルダにあるにもかかわらず、「なぜかHoge.dllを見つけることができず」結果として Test.dll のロードに失敗してしまったのが原因です。

では、なぜロードできないのでしょうか?(ちなみに、Test.dll はフルパスで指定されています)

これ、おいらもまったく同じ原因でエラーがでて、えええええええ!って感じだったんですが...

原因は、WinUnit が DLLをロードする際、LoadLibrary API でロードしていたのが原因です。

ではなぜ、これでエラーが出るのでしょうか?それは、Windows の DLLサーチパスに起因しています。

MSDN Library(もちろん英語)に Dynamic-Link Library Search Order とそのものずばりのページがありました(昔はなかったんだが...<いつの時代だよ!)

標準のDLL検索パスは、

  1. アプリケーション(EXE、すなわちホストプロセス)のあるディレクトリ
  2. システムディレクトリ。GetSystemDirectory API で取得できるディレクトリ
  3. 16bit 版のシステムディレクトリ(互換性に起因するもの。64bitOSだとたぶん使われないと思いますが正直不明w)
  4. Windows ディテクトリ。GetWindowsDirectory API で取得できるディテクトリ
  5. カレントディテクトリ
  6. PATH 環境変数に指定されているフォルダ(SafeDllSearchMode を操作すると除外可能)
  7. レジストリの、App Paths キーで指定されている特別なフォルダ(インストーラで設定されます)

となっています(詳しくは原文参照)。個人的には、3や4はもういらねーだろう?とは思うんですが、なにせ歴史のあるOSなのでそうも言えない互換性の問題があったりするんですよねw

よく見てください。上記のリストには、dll をロードしたパスは含まれていません。これが原因なんですね。

 

ではどうすればいいのでしょうか?

こちらは、Alternate Search Order の項にあるように、LoadLibraryEx API で、LOAD_WITH_ALTERED_SEARCH_PATH を指定するか、SetDllDirectory API(Vista以降)を使ってDLL検索パスを追加する方法で、解決することができます。

LoadLibraryEx を利用すると、標準検索パスの1番が、LoadLibraryEx で指定されたDLLと同じフォルダに切り替わります(DLLがフルパス指定されている場合)。

SetDllDirectory を利用した場合は、標準DLL検索パスの1と2の間で、SetDllDire で指定したパスを検索します。

 

ちなみに、MFC(DLLでリンクしている場合)では、LoadLibrary の代わりに、AfxLoadLibrary を、LoadLibraryEx の代わりに AfxLoadLibraryEx を使うと、安全にMFC拡張DLLをロードすることができます(マルチスレッドなアプリの場合だけですがw)。

あ、AfxLoadLibraryEx はリファレンスないのね。アンドキュメントですか...w

投稿日時 : 2008年3月14日 13:32

コメントを追加

# re: [Win32] LoadLibrary のサーチパス 2008/03/14 14:56 επιστημη

WinUnitソース:ExternalLogger.cpp:line85

_hmodule = LoadLibraryW(file);

_hmodule = LoadLibraryExW(file,0,LOAD_WITH_ALTERED_SEARCH_PATH);
に書き換えればおっけぃっちゅーことでよろしぃでしょか。

# re: [Win32] LoadLibrary のサーチパス 2008/03/14 15:10 シャノン

XP以降なら、マニフェストつければよくね?

で、今回の件には使えないだろうけど、.local ファイルによるリダイレクトとか、レジストリの KnownDll とかも絡んでこない?
もう魔境ですよ。

# re: [Win32] LoadLibrary のサーチパス 2008/03/14 15:24 とっちゃん

επιστημηさんへ

ExternalLogger のほうは、ログモジュール用です。
こっちは、複合型というのは少ないだろうし、そういう場合でも、exe と同じところに置いておいたほうが何かと使いやすいのでそのままでも問題ないと思う。

変更が必要なのは、TestModule.cpp:line 287 の部分です。
_hModule = LoadLibraryW(_fileName);

_hModule = LoadLibraryExW(_fileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
という形。
こっちは、テスト対象モジュールのロード部分なので、こっちが対処できてないとだめだと思う。

おいらは外部ログは現状使ってないので(というかまだそこまで勉強できてないしw)、LoadLibrary のまま。


シャノンさんへ
>XP以降なら、マニフェストつければよくね?
やってみないとわからんけど、DLL の依存だからだめかもしれない。
そもそもの検索パスの問題だし。

Isolated は、あくまでもexeから見たときの話だから、かなり条件違う。
ちなみに、COM は、CoLoadLibrary が LoadLibraryEx 使ってるので問題なし。
#Localかどうかは関係ない

# re: [Win32] LoadLibrary のサーチパス 2008/03/14 15:41 επιστημη

あー...コッチはloggerか。 orz
ファイル名で気づけよ > ぢぶん

# re: [Win32] LoadLibrary のサーチパス 2008/03/14 15:54 とっちゃん

だいじょぶ。おいらも間違ってそっち修正して状況変わらずで、悩んだものw<おい!

タイトル  
名前  
URL
コメント