




已阅读5页,还剩32页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Delphi基础:回调函数及其使用1 回调函数的概述 回调函数是这样一种机制:调用者在初始化一个对象(这里的对象是泛指,包括OOP中的对象、全局函数等)时,将一些参数传递给对象,同时将一个调用者可以访问的函数地址传递给该对象。这个函数就是调用者和被调用者之间的一种通知约定,当约定的事件发生时,被调用者(一般会包含一个工作线程)就会按照回调函数地址调用该函数。 这种方式,调用者在一个线程,被调用者在另一个线程。 消息: 消息也可以看作是某种形式的回调,因为消息也是在初始化时由调用者向被调用者传递一个句柄和一个消息编号,在约定的事件发生时被调用者向调用者发送消息。 这种方式,调用者在主线程中,被调用者在主线程或者工作线程中。 Delphi事件模型: 在Delphi的VCL中有很多可视化组件都是使用事件模型,例如TForm的OnCreate事件,其原理是:在设计时指定事件函数,在运行时事件触发,则会调用在设计时指定的事件函数。 在机制上,Delphi事件模型与回调是一样的。但具体形式有些区别,纯的回调函数是全局函数的形式,而Delphi事件是对象方法的形式,即可以定义如下回调函数类型 typeTCallBackFunc = procedure (pData: Pointer) of object;2 回调函数的使用说明 回调函数主要在两个场合使用,第一个是某些windows的API要求用回调函数作为其参数地址,另一种是用户在某种特定的场合定义的某个函数需要使用回调函数作为其参数地址,对于用户的定义的函数来说,一般是当调用动态连接库中的函数时使用。 对于使用一个回调函数主要有以下几个步骤: 1、定义一个回调函数类型,跟一般的函数过程的定义并没有什么区别,但其定义必须根据需要满足回调函数的函数要求,唯一的区别在于在函数或过程的定义后面必须声明其为windows标准调用; 例: typeTHDFunction= function(I:integer;s:string):integer; stdcall;2、 然后根据此原形定义一个相应的函数或过程,对于这个函数或过程来说名字没有什么要求,对函数其参数的类型和返回值的类型必须和定义的回调函数类型完全一致,对于过程来说,只需要其参数类型一样就可以了。 例:根据上面的函数和过程的原形定义一个相应的函数和一个相应的过程。 函数原形定义: Function HdFunExample(k:integer,sExam:string):integer; stdcall;过程定义: procedure HdProExample(sExam:string);stdcall;3、 在程序中实现此回调函数或着过程;Function HdFunExample(k:integer,sExam:string):integer; stdcall; BeginEnd; procedure HdProExample(sExam:string);stdcall; beginend; 4、 调用过程; 回调函数一般作为系统的某个函数的入口地址; 根据调用函数的原形: 进入讨论组讨论。 假设有如下调用函数:function DyHdFunExample(HdFun:THDFunction;I:integer):boolean;注: 在调用函数中通过对函数指针的处理可以直接调用回调函数(即调用函数中的那个是回调函数类型的参数,直接操作它),使回调函数履行一定的操作。即在调用函数中实现回调函数的功能。 调用: varI:integer; beginI:=DyHdFunExample(HdFunExample,i); /. End;3 举例说明 示例程序在H: 回调函数示例 目录下面。 回调函数的使用主要在于windows原有的API函数,但对于用户的自定义的调用函数一般在于动态连接库中。常规的同一个工程下面一般不需要使用回调函数。(个人认为).。Delphi中的消息处理 Delphi多线程及消息发送传递结构体参数 1、Unit2:unit Unit2;interfaceuses windows,classes,NMICMP,SysUtils,StdCtrls,messages;const WM_MY_PING = WM_USER +1024;type /要传递的消息记录. TPingMsg = record msg : array0.1023 of char; id : integer; Handled : boolean; msg2 : string; /建议如果需要动态管理,比如采用List,采用字符数组的方式会比较好, /因为在动态使用结构时,如过没有处理好,采用string就可能会造成内存泄露. /当然在这里例子中没关系. end; pPingMsg = TPingMsg;/定义结构体指针. OnPinging = procedure(Context: integer;Msg : string) of object; ThreadEnd = procedure(Context: integer;Msg:string) of object; TMyPingThread = class(TThread) private FPingEvent : OnPinging; FEndEvent : ThreadEnd; FMsg : string; FSequenceID : integer; FWinHandl : Hwnd; procedure OnPing(Sender: TObject; Host: String; Size, Time: Integer); procedure HandlingEnd; procedure HandlingPing; protected procedure Execute;override; procedure DoTerminate;override; public /采用函数指针的方式,因为传递过来如果是UI控件类的方法,该方法需要访问UI元素,则需要做同步处理, /否则可能会导致错误. constructor Create(WinHandl : Hwnd; SequenceID : integer;OutPut: OnPinging;EndEvent: ThreadEnd);overload; end;implementation TMyPingThread constructor TMyPingThread.Create(WinHandl : Hwnd;SequenceID : integer;OutPut: OnPinging; EndEvent: ThreadEnd); begin self.FPingEvent := OutPut; self.FEndEvent := EndEvent; FSequenceID := SequenceID; FWinHandl := WinHandl; inherited Create(true); end;procedure TMyPingThread.DoTerminate;begin inherited; Synchronize(HandlingEnd);end;procedure TMyPingThread.HandlingEnd();begin if Assigned(self.FEndEvent) then self.FEndEvent(FSequenceID,FMsg);end;procedure TMyPingThread.HandlingPing();begin if assigned(self.FPingEvent) then FPingEvent(FSequenceID,FMsg);end;procedure TMyPingThread.Execute;var PingObj : TNMPing;begin self.FreeOnTerminate := true; PingObj := TNMPing.Create(nil); PingObj.OnPing := OnPing; try PingObj.Pings := 30; PingObj.Host := ; PingObj.Ping; finally PingObj.Free; end;end;procedure TMyPingThread.OnPing(Sender: TObject; Host: String; Size, Time: Integer);var pMsg : pPingMsg; Msg : TPingMsg;begin /不能直接定义结构体,因为是局部变量,如果是PostMessage,不会等待,会释放的. /但如果采用如下的new方式,程序不会主动释放内存,需要配合Dispose方法用. new(pmsg); /这种情况下,消息接收方不一定能获取到正确的值. FMsg := host+:+ inttostr(size)+:+inttostr(Time); strcopy(pmsg.msg),pchar(FMsg); pmsg.id := self.FSequenceID; pmsg.Handled := false; pmsg.msg2 := FMsg+xxx;/注意,这里增加字符,并不能增加sizeof(pmsg) Msg.msg2 := FMsg+xxxx;/注意,这里增加字符,并不能增加sizeof(Msg) strcopy(Msg.msg),pchar(FMsg); /postmessage(FWinHandl,WM_MY_PING, self.FSequenceID,LPARAM(Msg); /因此我觉得采用SendMessage比较好,这样内存的释放可以在这里进行,不会造成内存泄露. Sendmessage(FWinHandl,WM_MY_PING, self.FSequenceID,LPARAM(Msg); /这种方法是让线程等待消息处理,实际上等效于SendMessage方法调用. while (pmsg.Handled=false) do begin sleep(10); end; /采用等待方法则在这里释放空间。如果采用消息接收方处理,则这里不需要释放。 Dispose(Pmsg); /Synchronize(HandlingPing);end;end.2 form 调用 Unit1unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,Unit2, StdCtrls;type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Memo2: TMemo; Memo3: TMemo; Memo4: TMemo; procedure Button1Click(Sender: TObject); private Private declarations FThreadCount : integer; procedure HandlingPing(Context:integer;Msg : string); procedure HanglingEnd(Context:integer;Msg : string); procedure OutPut(Context:integer;Msg : string); procedure PingMsgHdl(var Msg:TMessage);message WM_MY_PING; public Public declarations end;var Form1: TForm1;implementation$R *.dfmprocedure TForm1.Button1Click(Sender: TObject);var AThread : TMyPingThread;begin FThreadCount := 4; AThread := TMyPingThread.Create(self.Handle, 1,HandlingPing,HanglingEnd); AThread.Resume; AThread := TMyPingThread.Create(self.Handle,2,HandlingPing,HanglingEnd); AThread.Resume; AThread := TMyPingThread.Create(self.Handle,3,HandlingPing,HanglingEnd); AThread.Resume; AThread := TMyPingThread.Create(self.Handle,4,HandlingPing,HanglingEnd); AThread.Resume;end;procedure TForm1.HandlingPing(Context:integer;Msg: string);begin OutPut(Context,Msg);end;procedure TForm1.HanglingEnd(Context:integer;Msg: string);begin OutPut(Context,Msg); FThreadCount := FThreadCount -1; OutPut(1,inttostr(FThreadCount);end;procedure TForm1.OutPut(Context: integer; Msg: string);begin case context of 1: memo1.Lines.Append(Msg); 2: memo2.Lines.Append(Msg); 3: memo3.Lines.Append(Msg); 4: memo4.Lines.Append(Msg); end;end;procedure TForm1.PingMsgHdl(var Msg:TMessage);var pMsg : pPingMsg;begin pMsg := pPingMsg(Msg.LParam); OutPut(Msg.WParam, pmsg.msg2+=+inttostr(sizeof(pmsg); /这个用于等待线程,这里已经处理完毕。当然这只是一种方法. pMsg.Handled := true; /另外一种方法是在这里释放内存,但用户又可能会忘记释放。 /dispose(pMsg);end;end.用PostMessage需要做内存变量共享,比较麻烦,其实可以简单的用一个全局队列做,线程将消息放入队列,主程序用一个循环去取,这样线程不会等待主线程,效果更好些。1、windows的消息驱动体系 在windows系统中,消息传递是实现对乡间通信和控制的主要手段。可以额系统都以消息驱动的方式工作。系统中发生的用户输入操作、显示信息的改变、系统环境参数变化等所有时间都以系统定义消息的形式出现在相关的应用程序和窗口。所以程序设计的主要任务就是为这些消息的处理设计代码。 在应用程序中,发送者可以通过发送消息要求接收者完成相应的处理。当程序运行时,windows系统为每个应用程序实例建立一个消息队列,一次保存发送给该程序实例的消息,在应用程序的主控部分,需要设置一个消息循环,利用一组系统函数从该程序实例的消息队列中依次读取和分析消息,并将他们发送给指定的消息。这种消息传递可以使用系统定义消息,也可以使用用户自定义的消息。现有的多种可视化开发环境,都已经将这一过程封装到了各种类,部件,以及部件的方法中。当我们利用它们写一个应用程序时,必须考虑每个对象要做什么,将他们分解为不同的消息处理函数,并且对应不同的消息。虽然有的控件(部件)种类繁多,编写程序非常便利。但这些毕竟是一些通用的对象和消息处理函数,如果我们要编写自己的部件或者想要更加灵活的运用现有控件就必须深刻了解消息驱动体系以及消息的映射,消息处理函数编写和系统有关消息操作的函数。 2、DELPHI开发环境: DELPHI已成为日益普及的优秀的32位WINDOWS开发环境,其中一个最大的特点就是在DELPHI 中可自定义消息写自己的构件(Cmponent)的,因为编写构件一般要对相应的消息进行处理.。 3、DELPHI中的消息: 在Delphi 中所有的类都有一个内建的用于处理消息、调用消息处理过程的机制。VCL定义了发送所有WINDOWS消息(包括自定义消息)的消息派送系统,你只需要建立消息处理方法。一个Windows 消息是一个包含多个字段的数据记录。其中最重要的是消息标识,在WINDOWS中已经定义了许多消息其他有用的字段是两个参数和一个结果字段。一个16位和一个32位参数wparan录数据类型:type TMessage = packed record Msg: Cardinal; case Integer of 0: ( WParam: Longint; LParam: Longint; Result: Longint); 1: ( WParamLo: Word; WParamHi: Word; LParamLo: Word; LParamHi: Word; esultLo: Word; ResultHi: Word); end;4、消息的使用: 在DELPHI 中用户可以自定义消息及消息处理句柄消息处理句柄的定义有如下几个原则:1、消息处理句柄方法必须是一个过程 2、方法声明后要有一个message命令型常数). 3、消息处理句柄方法不需要用override 命令来显式指明重载祖先的一个消息处理句柄 4、在消息处理句柄中一般先是用户自己对消息的处理用祖先类中对应此消息的处理句柄(有些情况下可能正相反).由于可能对祖先类中对此消息的处理句柄的名字和参数类型不清楚避免此麻烦调用Defaulthandler方法.(当然如果要屏蔽掉此消息了)。消息处理句柄方法声明为: procedure Mymsgmethod(var message:Tmessage); message Msgtype;同样用户也可以定义自己的消息自定义消息及消息处理句柄举例如下: const my_paint=Wm_user 1; type Tmypaint=record msgid:cardinal; msize:word; mcolor:longint; msgresult:longint; end; /也可以使用Tmessage类型 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;重载消息句柄方法:你可以直接重载windows定义的窗口消息添加自己的处理。例如在应用中重载windows的窗口消息wm_paint:type TMyComponent = class(.) . procedure WMPaint(var Message: TWMPaint); message WM_PAINT; end; 然后再实现部分添加消息句柄的过程。 5、delphi中有关消息的重要函数和属性:(1) procedure DefaultHandler(var Message); virtual; 如果一个对象处理某个消息,则该函数提供对消息的默认处理。 (2) procedure Dispatch(var Message); virtual; 这个函数用于调用对象的消息处理函数。 (3)Windowproc属性: type TWndMethod = procedure(var Message: TMessage) of object; property WindowProc: TWndMethod;这个属性用于指定一个函数,用这个函数来响应message参数指定的windows消息。例如:procedure TMyListBoxDescendant.ToggleSubClass(On: Boolean); begin if On then WindowProc := SubClassWndProc; else WindowProc := WndProc; end; (4)function Perform(Msg: Cardinal; WParam这个函数用来向控件自己发一个消息。 例如: procedure TForm1.Button1Click(Sender: TObject); begin with RichEdit1 do Begin SelStart := StrToInt(MaskEdit1.Text); RichEdit1.Perform(EM_SCROLLCARET end; end; (5)procedure Broadcast(var Message);这个函数用于向所有的子控件广播一个消息。 (6)procedure DefaultHandler(var Message); virtual;该函数提供了队所有消息的默认处理,可以重载这个虚拟函数。Message参数是无类型的,但可以强制转换成Tmessage类型。 6、windows API中有关消息的几个重要函数:(1)GetMessage函数: BOOL GetMessage(LPMSG lpMsg HWND hWnd UINT wMsgFilterMin UINT wMsgFilterMax/ 最后的消息); delphi中的定义: function GetMessageA(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin 这个函数可以有选择的从消息队列重得到消息。 (2)TranslateMessage函数: BOOL TranslateMessage(CONST MSG *lpMsg / 消息的地址); delphi中的定义: function TranslateMessage(const lpMsg: TMsg): BOOL; stdcall; 这个函数用于将virtul-key消息翻译为字符消息。 (3)DispatchMessage函数: LONG DispatchMessage(CONST MSG *lpmsg / 消息的指针); delphi中的定义: function DispatchMessage(const lpMsg: TMsg): Longint; stdcall; 这个函数派送消息到窗口过程。 (4)PostMessage函数: BOOL PostMessage(HWND hWnd UINT Msg WPARAM wParam LPARAM lParam / 第二个消息参数); delphi中的定义: function PostMessageA(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;这个函数用于将消息传送到窗口的消息队列。 我在分析 VCL 消息机制的过程中,基本上只考查了三个类 TObject、TControl 和 TWinControl。虽然我没有阅读上层类(如 TForm)的代码,但我认为这些都是实现的细节。我相信 VCL 消息系统中最关键的东西都在这三个类中。纲举而目张,掌握基础类的消息处理方法之后再读其他类的消息处理过程就容易得多了。 要想读懂本文,最低配置为: 了解 Win32 消息循环和窗口过程 基本了解 TObject、TControl 和 TWinControl 实现的内容 熟悉 Delphi 对象的重载与多态 推荐配置为: 熟悉 Win32 SDK 编程 熟悉 Delphi 的对象机制 熟悉 Delphi 内嵌汇编语言 推荐阅读: Delphi 的原子世界 / VCL窗口函数注册机制研究手记,兼与MFC比较 /delphibbs/dispq.asp?lid=584889 Delphi的对象机制浅探 /delphibbs/dispq.asp?LID=2390131 本文排版格式为: 正文由窗口自动换行;所有代码以 80 字符为边界;中英文字符以空格符分隔。 (作者保留对本文的所有权利,未经作者同意请勿在在任何公共媒体转载。) 目 录 = 一个 GUI Application 的执行过程:消息循环的建立 TWinControl.Create、注册窗口过程和创建窗口 补充知识:TWndMethod 概述 VCL 的消息处理从 TWinControl.MainWndProc 开始 TWinControl.WndProc TControl.WndProc TObject.Dispatch TWinControl.DefaultHandler TControl.Perform 和 TWinControl.Broadcast TWinControl.WMPaint 以 TWinControl 为例描述消息传递的路径 = 正 文 = 一个 GUI Application 的执行过程:消息循环的建立 = 通常一个 Win32 GUI 应用程序是围绕着消息循环的处理而运行的。在一个标准的 C 语言 Win32 GUI 程序中,主程序段都会出现以下代码: while (GetMessage(&msg, NULL, 0, 0) / GetMessage 第二个参数为 NULL, / 表示接收所有应用程序产生的窗口消息 TranslateMessage(&msg); / 转换消息中的字符集 DispatchMessage(&msg); / 把 msg 参数传递给 lpfnWndProc lpfnWndProc 是 Win32 API 定义的回调函数的地址,其原型如下: int _stdcall WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); Windows 回调函数(callback function) 也通常被称为窗口过程(window procedure),本文随意使用这两个名称,代表同样的意义。 应用程序使用 GetMessage 不断检查应用程序的消息队列中是否有消息到达。如果发现了消息,则调用 TranslateMessage。TranslateMessage 主要是做字符消息本地化的工作,不是关键的函数。然后调用 DispatchMessage(&msg)。DispatchMessage(&msg) 使用 msg 为参数调用已创建的窗口的回调函数(WndClass.lpfnWndProc)。lpfnWndProc 是由用户设计的消息处理方法。 当 GetMessage 在应用程序的消息队列中发现一条 WM_QUIT 消息时,GetMessage 返回 False,消息循环才告结束,通常应用程序在这时清理资源后也结束运行。 使用最原始的 Win32 API 编写的应用程序的执行过程是很容易理解的,但是用 Delphi VCL 组件封装消息系统,并不是容易的事。首先,Delphi 是一种面向对象的程序设计语言,不但要把 Win32 的消息处理过程封装在对象的各个继承类中,让应用程序的使用者方便地调用,也要让 VCL 组件的开发者有拓展消息处理的空间。其次,Delphi 的对象模型中所有的类方法都是对象相关的(也就是传递了一个隐含的参数 Self),所以 Delphi 对象的方法不能直接被 Windows 回调。Delphi VCL 必须用其他的方法让 Windows 回调到对象的消息处理函数。 让我们跟踪一个标准的 Delphi Application 的执行过程,查看 Delphi 是如何开始一个消息循环的。 program Project1; beginApplication.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. 在 Project1 的 Application.Initialize 之前,Delphi 编译器会自动插入一行代码: SysInit._InitExe。_InitExe 主要是初始化 HInstance 和模块信息表等。然后 _InitExe 调用 System._StartExe。System._StartExe 调用 System.InitUnit;System.InitUnit 调用项目中所有被包含单元的 Initialization 段的代码;其中有 Controls.Initialization 段,这个段比较关键。在这段代码中建立了 Mouse、Screen 和 Application 三个关键的全局对象。 Application.Create 调用 Application.CreateHandle。Application.CreateHandle 建立一个窗口,并设置 Application.WndProc 为回调函数(这里使用了 MakeObjectInstance 方法,后面再谈)。Application.WndProc 主要处理一些应用程序级别的消息。 我第一次跟踪应用程序的执行时没有发现 Application 对象的创建过程,原来在 SysInit._InitExe 中被隐含调用了。如果你想跟踪这个过程,不要设置断点,直接按 F7 就发现了。 然后才到了 Project1 的第 1 句: Application.Initialize; 这个函数只有一句代码: if InitProc nil then TProcedure(InitProc); 也就是说如果用户想在应用程序的执行前运行一个特定的过程,可以设置 InitProc 指向该过程。(为什么用户不在 Application.Initialize 之前或在单元的 Initliazation 段中直接运行这个特定的过程呢?一个可能的答案是:如果元件设计者希望在应用程序的代码执行之前执行一个过程,并且这个过程必须在其他单元的 Initialization 执行完成之后执行比如说 Application 对象必须创建,则只能使用这个过程指针来实现。) 然后是 Project1 的第 2 句: Application.CreateForm(TForm1, Form1); 这句的主要作用是创建 TForm1 对象,然后把 Application.MainForm 设置为 TForm1。 最后是 Project1 的第 3 句: Application.Run; TApplication.Run 调用 TApplication.HandleMessage 处理消息。Application.HandleMessage 的代码也只有一行: if not ProcessMessage(Msg) then Idle(Msg); TApplication.ProcessMessage 才真正开始建立消息循环。ProcessMessage 使用 PeekMessage API 代替 GetMessage 获取消息队列中的消息。使用 PeekMessage 的好处是 PeekMessage 发现消息队列中没有消息时会立即返回,这样就为 HandleMessage 函数执行 Idle(Msg) 提供了依据。 ProcessMessage 在处理消息循环的时候还特别处理了 HintMsg、MDIMsg、KeyMsg、DlgMsg 等特殊消息,所以在 Delphi 中很少再看到纯 Win32 SDK 编程中的要区分 Dialog Window、MDI Window 的处理,这些都被封装到 TForm 中去了(其实 Win32 SDK 中的 Dialog 也是只是 Microsoft 专门写了一个窗口过程和一组函数方便用户界面的设计,其内部运作过程与一个普通窗口无异)。 function TApplication.ProcessMessage(var Msg: TMsg): Boolean; varHandled: Boolean; beginResult := False; if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then / 从消息队列获取消息 beginResult := True; if Msg.Message WM_QUIT thenbeginHandled := False; / Handled 表示 Application.OnMessage 是否已经处理过 / 当前消息。 / 如果用户设置了Application.OnMessage 事件句柄, / 则先调用 Application.OnMessage if Assigned(FOnMessage) then FOnMessage(Msg, Handled); if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) andnot IsKeyMsg(Msg) and not IsDlgMsg(Msg) then/ 思考:not Handled 为什么不放在最前? beginTranslateMessage(Msg); / 处理字符转换 DispatchMessage(Msg); / 调用 WndClass.lpfnWndProc end; endelseFTerminate := True; / 收到 WM_QUIT 时应用程序终止 / (这里只是设置一个终止标记) end; end; 从上面的代码来看,Delphi 应用程序的消息循环机制与标准 Win32 C 语言应用程序差不多。只是 Delphi 为了方便用户的使用设置了很多扩展空间,其副作用是消息处理会比纯 C Win32 API 调用效率要低一些。 = TWinControl.Create、注册窗口过程和创建窗口 = 上面简单讨论了一个 Application 的建立到形成消息循环的过程,现在的问题是 Delphi 控件是如何封装创建窗口这一过程的。因为只有建立了窗口,消息循环才有意义。 让我们先回顾 Delphi VCL中几个主要类的继承架框: TObject 所有对象的基类 TPersistent 所有具有流特性对象的基类 TComponent 所有能放在 Delphi Form Designer 上的对象的基类 TControl 所有可视的对象的基类 TWinControl 所有具有窗口句柄的对象基类 Delphi 是从 TWinControl 开始实现窗口相关的元件。所谓窗口,对于程序设计者来说,就是一个窗口句柄 HWND。TWinControl 有一个 FHandle 私有成员代表当前对象的窗口句柄,通过 TWinControl.Handle 属性来访问。 我第一次跟踪 TWinControl.Create 过程时,竟然没有发现 CreateWindow API 被调用,说明 TWinControl 并不是在对象创建时就建立 Windows 窗口。如果用户使用 TWinControl.Create(Application) 以后,立即使用 Handle 访问窗口会出现什么情况呢? 答案在 TWinControl.GetHandle 中,Handle 是一个只读的窗口句柄: property TWinControl.Handle: HWnd read GetHandle; TWinControl.GetHandle 代码的内容是:一旦用户要访问 FHandle 成员,TWinControl.HandleNeeded 就会被调用。HandleNeeded 首先判断 TWinControl.FHandle 是否是等于 0 (还记得吗
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年医疗设施食堂租赁及全面食品安全服务协议
- 2025年葡萄种植户与葡萄酒出口企业联营加工销售合同
- (2025年标准)帮扶学校协议书
- 2025年跨境电商平台运营优化及品牌营销顾问服务协议
- 2025年度循环经济发展战略合作伙伴协议
- 2025年城市绿化工程中堰塘景观设计承包施工合同
- 2025年草原改良工程草种供应与配套技术服务合同
- 2025年海鲜食材冷链配送与智能化物流网络构建合同
- 2025远程医疗设备临床试验伦理审查顾问合同模板
- 房价长期趋势预测方法-洞察及研究
- 企业会计准则实施典型案例
- 降低留置针堵管发生率:PDCA质量持续改进
- 居间工程合同协议书范本
- 香港应聘简历模板
- 高考英语一轮专项复习:高考试题中的熟词生义(含解析)
- 2025年全国卷高考历史小论文题指导分析及例题鉴赏
- 《Java程序设计任务式教程》课件 603 构造方法
- 军队基本医疗设备配备标准
- 特种设备监察培训
- GB/T 23713.1-2024机器状态监测与诊断预测第1部分:一般指南
- 2023-2029年中国城市隧道养护管理行业市场发展现状及投资战略咨询报告
评论
0/150
提交评论