Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2016年05月17日] [更新 2019年06月10日] Delphi 一般・その他
   
配列のメッセージによる送受信とポインタ型
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
[整理番号 - 0680]







メッセージによる配列の送受信と配列のポインタ型


配列をメッセージで送信,受信する場合と,配列のポインタ型に関するサンプルです.






01_配列をメッセージで送信して受信する例 - ポインタ型の定義


配列型やレコード型を定義して使用する際,その型のポインタ型も定義しておくと便利です.
例えば以下のようにします.Delphi では,ポインタ型の名前のプリフィックスとして大文字の P を付ける慣習となっています.


type
  PIntegerArray = ^TIntegerArray;
  TIntegerArray = array[1..10] of Integer;


実際に上記の定義で整数型の配列を,メッセージの引数に渡したり,メッセージのパラメータから取得する例です.プロシージャーの引数に配列を渡すテストもしています.
ポインタ型を定義しないで処理する場合は,Pointer でキャストすることになりますが,Pointer は型の情報を持っていません.更に型のキャストが必要となります.上のようなポインタ型を定義すれば,TIntegerArray のポインタ型になって,型情報を持つポインタとなります.



図1
実行結果
  • メッセージを送る時の変数のアドレスと受信した時のアドレスが同じであることが確認できる

リスト1
配列をメッセージで送信して受信する (ポインタ型を定義して利用する例)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    procedure WMApp100(var Message: TMessage); message WM_APP+100;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  PIntegerArray = ^TIntegerArray;
  TIntegerArray = array[1..10] of Integer;

var
  FIntArray : TIntegerArray;

//=============================================================================
//  メッセージの引数に配列を渡す
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Clear;

  FIntArray[1] := 100;
  Memo1.Lines.Add(IntToHex(NativeUInt(@FIntArray), 8));

  SendMessage(Handle, WM_APP+100, Length(FIntArray), LPARAM(@FIntArray));
end;

//-----------------------------------------------------------------------------
//  関数やメソッドの引数に配列を渡す
//  ポイント型で渡す場合
//-----------------------------------------------------------------------------
procedure SetIntArrayData(ABuff : PIntegerArray);
begin
  ABuff[1] := ABuff[1] + 500;
  ABuff[3] := 133;
end;

//-----------------------------------------------------------------------------
//  メッセージ受信処理
//  メッセージのパラメータから配列を受け取る
//-----------------------------------------------------------------------------
procedure TForm1.WMApp100(var Message: TMessage);
var
  LpIntBuff : PIntegerArray;
  LMsgText  : string;
begin
  LpIntBuff := PIntegerArray(Message.lParam);
  Memo1.Lines.Add(IntToHex(NativeUInt(LpIntBuff), 8));

  SetIntArrayData(LpIntBuff);

  LMsgText := '配列の要素数 : ' + IntToStr(Message.WParam) + sLineBreak
            + IntToStr(LpIntBuff[1]) + sLineBreak
            + IntToStr(LPIntBuff[3]);

  MessageBox(Handle, PChar(LMsgText), '結果', MB_ICONINFORMATION);
end;

end.




02_関数の引数にポインタ型を使用しない場合 - 逆参照で渡す例


これまでのサンプルで使用している,配列要素の操作を行う関数,SetIntArrayDate のプロシージャーの引数の変数の型を TIntegerAray 型で定義することもあります.この場合は,逆参照で (実際の値として) 引数に渡します.



リスト2
配列をメッセージで送信して受信する (ポインタ型を定義して利用する例)
関数等の引数にポインタ型を使用しない場合
type
  PIntegerArray = ^TIntegerArray;
  TIntegerArray = array[1..10] of Integer;

var
  FIntArray : TIntegerArray;

//=============================================================================
//  メッセージの引数に配列を渡す
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  FIntArray[1] := 100;
  SendMessage(Handle, WM_APP+100, Length(FIntArray), LPARAM(@FIntArray));
end;

//-----------------------------------------------------------------------------
//  関数やメソッドの引数に配列を渡す
//  引数にポイント型を使用しない場合
//  引数に値を返すにはvar指定する
//-----------------------------------------------------------------------------
procedure SetIntArrayData(var ABuff : TIntegerArray);
begin
  ABuff[1] := ABuff[1] + 500;
  ABuff[3] := 133;
end;

//-----------------------------------------------------------------------------
//  メッセージ受信処理
//  メッセージのパラメータから配列を受け取る
//-----------------------------------------------------------------------------
procedure TForm1.WMApp100(var Message: TMessage);
var
  LpIntBuff : PIntegerArray;
  LMsgText  : string;
begin
  LpIntBuff := PIntegerArray(Message.lParam);
  //関数の引数がポインタ型でない場合は逆参照を渡す
  SetIntArrayData(LpIntBuff^);

  LMsgText := '配列の要素数 : ' + IntToStr(Message.WParam) + sLineBreak
            + IntToStr(LpIntBuff[1]) + sLineBreak
            + IntToStr(LPIntBuff[3]);

  MessageBox(Handle, PChar(LMsgText), '結果', MB_ICONINFORMATION);
end;




03_関数の引数にポインタ型を使用しない場合 - クラス型の変数


上のサンプルと同じで,配列要素の操作を行う関数,SetIntArrayDate のプロシージャーの引数の変数の型を TIntegerAray 型で定義している場合です.
この時,引数にクラス型の変数の値として渡すには,メッセージで受信した値を,逆参照でクラス型の変数に代入します.

Delphi の関数やメソッドのプロシージャでよく使用される Sender:TObject の Sender と同じように,メッセージを送る時は型情報があります.しかし,メッセージの受信では,メッセージパラメータそのものには何の型のデータであるかという情報はありません.そのため,Sender:TObject でクラス型等の情報を取得する時と同じように,型のキャスト (型変換) が必要となります.
そのためにもポインタ型を定義しておくと便利と言えます.



リスト3
配列をメッセージで送信して受信する (ポインタ型を定義して利用する例)
関数等の引数にポインタ型を使用しない場合
type
  PIntegerArray = ^TIntegerArray;
  TIntegerArray = array[1..10] of Integer;

var
  FIntArray : TIntegerArray;

//=============================================================================
//  メッセージの引数に配列を渡す
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  FIntArray[1] := 100;
  SendMessage(Handle, WM_APP+100, 0, LPARAM(@FIntArray));
end;

//-----------------------------------------------------------------------------
//  関数やメソッドの引数に配列を渡す
//  引数にポイント型を使用しない場合
//  引数に値を返すにはvar指定する
//-----------------------------------------------------------------------------
procedure SetIntArrayData(var ABuff : TIntegerArray);
begin
  ABuff[1] := ABuff[1] + 500;
  ABuff[3] := 133;
end;

//-----------------------------------------------------------------------------
//  メッセージ受信処理
//  メッセージのパラメータから配列を受け取る
//-----------------------------------------------------------------------------
procedure TForm1.WMApp100(var Message: TMessage);
var
  LIntBuff : TIntegerArray;
  LMsgText : string;
begin
  //逆参照で受け取る
  LIntBuff := PIntegerArray(Message.lParam)^;
  SetIntArrayData(LIntBuff);

  LMsgText := IntToStr(LIntBuff[1]) + sLineBreak
            + IntToStr(LIntBuff[3]);

  MessageBox(Handle, PChar(LMsgText), '結果', MB_ICONINFORMATION);
end;




04_配列以外のオブジェクトでポインタ型を使用 - TBitmap の例


配列以外のオブジェクトでもポインタ型を定義して使用すると便利なことがあります.
以下は TBitmap の例です.ただし,SendMessage でメッセージを送信しています.PostMesssage は処理を待たずに送信が終了するため,ビットマップが描画されません.

本サンプルは,ポインタ型を定義して利用する方法のテストです.TBitmap 等をメッセージで送受信するには,WM_COPYDATA メッセージを使用した方が便利です.




図2
実行結果

リスト4
配列ではないオブジェクトでポインタ型を定義して使用する例
//=============================================================================
//  メッセージの引数にオブジェクトを渡す
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LBitmap : TBitmap;
begin
  LBitmap := TBitmap.Create;
  try
    LBitmap.LoadFromFile('503.bmp');
    SendMessage(Handle, WM_APP+100, 0, LPARAM(@LBitmap));
  finally
    FreeAndNil(LBitmap);
  end;
end;

//-----------------------------------------------------------------------------
//  メッセージ受信処理
//  メッセージのパラメータからオブジェクトを受け取る
//-----------------------------------------------------------------------------
procedure TForm1.WMApp100(var Message: TMessage);
type
  PBitmap = ^TBitmap;
var
  LBitmap : TBitmap;
begin
  //LBitmap := TBitmap(Pointer(Message.lParam)^);でもよいが...
  LBitmap := PBitmap(Message.lParam)^;
  Image1.Canvas.Draw(0, 0, LBitmap);
end;