【问题标题】:Delphi thread not receiving messagesDelphi线程没有收到消息
【发布时间】:2016-10-12 14:59:06
【问题描述】:

我正在开发一个多线程应用程序 (RAD Studio XE5)。在应用程序开始时,我创建了一个线程,该线程将与主窗体一样长。

我能够将消息从线程发送到在我的应用程序中创建的任何表单,但是我找不到相反的方法,将消息从主 VCL 线程发送到工作线程。

创建主窗体时,我创建工作线程并将句柄复制到公共变量中:

  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);

但没有结果。 有什么想法吗?

【问题讨论】:

  • 您不会 PostMessage 到线程句柄,阅读 PostMessage 的文档会有所帮助。
  • 忘了说线程是从TThread类派生的

标签: multithreading delphi vcl


【解决方案1】:

来自MSDN PostMessage function documentation

在与创建指定窗口的线程关联的消息队列中放置(发布)消息,并在不等待线程处理消息的情况下返回。

要在与线程关联的消息队列中发布消息,请使用 PostThreadMessage 函数。

因此,您应该使用PostThreadMessage:

将消息发布到指定线程的消息队列中。它不等待线程处理消息就返回。

特别注意备注部分。接收线程需要一个消息队列。按照以下步骤强制线程有一个:

  • 创建一个事件对象,然后创建线程。
  • 使用WaitForSingleObject函数等待事件被设置为 调用PostThreadMessage之前的信号状态。
  • 在要发布消息的线程中,调用PeekMessage,如图所示,强制系统创建消息队列。

    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
    
  • 设置事件,以表明 线程已准备好接收发布的消息。

然后,当使用PeekMessage 时,您将-1 的句柄值传递给函数,如文档所述。

【讨论】:

    【解决方案2】:
    PeekMessage(vclMSG, FrmSender.Handle, 0, 0, PM_NOREMOVE)
    

    第二个参数意味着您将只检索发送给发件人FrmSender.Handle 的消息。但是您将消息发送给了收件人uMain.serverThreadHandle。这也是PeekMessage 永远无法返回的原因之一。

    像你这样从线程访问 VCL 是错误的。表单的句柄受制于 VCL 窗口重新创建,HandleAllocatedHandle 上存在明显的竞争。所以即使你想知道FrmSender.Handle,在帖子里问也是不对的。

    您实际上是将消息发送到线程句柄而不是窗口句柄。这意味着消息甚至永远不会发送,这是PeekMessage 无法返回的另一个原因。如果您在调用PostMessage 时检查了返回值,您就会知道这一点。

    我会使用PostThreadMessage,或者将消息发布到线程中分配的窗口并调用AllocateHWnd

    一旦你确实成功地发送了消息,你使用PM_NOREMOVE 意味着消息队列永远不会被清空。

    您对Sleep 的使用在我看来非常可疑。为什么不使用GetMessage 并阻塞直到消息到达。任何时候看到Sleep 的电话,都要非常怀疑。

    您对Integer 的强制转换将导致 64 位构建中的指针截断。要转换为的正确类型是 LPARAM

    我预计还有其他错误,这些只是我在 2 分钟的快速扫描中可以看到的错误。

    【讨论】:

    • 不知道 x64 构建上的 Integer() 转换问题。谢谢!
    • 你真的需要解决所有其他问题
    • 不仅代码会受到窗口重新创建竞赛的影响,而且另一个问题是一个线程根本无法获取属于另一个线程的窗口的消息(除非您通过@987654338 使用消息挂钩@)。只有创建窗口的线程才能接收该窗口的消息。另请注意,AllocateHWnd() 不是线程安全的,不应在主线程之外使用。
    • 我总是忘记这一点,因为我使用的是线程安全的版本
    • 实际上我的问题有一个错误,因为我的印象是 PeekMessage() 的第二个参数是消息来自的表单的句柄。我现在传递 0 并向线程检索任何消息,因为它没有任何自己的形式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-31
    • 1970-01-01
    • 2020-11-15
    • 2011-09-07
    • 2017-10-03
    • 1970-01-01
    相关资源
    最近更新 更多