首页 > 解决方案 > 将 Delphi 的消息关键字处理程序语法与变量常量一起使用?

问题描述

精简版

任何使用方式:

    procedure WMStuff(var Message: TMessage); message WM_Stuff;

什么时候WM_Stuff是变量?

长版

Delphi 有一个非常可爱的编译器魔法让处理消息变得如此简单。您只需使用message WM_TheMessage关键字标记您的过程:

procedure WMGrobFrobber(var Message: TMessage); message WM_GrobFrobber;

并且您的程序将被调用来处理该消息。没有子类化。没有替换、存储和调用基本窗口过程。只是简单的魔术。

对于常数

WM_GrobFrobber是 a时,该消息效果很好const

const
   WM_GrobFrobber = WM_APP + $12A9;  //hopefully nobody's used this message before

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

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

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

RegisterWindowMessage函数通常用于注册消息,以便在两个协作应用程序之间进行通信。

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

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

这就是我需要的:

所以我注册了我的消息:

var
   WM_GrobFrobber: Cardinal;

initialization
    WM_GrobFrobber := RegisterWindowMessage('Contoso.Grobber.GrobFrobber message');

常数变量

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

procedure WMGrobFrobber(var Message: TMessage); message WM_GrobFrobber;
   //Constant expression expected

:(

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

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

initialization
    WM_GrobFrobber := RegisterWindowMessage('Contoso.Grobber.GrobFrobber message');

message关键字也不接受 *pseudo-*constants :

procedure WMGrobFrobber(var Message: TMessage); message WM_GrobFrobber;
   //Constant expression expected

有什么方法可以挽救可爱、简单的message语法,并且不必对每个可能想要处理消息的窗口进行子类化?

特别是因为消息确实不是常数;而是由不是我的人发明和注册的。

标签: delphi

解决方案


正如 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;

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

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

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

这显然比在回答这个问题时更适合处理,但可能值得考虑。


推荐阅读