前々回はDLLに関数を作製しました。今回はクラスを作製します。
で、クラスを作るだけだとかなり簡単なので、デバッグバージョンとリリースバージョンについての解説もしたいと思います。 |
プロジェクトの作製
前々回と同じく、「MFC AppWizard (DLL)」を作製し、ウィザードダイアログで「MFC の拡張 DLL(MFC の共有 DLL 使用)」を選択して、「終了」ボタンを押せばOK。ってゆーか、今回は前々回のをそのまま使いますので、まだ見てない方はそちらからご覧ください。 |
クラスの作製
最初に、エクスポートするクラスを作製します。 ワークスペースの「Class View」を表示し、一番上の「* クラス」(*はプロジェクト名)を右クリックし、「クラスの新規作製」を選んでください。 今回はテストなので、基本的にはどんなクラスでもOKです(実際のクラスの作製方法についてはクラスを作ろう!を参考にしてください)。とりあえず「クラスの種類」を「Generic クラス」にして、クラス名を「CTestCls」とでもしてください。 作製したら、テスト用にメンバ関数を追加しておきます。「* クラス」のすぐ下の「CTestCls」の上で右クリック、「メンバ関数の追加」を選び、「関数の型」をint、「関数の宣言」をDllFunc( CString &p_rcStr, CWnd *p_pcWnd )としてください。「アクセス制御」はそのままpublicです。 そして、今作った関数に次のコードを書き込んでください。 |
int CTestCls::DllFunc(CString & p_rcStr, CWnd * p_pcWnd)
{
// 単純にメッセージボックスの表示。
return p_pcWnd->MessageBox( p_rcStr );
}
関数そのものは前々回のものと同じなので解説はいいですね。単にメッセージボックスを表示するものです。
さて、準備はできました。では今作った関数をエクスポートしてみましょう。 |
クラスのエクスポート
まずクラスのソースファイル(ここではTestCls.cpp)で次のようにDefs.hをインクルードしてください。 |
#include "stdafx.h"
#include "Defs.h" //ここを追加。
#include "TestCls.h"
順番がヒジョーに重要なことに注意してください。Defs.hに書かれてるDLL_EXPORTは、TestCls.hで使用するので、TestCls.hをインクルードする前にDefs.hをインクルードする必要があるからです。
次に、クラスの定義が書かれているファイル(ここではTestCls.h)を次のように書き換えてください。 |
// この下を書き換えてください。
class DLL_EXPORT CTestCls
{
public:
// 以下略。
と、以上のようにclassと「クラス名」の間に__declspec(dllexport)を挟み込む形にします。これで、クラスそのものがエクスポートされますので、メンバ関数をひとつずつエクスポートする必要はありません。
で、クラスのエクスポートはこれで終了。ビルドしてみてください。
あと、前回の「標準インクルードファイル」にも次のように書き加えておいてください。 |
// ヘッダーファイルを読み込みます。
#include <Defs.h>
#include <Func.h>
#include <TestCls.h>
これでDLL側の準備はひとまず終了しました。今度はExe側に回って、使ってみましょう。
|
自作クラスを使う
Exe側は、これも前回のものを流用してください。流用しているのなら、「標準インクルードファイル」はインクルードしてあるはずなので、特に何かする必要はありません。また、「検索ディレクトリ」もそのままなら、大丈夫でしょう。 あとは、先ほど作製したDLLをExeにコピーして準備はOK。コピーの自動化もしてあるのなら、これすら必要ないでしょう。
では、実際に使ってみましょう。といっても、例えばダイアログクラスの中で次のようなコードを追加するだけです。 |
// IDC_BTN_DLL_CLS のボタンを押したときのメンバ関数。
void CMy01Dlg::OnBtnDllCls()
{
CTestCls cTest; //DLLからエクスポートされたクラスです。
CString cStr = "てすと2";
cTest.DllFunc( cStr, this ); //メッセージボックスの表示。
}
このように、ごく普通に使用できるわけですね。ま、MFCなんかはそうなんですから、できて当然といったところでしょうか。
さて、再びDLLの方へと戻って、もう少し突っ込んだ内容に入ってみましょう。 |
__declspec(dllexport)を使う理由
前々回、「エクスポートの方法はふたつある」という解説をしましたが、今回は有無を言わさず__declspec(dllexport)を使ったエクスポートを紹介しました。というのも、実は定義ファイルではクラスのエクスポートが難しいという問題があるからです。
まず、基本的に「エクスポート」はクラス単位ではできません。エクスポートされるのは関数や変数としてです。クラスというのは、一種の「変数の型」のようなものなので、エクスポートの対象とはならないのです。
定義ファイルを使用してエクスポートする場合、普通の関数なら関数名と同じで良かったのですが、クラスのメンバ関数の場合にはC++の装飾名となってしまうため、「へんちくりん」な装飾名となってしまうのです。
こういった問題を、__declspec(dllexport)は簡単に解決してくれます。なんと言っても、クラスを丸ごとエクスポートしてくれるのですから。というわけで、こちらだけを紹介したというわけです。 |
エクスポートとインポート
これまでは「エクスポート」を中心に説明してきましたが、今度は「インポート」について見てみましょう。 DLL_EXPORTを用いたエクスポートの場合、フラグによってふたつの文字に替えられることは説明しましたね。
DLLの場合、StdAfx.hの中でDLL_EXPORT_DOのフラグを立てているため、クラスの宣言はclass __declspec(dllexport) CTestClsとなります。これによって、クラスはエクスポートされます。
通常、DLLの関数やクラスは、そのヘッダーファイルを読み込んで、実体が見つからない場合にライブラリファイル等を検索するという順序を取ります。
実は、通常のAPIも、このようにインポートを行っています。Winbase.h等を見ると、APIのプロトタイプ宣言の頭にWINBASEAPIという単語が付いています。そう、これが__declspec(dllimport)に替わるのです。 |
デバッグ版とリリース版
さて話は変わって、MFC42.dllとMFC42d.dllのように、デバッグ版とリリース版のふたつのDLLを作製してみます。そのために、このふたつについて確認しておきましょう。
VCでプロジェクトを作ると、「デバッグ版」と「リリース版」のふたつの「コンフィグ」がデフォルトで作られます。
「デバッグ版」と「リリース版」の違いは、一見しただけでは分かりませんが、実際には大きく違います。
通常、デバッグ版の関数は余分な処理を行っているため、動作が幾分遅くなります。また、デバッグ版の関数が入っているDLLは、一般のウィンドウズにはインストールされていません。こういった事情から、基本的に「デバッグ版」は配布しないことになっています。 |
ファイル名を変えるための変更
まず例によって「設定」ダイアログの「リンク」ページを開いてください。次に、ダイアログ左上の「設定の対象」を、デバッグ版のコンフィグ名(デフォルトでは「Win32 Debug」)にしてください。
次に、「プロジェクトオプション」の欄を書き換えます。
前置きが長くなりました。まず「一般」ページの出力ファイル名を、デバッグ版のものに変えてください。例えばデフォルトのファイル名がTestDLL.dllなら、これをTestDLLd.dllにしてください。 |
デバッグ版用の定義ファイル
前々回の定義ファイル(拡張子がdefのファイル)を思い出してください。LIBRARYパラメーターでDLLのファイル名を指定する必要があります。デバッグ版とリリース版ではファイル名が違うわけですから、デバッグ版とリリース版のふたつ必要ということになります。リリース版はデフォルトのものを使えばいいので、デバッグ版を新たに作製しましょう。
まずフォルダ等で定義ファイル(今回の例ではTestDLL.def)を複製して、TestDLLd.defというファイル名にしてください(前述の「プロジェクトオプション」に指定したファイル名です)。
追加したら、「プロジェクトワークスペース」の「ファイルビュー」にこのファイルがあると思うので、ダブルクリックして開き、次のように書き換えてください。 |
LIBRARY "TestDLLd" ;最後の"d"を書き加えてね
こうすることで、この定義ファイルは、エクスポートされた関数がTestDLLd.dllにあるということをライブラリファイルに書き込みます。
でも、まだあと一仕事あります。先ほど警告が出てきた人は「定義ファイルはひとつの構成でひとつしか使えません」と書かれていたことでしょう。出力するDLLはひとつのコンフィグで一種類なので、定義ファイルもひとつだけでなければなりません。そこで、コンフィグごとに定義ファイルを変更してみましょう。
ひとつのコンフィグではひとつの定義ファイルしか使用できないわけですから、例えばデバッグ版ではTestDLLd.defをコンパイルし、TestDLL.defをコンパイルしないことにすればいいわけです。 |
|
以上のようになるように、コンフィグを設定してください。
で、ここまでくれば終わりです。DLLをビルドしてください。デバッグ版はデバッグ版のDLLとライブラリファイルが、リリース版はリリース版のDLLとライブラリファイルが作製されるはずです。
デバッグ版とリリース版を分ける方法についてまとめてみましょう。
うまくいってなくても必ずコンパイルエラーが出るので、そうしたら手順を追って調べてみてください。 |
「標準インクルードファイル」の変更
以上で「DLL」側は終わったのですが、最後に使用する側について書いておきます。 デバッグ版とリリース版のDLLを作製した場合、Exe側でも変更する必要があります。それは、ライブラリファイルの検索リストです。 DLLをExeから使用するときに必要なのは、DLLではなくライブラリファイルです。で、今回ライブラリファイルは2種類あるわけですから、Exeがデバッグ版ならデバッグ版の、リリース版ならリリース版のライブラリファイルを検索リストに加える必要があります。この使い分けを行うことで、実行時のDLLも自動的に使い分けられるというわけです。
コンフィグを使用して検索リストに加えている場合には、やっぱり「プロジェクト」−「設定」ダイアログの「リンク」−「一般」ページのオブジェクト/ライブラリモジュール」で、コンフィグに合わせてライブラリファイルを変更してください。 |
// ライブラリファイルを読み込みます。
#ifdef _DEBUG
#pragma comment(lib, "TestDLLd.lib")
#else
#pragma comment(lib, "TestDLL.lib")
#endif
今回の「デバッグ版とリリース版」の部分で説明したように、デバッグ版のコンフィグでは_DEBUGというスイッチが入るように設定されています。これはExeも同じです。このスイッチが入っているかどうかによって、検索するライブラリファイルを変更するというわけです。
これで使用する側もOKです。DLLのコピーや、「検索ディレクトリ」の設定等は忘れずに。 |
DLLのまとめ
さて、今回を持ちましてひとまずDLLの話は終了します。 最後の今回は、コンフィグなど比較的解りにくい部分を解説してみました。そのためにちょっと全体的には分かりにくくなってしまったかもしれません。KCL9542がサンプルになると思うので、そちらも試してみてください。
で、次回からは「フック」という技術について見ていこうと思います。フックからシステムフック、そしてサブクラス化と、きっとこちらの方が興味深い内容になると思います。とりあえず、期待しててください。 |
(C)KAB-studio 1998 ALL RIGHTS RESERVED. |