我正在使用多线程(RAD Studio XE5)开发应用程序。在应用程序开始时,我创建了一个单独的线程,只要主表单生效,它就会生效。
我能够将消息从线程发送到我的应用程序中创建的任何表单,但是我无法找到相反的方法,从主VCL线程向工作线程发送消息。 / p>
创建主表单时,我创建工作线程并将句柄复制到公共变量中:
serverThread := TMyThread.Create(True, ServerPort + 1);
serverThreadHandle := serverThread.Handle; // SAVE HANDLE
serverThread.Start;
然后(从另一种形式FrmSender)我向线程发送一条消息:
PostMessage(uMain.serverThreadHandle, UM_LOC_VCLMSG, UM_LOC_VCLMSG, Integer(PStrListVar));
这是线程的执行程序:
procedure TMyThread.Execute;
var
(..)
vclMSG : TMsg;
str1, str2 : string;
(..)
begin
while not(Terminated) do
begin
Sleep(10);
if Assigned(FrmSender) then
if FrmSender.HandleAllocated then
if PeekMessage(vclMSG, FrmSender.Handle, 0, 0, PM_NOREMOVE) then
begin
if vclMSG.message = UM_LOC_VCLMSG then
begin
try
pStrListVar := pStrList(vclMSG.lParam);
str1 := pStrListVar^.Strings[0];
str2 := pStrListVar^.Strings[1];
finally
Dispose(pStrListVar);
end;
end;
end;
(.. do other stuff ..)
end;
end;
然而 PeekMessage()永远不会返回true,就像它从未收到任何消息一样。我已经尝试将参数更改为 PeekMessage():
PeekMessage(vclMSG, 0, 0, 0, PM_NOREMOVE);
但没有结果。 有什么想法吗?
答案 0 :(得分:5)
PeekMessage(vclMSG, FrmSender.Handle, 0, 0, PM_NOREMOVE)
第二个参数表示您只会检索发送给发件人的邮件FrmSender.Handle
。但您已将邮件发送给收件人uMain.serverThreadHandle
。这是PeekMessage
永远无法返回的原因之一。
正如您所做的那样从线程访问VCL是错误的。表单的句柄需要重新创建VCL窗口,并且HandleAllocated
和Handle
上有明显的竞争。因此,即使你需要知道FrmSender.Handle
,在线程中提出它也是错误的。
您实际上是将消息发送到线程句柄而不是窗口句柄。这意味着永远不会发送消息,这是PeekMessage
无法返回的另一个原因。如果您在调用PostMessage
时检查了返回值,那么您就可以了解到这一点。
我使用PostThreadMessage
,或者通过调用AllocateHWnd
将消息发布到线程中分配的窗口。
一旦您设法实际发送消息,使用PM_NOREMOVE
意味着消息队列永远不会被清空。
您对Sleep
的使用对我来说非常可疑。为什么不使用GetMessage
并阻止消息到达。只要您看到Sleep
的来电,就会非常怀疑。
您对Integer
的强制转换将导致64位构建中的指针截断。要投射到的正确类型是LPARAM
。
我预计会有其他错误,这些只是我在2分钟快速扫描中看到的错误。
答案 1 :(得分:5)
来自MSDN PostMessage
function documentation:
在与创建指定窗口的线程关联的消息队列中放置(发布)消息,并返回而不等待线程处理消息。
要在与线程关联的消息队列中发布消息,请使用PostThreadMessage函数。
因此,您应该使用PostThreadMessage
:
将消息发布到指定线程的消息队列。它返回而不等待线程处理消息。
特别注意备注部分。收件人线程需要一个消息队列。按照以下步骤强制线程有一个:
WaitForSingleObject
功能等待事件设置为
在致电PostThreadMessage
之前发出信号状态。在邮件将发布到的帖子中,如此处所示调用PeekMessage
以强制系统创建邮件队列。
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
设置事件,以指示该事件 线程已准备好接收发布的消息。
然后,当使用PeekMessage
时,您将句柄值-1
传递给函数,如文档所述。