|
delphi中的消息处理机制 |
| delphi是borland公司提供的一种全新的windows编程开发工具.由于它采用了具有弹性的和可重用的面向对象pascal(object-oriented pascal)语言,并有强大的数据库引擎(bde), 快速的代码编译器, 同时又提供了众多出色的构件.受到广大编程人员的青 睐. 在众 多 的 编 程 语 言( 如vb,powerbuilder,powerpoint 等) 中 脱 颖 而 出. 其中一个delphi强于其他编程语言(如vb4.0)的地方就是在delphi中可自定义消息, 并可直接处理消息. 这对于那些希望编写自己 的 构 件(component), 或 者 希 望 截 获. 过 滤 消 息 的 用 户 来 说 是 必 不 可 少 的. 因 为编 写 构 件 一 般 要 对相应的消息进行处理.下面就对delphi 中消息处理机制进行一下介绍。 一.delphi vcl中消息的传递 delphi中每一个vcl(visual component library)构件( 如tbutton,tedit等)都有一内在的消息处理机制,其基本点就是构件类 接收到某些消息并把它们发送给适当的处理方法,如果没有特定的处理方法,则调用缺省的消息处理句柄。其中mainwndproc是定义在twincontrol类中的一个静态方法,不能被重载(override)。它不直接处理 消 息, 而 是 交 由wndproc 方 法 处 理, 并 为wndproc方法提供一个异常处理模块。mainwndproc方法声明如下: procedure mainwndproc(var message: tmessage); wndproc是在tcontrol类中定义的一个虚拟方法,由它调用dispatch方法来进行消息的分配, wndproc 方 法 声 明 如 下: procedure wndproc(var message: tmessage); virtual; dispatch 方 法 是 在tobject 根 类 中 定 义 的, 其 声 明 如 下: procedure tobject.dispatch(var message); 传 递 给dispatch 的 消 息 参 数 必 须 是 一 个 记 录 类 型, 且这 个 记 录 中 第 一 个 入 点 必 须 是 一 个cardinal 类 型 的 域(field), 它 包 含 了 要 分 配 的 消 息 的 消 息 号 码. 例 如: type tmessage=record msg:cardinal; wparam:word; lparam:longint; . result:longint; end; 而dispatch 方 法 会 根据 消 息 号 码 调 用 构 件 的 最 后 代 类 中 处 理 此 消 息 的 句 柄 方法. 如 果 此 构 件 和 它 的 祖 先 类 中 都 没 有 对 应 此 消 息 的 处 理句 柄,dispatch 方 法 便 会 调 用defaulthandler 方 法.defaulthandler 方法 是 定 义 于tobject 中 的 虚 拟 方 法, 其 声 明 如 下: procedure defaulthandler(var message);virtual; tobject 类 中 的defaulthandler 方 法 只 是 实 现 简 单 的 返 回 而 不 对 消 息 进 行 任 何 的 处 理. 我们 可 以 通 过 对 此 虚 拟 方 法 的 重 载, 在 子 类 中 实 现 对 消 息 的缺 省 处 理. 对 于vcl 中 的 构 件 而 言, 其defaulthandler 方 法 会 启 动 windows api 函 数defwindowproc 对 消 息 进 行 处 理. 二.delphi 中 的 消 息 处 理 句 柄 在delphi 中 用 户 可 以自 定 义 消 息 及 消 息 处 理 句 柄. 消 息 处 理 句 柄 的 定 义 有 如 下几 个 原 则: 消 息 处 理 句 柄 方 法 必 须 是 一 个 过 程, 且 只 能 传 递 一 个tmessage 型 变 量 参 数. 方 法 声 明 后 要 有 一 个message 命 令, 后 接 一 个 在0 到32767 之 间的 消 息 标 号( 整 型 常 数). 消 息 处 理 句 柄 方 法 不 需 要 用override 命 令 来 显 式 指 明 重 载祖 先 的 一 个 消 息 处 理 句 柄, 另 外 它 一 般 声 明 在 构 件 的protected 或private 区. 在 消 息 处 理 句 柄 中 一 般 先 是 用 户 自 己 对 消 息 的 处 理, 最 后用inherited 命 令 调 用 祖 先 类 中 对 应 此 消 息 的 处 理 句 柄( 有些 情 况 下 可 能 正 相 反). 由 于 可 能 对 祖 先 类 中 对 此 消 息 的处 理 句 柄 的 名 字 和 参 数 类 型 不 清 楚, 而 调 用 命 令inherited 可以 避 免 此 麻 烦, 同 样 如 果 祖 先 类 中 没 有 对 应 此 消 息 的 处 理句 柄,inherited 就 会 自 动 调 用defaulthandler 方 法.( 当 然 如 果 要屏 蔽 掉 此 消 息, 就 不 用inherited 命 令 了)。 消 息 处 理 句 柄 方 法 声 明 为: procedure mymsgmethod(var message:tmessage); message msgtype; 同 样 用 户 也 可 以定 义 自 己 的 消 息, 用 户 自 定 义 消 息 应 从wm_user 开 始. 自 定 义 消 息 及 消息 处 理 句 柄 举 例 如 下: const my_paint=wm_user+1; type tmypaint=record msgid:cardinal; msize:word; mcolor:longint; msgresult:longint; end; type tmycontrol=class(tcustomcontrol) protected procedure change(var message:tmypaint); message my_paint; ..... end; ...... procedure tmycontrol.change(var message:tmypaint); begin size:=message.msize; { 设 置tmybutton 尺 寸 属 性} color:=message.mcolor; { 设 置tmybutton 颜 色 属 性} {do something else} inherited; { 交 由tcustomcontrol 处 理} end; 三. 过 滤 消 息 过 滤 消 息 又 称 消 息陷 阱。 在 一 定 情 况 下, 用 户 可 能 需 要 屏 蔽 某 些 消 息. 或 者截 获 某 些 消 息 进 行 处 理。 由 以 上 介 绍 可 以 看 出 过 滤 消 息一 般 有 三 种 途 径:(1). 重 载 构 件 继 承 的 虚 拟 方 法wndproc. (2). 针 对 某 消 息 编 写 消 息 处 理 句 柄. (3). 重 载 构 件 继 承 的 虚 拟方 法defhandler, 在 其 中 对 消 息 进 行 处 理。 其 中 常 用 的 方 法是 方 法(2), 在 上 节 中 已 介 绍 过 了, 方 法(1) 与 方 法(3) 相 似,这 里 只 简 单 介 绍 一 下 方 法(1)。 重 载 虚 拟 方 法wndproc 的 一 般 过 程 如 下: procedure tmyobject.wndproc(var message:tmessage); begin {... 判 断 此 消 息 是 否 该 处 理..} inherited wndproc(message); { 未 处 理 的 消 息 交 由 父 辈wndproc 方 法 处 理} end; 由 此 可 以 看 出 在wndproc 方 法 中 处 理 消 息 的 优 势 是 可 以 过 滤 整 个 范 围 内 的 消 息,而 不 必 为 每 个 消 息 指 定 一 个 处 理 句 柄, 事 实 上tcontrol 构 件中 就 是 利 用 它 来 过 滤 并 处 理 所 有 的 鼠 标 消 息 的( 从wm_mousefirst 到wm_mouselast, 如 下 代 码 示). 同 样 利 用 它 也 可 以 阻 止 某 些 消息 被 发 送 给 处 理 句 柄。 procedure tcontrol.wndproc(var message: tmessage); begin if (message.msg>=wm_mousefirst) and (message.msg <= wm_mouselast) then if dragging then { 处 理 拖 曳 事 件} dragmousemsg(twmmouse(message)) else ... { 处 理 其 他 鼠 标 消 息} end; dispatch(message); { 否 则 正 常 发 送 消 息} end; 下 例 为 一 简 单 的 自定 义 构 件 例 子 : tmyedit 类 是 从tedit 类 派 生 出 的 一 个 新 类, 它 的 特 点 是 在 运 行 中 不 能 获 得 焦点, 不 能 由 键 盘 输 入( 有 点 类 似tlabel 构 件). 我 们 可 在 其wndproc 方 法 中 过 滤 出wm_setfocus,wm_mousemove 消 息 并 进 行 处 理 来 达 到上 述 要 求, 源 程 序 如 下: unit myedit; interface uses windows, messages, sysutils, classes, graphics, controls, forms, dialogs, stdctrls; type tmyedit = class(tedit) private { private declarations } protected { protected declarations } { other fields and methods} procedure wndproc(var message:tmessage);override; public { public declarations } published { published declarations } end; procedure register; implementation procedure register; begin registercomponents('samples', [tmyedit]); end; procedure tmyedit.wndproc(var message:tmessage); begin if message.msg=wm_mousemove then begin cursor:=crarrow; { 设 置 光 标 为crarrow, 而 不 是 缺 省 的crbeam 光 标} exit; end; if message.msg=wm_setfocus then exit; {屏蔽掉wm_setfocus消息,不让tmyedit控件获得输入焦点} inherited wndproc(message); {其他消息交父辈wndproc处理} end; end. 您可以将tmyedit 加到component palette中检验其性能。 由以上介绍可以看出,只有清楚了delphi vcl中的消息处理机制, 掌握好处理各种消息的方法和时机(必要时要借助各种工具, 如winsight32,spy 等),并结合oop语言的特点, 我们才可能编出高质量的构件。 这当然要靠读者在实践中不断摸索,积累经验. |