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);
要将消息添加到与另一个窗口关联的线程的队列中,您需要使用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线程。
另外,如果你正在调用TControl.Perform
远离GUI线程,那么这也是错误的。 期望间歇性且难以诊断故障。