终于懂了:TControl.Perform是有返回值的,且看VCL框架如何利用消息的返回值(全部例子都在这里)——它的存在仅仅是为了方便复用消息的返回值
代码如下:
function TControl.Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;
var
Message: TMessage;
begin
Message.Msg := Msg;
Message.WParam := WParam;
Message.LParam := LParam;
Message.Result := ;
if Self <> nil then WindowProc(Message);
Result := Message.Result;
end;
虽然函数本身有返回值,但是一般情况下,不使用函数的返回值,而是把返回值记录在消息结构体里面,举例:
procedure PerformEraseBackground(Control: TControl; DC: HDC);
var
LastOrigin: TPoint;
begin
GetWindowOrgEx(DC, LastOrigin);
SetWindowOrgEx(DC, LastOrigin.X + Control.Left, LastOrigin.Y + Control.Top, nil);
Control.Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(DC));
SetWindowOrgEx(DC, LastOrigin.X, LastOrigin.Y, nil);
end; procedure TControl.ReadState(Reader: TReader);
begin
Include(FControlState, csReadingState);
if Reader.Parent is TWinControl then Parent := TWinControl(Reader.Parent);
inherited ReadState(Reader);
Exclude(FControlState, csReadingState);
if Parent <> nil then
begin
Perform(CM_PARENTCOLORCHANGED, , );
Perform(CM_PARENTFONTCHANGED, , );
Perform(CM_PARENTSHOWHINTCHANGED, , );
Perform(CM_SYSFONTCHANGED, , );
Perform(CM_PARENTBIDIMODECHANGED, , );
end;
end; procedure TControl.Changed;
begin
Perform(CM_CHANGED, , Longint(Self));
end; procedure TControl.SetVisible(Value: Boolean);
begin
if FVisible <> Value then
begin
VisibleChanging;
FVisible := Value;
Perform(CM_VISIBLECHANGED, Ord(Value), );
RequestAlign;
end;
end; procedure TControl.SetEnabled(Value: Boolean);
begin
if FEnabled <> Value then
begin
FEnabled := Value;
Perform(CM_ENABLEDCHANGED, , );
end;
end; procedure TControl.SetTextBuf(Buffer: PChar);
begin
Perform(WM_SETTEXT, , Longint(Buffer));
Perform(CM_TEXTCHANGED, , );
end;
但是也有一些情况直接使用Perform函数的返回值,在Controls.pas单元里所有直接使用函数返回值的情况都摘录在这里了:
function TControl.GetTextLen: Integer;
begin
Result := Perform(WM_GETTEXTLENGTH, , );
end; function TControl.GetTextBuf(Buffer: PChar; BufSize: Integer): Integer;
begin
Result := Perform(WM_GETTEXT, BufSize, Longint(Buffer));
end; function TWinControl.IsControlMouseMsg(var Message: TWMMouse): Boolean;
var
Control: TControl;
P: TPoint;
begin
if GetCapture = Handle then
begin
if (CaptureControl <> nil) and (CaptureControl.Parent = Self) then
Control := CaptureControl
else
Control := nil;
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;
Message.Result := Control.Perform(Message.Msg, Message.Keys, Longint(PointToSmallPoint(P)));
Result := True;
end;
end; procedure TWinControl.DefaultHandler(var Message);
begin
if FHandle <> then
begin
with TMessage(Message) do
begin
if (Msg = WM_CONTEXTMENU) and (Parent <> nil) then
begin
Result := Parent.Perform(Msg, WParam, LParam);
if Result <> 0 then Exit;
end;
case Msg of
WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);
CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
begin
SetTextColor(WParam, ColorToRGB(FFont.Color));
SetBkColor(WParam, ColorToRGB(FBrush.Color));
Result := FBrush.Handle;
end;
else
if Msg = RM_GetObjectInstance then
Result := Integer(Self)
else
begin
if Msg <> WM_PAINT then
Result := CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam);
end;
end;
if Msg = WM_SETTEXT then
SendDockNotification(Msg, WParam, LParam);
end;
end
else
inherited DefaultHandler(Message);
end; procedure TWinControl.CNKeyUp(var Message: TWMKeyUp);
begin
if not (csDesigning in ComponentState) then
with Message do
case CharCode of
VK_TAB, VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN,
VK_RETURN, VK_EXECUTE, VK_ESCAPE, VK_CANCEL:
Result := Perform(CM_WANTSPECIALKEY, CharCode, );
end;
end; procedure TWinControl.CNSysChar(var Message: TWMChar);
begin
if not (csDesigning in ComponentState) then
with Message do
if CharCode <> VK_SPACE then
Result := GetParentForm(Self).Perform(CM_DIALOGCHAR,
CharCode, KeyData);
end; procedure TWinControl.WMContextMenu(var Message: TWMContextMenu);
var
Ctrl: TControl;
begin
if Message.Result <> 0 then Exit;
Ctrl := ControlAtPos(ScreenToClient(SmallPointToPoint(Message.Pos)), False);
if Ctrl <> nil then
Message.Result := Ctrl.Perform(WM_CONTEXTMENU, 0, Integer(Message.Pos)); if Message.Result = 0 then
inherited;
end;
这还不算,还得看看那些记录在消息结构体里的返回值是被如何使用的:
procedure TControl.MouseWheelHandler(var Message: TMessage);
var
Form: TCustomForm;
begin
Form := GetParentForm(Self);
if (Form <> nil) and (Form <> Self) then Form.MouseWheelHandler(TMessage(Message))
else with TMessage(Message) do
Result := Perform(CM_MOUSEWHEEL, WParam, LParam);
end; procedure TControl.DefaultHandler(var Message);
var
P: PChar;
begin
with TMessage(Message) do
case Msg of
WM_GETTEXT:
begin
if FText <> nil then P := FText else P := '';
Result := StrLen(StrLCopy(PChar(LParam), P, WParam - ));
end;
WM_GETTEXTLENGTH:
if FText = nil then Result := else Result := StrLen(FText);
WM_SETTEXT:
begin
P := StrNew(PChar(LParam));
StrDispose(FText);
FText := P;
SendDockNotification(Msg, WParam, LParam);
end;
end;
end; procedure TControl.WMMouseWheel(var Message: TWMMouseWheel);
begin
if not Mouse.WheelPresent then
begin
Mouse.FWheelPresent := True;
Mouse.SettingChanged(SPI_GETWHEELSCROLLLINES);
end;
TCMMouseWheel(Message).ShiftState := KeysToShiftState(Message.Keys);
MouseWheelHandler(TMessage(Message));
if Message.Result = 0 then inherited; // 如果消息没有被处理,就要送到DefaultHandler里去
end; procedure TControl.CMMouseWheel(var Message: TCMMouseWheel);
begin
with Message do
begin
Result := ;
if DoMouseWheel(ShiftState, WheelDelta, SmallPointToPoint(Pos)) then
Message.Result := 1
else if Parent <> nil then
with TMessage(Message) do
Result := Parent.Perform(CM_MOUSEWHEEL, WParam, LParam);
end;
end; procedure TWinControl.Broadcast(var Message);
var
I: Integer;
begin
for I := to ControlCount - do
begin
Controls[I].WindowProc(TMessage(Message));
if TMessage(Message).Result <> 0 then Exit; // 如果有一个子控件(图形和Win控件)处理过了,就退出广播
end;
end; procedure TWinControl.DefaultHandler(var Message);
begin
if FHandle <> then
begin
with TMessage(Message) do
begin
if (Msg = WM_CONTEXTMENU) and (Parent <> nil) then
begin
Result := Parent.Perform(Msg, WParam, LParam);
if Result <> then Exit; // 即使不退出,好像也没什么机会继续传递了
end;
case Msg of
WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);
CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
begin
SetTextColor(WParam, ColorToRGB(FFont.Color));
SetBkColor(WParam, ColorToRGB(FBrush.Color));
Result := FBrush.Handle;
end;
else
if Msg = RM_GetObjectInstance then
Result := Integer(Self)
else
begin
if Msg <> WM_PAINT then
Result := CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam);
end;
end;
if Msg = WM_SETTEXT then
SendDockNotification(Msg, WParam, LParam);
end;
end
else
inherited DefaultHandler(Message);
end; function DoControlMsg(ControlHandle: HWnd; var Message): Boolean;
var
Control: TWinControl;
begin
DoControlMsg := False;
Control := FindControl(ControlHandle);
if Control <> nil then
with TMessage(Message) do
begin
Result := Control.Perform(Msg + CN_BASE, WParam, LParam);
DoControlMsg := True; // 不多见的函数返回值写法
end;
end; procedure TWinControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
with ThemeServices do
if ThemesEnabled and Assigned(Parent) and (csParentBackground in FControlStyle) then
begin
{ Get the parent to draw its background into the control's background. }
DrawParentBackground(Handle, Message.DC, nil, False);
end
else
begin
{ Only erase background if we're not doublebuffering or painting to memory. }
if not FDoubleBuffered or
(TMessage(Message).wParam = TMessage(Message).lParam) then
FillRect(Message.DC, ClientRect, FBrush.Handle);
end; Message.Result := 1;
end;
结论:它的存在仅仅是为了方便复用消息的返回值,至少官方提供的Perform函数清清楚楚、明明白白,就只有这个意思。
当然Perform作为一个函数提供返回值,还有2个好处:1.在处理的过程中偷梁换柱 2.覆盖Perform函数都可以(虽然一般没有必要这么做),但这两点几乎不用考虑。普通程序员怎么可能会做这种修改VCL核心代码的事情,根本没必要。
终于懂了:TControl.Perform是有返回值的,且看VCL框架如何利用消息的返回值(全部例子都在这里)——它的存在仅仅是为了方便复用消息的返回值的更多相关文章
- 终于懂了:Delphi消息的Result域出现的原因——要代替回调函数的返回值!(MakeObjectInstance不会帮助处理(接收)消息回调函数的返回值)
MakeObjectInstance应该不会帮助处理(接收)消息回调函数的返回值,可是有时候又确实需要这个返回值,这可怎么办呢?我是看到这段文字的时候,想到这个问题的: 当WM_PAINT不是由Inv ...
- System V 消息队列 - 复用消息
消息队列中的消息结构可以由我们自由定义,具备较强的灵活性.通过消息结构可以共享一个队列,进行消息复用.通常定义一个类似如下的消息结构: #define MSGMAXDAT 1024 struct my ...
- 05 返回静态文件的多线程web框架
05 返回静态文件的多线程web框架 服务器server端python程序(多线程版): import socket from threading import Thread,currentThrea ...
- 04 返回静态文件的函数web框架
04 返回静态文件的函数web框架 服务器server端python程序(函数版): import socket server = socket.socket() server.bind((" ...
- 03 返回静态文件的高级web框架
03 返回静态文件的高级web框架 服务器server端python程序(高级版): import socket server=socket.socket() server.bind(("1 ...
- 深刻:截获windows的消息并分析实例(DefWindowProc),以WM_NCHITTEST举例(Windows下每一个鼠标消息都是由 WM_NCHITTEST 消息产生的,这个消息的参数包含了鼠标位置的信息)
1,回调函数工作机制 回调函数由操作系统自动调用,回调函数的返回值当然也是返回给操作系统了. 2,截获操作系统发出的消息,截获到后,将另外一个消息返回给操作系统,已达到欺骗操作系统的目的. 下面还是以 ...
- TGraphicControl(自绘就2步,直接自绘自己,不需要调用VCL框架提供的函数重绘所有子控件,也不需要自己来提供PaintWindow函数让管理框架来调用)与TControl关键属性方法速记(Repaint要求父控件执行详细代码来重绘自己,还是直接要求Invalidate无效后Update刷新父控件,就看透明不透明这个属性,因为计算显示的区域有所不同)
TGraphicControl = class(TControl) private FCanvas: TCanvas; procedure WMPaint(var Message: TWMPaint) ...
- 深入解析Windows窗口创建和消息分发(三个核心问题:怎么将不同的窗口过程勾到一起,将不同的hwnd消息分发给对应的CWnd类去处理,CWnd如何简单有效的去处理消息,由浅入深,非常清楚) good
笔记:争取不用看下面的内容,只看自己的笔记,就能记住这个流程,就算明白了: _tWinMain-->AfxWinMain,它调用四个函数: -->AfxWinInit用于做一些框架的初始化 ...
- WPF的消息机制(三)- WPF内部的5个窗口之处理激活和关闭的消息窗口以及系统资源通知窗口
原文:WPF的消息机制(三)- WPF内部的5个窗口之处理激活和关闭的消息窗口以及系统资源通知窗口 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/p ...
随机推荐
- Boost::filesystem 使用小笔记
今天拿起手要用C++写个小工具,从指定的目录递归遍历文件,然后做一下处理.又翻了一下boost的filesystem库.小结一下,希望能加深印象,免得下次又要查看文档. 1. path对象就是一个跨平 ...
- Android Camera系列开发 (二)通过Intent录制视频
Android Camera系列开发 (二)通过Intent录制视频 作者:雨水 2013-8-18 CSDN博客:http://blog.csdn.net/gobitan/ 概述 使用Camera ...
- ffmpeg h265
最新版本号的ffmpeg 支持 libh265,可是还是0基础測试阶段 在linux 上安装ffmpeg 支持h265编码器依照下面步骤: Anyhow here are the simple ste ...
- MSSQL - 视图操作
查询语句(包含使用Where子句): string sql = @"SELECT TableName, TablePosition,TableSate, TabelType,OpenTime ...
- java中int,char,string三种类型的相互转换
如何将字串 String 转换成整数 int? int i = Integer.valueOf(my_str).intValue(); int i=Integer.parseInt(str); 如何将 ...
- UVA 2039 Pets(网络流)
Problem Description Are you interested in pets? There is a very famous pets shop in the center of th ...
- linux卸载不完全
kali安装一个Nessus,但是在加载插件时候断网了,插件没下载完,后面打开也无法下载插件于是果断卸载了重装,但是无法删除配置文件,恼火了一天 完全卸载软件命令 sudo apt-get --pur ...
- 基于visual Studio2013解决C语言竞赛题之1068指针数组
题目 解决代码及点评 /* 68. 在主函数中输入10个不等长的字符串,用另一函数对它们排序. 然后在主函数中输出这10个已排好序的字符串,用指针数组完成. */ #inclu ...
- windows和linux套接字中的select机制浅析
先来谈谈为什么会出现select函数,也就是select是解决什么问题的? 平常使用的recv函数时阻塞的,也就是如果没有数据可读,recv就会一直阻塞在那里,这是如果有另外一个连接过来,就得一直等待 ...
- Linux内核驱动将多个C文件编译成一个ko文件的方法——每一个C文件中都有module_init与module_exit
以两个C文件为例: 将本该被分别编译成adc_device.ko和adc_driver.ko的adc_device.c.adc_driver.c编译成一个ko文件! 採用方法: 第一步.改动C文件 1 ...