目录
上一节讲述了RTL消息,本节讲述下Windows消息。首先看下演示程序如下:
对于Windows的消息说明如下:
- Windows消息一定是绑定在一个含有窗体句柄的对象上,例如TForm、TButton等;
- 如果不使用Windows系统自带的SendMessage和PostMessage发送消息,通过Dispatch分发消息,此时也可以不绑定在窗体对象上,只要是对象就可以;
- Demo程序演示了三种方式,其中SendMessage和PostMessage发送消息是使用的Windows的原生API。发送的方式有特别约定;Dispatch使用的是Delphi系统给对象封装的消息分发函数,可以用在任何对象上。
- 对于Windows的消息,实际上可以理解为一种有索引号的函数调用,调用参数可以灵活,包括字符串、数字、记录体甚至对象,但需要按照指针方式传递参数;
- 对于Dispatch调用,也可以传递任何参数,无需指针方式调用;
Windows消息使用流程
一、首先引用Winapi.Messages消息单元
uses
....
Winapi.Windows,
....;
二、定义一个消息ID,整型常数。对于用户自定义消息,该常数必须大于WM_USER。
const
WinMessageID = WM_USER + 1; //WM_USER 在Winapi.Messages中定义
三、在对象上定义一个响应消息的方法:
private
{ Private declarations }
//Winapi.Messages 中的消息响应事件
procedure OnWinMessage(var Message); message WinMessageID; //定义一个消息ID对应的消息处理函数过程
四、实现消息响应方法对应的函数体:
procedure TForm_Message_VCL.OnWinMessage(var Message);
var
PStudent : ^TStudent;
begin
PStudent := Pointer( Winapi.Messages.TMessage(Message).WParam);
Memo1.Lines.Add('Win消息处理记录消息 姓名: ' + PStudent.Name);
Memo1.Lines.Add('Win消息处理记录消息 年龄: ' + PStudent.age.ToString);
if PStudent.Sex then
Memo1.Lines.Add('Win消息处理记录消息 性别: 男')
else
Memo1.Lines.Add('Win消息处理记录消息 性别: 女');
//如果是SendMessage 可以不释放 PStudent 指针资源,因为SendMessage是同步执行的,发送消息函数应该自己销毁,当然这里销毁也是可以的
//如果是PostMessage,此时应该销毁这个指针,因为PostMessage是不需要等待结果的,此时如果释放资源就会出现问题。
Dispose(PStudent);
end;
五、发送消息,可以使用SendMessage或者PostMessage,其区别在于SendMessage需要等待函数执行完成返回结果,而PostMessage无需等待消息结果。
procedure TForm_Message_VCL.Button_SendMessageWinClick(Sender: TObject);
var
PStudent: ^TStudent; //记录指针
begin
//申请指针资源
New(PStudent);
//构造消息体内容
PStudent.Name := LabeledEdit_WinName.Text;
PStudent.age := SpinEdit_WinAge.Value;
PStudent.Sex :=CheckBox_WinSex.Checked;
//按照 windows 的约定发送消息,必须包含句柄
SendMessage(Self.Handle,WinMessageID,DWord(PStudent),0);
//此处是否释放需要根据程序决定,总之上面已经申请了资源,一定要记得在合适的地方释放
//Dispose(PStudent);
end;
Windows消息注意事项
必须使用SendMessage或者PostMessage,两个函数定义的格式(参见Windows文档)如下:
LRESULT SendMessage(
[in] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
参数说明
hWnd:表示的是窗口对象的句柄,窗口对象必须存在;
Msg:消息ID,整型数。对应Demo中的 WinMessageID
wParam:就是需要发送的消息体指针
IParam:附加的额外消息体指针
对于具体的消息体,可以任意定义,Demo中定义的是一个记录体:
type
TStudent = record
Name : string;
age : Byte;
Sex : Boolean;
end;
如果不使用Windows消息,而是使用Delphi系统提供的Dispatch机制,也可以实现消息分发。
Dispatch消息使用流程
一、 和Windows消息一样,首先引用Winapi.Messages消息单元
uses
....
Winapi.Windows,
....;
二、定义一个消息ID,整型常数。对于用户自定义消息ID,一般不要大于50000,否则会引起编译错误。同时定义一个需要传递的消息结构
const
DispatchMessageID = WM_USER + 2; //Dispatch 消息
type
//Dispatch需要的消息结构,注意增加了一个id字段
TStudent2 = record
id : Word; //消息ID
Name : string;
age : Byte;
Sex : Boolean;
end;
三、在对象上(注意不一定是窗口对象,也就是说不一定需要有句柄)定义一个消息响应函数过程:
private
{ Private declarations }
//Winapi.Messages 中的消息响应事件
procedure OnWinMessage(var Message); message WinMessageID; //定义一个消息ID对应的消息处理函数过程
procedure OnDispatchMessage(var Message); message DispatchMessageID; //定义一个消息ID对应的消息处理函数过程
四、实现消息响应方法对应的函数体(请注意和Windows消息的实现体的区别):
procedure TForm_Message_VCL.OnDispatchMessage(var Message);
var
Student2 : TStudent2;
begin
Student2 := TStudent2(Message);
Memo1.Lines.Add('Win2消息处理记录消息 姓名: ' + Student2.Name);
Memo1.Lines.Add('Win消息处理记录消息 年龄: ' + Student2.age.ToString);
if Student2.Sex then
Memo1.Lines.Add('Win消息处理记录消息 性别: 男')
else
Memo1.Lines.Add('Win消息处理记录消息 性别: 女');
end;
五、发送消息,调用对象上的Dispatch函数(Delphi系统已经在每个对象上都实现了Dispatch)
procedure TForm_Message_VCL.Button_DispatchClick(Sender: TObject);
var
Student2 : TStudent2;
begin
Student2.id := DispatchMessageID;
//构造消息体内容
Student2.Name := LabeledEdit_WinName.Text;
Student2.age := SpinEdit_WinAge.Value;
Student2.Sex :=CheckBox_WinSex.Checked;
//通过对象的Dispatch发送消息,实际上就是调用函数
Self.Dispatch(Student2);
end;
Dispatch消息注意事项:
Dispatch传递参数不需要指针,但是消息体中增加了一个字段id,是一个Word型的整数。对于Dispatch的定义在Delphi中有如下说明:
Calls message-handling methods for the object, based on the contents of the Message parameter.
Call Dispatch to automatically pass messages to the appropriate message handler.
Dispatch determines whether a message is in the list of message handlers declared for the object. If the object does not handle the message, Dispatch then examines the message-handler list of the ancestor class, and continues checking ancestors until it either finds a specific handler or runs out of ancestors, in which case it calls DefaultHandler.
The only assumption Dispatch makes about the data in Message is that the first two bytes contain a message ID—that is, an integer that determines which message handler Dispatch calls. Although any kind of data can be passed to Dispatch, most TObject descendants expect a message record such as TMessage or a specific data structure type.
注意红色加粗这句,意思是消息体的前两个字节必须包含一个 message ID。这个就是我们TStudent2中的id字段。
下一节:【Delphi】中使用消息Messages(六)总结及源代码