Delphi(VCL) で定義されている関数やクラス、例えば ShowMessage や TStringList などはProfessional 版以上であれば、その定義を見ることができます。
例えば、ShowMessage を利用する場合、次のようにコードを書くことで関数を呼び出す事が出来ます。
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage('Hello'); // ShowMessage 関数の呼び出し end; end.
新しくユニットを作成し、そこで ShowMessage 関数を呼び出すことにして見ましょう。メニュー欄から [ ファイル | 新規作成 | ユニット ] を選択してください。新規作成されたユニット内で適当な関数を作成し、そこで ShowMessage の呼び出しを行ってみます。
unit Unit2; interface implementation procedure MyProc; // 自作関数 begin ShowMessage('call ShowMessage'); // ShowMessage 関数の呼び出し end; end.
コンパイルしてみると分かりますが、「未定義の識別子 "ShowMessage"」とエラーが発生します。
新規に作成した Unit2 ユニットでは、ShowMessage は"未定義の識別子"となり、エラーとなります。これは、Delphi が ShowMessage 関数を見つけることが出来ない為に起こるエラーです。
別ユニットで定義されている関数(やクラスなど)を参照したい場合には、明示的に uses 節 に、その利用したい関数(やクラス)が定義されているユニットを指定する必要があります。
uses 節とは、ユニット内から他のユニットを参照する際に利用するものです。今回のケースでは、Unit2 ユニットから ShowMessage 関数を使用したいので、Unit2 の uses 節に (ShowMessage 関数が定義されている) Dialogs ユニットを指定することになります。
例えば、ShowMessage 関数がどのユニットで定義されているかを知りたい場合、Professional 版であれば、IDE のエディタで ShowMessage 関数の上にマウスカーソルを置き、Ctrl+左クリックで関数が定義されているユニットにジャンプできますが、Personal 版ではそれが出来ません。しかしヘルプを見れば、その関数(やクラス)がどのユニットで定義されているかが分かります。他の関数についても同様です。
では、実際に ShowMessage 関数を使用できるように uses 節に Dialogs ユニットを指定します。
unit Unit2; interface uses Dialogs; // Dialogs ユニットを指定 implementation procedure MyProc; // 自作関数 begin ShowMessage('call ShowMessage'); // ShowMessage 関数の呼び出し end; end.
今度は、無事コンパイルする事が出来ます。
ここで、少し振り返って Unit1 の uses 節を見てみる事にしましょう。
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; ........
Unit1 では最初から Dialogs ユニットを参照するようになっています。ですから、ShowMessage 関数を利用することができたわけです。
なお、System ユニットについては全てのアプリケーションによって自動的に使用され、uses 節で明示的に指定することは出来ません。このユニットでは、主にファイル入出力、文字列処理などのルーチンが実装されています。
それでは次に、この自作関数 MyProc を Button1Click イベントハンドラ内で呼び出す事にしましょう。当然、Unit1 ユニット内で Unit2 ユニットで定義された関数を呼び出すことになるわけですから、Unit1 の uses 節に Unit2 を加えれば OK ということになります。
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Unit2; // 追加 type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin MyProc; // MyProc 関数の呼び出し end; end.
実は、このコードはコンパイルできません。「未定義の識別子 "MyProc"」とコンパイルエラーが表示されます。uses 節には、ちゃんと Unit2 を指定してあります。今度は何がいけないのでしょうか。
ユニット内では、大きく分けて interface 部 と implementation 部 の 2 つの領域があります。interface 部、implementation 部について、それぞれがどの領域のことを指しているかは、次のコードを参考にして下さい。
unit Unit1; interface // ここから interface 部の始まり uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; // ここまで interface 部 implementation // ここから implementation 部の始まり {$R *.dfm} // ここまで implementation 部 end. ※initialization, finalization がある場合には、implemention 部は、 その前(initialization, 又は finalization)までです。 詳しい説明は、ヘルプにあります。
あるユニット内で他のユニットを参照する場合、uses 節に参照するユニットを指定するわけですが、uses 節に指定したユニットの内部全部が丸見えになるわけではありません。interface 部のみ参照可能となります。
もう一度 Unit2 ユニットを見てみましょう。
unit Unit2; interface uses Dialogs; // Dialogs を参照 implementation procedure MyProc; // 自作関数 begin ShowMessage('call ShowMessage'); // ShowMessage 関数の呼び出し end; end.
Unit1 の uses 節で Unit2 を指定した場合、Unit1 から参照できるのは、Unit2 のinterface 部のみです。ですから implementation 部で定義されている MyProc 関数を参照することは出来ません。結果、「未定義の識別子 "MyProc"」とエラーが出たわけです。
では、interface 部に MyProc 関数の定義を書けばいいかといえば、それは出来ません。
unit Unit2; interface uses Dialogs; ////// エラー!! //////////// procedure MyProc; / begin ShowMessage('call ShowMessage'); // ShowMessage 関数の呼び出し end; implementation end.
コンパイルしてみると分かりますが、interface 部に実行文を書くことは出来ません。
では、どうするかといいますと、interface 部に関数のヘッダーのみを置きます(注: interface 部に関数のヘッダーを置いた場合には、必ずそのユニット内で、その関数の定義宣言を記述する必要があります)。
つまり、以下のようにします。
unit Unit2; interface uses Dialogs; procedure MyProc; // ヘッダーのみ interface 部に置く implementation procedure MyProc; begin ShowMessage('call ShowMessage'); end; end.
interface 部で宣言されたルーチン(この場合は MyProc) は、同じユニット内のほかの場所や、他のユニット、そのルーチンが宣言されたユニットを使用するプログラムから呼び出すことが可能となります。
Unit1 を再び見てみます。
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Unit2; // 追加 type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin MyProc; // OK, 呼び出すことができる end; end.
今度は、OK です。
なお、uses 節については、 ユニットの循環参照 という問題も考慮する必要があります。