Version 15.27
DLL のまとめ
「さて、今回で DLL とか設定関係は終わりです」
『な、なんかやたらこまかかったよーな……』
「だね。この15章では、 Exe や DLL の中身について触れたからね」
『ひとつひとつはある程度分かるけど、全体となると結構複雑だったかも』
「じゃあ、もう一度ひとつひとつ見ていこうか」
『まず最初の Version 15.01 ( No.301 ) 〜 Version 15.02 ( No.302 ) は
コンパイルとリンクについてね』
「コンパイルはソースファイル単位、っていうことがまず重要」
『ビルドすると、だーっと全ファイルしちゃうもんね』
「このソースファイル単位っていうのは、インクルードファイルの話にも、
プロジェクトの設定の話にも継ながるから」
『あ! そか、プリコンパイル済みヘッダーのって、ソースファイル単位
だったもんね』
「そういうこと。コンパイル系の細かい設定も、ソースファイル単位ででき
るから」
『で、それをリンクで全部継なげるわけね』
「この辺が分かりにくい理由のひとつは、 VC++ で簡単にビルドできるから
かも。一度、ひとつひとつコンパイルしてリンクする、っていうのをすると
色々わかるんだけど」
『めんどくさー』
「まぁそうなんだけどね……」
『次の Version 15.03 ( No.303 ) 〜 Version 15.06 ( No.306 ) は、
インクルードファイルの話』
「重要なのは、インクルードファイルは本当にただファイルを読み込んでい
るだけなんだ、ってこと」
『なんてゆーか、いつもは関数宣言が入ってるヘッダーファイルを読み込む
だけってイメージだけど、そうじゃないってことよね』
「そういうこと。ソースファイルを読み込むことだってできるしね。だから
順番とかが重要ってこと」
『つか、もう少しこの辺便利にならないの?』
「他の言語では便利なんだけどね。それに、 C++ を下手に拡張されても、
って思うし」
『あー、改悪されちゃうとヤダね』
「本当に……本当にね……」
『……えっと、インクルードファイルの読み込みの順番とか関係とかって、
教わってもなんだか分からないんだけど』
「これは自分で実際に組んでみないとわからないかもね。次回からは、その
辺も見ていくから」
『次回? 次の章?』
「そう、少し総合的な部分を見ていくから、その時に複数のソースファイル
を使って、相互にヘッダーファイルを読み込むようなプログラムを組んでみ
るから」
『それって MFC でもできるでしょ』
「 MFC だと、最初に AppWizard が作ってくれたのを変えるのは難しいから
ね。一から作ってみた方が分かりやすいよ」
『難しそうだけど……』
「まぁ、もう火美ちゃんだってかなりプログラム組んできてるんだから」
『そりゃそうだけど』
「次のVersion 15.07 ( No.307 ) 〜 Version 15.08 ( No.308 ) は
スタティックリンクライブラリの解説でした」
『スタティックリンクライブラリ、って結局ほとんど使わない?』
「使わないことが多いね。スタティックリンクライブラリとして提供するく
らいならソースファイルごと提供することの方が多いし、 DLL なら、バグ
があっても DLL だけ入れ替えればいいからね」
『スタティックリンクライブラリを作る価値はない、と』
「でもオブジェクトファイルの理解が深まるし、 DLL と同じように
ライブラリファイルを使うから、勉強にはいいと思うよ」
『一度は作っとけ?』
「そういうこと」
『次の Version 15.09 ( No.309 ) 〜 Version 15.19 ( No.319 ) は、今回
の本命、 DLL ね』
「 DLL は、まず仕組みが難しいかな」
『 DLL はいいんだけど、ライブラリファイルが必要ってこととか、リンク
はライブラリファイルだけ必要とか、そういうとこが難しいかも』
「そういう情報って MSDN にははっきりと書かれてないしね」
『あとはやっぱりエクスポートかな、あれ難しい』
「そうそう、ひとつエクスポートについて触れ忘れたことがあったから、こ
こで解説します」
『え? ……結構重要なこと?』
「それなりに。まず、エクスポートする時の装飾名、って憶えてるよね」
『 Version 15.15 ( No.315 ) で出てきたね、ライブラリファイルに書かれ
た関数名、なんだよね』
「そうです。 DLL を使用するときには、ライブラリファイルの中からこの
装飾名を探して、関数が DLL にあることを確認します」
『うん、リンクするときにそうするんだよね』
「ここで重要なのは、関数名や引数を元に装飾名が作られて、その装飾名同
士で検索される、ってこと」
『どういうこと?』
「まず、 DLL でエクスポートするときに、関数名と引数を元に装飾名が作
られて、それがライブラリファイルに書かれます」
『うん』
「次に、使用する側の話。 Exe でインポートするときには、同じく関数名
と引数を元に装飾名が作られて、それを元にライブラリファイルを検索して
関数があるかどうか調べます」
『うんそうだね。何か問題あるの?』
「あるんです。装飾名の作り方、両方とも同じじゃないと」
『あ! 同じ関数名、同じ引数なのに、違う装飾名になっちゃうかもしれな
い、ってこと!?』
「そういうこと。具体的にどういう時に装飾名が異なるかというと、
ソースファイルが .c と .cpp とで異なります」
『え? 拡張子が違うだけで違うの?』
「拡張子が違うだけじゃないからね。 .c の時はC言語のソースファイルと
して、 .cpp の時はC++言語のソースファイルとしてコンパイルするから」
『言語が違う……』
「装飾名の時に説明したけど、C言語では同じ名前の関数はひとつだけだけ
ど」
『C++言語の時は、オーバーロードがある!』
「そういうこと。だから装飾名が違うんです。実際、 DLLTestHard と
BuildTest の例で、 DLLTestHard の Func.cpp を Func.c にして、
BuildTest でリビルドすると」
『あ、リンクエラー』
リンク中...
Main.obj : error LNK2001: 外部シンボル
""__declspec(dllimport) void __cdecl Output(char const *)"
(__imp_?Output@@YAXPBD@Z)" は未解決です
Debug/BuildTest.exe : fatal error LNK1120: 外部参照 1 が未解決です。
link.exe の実行エラー
「このように、たとえば DLL が .cpp で Exe が .c の場合、または
DLL が .c で Exe が .cpp の場合は、装飾名が異なるのでリンクできませ
ん」
『まさに見つからないって感じね』
「だから、基本的には .cpp の方で統一するようにしてください」
『? 質問!』
「はい火美ちゃん」
『なんで .cpp の方で統一するの?』
「だって、そうしないと C++ の機能が全然使えないから。オーバーロード
できないし、クラスも使えないでしょ」
『あ、そういえば』
「実際、関数の動的リンクを使わない限り、エクスポートはクラス単位でし
て、 DLL も Exe も .cpp にする、っていうのが一番楽かな」
『もうひとつ質問!』
「はい火美ちゃん」
『ソースファイルの拡張子が違うとリンクできないって行ったけど、 API
は .cpp でも .c でも使えるんじゃないの?』
「いい所に気付きました。実は Exe の側が .cpp でも .c でもちゃんと
リンクできる方法があるんです」
『あ、なんだあるんだ』
「でも今回はパス」
『えー?』
「それこそ、関数だけをエクスポートする DLL 、っていう場合にしか必要
ないから、これからそういう DLL を作ることはないと思うよ」
『んー、でもどんな感じか、だけでも教えて』
「そうだね、まず重要なのは、C言語側に合わせる、ってこと」
『 DLL でのエクスポートはC言語のにする、ってこと?』
「そういうこと。そうすれば、まず Exe の方が .c ならリンクできます」
『じゃあ、 .cpp の時は?』
「その場合のために、ヘッダーファイルの宣言部を extern というもので
囲みます。たとえばこんな感じに」
// Func.h
extern "C"
{
DLLTESTHARD_API void Output( const char *p_pch );
}
『 extern って初めて見た』
「この【extern "C"】っていうのは、囲んだ範囲をC言語形式にしてしまう
んです」
『C言語形式! なるほど、そうすればC言語の装飾名になるからリンクでき
るってことね』
「実際にはもう少し色々としなきゃいけないんだけど、簡単に説明するとこ
ういうこと。ただやっぱり、関数だけの DLL を、しかも動的リンク以外の
目的で作ることはないと思うから」
『必要ないってわけねー。まぁそうかもしれないけど』
「えと、話を戻して」
『あ、最後の Version 15.20 ( No.320 ) 〜 Version 15.26 ( No.326 ) は
【プロジェクトの設定】ダイアログだね』
「各設定について、駆け足だったけど説明しました」
『なんとなく全体的に分かったけど、細かい所となるとちょっと不安かも』
「でも実際、細かい所はほとんど必要ないよ。重要な設定は、こんなところ
かな」
・【一般】-【Microsoft Foundation Class】
・【一般】-【出力ディレクトリ】
・【デバッグ】-【一般】-【デバッグ セッションの実行可能ファイル】
・【C/C++】-【コード生成】-【使用するランタイム ライブラリ】
・【C/C++】-【プリコンパイル済みヘッダー】
・【C/C++】-【プリプロセッサ】-【プリプロセッサの定義】
・【C/C++】-【プリプロセッサ】-【インクルードファイルのパス】
・【リンク】-【一般】-【出力ファイル名】
・【リンク】-【インプット】-【オブジェクト/ライブラリ モジュール】
・【リンク】-【インプット】-【追加ライブラリのパス】
・【ビルド後の処理】
『あ、そんな多くないね。どれも DLL の時に使ったのだし』
「それに、最適化とかはむしろあまりいじらない方がいいしね。劇的に実行
速度が改善するわけじゃないから、そんなところをいじるくらいなら、
プログラムを修正した方がいいよ」
『確かにそうです……』
「さて、今回まではものすごく中の方について見ていきました」
『中?』
「ハードウェア寄りというか、すごく生っぽい話」
『なんとなく分かるような……』
「次回からは、外、プログラムについて見ていきます」
『プログラム?』
「そう。主にクラスを中心に、メンバ変数とメンバ関数、オーバーライドと
いったところを説明していきます」
『あー、そういえばその辺ってしっかりは教わってないかも。 MFC 使って
いた時になんとなく分かってた、って感じだし』
「その MFC を自分で作れるくらいには教えます」
『いや無理だから』
/*
Preview Next Story!
*/
「というわけで次回からはクラスの説明に入ります」
『 DLL とかよりは簡単そうかな』
「また違った難しさがあるからどうかな」
『げ、そういうもんなんだ』
「憶えること細かくて大変かも」
『というわけで次回』
< Version 16.01 クラスを作ってみよう! >
「につづく!」
『ところであと何章?』
「17章で完結……かな」
『お、もうそこまで』