由VCL中的代码理解VCL中的消息处理机制

SQL Server2008详细安装步骤(超详细步骤) https://pan.baidu.com/s/1yvolqfU-AbiuGrIzblA7pA 提取码:lzw81.下载好安装包后解压到除C盘以外的磁盘,避免系统卡顿2.使用管理员身份打开安装程序注意:使用管理员身份打开时,此时如果显示以下画面属于正常现象,下载安装即可。因为.net现在有很多软件都需要,包括SQLSERVER2008,它里面有visual studio都要有net,电脑系统没有的话安装一下好了。3.选择全新安装或向现有安装添加新功能4.点击确定5.出现以下画面之后,输入产品密钥即可(密钥在 阅读详情
 Delphi,一个非常优秀的开发工具,拥有强大的可视化开发环境、面向组件的快速开发模式、优秀的VCL类库、快速的代码编译器、强大的数据库和WEB开发能力、还有众多的第三方控件支持...(此处省略x千字,既然大家都知道了,不浪费口水了 ^_^)

说到VCL的优秀就不能不提到其对Windows消息及API的较全面和完美的封装,正因为如此开发者在大多数情况下甚至不需理会Windows消息处理的细节,而只需要写几行事件驱动代码即可!

但如果做为开发人员你还是想对此做些了解的话,那么就继续,通过VCL代码本身来体会VCL中的消息处理机制。

(以下代码取自Delphi 6)

说到VCL中的消息处理就不能不提到TApplication,Windows会为每一个当前运行的程序建立一个消息队列,用来完成用户与程序的交互,正是通过Application完成了对Windows消息的集中处理!

首先通过Application.Run进入消息循环进行消息的处理,其中调用了HandleMessage。

procedure TApplication.HandleMessage;
var
  Msg: TMsg;
begin
  if not ProcessMessage(Msg) then Idle(Msg);//这里先调用ProcessMessage处理,返回值为False调用Idle,就是在空闲时,即消息队列中无消息等待处理时调用Idle。
end;

function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
  Handled: Boolean;
begin
  Result := False;
  if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then//查询消息队列中有无消息等待处理,参数PM_REMOVE使消息在处理完后会被删除。
  begin
    Result := True;
    if Msg.Message <> WM_QUIT then//如果是WM_QUIT,终止进程,否则执行下面的代码
    begin
      Handled := False;
      if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
      if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
        not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
      begin
        TranslateMessage(Msg);//将记录Msg传递给Windows进行转换
        DispatchMessage(Msg);//将记录Msg回传给Windows
      end;
    end
    else
      FTerminate := True;
  end;
end;

然后程序中的各个VCL对象又是如何接收到Windows消息的呢?这还要从窗体的创建开始!

首先找到TWinControl.CreateWnd中的
Windows.RegisterClass(WindowClass)//调用RegisterClass注册一个窗体类

向上看
WindowClass.lpfnWndProc := @InitWndProc;//这里指定了窗口的消息处理函数的指针为@InitWndProc!

再找到function InitWndProc(HWindow: HWnd; Message, WParam, LParam: Longint): Longint;

发现了
CreationControl.FHandle := HWindow;
SetWindowLong(HWindow, GWL_WNDPROC,Longint(CreationControl.FObjectInstance));
没有?

原来InitWndProc初次被调用时候,又使用API函数SetWindowLong指定处理消息的窗口过程为FObjectInstance。

回到TWinControl.Create
FObjectInstance := Classes.MakeObjectInstance(MainWndProc);
找到关键所在了,也许有些朋友对MakeObjectInstance这个函数很熟了,它的作用就是将一个成员过程转换为标准过程。

绕了个圈子?为什么呢?很简单,因为窗体成员过程包括一隐含参数传递Self指针,所以需要转化为标准过程。

const
  InstanceCount = 313;//这个不难理解吧?314*13+10=4092,再大的话,记录TInstanceBlock的大小就超过了下面定义的PageSize

type
  PObjectInstance = ^TObjectInstance;
  TObjectInstance = packed record
    Code: Byte;
    Offset: Integer;
    case Integer of
      0: (Next: PObjectInstance);
      1: (Method: TWndMethod);
  end;

type
  PInstanceBlock = ^TInstanceBlock;
  TInstanceBlock = packed record
    Next: PInstanceBlock;
    Code: array[1..2] of Byte;
    WndProcPtr: Pointer;
    Instances: array[0..InstanceCount] of TObjectInstance;
  end;

var
  InstBlockList: PInstanceBlock;
  InstFreeList: PObjectInstance;

function StdWndProc(Window: HWND; Message, WParam: Longint;
  LParam: Longint): Longint; stdcall; assembler;
asm
        XOR     EAX,EAX
        PUSH    EAX
        PUSH    LParam
        PUSH    WParam
        PUSH    Message
        MOV     EDX,ESP  ;将堆栈中构造的记录TMessage指针传递给EDX 
        MOV     EAX,[ECX].Longint[4]  ;传递Self指针给EAX,类中的Self指针也就是指向VMT入口地址
        CALL    [ECX].Pointer  ;调用MainWndProc方法
        ADD     ESP,12
        POP     EAX
end;

function CalcJmpOffset(Src, Dest: Pointer): Longint;
begin
  Result := Longint(Dest) - (Longint(Src) + 5);
end;

function MakeObjectInstance(Method: TWndMethod): Pointer;
const
  BlockCode: array[1..2] of Byte = (
    $59,       { POP ECX }
    $E9);      { JMP StdWndProc }
  PageSize = 4096;
var
  Block: PInstanceBlock;
  Instance: PObjectInstance;
begin
  if InstFreeList = nil then
  begin
    Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//分配虚拟内存,并指定这块内存为可读写并可执行
    Block^.Next := InstBlockList;
    Move(BlockCode, Block^.Code, SizeOf(BlockCode));
    Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));
    Instance := @Block^.Instances;
    repeat
      Instance^.Code := $E8;  { CALL NEAR PTR Offset }
      Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
      Instance^.Next := InstFreeList;
      InstFreeList := Instance;
      Inc(Longint(Instance), SizeOf(TObjectInstance));
    until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
    InstBlockList := Block;
  end;
  Result := InstFreeList;
  Instance := InstFreeList;
  InstFreeList := Instance^.Next;
  Instance^.Method := Method;
end;

(注:上面出现的那些16进制代码其实就是些16进制的机器代码 $59=Pop ECX $E8=Call $E9=Jmp)

以上代码看起来有点乱,但综合起来看也很好理解!MakeObjectInstance实际上就是构建了一个Block链表

其结构看看记录TInstanceBlock的结构可知其结构如下:

Next//下一页指针
Code//Pop ECX和Jmp
WndProcPtr//和StdWndProc间的地址偏移
Instances//接下来是314个Instance链表

Instance链表通过记录TObjectInstance也很好理解其内容

Code//Call
Offset//地址偏移
Method//指向对象方法的指针(结合TMethod很好理解TWndMethod这类对象方法指针指向数据的结构)

好现在来把这个流程回顾一遍,Windows回调的是什么呢?其实是转到并执行一段动态生成的代码:先是执行Call offset ,根据偏移量转去执行Pop ECX,当然由于在Call这之前会将下一条指令入栈,所以这里弹出的就是指向对象方法的指针。接下来就是执行jmp [StdWndProc],其中将堆栈中构造的记录TMessage指针赋给了EDX,而根据上面的解释结合TMethod去理解,很容易理解
MOV     EAX,[ECX].Longint[4]  ;传递Self指针给EAX,类中的Self指针也就是指向VMT入口地址
CALL    [ECX].Pointer  ;调用MainWndProc方法

现在终于豁然开朗了,Windows消息就是这样被传递到了TWinControl.MainWndProc,相比MFC中的回调全局函数AfxWndProc来根据窗体句柄检索对应的对象指针的方法效率要高的多!VCL比MFC优秀的又一佐证! ^_^

现在终于找到了VCL接收消息的方法MainWndProc

procedure TWinControl.MainWndProc(var Message: TMessage);
begin
  try
    try
      WindowProc(Message);//由于TControl创建实例时已经将FWindowProc指向WndProc,所以这里实际也就是调用WndProc
    finally
      FreeDeviceContexts;
      FreeMemoryContexts;//调用FreeDeviceContexts和FreeMemoryContexts是为了保证VCL线程安全
    end;
  except
    Application.HandleException(Self);
  end;
end;

这里也不能忽略了TWinControl.WndProc

procedure TControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
  KeyState: TKeyboardState;  
  WheelMsg: TCMMouseWheel;
begin
  ...
  //省略以上的消息相关处理代码,研究某些特定消息时可自行查看
  ...
  Dispatch(Message);//调用Dispatch处理
end;

接下来,先不急着查看Dispatch中的相应代码。想想看,忘了什么?

上面只是继承于TWinControl的有句柄的控件,那继承于TGraphicControl的没有句柄的控件是如何获得并处理消息的?下面以鼠标消息为例:

TWinControl.WndProc中有下面的代码:

  case Message.Msg of
  ...
    WM_MOUSEFIRST..WM_MOUSELAST://注1:下面再解释这段
      if IsControlMouseMsg(TWMMouse(Message)) then
      begin
        { Check HandleAllocated because IsControlMouseMsg might have freed the
          window if user code executed something like Parent := nil. }
        if (Message.Result = 0) and HandleAllocated then
          DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam);
        Exit;
      end;
  ...
  end;
  inherited WndProc(Message);//执行祖先类的WndProc方法

function TWinControl.IsControlMouseMsg(var Message: TWMMouse): Boolean;
var
  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);//这里通过ControlAtPos获得了鼠标所在控件
  Result := False;
  if Control <> nil then
  begin
    P.X := Message.XPos - Control.Left;
    P.Y := Message.YPos - Control.Top;
    Message.Result := Control.Perform(Message.Msg, Message.Keys, Longint(PointToSmallPoint(P)));//调用Perform方法发送消息给对应的实例
    Result := True;
  end;
end;

property WindowProc: TWndMethod read FWindowProc write FWindowProc;

function TControl.Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;
var
  Message: TMessage;
begin
  Message.Msg := Msg;
  Message.WParam := WParam;
  Message.LParam := LParam;
  Message.Result := 0;
  if Self <> nil then WindowProc(Message);//由于TControl创建实例时已经将FWindowProc指向WndProc,所以这里实际也就是调用WndProc
  Result := Message.Result;
end;

VCL中就是这样将消息分发给了那些继承于TGraphicControl的没有句柄的图形控件。

上面说的都是Windows消息(Windows Messages),似乎还应该说说两条经常用到的VCL中自定义消息:CM_MOUSEENTER,CM_MOUSELEAVE(CM = Short of Control Message)

它们是如何被处理的呢?还是看上面的(if not ProcessMessage(Msg) then Idle(Msg);),这两条不是Windows消息,所以会触发Idle

procedure TApplication.Idle(const Msg: TMsg);
var
  Control: TControl;
  Done: Boolean;
begin
  Control := DoMouseIdle;//调用DoMouseIdle方法
  ...
end;

function TApplication.DoMouseIdle: TControl;
var
  CaptureControl: TControl;
  P: TPoint;
begin
  GetCursorPos(P);
  Result := FindDragTarget(P, True);//获取当前鼠标所停留在的控件
  if (Result <> nil) and (csDesigning in Result.ComponentState) then
    Result := nil;
  CaptureControl := GetCaptureControl;
  if FMouseControl <> Result then//判断以前记录的鼠标指针所指向的控件和现在所指向的控件是否相同
  begin
    if ((FMouseControl <> nil) and (CaptureControl = nil)) or
      ((CaptureControl <> nil) and (FMouseControl = CaptureControl)) then
      FMouseControl.Perform(CM_MOUSELEAVE, 0, 0);//发送消息CM_MOUSELEAVE给以前记录的鼠标指针所指向的控件
    FMouseControl := Result;//记录当前鼠标指针所指向的控件
    if ((FMouseControl <> nil) and (CaptureControl = nil)) or
      ((CaptureControl <> nil) and (FMouseControl = CaptureControl)) then
      FMouseControl.Perform(CM_MOUSEENTER, 0, 0);//发送消息CM_MOUSEENTER给鼠标指针现在所在的控件
  end;
end;

function FindDragTarget(const Pos: TPoint; AllowDisabled: Boolean): TControl;
var
  Window: TWinControl;
  Control: TControl;
begin
  Result := nil;
  Window := FindVCLWindow(Pos);//这里返回的是TWinControl,是一个有句柄的控件
  if Window <> nil then
  begin
    Result := Window;
    Control := Window.ControlAtPos(Window.ScreenToClient(Pos), AllowDisabled);//鼠标所指向处可能还存在一继承于TGraphicControl的图形控件,而上面返回的只是其容器控件
    if Control <> nil then Result := Control;//如果存在就返回用ControlAtPos所得到的控件
  end;
end;

于是又转到了上面的TControl.Perform

现在所有的问题又都集中到了Dispatch的身上,消息是如何触发事件的处理方法的呢?

首先看条消息处理方法的申明:
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;

这实际可以认为是申明了一个动态方法,调用Dispatch实际上就是通过消息号在DMT(动态方法表)中找到相应的动态方法指针,然后执行

//上面已经提到了,寄存器EAX中是类的Self指针,即VMT入口地址,寄存器EDX中是指向记录Message的指针
procedure TObject.Dispatch(var Message);
asm
    PUSH    ESI
    MOV     SI,[EDX]  ;消息号,也就是记录TMessage中Msg的值,对应CM_MOUSEENTER就是$B013(45075)
    OR      SI,SI
    JE      @@default
    CMP     SI,0C000H
    JAE     @@default
    PUSH    EAX
    MOV     EAX,[EAX]  ;VMT入口地址
    CALL    GetDynaMethod  ;调用GetDynaMethod查找
    POP     EAX
    JE      @@default  ;在GetDynaMethod中如果找到会将标志位寄存器的值置为0,如果是1,表示未找到,执行跳转
    MOV     ECX,ESI  ;传递指针给ECX
    POP     ESI
    JMP     ECX  ;跳转到ECX所指向的位置,也就完成了通过消息号调用CMMouseEnter的过程

@@default:
    POP     ESI
    MOV     ECX,[EAX]
    JMP     dword ptr [ECX].vmtDefaultHandler  ;如果此控件和它的祖先类中都没有对应此消息号的处理方法,调用Defaulthandler
end;

procedure       GetDynaMethod;
{       function        GetDynaMethod(vmt: TClass; selector: Smallint) : Pointer;       }
asm
        { ->    EAX     vmt of class            }
        {       SI      dynamic method index    }
        { <-    ESI pointer to routine  }
        {       ZF = 0 if found         }
        {       trashes: EAX, ECX               }

        PUSH    EDI
        XCHG    EAX,ESI  ;交换EAX和ESI的值,这之后ESI中为VMT入口地址,EAX为消息号,即对应动态方法的代号
        JMP     @@haveVMT
@@outerLoop:
        MOV     ESI,[ESI]
@@haveVMT:
        MOV     EDI,[ESI].vmtDynamicTable  ;尝试着将DMT的入口地址传递给EDI

        TEST    EDI,EDI  ;通过EDI是否为0来判断是否存在DMT
        JE      @@parent  ;不存在跳转到父类继续
        MOVZX   ECX,word ptr [EDI]  ;取[EDI],即DMT的头两个字节的值传递给ECX,即动态方法的个数
        PUSH    ECX
        ADD     EDI,2  ;地址加2,即跳过DMT中存储动态方法的个数的部分
        REPNE   SCASW  ;EAX与EDI指向的数据按字依次比较,直到找到(ZF=1)或ECX=0为止
        JE      @@found
        POP     ECX
@@parent:
        MOV     ESI,[ESI].vmtParent  ;尝试获得父类
        TEST    ESI,ESI  ;通过EDI是否为0来判断是否存在父类
        JNE     @@outerLoop ;存在就跳转到@@outerLoop进行查找
        JMP     @@exit ;退出

@@found:
        POP     EAX
        ADD     EAX,EAX
        SUB     EAX,ECX         { this will always clear the Z-flag ! }  ;这句的用途就上上面说到的将标志位ZF置0
        MOV     ESI,[EDI+EAX*2-4]  ;将获得的方法指针传递给ESI,理解这句先要对DMT结构的内容做些了解

@@exit:
        POP     EDI
end;

在VCL中,DMT的结构是这样的,前2个字节储存了DMT中动态方法的个数n,然后是方法代号,共4*n字节,最后是方法指针,也是4*n字节!

这样就很好理解了,EDI-4就是当前方法代号所在地址,EDI-4+4*n=EDI+EAX*2-4(因为已经执行了一句ADD EAX,EAX,所以EAX=2*n)所以,[EDI+EAX*2-4]就是所找到了相应方法指针。

结合下面的

TNotifyEvent = procedure(Sender: TObject) of object;

FOnMouseEnter: TNotifyEvent;

property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;

procedure TXXX.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  if Assigned(FOnMouseEnter) then
    FOnMouseEnter(Self);
end;

在跳转到CMMouseEnter执行后,判断方法指针FOnMouseEnter是否是nil,如果不为空,就执行相应的事件处理方法!

通过以上的一个看似复杂的过程,我们这些用Delphi的开发人员只需要很简单的在类似
procedure TFormX.XXXMouseEnter(Sender: TObject);
begin
  //
end;

(XXX.OnMouseEnter:=XXXMouseEnter;)

的过程中写两行简单的代码,就能很容易的实现所谓的事件驱动!

很多人也许只看中结果,并不在乎过程,从这不能简单评论谁对谁错,对于这些知识的了解是否有用,我们每个人都可以自己去体会~~~

java modbus通讯协议_Modbus通 讯 协 议 电磁流量计Modbus通 讯 协 议(版本号:9000-K V9.1.1)2020年01月01日通讯协议针对KFD电磁流量计工业应用设计,版本:9000-K V9.1.1,该版本主要用于实时数据采集、流量测量、流量累计控制及部分参数的修改。一、主机系统通讯部件要求1.国际标准RS-485通讯接口部件,不小于11Bytes 的通信缓冲区(FIFO),支持1200、2400、4800、96... 阅读详情

相关推荐

Xilinx Blockset Counter计数器模块使用及参数配置

本文详细介绍了Simulink中Xilinx Blockset库Counter模块的使用以及参数说明。

白码王子小张的博客 1094

Delphi处理Windows消息

一、PeekMessage()函数。下面是它的原形: BOOL PeekMessage( LPMSG lpMsg, // pointer to structure for message HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax, // last message UI

刘胜的专栏 2877

(一篇即成功)Ubuntu下搭建qt环境和安装Qt Creator

只需一篇就成功搭建Ubuntu下的QT环境 你还在等什么

m0_73450461的博客 1万+

VCL源码分析方法论

   最近一段时间似乎流行源码分析:)我也来谈谈在过去一段时间里对VCL源码的分析方法方面的一点体会,本文将不探讨VCL类库的构架和设计模式方面的东本,只是以我们常见的控件属性/方法的实现过程作简单的说明,希望对初学者有所帮助VCL分析方法例:TButton.Caption属性的由来(本文仅以此献给DELPHI初学者)   用过一段时间DELPHI的朋友,都会对VCL源码感兴趣。本人也常常在各大论

cg1120的Blog 1198

保姆级教程:在STM32CubeIDE上快速部署micro_ros到STM32F407(附常见问题解决)

本文提供了一份详细的保姆级教程,指导开发者如何在STM32CubeIDE上快速部署micro_ros到STM32F407微控制器。内容涵盖从Windows与Ubuntu双环境搭建、micro-ROS静态库编译、STM32CubeIDE工程深度集成,到串口通信联调与常见问题解决的完整流程,旨在帮助读者构建稳定可靠的ROS2与嵌入式设备通信系统。

o4p5q6r7s的博客 323

Word公式一键转MathType保姆级教程(附omml2mml.xsl报错终极解决方案)

本文提供Word公式一键转MathType的保姆级教程,重点解决常见的omml2mml.xsl报错问题。通过详细分析报错原因、官方解决方案实操步骤及转换后格式优化技巧,帮助用户高效完成公式转换,适用于科研论文和商业文档编辑。

weixin_30082367的博客 554

深入Delphi(三) 个人认为不错的delphi概要介绍,言简意明,值得一看

深入Delphi (三) Windows 消息机制 by machine   大家是不是很奇怪为什么我还没说到Delphi的控件呢?不过不用着急,有关深入控件的内容,将会很快出现了,但在这之前,还得了解Windows图形界面程序的机制——Windows的消息机制。  使用过Delphi的朋友都知道,Delphi是一个真正面向对象的编程环境,但是不但如此,Delphi的这种面向对象的机制

arsser的专栏 1478

手把手教你学Simulink——AC-DC整流器场景:基于传统晶闸管控制整流拓扑的建模:带反电动势负载的单相可控整流器输出特性仿真

在工业电力电子应用中,单相可控整流器常用于将交流电源转换为可调直流电压。当该整流器连接到具有**反电动势(Back EMF)**的负载时(如直流电机、电池充电系统等),其输出特性和工作原理与纯电阻性负载有所不同。本文将带你使用搭建一个带反电动势负载的单相全控桥式整流器模型,并通过仿真分析其输出电压、电流波形以及触发角对输出特性的影响。本文详细介绍了如何使用Simulink搭建一个带反电动势负载的单相全控桥式晶闸管整流器模型,并进行了输出特性的仿真分析。

MHD0815的博客 232

PeekMessage究竟做了什么?

把Delphi里TThread的WaitFor函数转化成C++代码,就会是下面这个样子。 BOOL TThread::WaitFor(HANDLE hThread){     MSG msg;     HANDLE handle[1];     handle[0] = hThread;     DWORD dwWaitResult = 0;     do

swimmer2000的专栏 7443

《PCI Express体系结构导读1》-- PCI简介

请先阅读完一遍此书再来看文章哦,这样效率更高~

HoneyIC的博客 151

C++软件调试与异常排查从入门到精通专栏介绍与文章汇总

根据近几年排查软件异常的实践与经验,系统地讲解了C++软件异常常见原因与常用排查方法,以图文并茂的方式给出具体的分析实例,带领大家逐步掌握C++软件异常排查的相关技术与要领。

dvlinker的技术专栏 23万+

由VCL中的代码理解VCL中的消息处理机制(Delphi对消息机的封装)

由VCL中的代码理解VCL中的消息处理机制Delphi,一个非常优秀的开发工具,拥有强大的可视化开发环境、面向组件的快速开发模式、优秀的VCL类库、快速的代码编译器、强大的数据库和WEB开发能力、还有众多的第三方控件支持...(此处省略x千字,既然大家都知道了,不浪费口水了 ^_^)说到VCL的优秀就不能不提到其对Windows消息及API的较全面和完美的封装,正因为如此开发者在大多数情况

不得闲.闲忆居 1341

ros_gz_bridge__镜像__readme

Run。

1113

【转】Delphi 的消息机制浅探

Delphi 的消息机制浅探 savetime2k@yahoo.com 2004.1.9 我从去年 12 月上旬开始等待李维的《Inside VCL》。我当时的计划是,在这本书的指导下深入学习 Delphi。到了 12 月底,书还没有出来,我不愿再等,开始阅读 VCL 源代码。在读完 TObject、TPersistant 和 TComponent 的代码之后,我发现还是不清...

weixin_30613343的博客 189

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

由VCL代码理解VCL的消息机制

由VCL代码理解VCL的消息机制(转贴)Delphi,一个非常优秀的开发工具,拥有强大的可视化开发环境、面向组件的快速开发模式、优秀的VCL类库、快速的代码编译器、强大的数据库和WEB开发能力、还有众多的第三方控件支持...(此处省略x千字,既然大家都知道了,不浪费口水了 ^_^)说到VCL的优秀就不能不提到其对Windows消息及API的较全面和完美的封装,正因为如此开发者在大多数情况下甚至不需

Qi_jianzhou的资料室 1386

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

VCL消息处理机制的内幕

由VCL中的代码理解VCL中的消息处理机制 Delphi,一个非常优秀的开发工具,拥有强大的可视化开发环境、面向组件的快速开发模式、优秀的VCL类库、快速的代码编译器、强大的数据库和WEB开发能力、还有众多的第三方控件支持...(此处省略x千字,既然大家都知道了,不浪费口水了 ^_^) 说到VCL的优秀就不能不提到其对Windows消息及API的较全面和完美的封装,正因为如此开发者在大多数

Jenhy的专栏 702

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

技术演进中的开发沉思-48 DELPHI VCL系列:TWinControl

TWinControl是Delphi VCL框架中封装Windows窗口控件的核心类,其精妙设计体现在三方面:首先,它通过类映射机制将VCL控件转换为Windows窗口类,重构了原生控件行为模式;其次,其控件树构建和布局算法实现了高效的父子控件管理,通过BeginUpdate/EndUpdate优化批量操作性能;最后,消息处理机制采用"消息冒泡"和优先级调度策略,支持灵活的事件拦截与分发。此外,双缓冲重绘和懒加载策略有效解决了界

你的天空阅读专栏 889

深入BCB理解VCL的消息机制

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

diligentcat的专栏 1527

Delphi VCL消息处理机制(2)

=============================================================================== ⊙ VCL 的消息处理从 TWinControl.MainWndProc 开始 =========================================================================

流年~~ 1677

深入VCL 理解BCB的消息机制

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

心情之旅 1582

VCL消息处理机制

说到VCL中的消息处理就不能不提到TApplication,Windows会为每一个当前运行的程序建立一个消息队列,用来完成用户与程序的交互,正是通过Application完成了对Windows消息的集中处理!     首先通过Application.Run进入消息循环进行消息的处理,其中调用了HandleMessage。 procedure TApplication.HandleMessage;...

weixin_33721427的博客 117

深入 BCB理解 VCL的消息机制

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

910
上一篇: VCL中消息处理初探
下一篇: 关于VCL的编写 (一) 怎样编写自己的VCL控件
Dcdu
博客等级 码龄21年 44粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值