⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 delphi消息机制.txt

📁 Delphi的消息机制
💻 TXT
📖 第 1 页 / 共 4 页
字号:
从名字也可以看出该函数的大概目的:最终的消息处理函数。在 TObject 的定义中 DefaultHandler 并没有代码,DefaultHandler 是在需要处理消息的类(TControl)之后被重载的。

从上面的讨论中已经知道 DefaultHandler 是由 TObject.Dispatch 调用的,所以 DefaultHandler 和 Dispatch 的参数类型一样都是无类型的 var Message。

由于 DefaultHandler 是个虚方法,所以执行流程是从子类到父类。在 TWinControl 和 TControl 的 DefaultHandler 中,仍然遵从 WndProc 的执行规则,也就是 TWinControl 没处理的消息,再使用 inherited 调用 TControl.DefaultHandler 来处理。

在 TWinControl.DefaultHandler 中先是处理了一些不太重要的Windows 消息,如WM_CONTEXTMENU、WM_CTLCOLORMSGBOX等。然后做了两件比较重要的工作:1、处理 RM_GetObjectInstance 消息;2、对所有未处理的窗口消息调用 TWinControl.FDefWndProc。
下面分别讨论。

RM_GetObjectInstance 是应用程序启动时自动使用 RegisterWindowMessage API 注册的 Windows 系统级消息ID,也就是说这个消息到达 Dispatch 后会无条件地传递给 DefaultHandler(见 Dispatch 的分析)。TWinControl.DefaultHandler 发现这个消息就把 Self 指针设置为返回值。在 Controls.pas 中有个函数 ObjectFromHWnd 使用窗口句柄获得 TWinControl 的句柄,就是使用这个消息实现的。不过这个消息是由 Delphi 内部使用,不能被应用程序使用。(思考:每次应用程序启动都会调用 RegisterWindowMessage,如果电脑长期不停机,那么 0xC000 - 0xFFFF 之间的消息 ID 是否会被耗尽?)

另外,TWinControl.DefaultHandler 在 TWinControl.FHandle 不为 0 的情况下,使用 CallWindowProc API 调用 TWndControl.FDefWndProc 窗口过程。FDefWndProc 是个指针,它是从哪里初始化的呢?跟踪一下,发现它是在 TWinControl.CreateWnd 中被设置为如下值:

    FDefWndProc := Params.WindowClass.lpfnWndProc;

还记得前面讨论的窗口创建过程吗?TWinControl.CreateWnd 函数首先调用 TWinControl.CreateParams 获得待创建的窗口类的参数。CreateParams 把 WndClass.lpfnWndProc 设置为 Windows 的默认回调函数 DefWindowProc API。但 CreateParams 是个虚函数,可以被 TWinControl 的继承类重载,因此程序员可以指定一个自己设计的窗口过程。

所以 TWinControl.DefaultHandler 中调用 FDefWndProc 的意图很明显,就是可以在 Win32 API 的层次上支持消息的处理(比如可以从 C 语言写的 DLL 中导入窗口过程给 VCL 控件),给程序员提供充足的弹性空间。

TWinControl.DefaultHandler 最后一行调用了 inherited,把消息传递给 TControl 来处理。

TControl.DefaultHandler 只处理了三个消息 WM_GETTEXT、WM_GETTEXTLENGTH、WM_SETTEXT。为什么要处理这个几个看似不重要的消息呢?原因是:Windows 系统中每个窗口都有一个 WindowText 属性,而 VCL 的 TControl 为了模拟成窗口也存储了一份保存在 FText 成员中,所以 TControl 在此接管这几个消息。

TControl.DefaultHandler 并没有调用 inherited,其实也没有必要调用,因为 TControl 的祖先类都没有实现 DefaultHandler 函数。可以认为 DefaultHandler 的执行到此为止。

VCL 的消息流程至此为止。

===============================================================================
⊙ TControl.Perform 和 TWinControl.Broadcast
===============================================================================
现在介绍 VCL 消息系统中两个十分简单但调用频率很高的函数。

TControl.Perform 用于直接把消息送往控件的消息处理函数 WndProc。Perform 方法不是虚方法,它把参数重新组装成一个 TMessage 类型,然后调用 WindowProc(还记得 WindowProc 的作用吗?),并返回 Message.Result 给用户。它的调用格式如下:

  function TControl.Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;

Perform 经常用于通知控件某些事件发生,或得到消息处理的结果,如下例:

  Perform(CM_ENABLEDCHANGED, 0, 0);
  Text := Perform(WM_GETTEXTLENGTH, 0, 0);

TWinControl.Broadcast 用于把消息广播给每一个子控件。它调用 TWinControl.Controls[] 数组中的所有对象的 WindowsProc 过程。

  procedure TWinControl.Broadcast(var Message);

注意 Broadcast 的参数是无类型的。虽然如此,在 Broadcast 函数体中会把消息转换为 TMessage 类型,也就是说 Broadcast 的参数必须是 TMessage 类型。那么为什么要设计为无类型的消息呢?原因是 TMessage 有很多变体(Msg 和 Result 字段不会变,WParam 和 LParam 可设计为其它数据类型),将 Broadcast 设计为无类型参数可以使程序员不用在调用前强制转换参数,但调用时必须知道这一点。比如以下字符消息的变体,是和 TMessage 兼容的:

  TWMKey = packed record
    Msg: Cardinal;
    CharCode: Word;
    Unused: Word;
    KeyData: Longint;
    Result: Longint;
  end;

===============================================================================
⊙ TWinControl.WMPaint
===============================================================================
上面在讨论 TWinControl.WndProc 时提到,TControl 类控件的鼠标和重绘消息是从 Parent TWinControl 中产生的。但我们只发现了鼠标消息的产生,那么重绘消息是从哪里产生出来的呢?答案是TWinControl.WMPaint:

    procedure TWinControl.WMPaint(var Message: TWMPaint); message WM_PAINT;

在 TWinControl.WMPaint 中建立了双缓冲重绘机制,但我们目前不关心这个,只看最关键的代码:

    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      inherited                 // 注意 inherited 的实现
    else
      PaintHandler(Message);    

这段代码的意思是,如果控件不支持自绘制并且不包含 TControl 就调用 inherited。
inherited 是什么呢?由于 TWinControl.WMPaint 的父类 TControl 没有实现这个消息句柄,Delphi 生成的汇编代码竟然是:call Self.DefaultHandler。(TWinControl.DefaultHandler 只是简单地调用 TWinControl.FDefWndProc。)

如果条件为否,那么将调用 TWinControl.PaintHandler(不是虚函数)。PaintHandler 调用 BeginPaint API 获得窗口设备环境,再使用该设备环境句柄为参数调用 TWinControl.PaintWindow。在 TWinControl 中 PaintWindow 只是简单地把消息传递给 DefaultHandler。PaintWindow 是个虚函数,可以在继承类中被改写,以实现自己需要的绘制内容。PaintHandler 还调用了 TWinControl.PaintControls 方法。PaintControls 使用 Perform 发送 WM_PAINT 消息给 TWinControl 控件包含的所有 TControl 控件。

这样,TControl 控件才获得了重绘的消息。

让我们设计一个 TWinControl 的继承类作为练习:

TMyWinControl = class(TWinControl)
  protected
    procedure PaintWindow(DC: HDC); override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

constructor TMyWinControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlState := ControlState + [csCustomPaint];
  // 必须通知 WMPaint 需要画自己
end;

procedure TMyWinControl.PaintWindow(DC: HDC);
var
  Rect: TRect;
begin
  Windows.GetClientRect(Handle, Rect);
  FillRect(DC, Rect, COLOR_BTNSHADOW + 1);
  SetBkMode(DC, TRANSPARENT);
  DrawText(DC, ‘Hello, TMyWinControl’, -1, Rect, DT_SINGLELINE or DT_VCENTER
    or DT_CENTER);
end;

上面实现的 TMyWinControl 简单地重载 PaintWindow 消息,它可以包含 TControl 对象,并能正确地把它们画出来。如果你确定该控件不需要包含 TControl 对象,你也可以直接重载 WMPaint 消息,这就像用 C 语言写普通的 WM_PAINT 处理函数一样。

===============================================================================
⊙ 以 TWinControl 为例描述消息传递的路径
===============================================================================
下图描述一条消息到达后消息处理函数的调用路径,每一层表示函数被上层函数调用。

TWinControl.FObjectInstance
 |-TWinControl.MainWndProc
      |-TWinControl.WindowProc
          |-TWinControl.WndProc
              |-TControl.WndProc
                  |-TObject.Dispatch
                      |-Call DMT messages
                      |-TWinControl.DefaultHandler
                          |-TControl.DefaultHandler

注:
如前文所述,上图中的 WindowProc 是个指针,所以它在编译器级实际上等于 WndProc,而不是调用 WndProc,图中为了防止与消息分枝混淆特意区分成两层。
TObject.Dispatch 有两条通路,如果当前控件以 message 关键字实现了消息处理函数,则呼叫该函数,否则调用 DefaultHandler。
有些消息处理函数可能在中途就已经返回了,有些消息处理函数可能会被递归调用。

===============================================================================
结束语
VCL 的消息机制就讨论到这里。希望我们通过本文的讨论理清了 VCL 处理消息的框架,今后我们将使用这些最基础的知识开始探索 Delphi 程序设计的旅程。
===============================================================================

   Post in Delphi    


--------------------------------------------------------------------------------

0 Responses to “Delphi 的消息机制浅探”
Feed for this Entry Trackback Address 
没有评论 Leave a Reply
 名称 

 Mail ( will not be published ) 

 网址 

 

 

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>   


提示:如果你刚刚提交过评论,但是还没有被显示出来,请点击这里刷新一下: 刷新评论 。

--------------------------------------------------------------------------------
Robot5’s Blog is proudly powered by WordPress and Blue Memories by iqwolf. AK47 | ITech Upload. 
Entries (RSS) and Comments (RSS).

0 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -