Delphi线程没有收到消息

tsm*_*smr 4 delphi multithreading vcl

我正在开发一个多线程应用程序(RAD Studio XE5).在应用程序的开始,我创建一个单独的线程,只要主表单生效.

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

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

  serverThread := TMyThread.Create(True, ServerPort + 1);
  serverThreadHandle := serverThread.Handle; // SAVE HANDLE
  serverThread.Start;
Run Code Online (Sandbox Code Playgroud)

然后(从另一种形式FrmSender)我向线程发送一条消息:

  PostMessage(uMain.serverThreadHandle, UM_LOC_VCLMSG, UM_LOC_VCLMSG, Integer(PStrListVar));
Run Code Online (Sandbox Code Playgroud)

这是线程的执行过程:

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;
Run Code Online (Sandbox Code Playgroud)

但是,PeekMessage()永远不会返回true,就好像它从未接收任何消息一样.我已经尝试将参数更改为PeekMessage():

PeekMessage(vclMSG, 0, 0, 0, PM_NOREMOVE);
Run Code Online (Sandbox Code Playgroud)

但没有结果.有任何想法吗?

Dav*_*nan 5

PeekMessage(vclMSG, FrmSender.Handle, 0, 0, PM_NOREMOVE)
Run Code Online (Sandbox Code Playgroud)

第二个参数意味着您只会检索发送给发件人的邮件FrmSender.Handle.但是你把邮件发送给收件人了uMain.serverThreadHandle.这是为什么PeekMessage永远不能回归的原因之一.

像你一样从线程访问VCL是错误的.表单的句柄受VCL窗口重新创建的影响,并且有一个明确的比赛HandleAllocatedHandle.因此,即使您需要知道FrmSender.Handle,在线程中询问它也是错误的.

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

我会使用其中一个PostThreadMessage,或者通过调用将消息发布到线程中分配的窗口AllocateHWnd.

一旦您设法实际发送消息,您的使用PM_NOREMOVE意味着消息队列永远不会被清空.

Sleep对我的使用看起来非常可疑.为什么不使用GetMessage并阻止消息到达.无论何时你看到一个电话Sleep,都要非常怀疑.

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

我希望还有其他错误,这些只是我在快速2分钟扫描中看到的错误.


Ren*_*ann 5

MSDN PostMessage功能文档:

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

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

因此,你应该使用PostThreadMessage:

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

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

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

    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
    
    Run Code Online (Sandbox Code Playgroud)
  • 设置事件,以指示线程已准备好接收已发布的消息.

然后,在使用时PeekMessage,将句柄值传递-1给函数,如文档所示.