以下構成のプログラムでDLL1が存在しない時に、DLL2側の異常処理が予定通り動作するか
確認しようとしたのですが、
VB6製のプログラムを実行すると
>『実行時エラー'53'』:
>ファイルが見つかりません。
>c:\xxxxxxxxxxDLL3.dll
が表示されます。
DLL1が存在しない設定にする前まではDLL1~3まで正常に呼び出せることを確認していますので
原因が解りません。
また、DLL1をインストールすると上記エラーは発生しなくなります。
この現象について調査する方法(もしくは説明)などを教えて頂きたく宜しくお願いします。
<プログラムの構成>
・???製のDLL1・・・・API群のDLL(既存リソース)
・VC6製のDLL2・・・・DLL1を簡単に使うためのラッパーDLL(既存リソース)
・VC6製のDLL3・・・・DLL2をVBから呼び出すためのラッパーDLL
・VB6製のプログラム・・・DLLにパラメータを渡し処理結果を表示する
<処理の流れ>
VB6製プログラム→VC6製のDLL3→VC6製のDLL2→???製のDLL1
備考1:DLLが全て存在する場合はプログラムからDLL1~3まで正常に呼び出せることを確認している。
DLL3、DLL2は、依存するDLLに対し、pragma commentによるlib指定か、リンクオプションでlibを指定していると推測します。
これは「暗黙的リンク」と呼ばれます。
http://msdn.microsoft.com/ja-jp/library/253b8k2c(VS.80).aspx
動的にリンクされた参照を含むプログラムが起動されると、プログラムの実行可能ファイル内の情報に従って、必要な DLL を探します。DLL が見つからないと、システムは処理を停止し、ダイアログ ボックスを表示して、エラーを報告します。見つかった場合は、DLL モジュールがプロセスのアドレス空間に割り当てられます。
今回の場合、DLL3のロードで、DLL2も同時にロードされます。同様にDLL2のロードでDLL1がロードされます。
----
遅延ロードの指定を試してください。
昔は無かった機能ですが、pragmaの指定で「明示的リンク」を代替できます。
http://msdn.microsoft.com/ja-jp/library/151kt790(VS.80).aspx
DLL3のどこかに
#pragma comment(lib, "delayimp.lib") #pragma comment(linker, "/DELAYLOAD:dll2.dll")
と、書いておき、
DLL2には
#pragma comment(lib, "delayimp.lib") #pragma comment(linker, "/DELAYLOAD:dll1.dll")
と、書いておきます。
このpragma指定で、DLL内関数を使用する直前でDLLがロードされるようになります。
(DLL3をロードするとき、DLL2はロードされない)
(DLL2をロードするとき、DLL1はロードされない)
結果、DLL3のロードでDLL1の不在例外が上がることはありません。
(DLL1のDLL内関数を使用する直前で例外が上がる)
VC6のDLLは静的リンクをしているようですね。
静的リンクの場合、カーネルはプログラムの開始時にすべてのDLLをロードし、アドレス解決を行います。
そのため、VB6→DLL3→DLL2→DLL1のようにDLLが静的リンクで依存関係になっている場合、ロード順(アドレス解決順)は、DLL1→DLL2→DLL3→VB6の順番で行われます。(DLL1が一番最初にロードされる)
そのため、DLL1自体が存在しない場合は、DLL2がロードできず、DLL2がロードできないとDLL3がロードできず、DLL3がロードできないため、結果的に「DLL3がロードできない」というエラーになります。(「ファイルが見つからない」というのは本来あまり適切なメッセージではありません。正しくは、「リンクしているDLLのいずれかのファイルが見つからない」とでも言うべきです)
DLL1が存在しないことをDLL2で検出して異常処理を行いたいということであれば、DLL2からDLL1を静的リンクせず実行時リンクを行う必要があります。
静的リンクの場合は、カーネルがすべてのアドレス解決を行いますので、DLLが存在しない場合は検出できません(プログラム自体起動できない)
実行時リンクは、DLLのアドレス解決をカーネルに任せるのではなく、自分自身が行う必要があります。LoadLibraryでDLLを指定してロードし、GetProcAddressでDLL内の関数へのポインタを取得します。DLLが存在しない場合はLoadLibraryがエラーコードを返しますので、適切な異常処理を行うことができます。
静的リンクではなく実行時リンクを使うには、例えば以下のようなページを参考にしてください。
VC++「DLL作成/呼出方法」メモ(Hishidama's VC++Memo "DLL")
「実行時リンク LoadLibrary」等のキーワードで検索してもたくさん見つかります。
なお、DLL内のDllMainは、そのDLLが完全にロード完了(静的リンクしているすべてのDLLがロードされアドレス解決された状態)した後に初めて呼び出されますので、静的リンクしているDLLの1つでもロードできなかった場合はロードが完了しない為DllMainも呼ばれません。
VC6製のDLL3からDLL2の呼び出しメソッドを削除したら
警告が出なくなりました。
DLL3を呼び出したときに、包括されて呼び出されるDLL1の存在をチェックし、
ない場合は警告を表示するようになっているのでしょうか?
参考資料が欲しい・・・
http://msdn.microsoft.com/ja-jp/library/cc429241.aspx
http://msdn.microsoft.com/ja-jp/library/cc429094.aspx
おそらく、「DLL3→DLL2→DLL1が無い」ではないかと。
デバッグ実行したのですが、DllMain内のブレークポイントで
処理はとまりませんでした。
ということでDllMainすら呼び出されていないということになりそうです。
「Dependency Walker」は、DLL の依存関係の調査を行う為のツールです。
<参考情報>
■ Dependency Walker (depends.exe) Home Page
http://www.dependencywalker.com/
■ Windowsユーザーのためのワンポイント・レッスン 第9回 - 1週間で学ぶIT基礎の基礎:ITpro
http://itpro.nikkeibp.co.jp/members/ITPro/ITBASIC/20040122/1/