微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将方法指针作为窗口消息参数发送?

如何解决如何将方法指针作为窗口消息参数发送?

我想调用一个通过窗口消息作为参数发送的方法。我在下面的示例中尝试过...但是在执行 DoWork 方法时出现访问冲突。我认为过程 CallBack 变量没有在消息处理程序中正确重建。在 Delphi documentation 中它说一个方法指针”变量有两个指针:“这些类型代表方法指针。方法指针实际上是一对指针;第一个存储方法的地址,而second 存储对方法所属对象的引用。"...但我不知道如何访问第二个指针,然后用这两个指针重构变量...

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.

解决方法

但是我不知道如何访问第二个指针,然后用这两个指针重构变量...

来自 internal data formats 的文档:

方法指针存储为指向方法入口点的 32 位指针,后跟指向对象的 32 位指针。

在 32 位应用中。对于 64 位应用程序,同样适用,但当然使用 64 位指针。

这实际上是使用指针算法访问这些指针所需要知道的全部内容,但使用预定义的 TMethod 记录要好得多:

如果 M 是任何方法指针,那么 TMethod(M).Code 是代码(过程)指针,TMethod(M).Data 是数据(对象)指针。

如果你想发送或发布一个方法指针,你可以将 LPARAM 和 WPARAM 用于这两个指针,或者你可以发送一个指向包含两者的单个记录(或对象)的单个指针。请记住,当发布消息时,您不能传递局部变量的地址,因为该变量可能会在收件人收到之前超出范围。


写作时

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,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是同步的,所以直到消息被接收者处理完才会返回;特别是,SendMessage 不会在 TForm1.Test 返回之前返回。因此,CallBack 局部变量在此处理期间将处于活动状态,因此在此期间使用指向此变量的指针是完全安全的。

另一方面,如果您改为使用 PostMessage发布消息,它会立即返回,如果幸运的话,您将崩溃,否则可能会导致内存损坏。确实,那么 Button1Click 将到达它的 endCallBack 局部变量将被丢弃,然后TForm1.Test 才会运行——被赋予一个悬空指针。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。