Delphi で DLL を作成する
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Delphi で DLL を作成する例
{
プロジェクトソースコード
次の例は 2 つのエクスポートされる関数を持つ簡単な DLL を実現します
プロジェクトの新規作成をしてからプロジェクトソースに以下文を記入する。
}
library MinMax;
uses
Unit1 in 'UNIT1.PAS';
{
exports 節は 2 つのルーチンをエクスポートし,それぞれにオプションの
序数を与えます
}
exports
Min index 1,
Max index 2;
begin
end.
{
ユニットソースコード
}
unit Unit1;
interface
function Min(X, Y: Integer): Integer; export;
function Max(X, Y: Integer): Integer; export;
implementation
function Min(X, Y: Integer): Integer;
begin
if X < Y then Min := X else Min := Y;
end;
function Max(X, Y: Integer): Integer;
begin
if X > Y then Max := X else Max := Y;
end;
end.
DriveComboBox でのシステムエラーのハンドリング
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
DriveComboBox で,ディスクの入っていないドライブにアクセスしたときに Windows のシステムエラーが表示されないようにして,任意の処理をしたい。
A:
エラーが発生した場合の処理は, try~except で行うことができます。
ただし,ドライブの準備ができていない場合のエラーに対応する例外ハンドラはありませんので,独自に定義する必要があります。
type
EDriveError = class(Exception);
function GetDiskSize(Drive: Char): Longint;
var
OldMode: Word;
begin
OldMode := SetErrorMode(SEM_FAILCRITICALERRORS);
Result := DiskSize(Ord(UpCase(Drive)) - $40);
SetErrorMode(OldMode);
if Result = -1 then
raise EDriveError.Create('Drive not ready');
end;
procedure TForm1.DriveComboBox1Change(Sender: TObject);
var
Size: LongInt;
begin
try
Size := 0;
Size := GetDiskSize(DriveComboBox1.Drive);
Label1.Caption := Format('%d', [Size]);
except
on E:EDriveError do ShowMessage(E.Message); {ここで必要な処理を行う}
end;
end;
EOutOfResources のハンドリングの仕方
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
EOutOfResources クラスの例外はどのように対処すればよいでしょうか?
A:
EOutOfResources 例外は,try~except の例外処理ではハンドリングすることができません。 このような場合は,OnException イベントでエラー処理を行います。
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnException := AppException;
end;
procedure TForm1.AppException(Sender: TObject; E: Exception);
begin
if E is EOutOfResources then
ShowMessage('Out of Resource')
else
ShowMessage('Other Error');
end;
プロシージャの処理を中断したい
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
プロシージャの中でそのプロシージャの処理を中断したいのですが,C言語 などの "return" に当たるコマンドは Delphi では何ですか?
A:
Exit 手続きを使用してください。
固定長の場合の VB の(型付き)ファイルの Delphi への変換
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
VB の固定長の型付きバイナリファイルを Delphi で読み込むと1byte 目が欠落するのはなぜですか?
・record の形式の例 (VB形式)
type dat
a As String * 30
b (1 To 20) As String * 50
c (1 To 196) As Double
d As String * 2
End Type
A:
Delphi(Pascal)の String の形式と VB の String の形式が異なるためです。
Delphi の String 型は,初めの 1byte 目に文字列の長さが格納されているため VB の String 型を読み込んだ場合,1byte 目が欠落し,その後のデータが全て1byte ずれたような結果になります。
回避方法は,型付きファイルの record を作成する際に String 型ではなく以下のような文字型の配列にする等の方法があります。
上記 VB 形式のファイルの record の大きさが以下のサイズと仮定した場合の例
a 30byte + b 1000byte + c 1568byte + d 2byte = 2600byte
type
dat = record
a: array[1..30] of char;
b: array[1..20, 1..50] of char;
c: array[1..196] of double;
d: word;
end;
Delphi のグローバル変数(class)
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
プログラムのどのユニットからでも参照,変更できる,グローバル変数といったもないのでしょうか?
A:
あります。プログラム中の interface 部 ( interface と書いてあるところから implementation の直前まで) の var 宣言で定義した変数は,他のユニットから参照できます。
たとえば,次のように記述すれば,他のユニットから Counter が参照できます。
type
TForm1: class(TForm)
...
public
Member: Integer;
end;
var
Form1: TForm1;
Counter: Integer; { グローバル変数 Counter }
implementation
...
イベントのバブリング
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
Paradox/win のバブリング(包含階層をイベントが伝わっていく)と同じ様なしくみは Delphi でも実現されているのでしょうか?
A:
イベントのバブリングに関しては,Paradox のような包含階層はありません。
イベントは,受け取ったコンポーネントが処理しますので,コンポーネントが自分の親に送らなければ,親にはイベントは伝わりません。
Turbo Pascal for Windows で作成したソースコードの互換性
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
Turbo Pascal for Windows で作成したソースコードは Delphi で使えるのでしょうか?
例えば,標準手続+Windows API + 同梱のUnit(例えばWinCRTとかWinNLSとか) 程度で構成してある(ObjectWindowは使っていない)として,これが全部書き換えるくらいの労力が必要なのか,そのまま通るのか,またObjectWindowを使っているものは通るのか等です。
A:
基本的な上位互換性を持っていますので,標準手続き + Windows API + 同梱 Unit 程度であれば,ほとんど変更は必要ないと思います。
ObjectWindows を使っている場合も,同様です。
ただし,言語が拡張された部分,たとえば try や finally などの予約語を使っている場合,識別子を変更する必要があるとか,その程度の変更は必要になるでしょう。
Virtial と OverRide の使い分けかた
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
今までの virtual と新しい override はどのように場合に使い分けるのでしょうか?
A:
基本的には新しく定義する場合に virtual,(または dynamic),既存のメソッドをオーバーライドする(文字通り)場合に override を使います。
Delphi で共用体を作成するには
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
C 言語の共用体に相当する物を作成したいのですが,どのようにしたら良いのでしょうか。
A:
C 言語の共用体の様に同一空間を複数の型で参照する定義は Pascal でも可能です。
実際にはrecord型を定義する時に以下のように可変部といわれる部分を定義することになります。
type
foomode = 0..1;
foo = record
case mode:foomode of (* ここから *)
0 : (bar : integer);
1 : (baz : boolean;
qux : array [0..2] of char); (* ここまで可変部の定義 *)
end;
ここで mode は tag field といわれるもので,どの型で参照しているかを示すために使われる領域ですが,大抵のプログラムではあまり必要を感じないと思います。
そこで tag field の宣言を省略して,以下のように記述することもできます。
type
foo = record
case Integer of
0 : (bar : integer);
1 : (baz : boolean;
qux : array [0..2] of char);
end;
これで,C の共用体とほぼ同様の扱いが可能になります。 なおDelphiのマニュアルですと,Object pascal language guide の CHAPTER 3:TYPES の Records の節に可変部の説明があります。 正確な定義は同マニュアルやヘルプファイルに載っている構文,図の record types の部分などを参照してください。
Delphi で 255 バイトを越える文字列を取り扱う方法
該当するバージョン:Delphi 1.0
Q:
文字型変数が扱える長さですが,DELPHI (PASCAL)では 255 バイトまでのようですが,それ以上の長さの文字列を扱うにはどうすれば良いのでしょうか。
A:
Object Pascal の string 型では,文字列長は 255 バイトまでに制限されます。このため,256 バイト以上の文字列を処理するためには,ヌルで終わる(C/C++スタイルの)文字列として処理する必要があります。
ヌルで終わる文字列は,インデックス 0 で始まる任意長の文字(Char)の配列として扱えます。また,長い文字列バッファが必要な場合はStrAlloc/StrDispose を使えます。
Memo コンポーネントなどで長い文字列を必要とする場合は,GetTextBuf SetTextBuf というメソッドを使います。
Pascal スタイルの文字列(string 型)とヌルで終わる文字列については,オンラインヘルプの「ヌルで終る文字列」を参照してください。
なお,Delphi 2.0J の場合は,255 文字以上扱えます。
PChar 型の変数にデータを代入するには
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
PChar 型の変数にデータを代入したり,そのデータを表示したいのですが。
A:
以下のようにお試しください。
procedure TForm1.Button1Click(Sender: TObject);
type
Str18 = String[18];
var
P: ^Str18;
P1 : PChar;
begin
try
New(P1);
StrPcopy(P1, Caption);
Canvas.TextOut(20,20, StrPas(P1));
New(P);
P^ := 'こんにちは';
Canvas.TextOut(20,50, P^);
finally
Dispose(P);
Dispose(P1);
end;
end;
例外処理(無効な処理)が発生した場合,その処理をトラップしたい
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
例外処理(無効な処理)が発生した場合,その処理をトラップしたいのですが。
A:
以下のコードを参照してください。 フォームには Button1 と ScrollBar1 を配置しています。
procedure TForm1.Button1Click(Sender: TObject);
begin
try
ScrollBar1.Max := ScrollBar1.Min -1;
except
on E: EInvalidOperation do
messageDlg('例外を無視: ' + E.Message, mtInformation, [mbOK], 0);
end;
end;
グローバルなクラスを Create する際の書式
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
メインフォームが生成される前にグローバルなオブジェクトを生成する方法。
A:
ユニットの begin .. end. (initialization)内でコンストラクタを呼びだす事で実現できます。
type
TMyObject = class(TObject); {オブジェクトの宣言}
....
end;
var
MyObject: TMyObject;
implementation
{ Code A }
procedure ...
begin
.....
end;
{ Code B }
begin
MyObject := TMyobject.Create; {オブジェクトのインスタンスを作成}
end.
この例では,{Code B}はプロジェクトのコードが実行される前に実行されます。
動的に配列を作る方法
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
動的に配列を作る方法は?
A:
GetMem を利用してください。ver 1.0 では,GetMem で割り当てられる最大サイズは, 605,528 バイトです。
以下は,構造体の配列を動的に割り当てるサンプルプログラムです。
type
TRec = Record
Item1: Integer ;
Item2: String ;
end ;
PRec = array[1..1] of TRec ;
var
DynRec: ^PRec ;
StrSize: LongInt ;
Seed: LongInt ;
I: Integer ;
begin
ListBox1.Items.Clear ;
Seed := StrToIntDef(Edit1.Text, 1) ;
StrSize := LongInt(SizeOf(TestRec)) * Seed ;
if (MaxAvail < StrSize) or (StrSize > 65528) then begin
ListBox1.Items.Add('Memory Allocation Error') ;
exit ;
end ;
GetMem(DynRec, SizeOf(TestRec) * Seed) ;
for I := 1 to Seed do begin
DynRec^[I].Item1 := I ;
DynRec^[I].Item2 := 'Item Number - ' + IntToStr(I) ;
end ;
for I := 1 to Seed do
ListBox1.Items.Add(DynRec^[I].Item2) ;
FreeMem(DynRec, StrSize) ;
end ;
EXE からコールされた DLL で実行時エラー発生
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
Delphi で作成した EXE と DLL で,EXE から TFont オブジェクトをパラメータとして DLL をコールしたところ,DLL 側で受け取った Font を代入するところで実行時エラーが発生しました。 エラーは単にダイアログボックスが出て,エラーと表示されるだけです。 これはどういうことですか。
A:
このエラーは,パラメータをオブジェクトで渡したために発生したものと思われます。
(EXE と DLL でインスタンスが別のため)Font の詳細(Color,Name,Sizeなど)に分けてパラメータで渡してください。
グローバル変数定義でコンパイルエラー
該当するバージョン:Delphi 1.0
Q:
Record 型で配列[1..660]をグローバル変数で定義したところ,コンパイルエラー(データセグメントが大きすぎます)になってしまいました。1レコードは88バイトです。 データセグメントは最大64Kのはずですが,何故コンパイルエラーになるのですか。
A:
データセグメントには,スタックとローカルヒープを含みます。(デフォルトでスタックは16K,ローカルヒープは8K)また,その他に uses で定義されたDelphi の各ユニットで定義されたグローバル変数もデータセグメントに常駐します。
グローバル変数は,これらを考慮した上で定義する必要があります。 Windows プログラムでは一般的に大きなグローバルデータを取るべきではありません。
リソースや動的メモリをご使用になられた方が良いでしょう。
無限ループの作り方
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
Pascal で無限ループをつくるには,どうすればよいですか。
A:
次のようにします。
while True do begin
{ コード }
end;
ImageEditer で作成したリソースを利用する場合の注意
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
ImageEditor で作成した(.RES)は、ソースコード内で {$R filename.RES} のようにして利用できます。
ImageEditor で作成した、BITMAP_1, COUSOR_1, ICON_1 等のリソースの識別名は、大文字小文字の区別のない PASCAL プログラムの中でも、大文字小文字の区別をします。LoadBitmap, LoadCousor, LoadIcon 等の API で使用する際は注意して下さい。
イメージエディタでの識別子の名前は、すべて大文字で扱う必要があります。
BC で作成した DLL を,Delphi2.0 からコール
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
Borland C++ で作成した DLL を Delphi 2.0 からコールするには,どうすればよいですか?
A:
以下の C のソースコードと Delphi からの呼び出しのコードを参照してください。
extern "C" {
int pascal _export IncInteger( int i );
int pascal _export IncInteger( int i )
{
int integer;
integer = i + 1;
return integer;
}
}
Delphi のソースコード
※ C で作成された DLL や WinApi32 の関数をコールする際は,stdcall 呼び出し規約を使用してください。
function IncInteger( i: integer ): Integer;
stdcall; external 'TESTDLL.DLL' index 1;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := StrToInt( Edit1.Text );
Edit1.Text := IntToStr( IncInteger( i ) );
end;
EXE 内の関数を DLL からコールするには
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
C で作成した DLL に EXE 内の関数のポインタを渡すことによって,DLL 内でその関数をコールできます。
以下の例を参照してください。
// .CPP
#include <windows.h>
extern "C" {
typedef void (CALLBACK myProc)( int * );
int pascal _export IncInteger( int i );
int pascal _export callProc( int *pi, myProc p );
int pascal _export IncInteger( int i )
{
int integer;
integer = i + 1;
return integer;
}
int pascal _export callProc( int *pi, myProc p )
{
(*p)( pi );
return 0;
}
}
// Pascal
type
MyProc = procedure( var i: Integer ) stdcall;
function IncInteger( i: integer ): Integer;
stdcall; external 'TESTDLL.DLL' index 1;
function callProc( var i: integer; proc: MyProc ): Integer;
stdcall; external 'TESTDLL.DLL' index 2;
procedure MyIncInteger( var i: Integer ) stdcall;
begin
Inc( i );
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := StrToInt( Edit1.Text );
(*
Edit1.Text := IntToStr( IncInteger( i ) );
*)
callProc( i, MyIncInteger );
Edit1.Text := IntToStr( i );
end;
exports
MyIncInteger;
end.
Edit の特定の文字のチェックを行うには
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
Edit コンポーネントに特定の文字が入力された場合,あるイベントを発生させたいのですが,どのようにするとよいのでしょうか?
A:
下記のコードを参考にして下さい。
Edit コンポーネントに特定の文字が入力されると Label コンポーネントに一定の文字列が表示されます。
procedure TForm1.Button1Click(Sender: TObject);
type
Tabcde = set of char;
var
a2e: Tabcde;
begin
a2e := ['a', 'b', 'c','d','e'];
if edit1.text[1] in a2e then
label1.caption := 'OK'
else
label1.caption := 'NG';
end
型なし Pointer を引数をとして受け渡す例
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
型なし Pointer を引数をとして受け渡すには,どうしますか?
A:
以下のコードを参照してください。 Pointer で受け取って,Integer 型へのポインタにキャストしています。
type
IntP = ^Integer;
IntArray = Array [1..5] of Integer;
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
d : IntArray;
implementation
{$R *.DFM}
{function MyIntPointerArray(x : IntP ) : Integer; 通常はこれ }
function MyIntPointerArray(P : Pointer ) : Integer; { ポインタ渡し }
var
x : IntP;
begin
x:= IntP(P); { キャストする }
Form1.Edit1.Text := IntToStr( x^);
inc (x);
Form1.Edit2.Text := IntToStr( x^);
inc (x);
Form1.Edit3.Text := IntToStr( x^);
inc (x);
Form1.Edit4.Text := IntToStr( x^);
inc (x);
Form1.Edit5.Text := IntToStr( x^);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
d[1] := 1;
d[2] := 3;
d[3] := 5;
d[4] := 7;
d[5] := 9;
MyIntPointerArray( addr(d) );
end;
レコード配列へのポインタの扱いはどうしますか?
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
レコード配列へのポインタの扱いはどうしますか?
A:
以下のコード参照してください。
CArray のアドレスを RecordPointerArray に渡しています。
type
TCap = ^TCapital;
TCapital = record
Capital: string[20];
Country: String[20];
end;
CapitalArray = Array [1..3] of TCapital;
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
BitBtn1: TBitBtn;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
CArray : CapitalArray;
implementation
{$R *.DFM}
procedure RecordPointerArray(rec : TCap );
begin
Form1.Edit1.Text := Rec^.Capital;
Form1.Edit2.Text := Rec^.Country;
Inc (rec);
Form1.Edit3.Text := Rec^.Capital;
Form1.Edit4.Text := Rec^.Country;
Inc (rec);
Form1.Edit5.Text := Rec^.Capital;
Form1.Edit6.Text := Rec^.Country;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
CArray[1].Capital := 'Tokyo';
CArray[1].Country := 'Japan';
CArray[2].Capital := 'Washington D.C.';
CArray[2].Country := 'USA';
CArray[3].Capital := 'Canbera';
CArray[3].Country := 'Australia';
RecordPointerArray(Addr(CArray));
end;
オープン配列は,どのように使用しますか?
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
オープン配列は,どのように使用しますか?
A:
以下のコードを参照してください。
EDit1 には,2
Edit2 には,9.14159
が表示されます。
function Sum(const OpenArr: array of Double): Double;
var
I: Integer;
S: Double;
begin
Form1.Edit1.Text := IntToStr(High(OpenArr));
S := 0;
for I := 0 to High(OpenArr) do
S := S + OpenArr[I];
Sum := S;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
A, B, C: Double;
X : Double;
Str : String[20];
begin
A := 1;
B := 2;
C := 3;
X := Sum([A, 3.14159, B + C]);
EDit2.Text := FloatToStr(X);
end;
DLL に Double 型の配列のアドレスを渡すには,どうしたら良いですか?
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
DLL に Double 型の配列のアドレスを渡すには,どうしたら良いですか?
A:
関数の引数として,Addr() 関数を使用して Double 型のポインタを渡します。
DLL 側では,ポインタの指し示す内容,キャレット(^) を用いて取得します。
以下の例では,Double 型の配列の1番目に2番めの内容を加えた結果を取得します。
{ MyDll }
library Mydll;
uses
SysUtils, Classes, Dialogs;
type
DoubleP = ^Double;
function Keisan(DPtr: DoubleP): Double; stdcall; export;
var
sum: Double;
begin
sum := DPtr^; { DPtr のポイントする内容を取得 }
Inc(DPtr); { DPtr のインクリメント }
sum := sum + DPtr^;
Result := sum;
end;
exports
Keisan;
begin
end.
-----------------------------------------------------------
{ MyDll を呼び出すプログラム }
unit callDll1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
DoubleP = ^Double; { Double 型のポインタを定義 }
DoubleArray = array[1..3] of Double; { Double 型の配列の定義 }
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
d1: DoubleArray; { DoubleArray の実体の宣言 }
implementation
{$R *.DFM}
{ MyDll にある Keisan 関数のプロトタイプ }
function Keisan(DPtr: DoubleP): Double; stdcall; external 'MyDll';
procedure TForm1.Button1Click(Sender: TObject);
var
retDouble: Double;
begin
d1[1] := 1.1;
d1[2] := 2.2;
d1[3] := 3.3;
retDouble := Keisan(Addr(d1)); { MyDll に Double 型配列の先頭アドレスを渡す }
Edit1.text := FloatToStr(retDouble); { 3.3 と表示される}
end;
end.
サイズを動的に指定する配列を作成できますか?
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
サイズを動的に指定する配列を作成できますか?
A:
まず,必要最大限の配列のサイズの型を定義します。型を定義する場合は,メモリは消費されません。その型の変数を宣言すると,コンパイラは必要なメモリを割り当てます。サイズを動的に指定する場合は,かわりにその型へのポインタを宣言します。こうするとコンパイラは,ポインタ用に 4 バイトのメモリを割り当てます。
配列を使用する前に,メモリを割り当てる必要があります。AllocMem を使用することで,割り当てるメモリ量を制御することができます。割り当てるメモリ量は,配列の要素数と個々の要素のサイズを掛けて決定されます。16 ビット環境では,同時に割り当てることができる最大のメモリブロックは 64 KBです。
32 ビット環境では,同時に割り当てることができる最大のメモリブロックは4 GBです。配列が取り得ることのできる要素数(16 ビット環境)を決定するには,65520 を個々の要素のサイズでわります。
例: 65520 div Sizeof(LongInt)
unit Array_;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
ElementType = Integer;
const
MaxArraySize = (65520 div SizeOf(ElementType));
{ 16-bit 環境の場合 }
type
TDynamicArray = array[1..MaxArraySize] of ElementType;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
P: ^TDynamicArray; { 配列型へのポインタを宣言 }
const
{ 型付きの定数。実行時にソースコードの値によって初期化される変数で
実際はスタティック変数です。つまりこれは,他の変数を使用するのと
同様に,型付きの定数を使用できるということです。さらに,変数が
自動的に初期化されるメリットもあります。}
DynamicArraySizeNeeded: Integer = 10;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
{ 配列にメモリを割り当てます。必要なメモリを割り当てる際は,
十分注意してください。割り当てた以上に書き込もうとすると,
コンパイルはとおりますが,データが破壊されます。
}
DynamicArraySizeNeeded := 500;
P := AllocMem(DynamicArraySizeNeeded * SizeOf(Integer));
P^[5] := 68; { 配列の5番目にデータを入れます }
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{ データの表示 }
Button1.Caption := IntToStr(P^[5]);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{ 配列用に割り当てたメモリの解放 }
FreeMem(P, DynamicArraySizeNeeded * SizeOf(Integer));
end;
end.
配列プロパティをパブリッシュ(published)にするには,どうしますか?
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
配列プロパティをパブリッシュ(published)にしたいのですが,プロパティはパブリッシュにできないというエラー(#202)が発生してしまいます。プロパティエディタを持つプロパティはパブリッシュにできるのではないでしょうか?
A:
パブリッシュにできるフィールドやプロパティを確認するためにオンラインヘルプの published 部(Ver 1.0 では「パブリッシュ部」)の項目を参照してください。
配列プロパティを含め,標準でないデータ型のストリームのためには,コンポーネントが継承した DefineProperties をオーバーライドしてプロパティの読み込み/書き込みルーチンを登録しなければなりません。
べき乗演算はどうすればよいでしょうか?
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
べき乗演算はどうすればよいでしょうか?
A:
Baseの Ex乗を求める場合は、usesに Mathを追加し、Exp(Ex * Ln(Base))、または Power(Base, Ex)とします。
引数に多次元配列を渡す方法
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
引数に多次元配列を渡す方法
配列を手続きや関数に渡す方法は簡単で期待通りに動作します。しかし,多次元配列を手続きや関数に渡すには同じ方法では処理できません。MyArray が以下のように宣言されているとします。
var
MyArray: array[1..3, 1..5] of double;
// DoSomeThing() 手続きに渡したいとします。
// なお,DoSomrThing() は,以下のように宣言されています。
procedure DoSomeThing(MyArray: array of double);
begin
showmessage( floattostr( MyArray[1 , 1]));
end;
DoSomeThing(MyArray); のようなシンプルな文でうまくいくと考えるかもしれませんが残念ながら動作しません。DoSomeThing(MyArray); はコンパイルできません。
コンパイラは,異なる型の2つのデータ構造があるものと見なし受け付けません。
DoSomeThing() 手続きは Double の配列を期待していますが,例では多次元配列を渡しています。
Delphi は,ユーザー定義の型として多次元配列を処理します。つまり,型を宣言しないと,引数が多次元配列であることを手続きに知らせる構文はありません。
型を宣言しその型を引数として使用することが,多次元配列を渡す正しい方法です。
ポインタを配列に渡すこともできますが,手続き内で型キャストを行なう必要があります。どの型にキャストするかは別の問題です。
同じ型の定義または宣言が2個所で必要となります。
型を定義するには,以下のようにします。
type
TMyArray = array[1..3, 1..5] of double;
var
MyArray : TMyArray;
procedure DoSomeThing(MyArray: TMyArray);
begin
showmessage( floattostr( MyArray[1 , 1]));
end;
実際の呼び出しは以下のようになります。
DoSomeThing(MyArray);
ポインタを渡す方法を使用する場合には,手続きは以下のようになります。
type
PMyArray = ^TMyArray;
TMyArray = array[1..3, 1..5] of double;
var
MyArray : TMyArray;
procedure DoSomeThing(MyArray: PMyArray);
begin
showmessage ( floattostr( (MyArray[2,3]) ));
end;
32ビット版では,DoSomeThing() 内で MyArray 変数を逆参照する必要はありません。
古い版では,MyArray を MyArray^ として参照する必要があります。
汎用のポインタを渡す場合には,直接使用できない場合があります。まず,ローカル変数を宣言してその変数を使用します。ここでもやはり,古い版の PASCAL ではローカル変数をキャストする必要があります。勿論,この方法ではデータの処理により柔軟性があります。
procedure DoSomeThing(MyArray: pointer);
var
t : ^TMyArray;
begin
t := MyArray;
ShowMessage ( FloatToStr( t[2,3]) );
end;
ポインタを使用する方法はいずれの場合でも,以下のようになります。
MyArray[2, 3] := 5.6;
DoSomeThing(@MyArray);
日付の形式の文字列を Delphi で日付型として扱う方法
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
和暦など,日付の形式の文字列を Delphi で日付型として扱いたいのですが?
A:
バリアント型を使用して簡単に TDateTime 型へ変換できます。
ar
v : Variant;
d : TDateTime;
begin
v := '平成10年5月5日';
d := VarToDateTime( v );
ShowMessage( DateToStr( d ) );
end;
Currency 型と文字列の変換関数について
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
Currency 型と文字列の変換関数はありますか?
A:
文字列から Currency 型への変換は以下のものがあります。
文字列 → Currency 型 : StrToCurr
Currency 型 → 文字列 : CurrToStr
また,文字列へ変換する場合に,通貨書式に変換する場合には CurrToStrF や,形式指定子を用いた書式指定を行っての文字列の変換には,FormatCurr が用意されています。
メソッドのアドレスを持つ配列を定義して実行したいのですが? (98/11/2)
該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
メソッドのアドレスを持つ配列を定義して実行したいのですが?
A:
メソッドポインタの宣言をします。メソッドポインタは,of object を付けて宣言します。
type
TMyEvent= procedure(i: integer) of object; // メソッドポインタの宣言
TArrayEvent = array [1..3] of TMyEvent; // 配列の宣言
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
count: Integer;
ArrayEvent: TArrayEvent;
// Proc1~Proc3 は実際に実行されるメソッド
procedure Proc1(i: Integer);
procedure Proc2(i: Integer);
procedure Proc3(i: Integer);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
count := 1;
ArrayEvent[1] := Proc1; // アドレスを代入
ArrayEvent[2] := Proc2;
ArrayEvent[3] := Proc3;
end;
procedure TForm1.Proc1(i: Integer);
begin
Label1.Caption := 'Proc1 (ix1)';
Edit1.Text := IntToStr(i*1);
end;
procedure TForm1.Proc2(i: Integer);
begin
Label1.Caption := 'Proc2 (ix2)';
Edit1.Text := IntToStr(i*2);
end;
procedure TForm1.Proc3(i: Integer);
begin
Label1.Caption := 'Proc3 (ix3)';
Edit1.Text := IntToStr(i*3);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// 配列に指定されたメソッドの呼び出し
ArrayEvent[count](count);
Inc(count);
if (count >= 4) then
count := 1;
end;
多次元配列をパラメータとして指定する方法 (98/12/2)
該当するバージョン:Delphi 1.0,Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4
Q:
多次元配列をパラメータとして指定する方法を教えてください。
A:
多次元配列をパラメータとして指定する場合には,データ型として定義し,それを使用する事によって,使用することが可能となります。
1次元配列のパラメータには,多くの場合, オープン配列(例1)の形で宣言されています。
例1. オープン配列
function Find(A: array of Char): Integer;
そのほかにも, type 指令により配列型を定義し, パラメータ部分に定義済みの型を指定する方法も可能です。
次の例は, 配列の要素数固定の静的配列(例2), および要素数を指定しない動的配列(例3)の宣言です。
例2. 静的配列
type StaticArray = array[0..79] of Char;
function Find(A: StaticArray): Integer;
例3. 動的配列
type DynamicArray = array of Char;
function Find(A: DynamicArray): Integer;
注: 動的配列を使うには Delphi 4 以降のバージョンが必要になります。
多次元配列の場合には, 例1のような配列型を宣言しないのようなパラメータは使用できません。
多次元配列を現すには, 例4, 例5 のように配列型を宣言してから, パラメータの型としてその型を使用します。
例4. 多次元の静的配列
type StaticArray2 = array[0..79,0..79] of Char;
function Find(A2: StaticArray2): Integer;
例5. 多次元の動的配列
type DynamicArray2 = array of array of Char;
function Find(A2: DynamicArray2): Integer;
多次元配列でもオープン配列は使えるのですが,オープン配列はそのうちの1次元部分に対してのみしか指定できませんので,オープン配列を使用する場合には例6.のように1次元の配列型を定義し,その型をオープン配列として指定することによって2次元配列を実現するようになります。
例6.
type StaticArray = array[0..79] of Char;
function Find(A2: array of StaticArray): Integer;