【特定の条件の時だけVB6のDLL呼び出しが失敗する現象】

以下構成のプログラムで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まで正常に呼び出せることを確認している。

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:
  • 終了:2010/03/11 01:05:00
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:mj99 No.1

回答回数138ベストアンサー獲得回数38

ポイント250pt

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内関数を使用する直前で例外が上がる)

id:harunoharuno

回答ありがとうございました。

とりあえず

VB側にDLL呼び出し失敗のときのための

goto Errorを作成していましたが

原因が分からず、非常に気になっている問題でした。

ご指摘の通り、

以下のDLLはlibを指定してリンクしています。

・VC6製のDLL2・・・・DLL1を簡単に使うためのラッパーDLL(既存リソース)

・VC6製のDLL3・・・・DLL2をVBから呼び出すためのラッパーDLL

遅延ロードを行えば、DLL内で異常処理も記述できるのですね。

すっきりしました。

2010/03/10 10:25:38

その他の回答1件)

id:mj99 No.1

回答回数138ベストアンサー獲得回数38ここでベストアンサー

ポイント250pt

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内関数を使用する直前で例外が上がる)

id:harunoharuno

回答ありがとうございました。

とりあえず

VB側にDLL呼び出し失敗のときのための

goto Errorを作成していましたが

原因が分からず、非常に気になっている問題でした。

ご指摘の通り、

以下のDLLはlibを指定してリンクしています。

・VC6製のDLL2・・・・DLL1を簡単に使うためのラッパーDLL(既存リソース)

・VC6製のDLL3・・・・DLL2をVBから呼び出すためのラッパーDLL

遅延ロードを行えば、DLL内で異常処理も記述できるのですね。

すっきりしました。

2010/03/10 10:25:38
id:ardarim No.2

回答回数897ベストアンサー獲得回数145

ポイント250pt

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も呼ばれません。

id:harunoharuno

解りやすい解説ありがとうございます。

原因が分からず、非常に気になっていたのですが、

静的リンクの場合は、

『プログラムの開始時にすべてのDLLをロードし、アドレス解決』を行っていたのですね。

アドレス解決の順序などもしらなかったためエラーメッセージに首をかしげていたのですが

お蔭様で少し納得することができました。

>>DLL2からDLL1を静的リンクせず実行時リンクを行う必要があります。

具体的なご指摘ありがとうございます。

DLL2は既存リソースなので手を加えない予定ですが、

対応方法が分かっただけでも嬉しいです。

2010/03/10 10:40:05
  • id:harunoharuno
    [追記]
    VC6製のDLL3からDLL2の呼び出しメソッドを削除したら
    警告が出なくなりました。

    DLL3を呼び出したときに、包括されて呼び出されるDLL1の存在をチェックし、
    ない場合は警告を表示するようになっているのでしょうか?
    参考資料が欲しい・・・
  • id:infograve
    通常、LoadLibrary()で下位のDLLをLoadすると、エントリポイントDllMain()を呼び出すと思います。
    http://msdn.microsoft.com/ja-jp/library/cc429241.aspx
    http://msdn.microsoft.com/ja-jp/library/cc429094.aspx

    おそらく、「DLL3→DLL2→DLL1が無い」ではないかと。
  • id:harunoharuno
    回答ありがとうございます。DLL3からVB6製のプログラムを呼び出し
    デバッグ実行したのですが、DllMain内のブレークポイントで
    処理はとまりませんでした。

    ということでDllMainすら呼び出されていないということになりそうです。
  • id:cx20
    「Dependency Walker(depends.exe)」を使ってみてください。
    「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/

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません