使用带有可变常量的Delphi的message关键字处理程序语法?

Ian*_*oyd 9 delphi

简洁版本

任何使用方式:

    procedure WMStuff(var Message: TMessage); message WM_Stuff;
Run Code Online (Sandbox Code Playgroud)

什么时候WM_Stuff是变量?

长版

Delphi具有一点点可爱的编译器魔力,使处理消息变得如此容易。您只需使用message WM_TheMessage关键字标记过程即可:

procedure WMGrobFrobber(var Message: TMessage); message WM_GrobFrobber;
Run Code Online (Sandbox Code Playgroud)

然后将调用您的过程来处理该消息。没有子类。无需替换,存储和调用基本窗口过程。只是简单而简单的魔术。

对于常数

WM_GrobFrobber是时,此消息效果很好const

const
   WM_GrobFrobber = WM_APP + $12A9;  //hopefully nobody's used this message before
Run Code Online (Sandbox Code Playgroud)

但是这样声明的常量的缺点是:

  • 我必须希望当消息广播到所有窗口时,没有其他组件或库已经将该常量用于其他用途
  • 我无法将该消息广播,发送或发布到其他进程

Windows建议您使用RegisterWindowMessage确保安全地拥有唯一的消息号:

定义一个新的窗口消息,该消息在整个系统中保证是唯一的。在发送或发布消息时可以使用消息值。

RegisterWindowMessage函数通常用于为两个合作的应用程序之间进行通信的注册消息。

如果两个不同的应用程序注册相同的消息字符串,则这些应用程序将返回相同的消息值。该消息将保持注册状态,直到会话结束。

仅当多个应用程序必须处理同一条消息时才使用RegisterWindowMessage。为了在窗口类中发送私人消息,应用程序可以使用WM_USER到0x7FFF 范围内的任何整数。(此范围内的消息专用于窗口类,而不是应用程序专用。例如,预定义的控件类(例如BUTTONEDITLISTBOXCOMBOBOX)可以使用此范围内的值。)

那就是我所需要的:

  • 我将从其他窗口类(例如TForm1vs TForm2vs TVirtualTreeHintWorkerThread)发送(或接收)消息
  • 我将从其他应用程序中的其他Windows类发送(或接收)消息。

因此,我注册了我的消息:

var
   WM_GrobFrobber: Cardinal;

initialization
    WM_GrobFrobber := RegisterWindowMessage('Contoso.Grobber.GrobFrobber message');
Run Code Online (Sandbox Code Playgroud)

常数变数

但是现在我不能再使用漂亮的语法了:

procedure WMGrobFrobber(var Message: TMessage); message WM_GrobFrobber;
   //Constant expression expected
Run Code Online (Sandbox Code Playgroud)

:(

我试图破解一个可分配的类型常量

{$J+}
const
   WM_GrobFrobber: Cardinal = 0;

initialization
    WM_GrobFrobber := RegisterWindowMessage('Contoso.Grobber.GrobFrobber message');
Run Code Online (Sandbox Code Playgroud)

但是该message关键字也不接受* pseudo- *常量:

procedure WMGrobFrobber(var Message: TMessage); message WM_GrobFrobber;
   //Constant expression expected
Run Code Online (Sandbox Code Playgroud)

有什么方法可以挽救可爱,简单的message语法,而不必将可能要处理消息的每个窗口都子类化?

尤其是由于该消息实际上不是恒定的;但是是不是我的人发明和注册的。

Del*_*ics 11

正如David在他的回答中指出的那样,声明性消息处理程序中的消息ID必须是常量表达式,因此无法以这种方式实现可变消息号的处理程序。

但是,您仍然不需要对每个窗口进行子类化即可响应此类消息。或更确切地说,您不需要通过声明表单或控件类就可以执行任何其他子类化。

您可以通过覆盖虚拟WndProc方法来处理自定义的注册消息。您将无法使用select .. case语句来处理消息,因为这同样需要匹配情况的常量表达式,但是您可以使用简单的if .. then语句来捕获消息,并调用inherited其他所有内容:

procedure TMyForm.WndProc(var aMessage: TMessage);
begin
  if aMessage.Msg = WM_GrobFrobber then
  begin
    { Handle the message or pass to a WMGrobFrabber() method 
      with suitably repacked and typed params, as required/desired }
  end
  else
    inherited WndProc(aMessage);
end;
Run Code Online (Sandbox Code Playgroud)

您可以virtual WMGrobFrabber在一个表单类中引入一个,然后将其始终用作应用程序中所有表单的基类,以便您可以简单地重写该方法来处理此消息,而不必WndProc每次都重新定义条件处理程序代码时间。

这不能解决您的所有问题。它没有提供使用声明式消息处理程序语法的方法,但是仍然很优雅(恕我直言)。

如果此类消息专门用于响应广播消息(我认为这是您唯一需要关注的消息ID与其他人所使用的ID冲突的情况),则可以创建一个非可视组件来专门实现消息处理程序通过使用已发布的事件处理程序触发事件来响应此消息。然后,您根本不需要子类即可在表单上实现对此类广播的响应,只需将处理程序组件放在表单上并为该组件的事件实现处理程序即可。

这显然比在回答这个问题时要处理的要复杂得多,但可能值得考虑。

  • 您甚至不需要重写该类。您可以替换`WindowProc`属性。 (3认同)

Dav*_*nan 8

消息方法关联的消息ID 必须为常量表达式