- ベストアンサー
exeファイルはどのdllを参照すべきか、知っているのでしょうか。
dllとは、プログラムが実行時に参照するものですよね。 よく使われる機能があらかじめ作られているものですよね。 私はC言語しか知らないので、C言語でプログラミングすることで話を進めさせてください。 (とは言っても、ごく初歩的なプログラムが組めるだけの素人です。) OSはWindowsということにします。 C言語は、関数の集まりでソースが書かれています。 dllも、中身に関数の実体が記述されている、そういうイメージでしょうか。 私がもっと勉強して、複雑なプログラムを組んだりすると、 「あ、これはdllに実体がある機能だから、いちいち実装を書かなくてもいいんだな」 と考えて、ソースファイル( ~.c )の中でその関数を使うだろうと思います。 私がよくわからないのはここからです。 dllにある関数をソースで使って、無事コンパイルして、さあリンクしてexeを作りましょう、 というときに、リンカ(リンクを行うプログラム)は、 「そんな関数、実体がないぞ」 って文句を言ってこないのでしょうか。 いや、リンカは、dllに実体がある関数だということを知っているはず。 でもそれって、いちいち 「この関数はどこどこにある ○○.dll というdllに書いてあります」 というふうに、リンカに教えてあげなくてはいけないのでしょうか。 (それってまさか、ソースに書くわけじゃないですよね? ) それから、 exeファイルっていうのは「この機能はdllに行わせる機能だ」ということを知っているのでしょうか。 dllに行わせる機能だということを知っているとしても、 どのディレクトリにある なんと言う名のdllに その機能が書かれていることまで知っているのでしょうか。 (でもそこまで知っていたら逆に、ディレクトリ構成の違うマシンでは実行できなくなってしまうし。) そういうような原理的なことを教えてください。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
どちらも、ダイナミックリンクというのだと思うのですが、そのリンク方法の中に、スタティックな方法(hとLIBを用いる方法)とダイナミックな方法(LoadLibrary()とGetProcAddress()を使う方法)とがあるという・・・まさしく訳が分かんないんですけど、とにかく、DLLのリンク方法には大きく分けて2種類有るんです。(^^; >「○○.dllが見つからない」というエラーですね。 その通りです。 >「コード内にDLLファイル名を記述する必要があるわけです」というのは、.LIBを作るときの話ですよね? >要するにソース中に"C:\\exsample\\exsample.DLL"みたいに直接書いてあったらそこを探しに行くということでしょう(たぶん)。 いわゆるダイナミックなリンク方法のとき、3番の回答者のおっしゃるとおり、LoadLibraryの引数にはDLLファイル名を指定する必要があると言うことです。 このとき、フルパスでもパス無しでも指定できますが、パス無しの時は2番の参考URLに書いて有るとおりの検索順序でDLLを探しに行きます。 LIBをつくるにはlibというVC++のツールを使うか、Borlandのimplibというツールで作れるようです。 ダイナミックリンクのメカニズムの説明が不足していましたね。3番の方ありがとうございます。質問者には混乱させてすみませんでした(^^;
その他の回答 (4)
- BLACKTALON
- ベストアンサー率37% (110/293)
>.LIBファイル名とそのパスはどのように教えるのでしょうか。 VC++ならば、、、ええとプロジェクトメニューの設定でプロジェクトの設定 ダイアログをひらき、リンクタブをクリックして、 オブジェクトライブラリモジュールセクションに記述する、だと思います。 これだとデバッグ時とリリースビルド時個別に設定できます。 で、Win32Applicationを作成するプロジェクトを生成させるとここには、kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.libと言う記述がすでにされているため、「ほぼ自動でできる」んだと思います。LIBファイルのパスは別な場所(ツールメニューのオプションをクリックし出てくるダイアログのディレクトリタブ)で設定があり、インストール時に勝手にやってくれているはずです。足りなければ追加できるかと。追加すべきLIBファイルはやはり自分で調べなくてはなりません。 なお、普通にプロジェクトに追加しても、なぜかちゃんとリンクできたりします(^^; プロジェクト個別にライブラリを変えることができることを考えると、ライブラリの追加はプロジェクトファイルに記述されているのでしょう(パスは共通設定のようです)。 統合環境でない場合はMakeFile等に記述するんでしょうね、きっと。 ヘッダファイルに関しては、たいていwindows.hをインクルードすれば事が足りるんで(こんな事言うと専門家の人たちに文句言われそうですが)。 >.LIBファイルというのはリンク時に参照されるものなので、実行時にはなくてもいいんですよね? でないと、開発環境のないパソコンでは実行できなくなりますよね?(笑) ちなみにLIBが2つしかないと言うことはまず無いと思います。きっとどこかにあるんだと思いますよ。 ダイナミックによるDLLロードが必要になる例を参考URLにあげておきます。 私も素人プログラマなんで(^^;経験が参考になれば幸いです。
お礼
みなさんありがとうございました。 締め切らせていただきます。特にBLACKTALONさん、ありがとうございました。 >>.LIBファイルというのはリンク時に参照されるものなので、実行時にはなくてもいいんですよね? >でないと、開発環境のないパソコンでは実行できなくなりますよね?(笑) >ちなみにLIBが2つしかないと言うことはまず無いと思います。きっとどこかにあるんだと思いますよ。 質問から離れるので流しますが、、、私がこのサイトを見るPC(開発環境はない。)では.LIBはC:\Program Files\ArchiverDLL\UNLHA32にあるUnlha32.libとUnlhavc.libの二つです。 (OSは98SE) あ、そうそう、私は質問で >「この関数はどこどこにある ○○.dll というdllに書いてあります」 >というふうに、リンカに教えてあげなくてはいけないのでしょうか。 >(それってまさか、ソースに書くわけじゃないですよね? ) などと書きました。 「ソースに書くわけではないですよね」というのは、今振り返ってみると、 私が「スタティックな方法」を念頭に置いて質問を書いていたからです。
補足
ありがとうございます。じゅうぶんです。わかりました。 本当におせわになりました。 (「補足」に書きましたが、もうほとんどお礼のつもりです。)
- fatal_error
- ベストアンサー率39% (23/58)
横槍気味の解説ですが... ダイナミックリンクがよくわからなくなるのは 「関数のポインタ」のせいではないでしょうか。 変数に実体をポインタがあって、 (ポインタに格納されている)アドレスからもアクセスできるのだから、 関数にだって実体とポインタがあって、 そのアドレスから実行できる、ということです。 配列名が配列の先頭アドレスになるように、 関数名が関数の先頭アドレスになるのです。 その関数のアドレスを返すのがGetProcAddress関数で、 関数名の文字列を渡すとその関数のアドレスが返ってきます。 このとき「どのDLLを読みに行くか」を指定するのがLoadLibrary関数で、 この関数の戻り値を指定してDLLを選択します。 >パスをハードコーディングしていれば 要するにソース中に"C:\\exsample\\exsample.DLL"みたいに 直接書いてあったらそこを探しに行くということでしょう(たぶん)。 以上、同じくDLLの扱いに苦しんだ者の独り言でした。
補足
ダイナミックな方法の話ですね。 関数のポインタですね。 intへのポインタ(int*)や 構造体へのポインタがあるように、関数へのポインタがあるわけですね。 intへのポインタapがint型の変数aをさしていれば、*apは aのことですね。 同じように、関数へのポインタがあってそれが関数をさしていれば、関数を直接書くような感じで使えるわけですね。 問題は、それでは、関数のアドレスをどうやって取得するか(言葉を替えると、関数へのポインタが関数をさすようにするにはどうすればよいか)、というところにあるわけですね。 >その関数のアドレスを返すのがGetProcAddress関数で、 関数名の文字列を渡すとその関数のアドレスが返ってきます。 あ、なるほど、わかりました。 ソース中に書いてあるのは、関数名という「文字列」なんですね。 >>パスをハードコーディングしていれば >要するにソース中に"C:\\exsample\\exsample.DLL"みたいに >直接書いてあったらそこを探しに行くということでしょう(たぶん)。 以上のことと回答#2を総合すると、ダイナミックな方法の場合には、 「関数名を文字列としてソースコード中に記述し、さらに、その関数があるdllの名前もソースコード中に記述しなければならない。 そのときに、ソースコード中にパスを指定する場合と指定しない場合がある。 ソースコード中にパスを指定した場合は、そのパスにそのdllがなければ、エラーになる。 パスを指定していない場合は、LoadLibrary関数がdllを探しに行く。 (探しに行くディレクトリの順番は、回答#2の参照URLを参照のこと。)」 という具合になっているわけですね。
- BLACKTALON
- ベストアンサー率37% (110/293)
DLLの場合、俗にスタティックリンクという方法とダイナミックリンクというリンクの方法と2つあります。 (本当に俗なので正しい呼称は知りません) まず、スタティックリンクのときは通常通り、.hファイルと.LIBファイルが存在します。 で、普通にリンクできます。 ただし、ライブラリの実体はリンクされず、プログラムの実行時にDLLファイルからロードされる仕組みになっているようです。 このとき、ある順序でDLLを探して無い場合は実行できません。 エラーが出るはずです。 つまり、LIBファイルはどのDLLにどの関数があるかの記述があるのだろうと 思われます。 ダイナミックリンクの時はLoadLibrary()とGetProcAddress()Win32APIを使って関数のポインタを取得することで使用が可能になります。 この場合、.hも.LIBも要らないはずです。この場合はどのDLLにどの関数が入っているかを当然プログラマは熟知し、コード内に記述する必要があります。 DLLの場所に関してはLoadLibrary()の検索順序に当てはまるようにする必要がありますしパスをハードコーディングしていればそのディレクトリにDLLが無いと見つかりません。 なお、最近はDLLのバージョンの違いによる障害・トラブルの解決などいろんな技術によって若干この話が当てはまらないことがあるかも知れないです。 LoadLibraryの検索順序に関しては参考URLの解説をご覧ください。 1番の方はSystemディレクトリにDLLを置くと動作するのはPATHのためだと解説されていますが、Systemディレクトリに関してはPATHよりもLoadLibrary()の仕様であると解釈した方が良さそうです。 ただしPATHが通っているとDLLの関数を実行できることは間違いないです。 どちらにせよ、ライブラリをリンクしたり、ヘッダファイルをインクルードしたり、コード内にDLLファイル名を記述する必要があるわけですから、プログラマはその関数について有る程度知っている必要はありますよね。 参考URLのLoadLibrary()のヘルプにもLoadLibrary()自体がどのDLLに記述されていてどのLIBをリンクし、どのヘッダをインクルードすればよいかが最後に書いてあります(ただし、VC++はほとんど自動でやってくれますけどね)。
補足
>DLLの場合、俗にスタティックリンクという方法とダイナミックリンクというリンクの方法と2つあります。 すみません。「DLL=ダイナミックリンク。」だと思っていました。 (DLLは「ダイナミックリンクライブラリ」ですよね。) >スタティックリンクのときは通常通り、.hファイルと.LIBファイルが存在します。 で、普通にリンクできます。 >ただし、ライブラリの実体はリンクされず、プログラムの実行時にDLLファイルからロードされる仕組みになっているようです。 ↑これを「ダイナミックリンク」というのだと思っていました。 >このとき、ある順序でDLLを探して無い場合は実行できません。 >エラーが出るはずです。 「○○.dllが見つからない」というエラーですね。 >つまり、LIBファイルはどのDLLにどの関数があるかの記述があるのだろうと 思われます。 あ、そうか。リンカは、リンクを行うときに、.LIBファイルを見て、 「この関数の実体は○○.dllというdllにあるんだな」 ということを知るわけですね。 リンカは、exeの中に「この関数の実体は○○.dllというdllにある」ということを吹き込むわけですね。 だから、exeは、そのことを知っているわけですね。 >ダイナミックリンクの時は... すみません。私の頭がついていけません。 ゆっくり考えさせてください。 >どちらにせよ、ライブラリをリンクしたり、ヘッダファイルをインクルードしたり、コード内にDLLファイル名を記述する必要があるわけですから、 (ダイナミックリンクはちょっと理解できていないんですが...) スタティックリンクのときは .LIBにdll名が書かれているわけだから、 「コード内にDLLファイル名を記述する必要があるわけです」というのは、.LIBを作るときの話ですよね?
- honiyon
- ベストアンサー率37% (331/872)
こんにちは、honiyonです。 ソースコードは、自分自身、というイメージです。 作成者が、「ソースコード」という形で処理手順等をコンピュータに教えてあげます。 逆に言えば作成者が教えたこと以外は何知りません。 何がいいたいかというと、宣言した関数が、ソースコードの中にあるか、外部(DLL)の中にあるか、というのも作成者が教えてあげるという事です。 暇があれば、LoadLibraryという WinAPI32関数について調べてみてください。その片鱗を見ることが出来ます。 ディレクトリ構成の違うマシンでも何故実行できるか、という点については、PATHという環境変数がカギを握っています。 ディレクトリを指定せずファイルが参照され時、それがカレントに無い場合は自動的に環境変数にかかれているディレクトリの中身を探します。 WinNT系は、DLLを C:\WINNT\SYSTEM32、Win95系は C:\WINDOWS\SYSTEM32に置きますが、どちらのケースでも同一プログラムが動く理由はここにあります。 そのディレクトリを PATHという環境変数に書いているためです。 頭がまわってないのでうまく説明出来ているかちょっと心配ですが...(^^; 参考になれば幸いです(..
補足
ありがとうございます。 >何がいいたいかというと、宣言した関数が、ソースコードの中にあるか、外部(DLL)の中にあるか、というのも作成者が教えてあげるという事です。 ソースコードの中にあるか外部(dll)にあるか、は ソースコードに記述するんでしょうか。 (C言語のことをご存知なら、「dllにある関数である」ということはどのように書くのか、お教えください。 例えばexternっていうのは、コンパイラに「ソースコードにはないけど、他の目的モジュールにありますよ」と教えるもので、dllに書かれているということを教えるためのものではないですよね。) 別な方法でリンカに教えるのでしょうか。 >暇があれば、LoadLibraryという WinAPI32関数について調べてみてください。 すみません。ひまなときにさせてください。(^^; >ディレクトリを指定せずファイルが参照され時、それがカレントに無い場合は自動的に環境変数にかかれているディレクトリの中身を探します。 >そのディレクトリを PATHという環境変数に書いているためです。 これは、つまり、こういうことですね。 exeは、関数がなんと言う名のdllにあるのか知っているが、そのdllがどのディレクトリにあるのかは知らない。だから、PATHに書かれているディレクトリを探しに行くんだ。 でも、exeは、 関数が「dllにある関数だ」ということは知っていても、「なんと言う名のdllにある関数だ」ということは、なぜ知っているのでしょうか。
補足
わかりました。ダイナミックリンクには、スタティックな方法とダイナミックな方法の二つがあるんですね。 すっきりとしました。 >いわゆるダイナミックなリンク方法のとき、3番の回答者のおっしゃるとおり、LoadLibraryの引数にはDLLファイル名を指定する必要があると言うことです。 わかりました。ダイナミックな方法のときには、DLLファイル名はソースコード中に書かなければいけない、ということですね。 なんだか、難しそうな、ダイナミックな方法のほうが良くわかってきました。 スタティックな方法のほうですが、突っ込んでいいですか。(^^; .LIBファイル中にDLLファイル名が記述されていてそれがリンク時に参照されるから、実行ファイルは、実行時になんという名のDLLを見に行くか、判断できる。 しかし、リンク時になんという名の.LIBファイルを参照すればよいのか、そしてそれはどこにあるのかは、リンクをするプログラム(リンカ)が知っていなければいけないですよね。 .LIBファイル名とそのパスはどのように教えるのでしょうか。 (私が今このサイトを見ているパソコンでは、.LIBファイルなんて2つしかないですが、開発環境のあるマシンではいくつも.LIBファイルがあるんですよね。) (回答#2より) >参考URLのLoadLibrary()のヘルプにもLoadLibrary()自体がどのDLLに記述されていてどのLIBをリンクし、どのヘッダをインクルードすればよいかが最後に書いてあります(ただし、VC++はほとんど自動でやってくれますけどね)。 VC++が自動的にやるということですが、リンク時マシンじゅうの.LIBファイルを片っ端から見に行くのでしょうか。(パスくらいは指定するんですよね? それはソースコード中に書くのではないですよね。) (すみません。しつこい奴だなどと気を悪くなさいませんように。) あと当然のことかもしれませんが、確認させてください。.LIBファイルというのはリンク時に参照されるものなので、実行時にはなくてもいいんですよね?