深入VCL 理解BCB的消息机制(二)

C++ Builder实战指南:从VCL框架到串口调试助手的现代开发 C++ Builder作为一款经典的集成开发环境,其核心价值在于将C++语言的底层控制能力与可视化快速开发(RAD)理念相结合。通过其特有的可视化组件库(VCL),开发者能够以拖拽控件的方式高效构建Windows桌面应用程序界面,并直接编写C++业务逻辑。这种开发模式特别适用于对性能有要求、需要直接进行硬件交互或数据采集的工业控制、仪器上位机等应用场景。在当今的软件开发中,尽管面临现代框架的竞争,但理解其独特的字符串处理机制(如AnsiString与UnicodeString)、基于所有权的内存管理模型以及 阅读详情

 

深入VCL 理解BCB的消息机制

方法2

Written by
CKER

方法2。重载TControl的WndProc方法

还是先谈谈VCL的继承策略。VCL中的继承链的顶部是TObject基类。一切的VCL组件和对象都继承自TObject。

打开BCB帮助查看TControl的继承关系:

TObject->TPersistent->TComponent->TControl

呵呵,原来TControl是从TPersistent类的子类TComponent类继承而来的。TPersistent抽象基类具有使用流stream来存取类的属性的能力。

TComponent类则是所有VCL组件的父类。

这就是所有的VCL组件包括您的自定义组件可以使用dfm文件存取属性的原因『当然要是TPersistent的子类,我想您很少需要直接从TObject类来派生您的自定义组件吧』。

TControl类的重要性并不亚于它的父类们。在BCB的继承关系中,TControl类的是所有VCL可视化组件的父类。实际上就是控件的意思吧。所谓可视化是指您可以在运行期间看到和操纵的控件。这类控件所具有的一些基本属性和方法都在TControl类中进行定义。

TControl的实现在/Borland/CBuilder5/Source/Vcl/control.pas中可以找到。『可能会有朋友问你怎么知道在那里?使用BCB提供的Search -> Find in files很容易找到。或者使用第三方插件的grep功能。』

好了,进入VCL的源码吧。说到这里免不了要抱怨一下Borland。哎,为什么要用pascal实现这一切.....:-(

TControl继承但并没有重写TObject的Dispatch()方法。反而提供了一个新的方法就是xycleo提到的WndProc()。一起来看看Borland的工程师们是怎么写的吧。

procedure TControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
begin
//由拥有control的窗体来处理设计期间的消息
  if (csDesigning in ComponentState) then
  begin
    Form := GetParentForm(Self);
    if (Form <> nil) and (Form.Designer <> nil) and
      Form.Designer.IsDesignMsg(Self, Message) then Exit;
  end
//如果需要,键盘消息交由拥有control的窗体来处理
  else if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then
  begin
    Form := GetParentForm(Self);
    if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit;
  end
//处理鼠标消息
  else if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then
  begin
    if not (csDoubleClicks in ControlStyle) then
      case Message.Msg of
        WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:
          Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
      end;
    case Message.Msg of
      WM_MOUSEMOVE: Application.HintMouseMessage(Self, Message);
      WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
        begin
          if FDragMode = dmAutomatic then
          begin
            BeginAutoDrag;
            Exit;
          end;
          Include(FControlState, csLButtonDown);
        end;
      WM_LBUTTONUP:
        Exclude(FControlState, csLButtonDown);
    end;
  end
// 下面一行有点特别。如果您仔细的话会看到这个消息是CM_VISIBLECHANGED.
// 而不是我们熟悉的WM_开头的标准Windows消息.
// 尽管Borland没有在它的帮助中提到有这一类的CM消息存在。但很显然这是BCB的
// 自定义消息。呵呵,如果您对此有兴趣可以在VCL源码中查找相关的内容。一定会有不小的收获。
  else if Message.Msg = CM_VISIBLECHANGED then
    with Message do
      SendDockNotification(Msg, WParam, LParam);
// 最后调用dispatch方法。
  Dispatch(Message);
end;

看完这段代码,你会发现TControl类实际上只处理了鼠标消息,没有处理的消息最后都转入Dispatch()来处理。

但这里需要强调指出的是TControl自己并没有获得焦点Focus的能力。TControl的子类TWinControl才具有这样的能力。我凭什么这样讲?呵呵,还是打开BCB的帮助。很多朋友抱怨BCB的帮助实在不如VC的MSDN。毋庸讳言,的确差远了。而且这个帮助还经常有问题。但有总比没有好啊。

言归正传,在帮助的The TWinControl Branch 分支下,您可以看到关于TWinControl类的简介。指出TWinControl类是所有窗体类控件的基类。所谓窗体类控件指的是这样一类控件:

1. 可以在程序运行时取得焦点的控件。

2. 其他的控件可以显示数据,但只有窗体类控件才能和用户发生键盘交互。

3. 窗体类控件能够包含其他控件(容器)。

4. 包含其他控件的控件又称做父控件。只有窗体类控件才能够作为其他控件的父控件。

5. 窗体类控件拥有句柄。

除了能够接受焦点之外,TWinControl的一切都跟TControl没什么分别。这一点意味着TwinControl可以对许多的标准事件作出响应,Windows也必须为它分配一个句柄。并且与这个主题相关的最重要的是,这里提到是由BCB负责来对控件进行重画以及消息处理。这就是说,TwinControl封装了这一切。

似乎扯的太远了。但我要提出来的问题是TControl类的WndProc方法中处理了鼠标消息。但这个消息只有它的子类TwinControl才能够得到啊!?

这怎么可以呢... Borland是如何实现这一切的呢?这个问题实在很奥妙。为了看个究竟,再次深入VCL吧。

还是在control.pas中,TWinControl继承了TControl的WndProc方法。源码如下:

procedure TWinControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
  KeyState: TKeyboardState;
  WheelMsg: TCMMouseWheel;
begin
  case Message.Msg of
    WM_SETFOCUS:
      begin
        Form := GetParentForm(Self);
        if (Form <> nil) and not Form.SetFocusedControl(Self) then Exit;
      end;
    WM_KILLFOCUS:
      if csFocusing in ControlState then Exit;
    WM_NCHITTEST:
      begin
        inherited WndProc(Message);
        if (Message.Result = HTTRANSPARENT) and (ControlAtPos(ScreenToClient(
          SmallPointToPoint(TWMNCHitTest(Message).Pos)), False) <> nil) then
          Message.Result := HTCLIENT;
        Exit;
      end;
    WM_MOUSEFIRST..WM_MOUSELAST:
      //下面这一句话指出,鼠标消息实际上转入IsControlMouseMsg方法来处理了。
      if IsControlMouseMsg(TWMMouse(Message)) then
      begin
        if Message.Result = 0 then
          DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam);
        Exit;
      end;
    WM_KEYFIRST..WM_KEYLAST:
      if Dragging then Exit;
    WM_CANCELMODE:
      if (GetCapture = Handle) and (CaptureControl <> nil) and
        (CaptureControl.Parent = Self) then
        CaptureControl.Perform(WM_CANCELMODE, 0, 0);
  else
    with Mouse do
      if WheelPresent and (RegWheelMessage <> 0) and
        (Message.Msg = RegWheelMessage) then
      begin
        GetKeyboardState(KeyState);
        with WheelMsg do
        begin
          Msg := Message.Msg;
          ShiftState := KeyboardStateToShiftState(KeyState);
          WheelDelta := Message.WParam;
          Pos := TSmallPoint(Message.LParam);
        end;
        MouseWheelHandler(TMessage(WheelMsg));
        Exit;
      end;
  end;
  inherited WndProc(Message);
end;

鼠标消息是由IsControlMouseMsg方法来处理的。只有再跟到IsControlMouseMsg去看看啦。源码如下:

function TWinControl.IsControlMouseMsg(var Message: TWMMouse): Boolean;
var
  //TControl出现啦
  Control: TControl;
  P: TPoint;
begin
  if GetCapture = Handle then
  begin
    Control := nil;
    if (CaptureControl <> nil) and (CaptureControl.Parent = Self) then
      Control := CaptureControl;
  end else
    Control := ControlAtPos(SmallPointToPoint(Message.Pos), False);
  Result := False;
  if Control <> nil then
  begin
    P.X := Message.XPos - Control.Left;
    P.Y := Message.YPos - Control.Top;
    file://TControl的Perform方法将消息交由WndProc处理。
    Message.Result := Control.Perform(Message.Msg, Message.Keys, Longint(PointToSmallPoint(P)));
    Result := True;
  end;
end;

原来如此,TWinControl最后还是将鼠标消息交给TControl的WndProc来处理了。这里出现的Perform方法在BCB的帮助里可以查到是TControl类中开始出现的方法。它的作用就是将指定的消息传递给TControl的WndProc过程。

结论就是TControl类的WndProc方法的消息是由TwinControl类在其重载的WndProc方法中调用IsControlMouseMsg方法后使用Peform方法传递得到的。

由于这个原因,BCB和Delphi中的TControl类及其所有的派生类都有一个先天的而且是必须的限制。那就是所有的TControl类及其派生类的Owner必须是TwinControl类或者TWinControl的派生类。Owner属性最早可以在TComponent中找到,一个组件或者控件是由它的Owner拥有并负责释放其内存的。这就是说,当Owner从内存中释放的时候,它所拥有的所有控件占用的内存也都被释放了。Owner最好的例子就是Form。Owner同时也负责消息的分派,当Owner接收到消息的时候,它负责将应该传递给其所拥有的控件的消息传递给它们。这样这些控件就能够取得处理消息的能力。TImage就是个例子:你可以发现Borland并没有让TImage重载TControl的WndProc方法,所以TImage也只有处理鼠标消息的能力,而这种能力正是来自TControl的。

唧唧崴崴的说了一大堆。终于可以说处理消息的第二种方法就是重载TControl的WndProc方法了。例程如下:

void __fastcall TForm1::WndProc(TMessage &Message)
{
     switch (Message.Msg)
     {
            case WM_CLOSE:
                 OnCLOSE(Message);  // 处理WM_CLOSE消息的方法
            break;
     }
     TForm::WndProc(Message);
}

乍看起来,这和上次讲的重载Dispatch方法好象差不多。但实际上还是有差别的。差别就在先后次序上,从前面TControl的WndProc可以看到,消息是先交给WndProc来处理,最后才调用Dispatch方法的啦。

这样,重载WndProc方法可以比重载Dispatch方法更早一点点得到消息并处理消息。

好了,这次就说到这里。在您的应用程序里还有没有比这更早得到消息的办法呢?有,下次再说。

 

版权说明:
您可以随意复制、分发、下载此文档。但未经本人同意,您不可以截取、改动本文片断,或用本文谋取任何形式的利益。

C++ Builder自研JSON解析与HTTP客户端库:解决VCL开发网络交互痛点 在现代软件开发中,JSON数据交换格式和HTTP网络协议是构建客户端-服务端应用的核心技术。JSON以其轻量、易读的特性,成为API通信的事实标准;而HTTP协议则是实现网络请求与响应的基础。理解JSON的语法结构、解析原理,以及HTTP的请求/响应模型、状态码机制,对于开发健壮的客户端应用至关重要。在C++ Builder的VCL生态中,由于编译器兼容性和库生态的限制,直接使用现代C++库(如nlohmann/json、cpp-httplib)常面临编译失败或集成困难。为此,自研一套深度契合VCL范式、对 阅读详情

相关推荐

C++Builder核心编程:VCL机制、内存管理与现代化改造实战

在Windows桌面应用开发领域,C++Builder作为经典的RAD开发平台,其核心架构基于VCL(Visual Component Library)对象模型与所有权机制。通过运行时类型信息(RTTI)和流化系统,VCL实现了高效的可视化设计与动态界面加载。在工程实践中,正确处理VCL对象生命周期与智能指针的融合、UnicodeString字符串编码转换以及多线程安全更新UI,是保障应用稳定性的关键。这些技术对于维护和升级遗留系统、在关键业务场景中实现渐进式现代化改造具有重要价值,特别是在处理大型C++B

weixin_30411239的博客 508

BCB实现文件上传下载(转)

1. INDY的安装   Internet Direct,一个开源的IDE网络应用组件,支持客户端、服务端、TCP、UDP、原始套接字等操作,组件包括SMTP、POP3、NNTP、HTTP、FTP等应用。  在BCB6下安装过程如下:  A. 删除旧indy组件:使用BCB6安装光盘,在自启动选项中选择Modify。在"Program Maintenance"中取消Indy,下一步后安装程

Lixinag的专栏 4203

睿思综述智能体:100篇高质量文献一键生成万字长文,还能联网PubMed,科研效率拉满!

对一个主题进行文献综述,是日常科研人绕不过去的工作,无论是写论文前言,综述论文,或者基金申请书的立项依据,需要根据主题,从海量文献中提取相关信息,梳理出清晰的逻辑线,再组织成流畅的文字。4 一键生成万字全文:大纲确认后,它会基于对所有论文的深度理解,逐个章节进行详细撰写,最终生成一篇逻辑连贯、内容翔实、引用规范的万字综述长文,并直接保存为Word(.docx)文件。3 智能撰写大纲:你可以提供自己的大纲,也可以让它根据所有文献内容,智能生成一个逻辑清晰的综述大纲,并支持你在此基础上修改。

dinaxuejie的博客 821

C++ builder 控件消息重载

最近在工作需要写一个功能就是把在P

明明先生的专栏 1220

科研作图不发愁:手把手教你用PyMOL给蛋白-DNA互作图‘美颜’(附完整配色与标注方案)

本文详细介绍了如何使用PyMOL对蛋白-DNA互作结构图进行专业美化,包括从PDB文件加载、结构展示与标注、互作位点突出显示到高质量渲染输出的完整流程。通过具体案例(PDB ID:8SSS)演示了锌指结构域区分、关键残基标注和互作距离测量等实用技巧,帮助科研人员制作可直接用于期刊投稿的高质量示意图。

weixin_42523792的博客 552

(程序设计方法与实践)达拉崩吧的酒宴

达拉崩吧的酒宴 Description 很久很久以前,巨龙突然出现,带来灾难带走了公主又消失不见,王国十分危险,世间谁最勇敢,一位勇者赶来大声喊:“我要带上最好的剑,翻过最高的山,闯进最深的森林,把公主带回到面前”……最后,英雄 达拉崩巴斑得贝迪卜多比鲁翁 ,他战胜了巨龙 昆图库塔卡提考特苏瓦西拉松 ,国王把公主 米娅莫拉苏娜丹妮谢莉红 嫁给了 达拉崩巴斑得贝迪卜多比鲁翁 。 于是国王第二天要在 蒙达鲁克硫斯伯古比奇巴勒城 举办酒宴,一共准备了 N 桶酒。此时一位忠诚的仆人来报,有人在其中一桶酒里下毒,

What_If_I的博客 892

深入VCL 理解BCB的消息机制

<!--write_title("深入VCL 理解BCB的消息机制 ");//-->

心情之旅 1582

无人机+消防车:高楼灭火系统技术详解

传统的消防设备,如登高车、车载水枪、水炮和高喷车,在50米以上高度的火源面前往往力不从心,100米以上高层火灾则更加依赖消防员“爬楼”灭火,效率低下且风险极高。消防水带:采用聚乙烯丝材质,超轻、耐腐蚀、强度高,能够携带大量水或灭火泡沫进行高空喷水灭火作业。“无人机+消防车”高楼灭火系统主要由消防车和无人机两部分组成,通过机车联动实现高效灭火。高效灭火:无人机能够快速到达高层建筑顶部或指定位置进行灭火作业,大大提高了灭火效率。空压泡沫系统:配备超大水箱,支持长时间供应灭火泡沫,满足无人机灭火剂的需求。

专注多旋翼、复合翼无人机技术研究,从事低空经济行业。 1670

深入BCB理解VCL的消息机制

引子:本文所谈及的技术内容都来自于Internet的公开信息。由笔者在闲暇之际整理后,贴出来以飴网友,姑且妄称原创。每次在国外网站上找到精彩文章的时候,心中都会暗自叹息,为什么在中文网站难以觅得这类文章呢?其实原因大家都明白。时至今日,学习Windows编程的兄弟们都知道消息机制的重要性。所以理解消息机制也成了不可或缺的功课。大家都知道,Borland的C++ Builder以及Delphi的

leiwu的专栏 1224

关于 SAP Fiori Elements 开发项目里的本地 annotation.xml 文件

文件通过引用 SAP 提供的标准词汇表和本地 OData 服务的元数据,为 Fiori Elements 应用定义了一个基础的注解结构。开发者可以在此基础上添加具体的注解,以满足特定的业务需求和界面定制。文件用于定义本地注解,以补充或覆盖来自后端 OData 服务的元数据。元素内添加具体的注解,以定制 Fiori Elements 应用的界面和行为。的架构,用于在本地注解中定义实体、类型和注解。这使得在本地注解中可以引用该 OData 服务的实体和类型。等,用于控制界面元素的显示和行为。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 472

机器人动力学仿真软件:PyBullet_(10).案例分析:双足机器人行走仿真

在本节中,我们将详细探讨如何使用PyBullet进行双足机器人的行走仿真。双足机器人行走仿真是一个复杂的动力学问题,涉及多个关节的协调运动和环境交互。通过本节的学习,您将了解如何创建和控制双足机器人模型,设置仿真环境,以及实现基本的行走算法。首先,我们需要创建一个双足机器人模型。PyBullet支持多种模型格式,包括URDF(Unified Robot Description Format)和MJCF(MuJoCo XML Format)。URDF是一种广泛使用的机器人模型描述格式,我们将使用URDF来创建

kkchenjj的博客 909

深入VCL 理解BCB的消息机制(三)

New Page 1<!--font { font-family: Arial; line-height: 140%;font-size:14; margin-top: 1; margin-bottom: 1 }body { font-family: font }-->

没吃过猪肉,光看见猪跑啦.................... 4721

网络层隧道技术之ICMP隧道(pingTunnel/IcmpTunnel)

目录 ICMP隧道 使用ICMP搭建隧道(PingTunnel) 使用ICMP搭建隧道(Icmptunnel) ICMP隧道 ICMP隧道简单实用,是一个比较特殊的协议。在一般的通信协议里,如果两台设备要进行通信,肯定需要开放端口,而在ICMP协议下就不需要。最常见的ping命令就是利用的ICMP协议,攻击者可以利用命令行得到比回复更多的ICMP请求。在通常情况下,每个ping命令都有相...

谢公子的博客 6695

深入VCL理解BCB的消息机制2

深入VCL理解BCB的消息机制2 2012-12-25 19:54未知admin . 关键字:   重载TControl的WndProc方法   还是先谈谈VCL的继承策略。VCL中的继承链的顶部是TObject基类。一切的VCL组件和对象都继承自TObject。   打开BCB帮助查看TControl的继承关系:   TObject->TPersistent->TComponent

ksrsoft的专栏 1476

【020】C++ 动态内存管理解析:new 和 delete 的正确使用

C++中的动态内存分配是在程序运行时根据需要进行内存空间的分配和释放,与静态内存分配相比,具有更大的灵活性和适应性。动态内存分配可以通过new和delete或者malloc()和free()等函数实现。在程序运行时无法确定所需的内存大小。需要在多个函数之间共享数据,并且数据可能被频繁地创建和销毁。静态数组无法满足需求,例如需要支持可变长度数组。如果在动态分配内存后忘记了释放它,则会导致内存泄漏。为避免内存泄漏,可以使用智能指针或手动管理内存的方式来释放动态分配的内存。

Lion_Long的博客 1470

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

深入 BCB理解 VCL的消息机制

深入 BCB理解 VCL的消息机制Tommy nag_amigo@yahoo.com.cn> TApplication、TScreen和TForm 构成了所有BCB风格的 Win32 GUI程序的脊梁,他们控制 着您程序的行为。TApplication类提供的属性和方法封装了标准Windows程序的行为。 TApplication表现了在Windows操作系统中创建、运行、支持和销毁应用程序的

910

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

深入VCL 理解BCB的消息机制 (一)

   <!--font { font-family: Arial; line-height: 140%; font-size: 12pt; margin-top: 1; margin-bottom: 1 }body { font-family: font }--><TABLE bo

没吃过猪肉,光看见猪跑啦.................... 4407

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

深入VCL 理解BCB的消息机制 (一) (转)

深入VCL 理解BCB的消息机制 (一) (转)[@more@] microsoft FrontPage 4.0" name=GENERATOR> 深入VCL 理解BCB的消...

csd3176的博客 186

C++Builder 6深度解析:VCL框架、RAD开发与遗留系统维护实践

在桌面应用开发领域,快速应用开发(RAD)通过可视化组件和事件驱动模型,极大提升了图形用户界面(GUI)的构建效率。其核心原理在于将界面元素封装为可重用的组件,并通过属性、方法和事件(PME)模型进行交互,使开发者能够通过拖拽和配置快速搭建应用程序框架。这种技术价值在于显著降低开发门槛,缩短项目周期,尤其适合需要快速迭代的业务场景。以经典的VCL(Visual Component Library)框架为例,它通过组件所有权模型实现半自动内存管理,并与运行时库(RTL)紧密协作,为Windows平台原生应用提

weixin_30781775的博客 394

深入分析基于VCL派生的ActiveX控件的实现原理及应用

深入分析基于VCL派生的ActiveX控件的实现原理及应用 前言 这篇文章虽然是以VCL为题,但却是基于BCB的,也就是说是在VCL基础上使用ATL实现的ActiveX的原理分析,如果你是Delphi程序员,这篇文章可能不适合你,不过作者如果有时间会再写一篇“Delphi版的深入分析”,本篇文章比较深入的分析了VCL实现ActiveX控件的原理、事件机制、属性页和ActiveX控件编写

w88529593的专栏 651

BCB临界区的使用

       使用BCB陆续十多年了,因为一直在工控领域主要是做串口和网络通信类的小软件,BCB6.0足够用,对C++应用得并不是很深。       在读写外部设备时也会用多线程(为了避免主程序在操控过程中的卡顿),如串口和数据库,最主要还是操作数据库,开几个线程来分开独立读写数据库还是挺自在的。但因为自己的项目中几乎没有出现同时写一个表的情况,所以都是在主程序中控制多线程的写入数据库时机,多少年...

gongzhu110的专栏 691
上一篇: 深入VCL 理解BCB的消息机制 (一)
下一篇: 深入VCL 理解BCB的消息机制(三)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值