【问题标题】:wparam value in GetMessage not what I expected (Delphi XE4)GetMessage 中的 wparam 值不是我所期望的(Delphi XE4)
【发布时间】:2015-02-27 18:37:55
【问题描述】:

我正在开发一个 Delphi XE4 服务应用程序。该服务为一些长时间运行的任务启动线程,线程通过 PostThreadMessage 调用返回状态。

ServiceExecute 主循环如下所示:

procedure TScanService.ServiceExecute(Sender: TService);
var
  CurrentMessage: TMsg;
begin
  LogServerEvent('ServiceExecute', 'Starting');
  while not Terminated do
  begin
    if not PeekMessage(CurrentMessage, 0, WM_NULL, msgHigh, PM_NOREMOVE) then
    begin
      Sleep(1000);
      Continue;
    end;

    GetMessage(CurrentMessage, 0, WM_NULL, msgHigh);

    LogServerEvent('ServiceExecute', 'CurrentMessage.message', IntToStr(CurrentMessage.message));
    LogServerEvent('ServiceExecute', 'CurrentMessage.wParam', IntToStr(CurrentMessage.wParam));
    LogServerEvent('ServiceExecute', 'CurrentMessage.lParam', IntToStr(CurrentMessage.lParam));

在线程中,消息发送如下:

    gThreadNumber: Integer;

    LogThreadEvent('Execute', 'Found Notice, Thread number: ' + IntToStr(gThreadNumber));
    PostThreadMessage(ParentThreadID, msgFound, gThreadNumber, 6);

消息到达正常,消息号正确(msgFound = WM_USER + 1);但是,我为 wParam、lParam 发送了 0、6,而我收到的是 4、0。我错过了什么?

注意:代码只运行了 2 个线程,其中一个是使用不同消息编号的计时器,并且在发生这种情况时不发送任何内容。

【问题讨论】:

  • 奇怪的消息循环,你可以单独使用GetMessage来阻止,直到有消息发布。
  • 我在您发布的代码中没有发现问题,这意味着问题可能不在您发布的代码中。你能把它缩减成一个小样本来演示我们可以实际编译和运行来重现它的问题吗?
  • 你没有选择你的HWND... RTL 使用WM_USER + x 范围内的一些消息;没有更多代码很难说,但是您可能会收到杂散的消息。如果您将-1 用于PeekMessageGetMessageHWND 参数,您将限制只接收使用NULL HWND 发布的消息(否则您还会收到窗口消息)- 前者只会包含消息使用带有空句柄的PostMessage 发送,使用PostThreadMessage 发送的消息。
  • @J...:不错的收获。我错过了。我通常使用 PostThreadMessage 在线程和 GUI 之间进行通信,并在收到消息时始终检查 Msg.hwnd = 0;我没想过通过服务这样做。
  • CM_SERVICE_CONTROL_CODE 是 WM_USER + 1,wparam 为 4 是 SERVICE_CONTROL_INTERROGATE。 TService.Controller 可能会发布它。如果是这种情况,检查 hwnd 将无济于事。

标签: multithreading delphi delphi-xe4 getmessage


【解决方案1】:

TService代码中你可以找到这个代码:

const
  CM_SERVICE_CONTROL_CODE = WM_USER + 1;
....
procedure TService.Controller(CtrlCode: DWord);
begin
  PostThreadMessage(ServiceThread.ThreadID, CM_SERVICE_CONTROL_CODE, CtrlCode, 0);
  if ServiceThread.Suspended then ServiceThread.Resume;
end;

所以TService 正在发送与您的消息具有相同消息编号的消息。你怎么能把它们区分开来?好吧,你不能。

来自WM_USER的文档:

用于定义私有窗口类使用的私有消息,通常形式为WM_USER+x,其中x为整数值。

通常这将用于发送到窗口的消息,并且消息的含义将是特定于类的。当我阅读TService代码时,设计者的观点是他们完全控制了消息队列,并有权确定其私人消息的含义。这样做的最终结果是,您不能使用 WM_USER 范围内的线程消息,因为它们已被 TService 类保留。

您的主要选择:

  • 将消息发送到窗口。
  • 使用RegisterWindowMessage 确保消息ID 的唯一性。
  • 停止使用 Windows 消息(不是专门用于服务)并使用其他机制进行通信。

【讨论】:

  • 如果我们遵守文档,'RegisterWindowMessage' 也不好。 “仅当有多个应用程序时才使用 RegisterWindowMessage ...”。我没有看到 WM_USER+ 有任何真正的危险,当您使用“PostThreadMessage”时,它永远不会被发送到窗口。那么它的意思就是你想要的意思。 WM_APP+ 可能会更好..
  • @Sertac 好的,如果用户会检查 hwnd,那么就不会有混淆。但是,我认为 WM_USER 是用于窗口类的。如果是我,我会创建一个窗口并且永远不会调用 PostThreadMessage。
  • -1 "这意味着您不能在 PostThreadMessage 中使用此范围内的消息" - WM_USER 的消息 ID 范围与 PostThreadMessage() 配合得很好(如果没有,TService本身不会运行,因为它在内部使用PostThreadMessage(WM_USER+1) 将SCM 请求发布到TService 消息队列)。 PostThreadMessage() 接受的消息 ID 没有限制。它所关心的只是一个有效的线程 ID,并附有一个消息队列。其他参数按原样发布到该线程的消息队列中。
  • OP 只是选择了一个 TService 已在使用的消息 ID,并将他的消息发布到 TService 将其私人消息发布到的同一线程消息队列中。就这样。这是使用WM_USER 的真正危险,而不是它根本不能与PostThreadMessage() 一起使用。
  • 好的,我能够确认收到的消息不是来自我的线程,因此作为临时解决方案,我将消息编号移高了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-02
  • 2013-11-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多