delphi - 将 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 范围内的任何整数。(此范围内的消息是窗口类私有的,而不是应用程序私有的。例如,预定义的控件类,如BUTTON、EDIT、LISTBOX和COMBOBOX可以使用此范围内的值。)
这就是我需要的:
- 我将从其他窗口类(例如
TForm1
vsTForm2
vsTVirtualTreeHintWorkerThread
)发送(或接收)消息 - 我将在其他应用程序中从其他 Windows 类发送(或接收)消息。
所以我注册了我的消息:
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
语法,并且不必对每个可能想要处理消息的窗口进行子类化?
特别是因为消息确实不是常数;而是由不是我的人发明和注册的。
解决方案
正如 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 冲突的唯一情况),那么您可以创建一个专门实现消息处理程序的非可视组件通过使用已发布的事件处理程序触发事件来响应此消息。然后,您根本不需要子类来实现对表单上此类广播的响应,只需在表单上放置一个处理程序组件并为该组件的事件实现一个处理程序。
这显然比在回答这个问题时更适合处理,但可能值得考虑。
推荐阅读
- python-3.x - 在 Pandas 中重新排列列然后连接
- database - 我需要规范化我的 SQLite3 数据库吗?
- hadoop - Presto 与 hive 的集成不起作用
- javascript - 即使遵循 react-navigation 文档,react-navigation 也无法在屏幕之间导航
- c - 参数没有从输入函数中传递出去
- sql - 如何使用 SQL Server 检索月份数据?
- python - Python 日志查询
- oracle - oracle中有“立即执行”时如何实现refcursor?
- reactjs - 如何访问数组对象中的元素 [API]
- reactjs - 当InfiniteScroll在reactjs中向下滚动时如何保持其他组件保持在顶部位置