如何将方法指针作为 window 消息参数发送?
How to send a method pointer as a window message parameter?
我想调用一个通过 window 消息作为参数发送的方法。我在下面的示例中进行了尝试...但是在执行 DoWork
方法时出现访问冲突。我认为程序 CallBack
变量没有在消息处理程序中正确重建。在 Delphi documentation 中,它表示“方法指针”变量有 2 个指针:“这些类型表示方法指针。方法指针实际上是一对指针;第一个存储方法的地址,第二个存储对该方法所属对象的引用。"...但我不知道如何访问第二个指针,然后用这两个指针重建变量...
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TCallBackNotify = function(CallBack: Pointer = nil): Boolean of object;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure Test(var Msg: TMessage); message WM_USER;
function DoWork(CallBack: Pointer = nil): Boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Test(var Msg: TMessage);
var CallBack: TCallBackNotify;
begin
if Msg.Msg = WM_USER then begin
@CallBack:= Pointer(Msg.WParam);
CallBack;
end;
end;
function TForm1.DoWork(CallBack: Pointer = nil): Boolean;
begin
Caption:= 'It''s working !';
end;
procedure TForm1.Button1Click(Sender: TObject);
var CallBack: TCallBackNotify;
begin
CallBack:= DoWork;
PostMessage(Handle, WM_USER, WPARAM(@CallBack) , 0);
end;
end.
But I don't know how to access the second pointer, and then reconstruct the variable with those two pointers...
来自 internal data formats 上的文档:
A method pointer is stored as a 32-bit pointer to the entry point of a method, followed by a 32-bit pointer to an object.
在 32 位应用程序中。对于 64 位应用程序,同样适用,但当然使用 64 位指针。
实际上这就是使用指针算法访问这些指针所需知道的全部内容,但使用预定义的 TMethod
记录要好得多:
如果M
是任何方法指针,那么TMethod(M).Code
是代码(过程)指针,TMethod(M).Data
是数据(对象)指针。
如果要发送或 post 一个方法指针,您可以将 LPARAM 和 WPARAM 用于这两个指针,或者您可以将单个指针发送到包含这两个指针的单个记录(或对象)。请记住,当 posting 一条消息时,您不能传递局部变量的地址,因为该地址可能会在收件人收到之前超出范围。
写的时候
var
CallBack: TCallBackNotify;
begin
CallBack := DoWork;
PostMessage(Handle, WM_USER, WPARAM(@CallBack) , 0);
您正在发送 @CallBack
。由于 CallBack
被声明为方法指针,因此 @CallBack
是代码指针,即 @CallBack = TMethod(CallBack).Code
。因此,根本不发送数据指针。
作为对比,@@CallBack
是局部CallBack
变量的地址。如果您改为将 CallBack
声明为 TMethod
(普通记录),则此地址将仅表示为 @CallBack
。
为了说明这一点,
procedure TForm1.Button1Click(Sender: TObject);
var
CallBack: TCallBackNotify;
CallBackRec: TMethod absolute CallBack;
begin
CallBack := DoWork;
ShowMessage(
'Data = ' + NativeInt(CallBackRec.Data).ToString + #13#10 +
' = Self = ' + NativeInt(Self).ToString + #13#10 +
'Code = ' + NativeInt(CallBackRec.Code).ToString + #13#10 +
' = @CallBack = ' + NativeInt(@CallBack).ToString + #13#10 +
'@@CallBack = ' + NativeInt(@@CallBack).ToString + #13#10 +
' = @CallBackRec = ' + NativeInt(@CallBackRec).ToString + #13#10
);
end;
可能会产生
Data = 17376288
= Self = 17376288
Code = 6278088
= @CallBack = 6278088
@@CallBack = 1635636
= @CallBackRec = 1635636
所以,要发送一个方法,你可以这样做,例如,
procedure TForm1.Button1Click(Sender: TObject);
var
CallBack: TCallBackNotify;
begin
CallBack := DoWork;
SendMessage(Handle, WM_USER, WParam(@@CallBack), 0);
end;
procedure TForm1.Test(var Msg: TMessage);
var
CallBack: TCallBackNotify;
begin
if Msg.Msg = WM_USER then
begin
CallBack := TCallBackNotify(PMethod(Msg.WParam)^);
CallBack;
end;
end;
由于SendMessage
是同步的,在消息被接收者处理之前它不会return;特别是,SendMessage
不会在 TForm1.Test
return 之前 return。因此,CallBack
局部变量在此处理期间将处于活动状态,因此在此期间使用指向该变量的指针是完全安全的。
另一方面,如果您改为 post 使用 PostMessage
的消息,这会立即 return,如果你很幸运,否则可能会导致内存损坏。事实上,然后 Button1Click
将达到其 end
,CallBack
局部变量将被丢弃,只有稍后 TForm1.Test
运行 -- 被赋予一个悬空指针.
我想调用一个通过 window 消息作为参数发送的方法。我在下面的示例中进行了尝试...但是在执行 DoWork
方法时出现访问冲突。我认为程序 CallBack
变量没有在消息处理程序中正确重建。在 Delphi documentation 中,它表示“方法指针”变量有 2 个指针:“这些类型表示方法指针。方法指针实际上是一对指针;第一个存储方法的地址,第二个存储对该方法所属对象的引用。"...但我不知道如何访问第二个指针,然后用这两个指针重建变量...
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TCallBackNotify = function(CallBack: Pointer = nil): Boolean of object;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure Test(var Msg: TMessage); message WM_USER;
function DoWork(CallBack: Pointer = nil): Boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Test(var Msg: TMessage);
var CallBack: TCallBackNotify;
begin
if Msg.Msg = WM_USER then begin
@CallBack:= Pointer(Msg.WParam);
CallBack;
end;
end;
function TForm1.DoWork(CallBack: Pointer = nil): Boolean;
begin
Caption:= 'It''s working !';
end;
procedure TForm1.Button1Click(Sender: TObject);
var CallBack: TCallBackNotify;
begin
CallBack:= DoWork;
PostMessage(Handle, WM_USER, WPARAM(@CallBack) , 0);
end;
end.
But I don't know how to access the second pointer, and then reconstruct the variable with those two pointers...
来自 internal data formats 上的文档:
A method pointer is stored as a 32-bit pointer to the entry point of a method, followed by a 32-bit pointer to an object.
在 32 位应用程序中。对于 64 位应用程序,同样适用,但当然使用 64 位指针。
实际上这就是使用指针算法访问这些指针所需知道的全部内容,但使用预定义的 TMethod
记录要好得多:
如果M
是任何方法指针,那么TMethod(M).Code
是代码(过程)指针,TMethod(M).Data
是数据(对象)指针。
如果要发送或 post 一个方法指针,您可以将 LPARAM 和 WPARAM 用于这两个指针,或者您可以将单个指针发送到包含这两个指针的单个记录(或对象)。请记住,当 posting 一条消息时,您不能传递局部变量的地址,因为该地址可能会在收件人收到之前超出范围。
写的时候
var
CallBack: TCallBackNotify;
begin
CallBack := DoWork;
PostMessage(Handle, WM_USER, WPARAM(@CallBack) , 0);
您正在发送 @CallBack
。由于 CallBack
被声明为方法指针,因此 @CallBack
是代码指针,即 @CallBack = TMethod(CallBack).Code
。因此,根本不发送数据指针。
作为对比,@@CallBack
是局部CallBack
变量的地址。如果您改为将 CallBack
声明为 TMethod
(普通记录),则此地址将仅表示为 @CallBack
。
为了说明这一点,
procedure TForm1.Button1Click(Sender: TObject);
var
CallBack: TCallBackNotify;
CallBackRec: TMethod absolute CallBack;
begin
CallBack := DoWork;
ShowMessage(
'Data = ' + NativeInt(CallBackRec.Data).ToString + #13#10 +
' = Self = ' + NativeInt(Self).ToString + #13#10 +
'Code = ' + NativeInt(CallBackRec.Code).ToString + #13#10 +
' = @CallBack = ' + NativeInt(@CallBack).ToString + #13#10 +
'@@CallBack = ' + NativeInt(@@CallBack).ToString + #13#10 +
' = @CallBackRec = ' + NativeInt(@CallBackRec).ToString + #13#10
);
end;
可能会产生
Data = 17376288
= Self = 17376288
Code = 6278088
= @CallBack = 6278088
@@CallBack = 1635636
= @CallBackRec = 1635636
所以,要发送一个方法,你可以这样做,例如,
procedure TForm1.Button1Click(Sender: TObject);
var
CallBack: TCallBackNotify;
begin
CallBack := DoWork;
SendMessage(Handle, WM_USER, WParam(@@CallBack), 0);
end;
procedure TForm1.Test(var Msg: TMessage);
var
CallBack: TCallBackNotify;
begin
if Msg.Msg = WM_USER then
begin
CallBack := TCallBackNotify(PMethod(Msg.WParam)^);
CallBack;
end;
end;
由于SendMessage
是同步的,在消息被接收者处理之前它不会return;特别是,SendMessage
不会在 TForm1.Test
return 之前 return。因此,CallBack
局部变量在此处理期间将处于活动状态,因此在此期间使用指向该变量的指针是完全安全的。
另一方面,如果您改为 post 使用 PostMessage
的消息,这会立即 return,如果你很幸运,否则可能会导致内存损坏。事实上,然后 Button1Click
将达到其 end
,CallBack
局部变量将被丢弃,只有稍后 TForm1.Test
运行 -- 被赋予一个悬空指针.