Delphi 线程未收到消息
Delphi thread not receiving messages
我正在开发一个多线程应用程序 (RAD Studio XE5)。在应用程序开始时,我创建了一个与主窗体一样长的线程。
我能够将消息从线程分派到在我的应用程序中创建的任何表单,但是我找不到相反的方法,即从主 VCL 线程向工作线程发送消息.
创建主窗体时,我创建工作线程并将句柄复制到 public 变量中:
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() 永远不会 returns 真,就好像它从未收到任何消息一样。我尝试将参数更改为 PeekMessage():
PeekMessage(vclMSG, 0, 0, 0, PM_NOREMOVE);
但是没有结果。
有什么想法吗?
PeekMessage(vclMSG, FrmSender.Handle, 0, 0, PM_NOREMOVE)
第二个参数表示您将只检索发送给发件人 FrmSender.Handle
的邮件。但是您将邮件发送给了收件人 uMain.serverThreadHandle
。这就是 PeekMessage
永远无法 return.
的原因之一
像你这样从线程访问VCL是错误的。窗体的句柄受 VCL window 重新创建的约束,并且在 HandleAllocated
和 Handle
上存在明显的竞争。所以即使你需要知道FrmSender.Handle
,在线程中询问它也是错误的。
您实际上是将消息发送到线程句柄而不是 window 句柄。这意味着消息甚至从未发送过,这是 PeekMessage
不能 return 的另一个原因。如果您在调用 PostMessage
时检查了 return 值,您就会了解到这一点。
我会使用 PostThreadMessage
或 post 将消息发送到线程中分配的 window 并调用 AllocateHWnd
.
一旦您设法真正发送消息,您使用 PM_NOREMOVE
意味着消息队列将永远不会被清空。
我觉得你对 Sleep
的使用很可疑。为什么不使用 GetMessage
等阻塞直到消息到达。任何时候看到对 Sleep
的调用,都要非常怀疑。
您对 Integer
的转换将导致 64 位构建中的指针截断。要强制转换的正确类型是 LPARAM
.
我预计还有其他错误,这些只是我在 2 分钟的快速扫描中看到的错误。
来自 MSDN PostMessage
function documentation:
Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
To post a message in the message queue associated with a thread, use the PostThreadMessage function.
因此,您应该使用 PostThreadMessage
:
Posts a message to the message queue of the specified thread. It returns without waiting for the thread to process the message.
请特别注意 备注 部分。接收线程需要一个消息队列。按照以下步骤强制拥有一个线程:
- 创建事件对象,然后创建线程。
- 使用
WaitForSingleObject
函数等待事件设置为
调用 PostThreadMessage
. 之前的信号状态
在消息将发布到的线程中,调用PeekMessage
,如下所示,强制系统创建消息队列。
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
设置事件,表示
线程已准备好接收发布的消息。
然后,当使用 PeekMessage
时,您将句柄值 -1
传递给函数,如文档所述。
我正在开发一个多线程应用程序 (RAD Studio XE5)。在应用程序开始时,我创建了一个与主窗体一样长的线程。
我能够将消息从线程分派到在我的应用程序中创建的任何表单,但是我找不到相反的方法,即从主 VCL 线程向工作线程发送消息.
创建主窗体时,我创建工作线程并将句柄复制到 public 变量中:
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() 永远不会 returns 真,就好像它从未收到任何消息一样。我尝试将参数更改为 PeekMessage():
PeekMessage(vclMSG, 0, 0, 0, PM_NOREMOVE);
但是没有结果。 有什么想法吗?
PeekMessage(vclMSG, FrmSender.Handle, 0, 0, PM_NOREMOVE)
第二个参数表示您将只检索发送给发件人 FrmSender.Handle
的邮件。但是您将邮件发送给了收件人 uMain.serverThreadHandle
。这就是 PeekMessage
永远无法 return.
像你这样从线程访问VCL是错误的。窗体的句柄受 VCL window 重新创建的约束,并且在 HandleAllocated
和 Handle
上存在明显的竞争。所以即使你需要知道FrmSender.Handle
,在线程中询问它也是错误的。
您实际上是将消息发送到线程句柄而不是 window 句柄。这意味着消息甚至从未发送过,这是 PeekMessage
不能 return 的另一个原因。如果您在调用 PostMessage
时检查了 return 值,您就会了解到这一点。
我会使用 PostThreadMessage
或 post 将消息发送到线程中分配的 window 并调用 AllocateHWnd
.
一旦您设法真正发送消息,您使用 PM_NOREMOVE
意味着消息队列将永远不会被清空。
我觉得你对 Sleep
的使用很可疑。为什么不使用 GetMessage
等阻塞直到消息到达。任何时候看到对 Sleep
的调用,都要非常怀疑。
您对 Integer
的转换将导致 64 位构建中的指针截断。要强制转换的正确类型是 LPARAM
.
我预计还有其他错误,这些只是我在 2 分钟的快速扫描中看到的错误。
来自 MSDN PostMessage
function documentation:
Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
To post a message in the message queue associated with a thread, use the PostThreadMessage function.
因此,您应该使用 PostThreadMessage
:
Posts a message to the message queue of the specified thread. It returns without waiting for the thread to process the message.
请特别注意 备注 部分。接收线程需要一个消息队列。按照以下步骤强制拥有一个线程:
- 创建事件对象,然后创建线程。
- 使用
WaitForSingleObject
函数等待事件设置为 调用PostThreadMessage
. 之前的信号状态
在消息将发布到的线程中,调用
PeekMessage
,如下所示,强制系统创建消息队列。PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
设置事件,表示 线程已准备好接收发布的消息。
然后,当使用 PeekMessage
时,您将句柄值 -1
传递给函数,如文档所述。