替代TControl.Perform

时间:2013-04-18 18:27:01

标签: windows delphi message-queue vcl

TControl.Perform代码为:

var
  Message: TMessage;
begin
  Message.Msg := Msg;
  Message.WParam := WParam;
  Message.LParam := LParam;
  Message.Result := 0;
  if Self <> nil then WindowProc(Message);
  Result := Message.Result;

程序执行等待返回,我是对的吗?

有一个替代方法,用于在同一个应用程序中的另一个线程内的TFORM队列中发布消息,而不等待返回?

修改

这种方法可以缓解这个问题吗?

interface

const  
  WM_DOSTUFF = WM_APP + $001;

TMyForm = class(TForm)
{stuff}
public
{Other stuff}
  procedure DoMyStuff(var Msg: TMessage); message WM_DOSTUFF;   
{More stuff}
end;  

var
  MyHandle: HWND;

implementation

constructor TMyForm.Create(AOwner: TComponent);
begin
  inherited;
  MyHandle := AllocateHWnd(DoMyStuff);
end; 

destructor TMyForm.Destroy;
begin
  DeallocateHWnd(MyHandle);
  inherited;
end;

在线程中正常使用:

  PostMessage(MyHandle, WM_DOSTUFF, 0, 0);   

1 个答案:

答案 0 :(得分:5)

要将消息添加到与另一个窗口关联的线程的队列,您需要使用PostMessage Windows API函数。

PostMessage(WindowHandle, Msg, WParam, LParam);

现在,如果您在与GUI线程不同的线程上执行此操作,则无法使用Form.Handle来获取窗口句柄。那是因为这样做引入了GUI线程的竞赛。如果需要重新创建句柄,它将与您的线程而不是GUI线程创建关联。记住规则:只与GUI线程中的VCL对象进行交互。

因此,您通常不会将PostMessage与VCL表单的句柄一起使用,因为您无法轻松保证将消息传递到正确的窗口。即使您同步访问窗口句柄,也可以重新创建窗口,并且您的消息不会到达。

异步传递消息的最简单方法是调用TThread.Queue。这不需要窗口句柄来操作,因此避免了与GUI线程的VCL对象关联的所有问题。调用Queue时发送的过程在GUI线程上执行,因此可以安全地执行所有VCL操作。

如果您使用的是在TThread.Queue之前的旧Delphi,那么它会更复杂。在这种情况下,您应该使用PostMessage。但是您必须将消息定向到与表单无关的窗口。将其指向使用AllocateHWnd创建的窗口。请记住,您必须在GUI线程上调用AllocateHWnd。以这种方式创建的Windows不受重新创建的影响,是PostMessage的安全目标。然后,该窗口的窗口过程可以将消息转发到您的表单。这是安全的,因为窗口过程在与其窗口关联的线程中执行。在这种情况下,这是GUI线程。

顺便说一句,如果你从GUI线程调用TControl.Perform,那也是错误的。期望间歇性且难以诊断故障。