Object Pascal のライブラリとは、Windowsのダイナミックリンクライブラリ(DLL)のことです。
ライブラリ内のデータや手続き/関数はエクスポートすることによりDLLを使用するプログラムから利用することができる普通のWindows DLLです。
ライブラリの基本的な構成について説明します。
ライブラリの構成 |
|---|
Object Pascalでは、ライブラリの構成を以下のように定義しています。
基本的にはプログラムの構成と似ており、DLLのための機能が追加されています。
【ライブラリ】
| @ ライブラリヘッダ (library) |
| A uses句 |
| B ブロック |
| C ライブラリ終了記号 (.) |
ライブラリは、ライブラリヘッダの「library」で始まり、ライブラリの終了記号「.」で終わります。
その中にuses句とブロックがあります。順序はuses句が先です。uses句が不要な場合は省略します。
ライブラリファイルには、この構成を一組だけ記述します。複数記述することはできません。
ライブラリヘッダ |
|---|
ライブラリヘッダは、libraryキーワードに続けてライブラリ名の識別子を指定します。末尾は「;」で終了します。
【ライブラリヘッダ】
library 識別子;
ライブラリ名には途中に「.」を含むことができます。「Hello」、「Hello.world」、「Hello.Hanako.Yamada」も有効です。「.」で始まったり「.」で終了することはできません。また「.」が連続するのもだめです。また「.」で分離されたものも識別子のルールに従います。「Hello.2nd」では、「2nd」が数字で始まっているので識別子になりません。
ライブラリ名は、コンパイルで作成されてるdllファイルの名前にはなりません。dllファイルの名前は、ライブラリのソースコードのファイル名の拡張子をdllに変えたものになります。通常はソースファイル名とプログラム名は合わせるのがよいと思いますが、異なっていても問題はありません。WindowsはDLLをファイル名で認識してロードします。
ソースファイル名が「Hello.pas」で、プログラムヘッダが「library Project1;」の場合、コンパイルしてできる実行ファイルは「Hello.dll」になります。コンパイルオプションで名前を指定すればその限りではありませんが。
では、識別子は何のためにあるかというと、そのライブラリ内(ソースファイル内)のネームスペースになります。「Project1」で宣言された識別子「X」はそのままでも有効ですが、正式には「Project1.X」となります。他のユニットの「X」と区別するために使用することができます。しかし、プログラムの識別子はプログラムのスコープ内では他のユニットと競合することはないので(Project1.が優先となるため)、明示的に示したいという理由でもなければ特に使用する機会はないと思います。
ライブラリヘッダは省略できません。ヘッダがないコードは、ライブラリとして認識されることはなく、プログラム(program)でヘッダが省略されたものとして解釈されるためexeファイルを生成しようとします。注意してください。
uses句 |
|---|
Object Pascalでは、プログラムをモジュール化してコードを管理する仕組みとしてユニット(unit)があります。ユニットは、型、定数、変数、手続き、関数などのコードをまとめライブラリで使用宣言することによりそのユニットのコードを利用することができます。ユニットが他のユニットを使用することもできます。
この使用宣言を行うのが「uses句」です。usesキーワードの後に使用するユニット名の識別子リストを並べて「;」で終了します。ユニット名リストは一つ以上のユニット名を「,」で区切って並べます。
【uses句】
uses
識別子1, 識別子2, ... , 識別子n;
使用するユニットがない場合は「uses句」を省略します。
ブロック |
|---|
ブロックは、Object Pascalプログラムの主要構成要素となります。ライブラリも同様です。手続きや関数、そしてユニットでもこのブロックまたはブロック要素の一部を利用して成り立っています。
ブロックは、宣言部と実行文部で構成されます。
【ブロック】
| @ ラベル宣言部 (label) A 定数宣言部] (const) B リソース文字列宣言部 (resourcestring) C 型宣言部 (type) D 変数宣言部 (var) E スレッド変数宣言部(threadvar) F プロパティ宣言部 (property) G 手続き・関数宣言部 (procedure / function) H exports句 |
| I 実行文部 (begin 〜 end) / (initialization 〜 finalization 〜 end) |
@〜Hが各宣言部です。宣言部は、順序を変えたり、他の宣言や定義をはさんで複数回記述する事ができます。必要がないものは省略できます。
最後のHはDLLのために用意された「exports句」です。エクスポートしたい宣言済みのデータや手続き/関数を指定します。順序も自由ですが、エクスポートしたいものが宣言された後である必要があります。宣言部の一番最後で指定するのが無難です。
【exports句】
exports
識別子1, 識別子2, ... , 識別子n;
宣言と定義の意味についてはこちらを参照。
Iが実行分部です。実行文部は、DLLの初期化や終了処理を処理のための実行文を記述します。
ライブラリの実行文部はプログラム(program)と同じ構文の場合、ライブラリロード時に実行される初期化処理の実行文となります。
begin
{ ライブラリの初期化処理の実行文 }
end
プログラム(program)と違い、初期化処理が不要な場合、「begin」も省略することができます。「end」は省略できません。
ライブラリロード時の初期化処理だけではなくライブラリアンロード時の終了処理も必要な場合は、ユニット(unit)と同じように以下の構文で記述します。この場合、「begin」は指定不可です。
initialization
{ ライブラリの初期化処理の実行文 }
finalization
{ ライブラリの終了処理の実行文 }
end
初期化処理や終了処理が不要な場合は、不要な方の実行文を空にします。また、空にするだけではなくinitializationキーワードやfinalizationキーワードも削除することも可能です。両方削除した場合でも最後の end は削除できません。
ライブラリ終了記号 |
|---|
ライブラリの終了記号は「.」(ドット記号)です。
ライブラリの記述終了を表します。
Pascalのライブラリは「library」で始まって「.」で終わり、その間にuses句とブロックが存在するという認識です。
libraryファイルのひな型(例) - その1、その2 |
|---|
Object Pascalのlibraryファイルのひな型について以下の投稿を参照してください。
WindowsのDLLエントリポイントの呼出し理由の対応 |
|---|
| 理由コード(値) | 説明 |
|---|---|
| DLL_PROCESS_ATTACH(1) | プロセス起動による静的ロードまたはLoadLibraryによる動的ロードによる呼出し時 |
| DLL_PROCESS_DETACH(0) | プロセス終了またはFreeLibraryによるアンロードによる呼出し時 |
| DLL_THREAD_ATTACH(2) | スレッド起動による呼出し時(DLLロード済) |
| DLL_THREAD_DETACH(3) | スレッド終了による呼出し時(DLLロード済) |
DLLロード時(DLL_PROCESS_ATTACH)
実行文部の初期化処理(initialization)が該当します。
DLLアンロード時(DLL_PROCESS_DETACH)
実行文部の終了処理(finalization)が該当します。
もう一つの方法として、
定義済みの手続き型変数「Dll_Process_Detach_Hook」が参照する処理が該当します。
既定では未設定(Nil)なので何も実行されません。
TDLL_Entry_Hook型の手続きを自分で定義し、初期化処理でそのアドレスを Dll_Process_Detach_Hook に設定することで有効になります。
終了処理部の処理は Dll_Process_Detach_Hook の設定いかんにかかわらず必ず実行されます。
Dll_Process_Detach_Hook に手続きを登録した場合は、まず Dll_Process_Detach_Hook の手続きを実行し、その後終了処理部の処理が実行されます。
通常は終了処理部だけ記述すればよいと思います。
スレッド起動時(DLL_THREAD_ATTACH)
定義済みの手続き型変数「Dll_Thread_Attach_Hook」が参照する処理が該当します。
既定では未設定(Nil)なので何も実行されません。
TDLL_Entry_Hook型の手続きを自分で定義し、初期化処理でそのアドレスを Dll_Thread_Attach_Hook に設定することで有効になります。
スレッド終了時(DLL_THREAD_DETACH)
定義済みの手続き型変数「Dll_Thread_Detach_Hook」が参照する処理が該当します。
既定では未設定(Nil)なので何も実行されません。
TDLL_Entry_Hook型の手続きを自分で定義し、初期化処理でそのアドレスを Dll_Thread_Detach_Hook に設定することで有効になります。
libraryファイルのひな型(例) - その3 |
|---|
DLLエントリポイントの理由コードに対応したObject Pascalのlibraryファイルのひな型について以下の投稿を参照してください。
Free PascalのOBJFPCモードを想定したひな型の例です。
特に何もしないプログラムコードです。これにプログラムの構成ルールにしたがってプログラムコードを追加すると便利です。