技術演進中的開發沉思-53 DELPHI VCL系列:windows的消息(下):TApplication窗體

今天我們梳理下關于TApplication的窗體消息下半部分的內容。前面也說過,在 Delphi 的世界里,TApplication 就像一位經驗豐富的總工程師,而主窗體則是它傾注心血打造的核心建筑。如果你第一次在實驗室里敲出 Delphi 代碼時,屏幕上彈出的空白窗體像塊剛裁好的畫布,其實這塊畫布的誕生藏著一整套精密的 "施工流程"。今天我們就循著TApplication代碼的脈絡,揭開這場數字建造的神秘面紗,感受那些隱藏在函數調用背后的匠心與智慧。

一、主窗體的誕生

創建主窗體的過程,恰似老木匠打造一張八仙桌 —— 先立框架,再裝榫卯,最后上漆打磨,每一步都暗藏玄機,缺一不可。

1、TWinControl.Create

當你在 Delphi 里拖出第一個 TForm 時,調試窗口閃過的 TWinControl.Create 這就像蓋房子時挖掘機挖出的第一方土。它在內存里開辟出一塊專屬空間,給窗體的所有 "器官"—— 按鈕、文本框、滾動條都預留了位置。就像老家蓋房時,父親總要先丈量地基尺寸,用石灰畫出輪廓,TWinControl.Create 就是那個用代碼畫輪廓的 "老把式"。

記得大學里用的Delphi 4,每次新建工程都會自動生成這句隱含的創建代碼。有次誤刪了窗體的父類引用,編譯時跳出的 "Cannot create form" 錯誤,像極了地基沒打好就想砌墻的荒唐。

2、TForm.HandleNeeded

當窗體剛創建時讀不到句柄值。這就像去傳達室領訪客證,你不主動要,人家不會給。當程序需要調用 Windows API 操作窗體時,就得靠 HandleNeeded"提醒" 系統準備好句柄。記得有次做屏幕截圖功能,剛創建的窗體還沒顯示就調用 GetDC,結果返回了 0。加上 HandleNeeded 后,就像給保安出示了身份證,系統才肯交出操作權限。這函數本身不生成句柄,卻像個盡責的秘書,確保你要用的時候,"通行證" 已經備好。

3、TForm.CreateHandle

HandleNeeded 只是提醒,真正打造句柄的是 TForm.CreateHandle。這就像派出所制作身份證的過程 —— 把你的個人信息(窗體屬性)錄入系統,生成唯一編號(句柄值)。在多窗體程序,發現兩個窗體的句柄偶爾會重復。跟蹤源碼才發現,是自定義窗體類里重寫 CreateHandle 時忘了調用 inherited。就像補辦身份證時沒走正規流程,拿到的號碼自然可能沖突。正常情況下,這個函數會調用 Windows 的 CreateWindowEx,把窗體的各種屬性翻譯成系統能理解的語言,最終生成那個獨一無二的整數句柄。

4、TWinControl.CreateWnd 與 TForm.CreateWnd

如果說句柄是身份證,那 CreateWnd 就是搭建窗體的 "鋼筋骨架"。TWinControl.CreateWnd 是所有窗口控件的通用骨架,而 TForm.CreateWnd 則在此基礎上增加了窗體特有的結構。記得做異形窗體時的經歷:想做個圓形登錄界面,重寫了 CreateWnd 卻總失敗。后來發現,TWinControl.CreateWnd 已經處理了窗口的基本創建邏輯,我直接覆蓋父類方法相當于拆了承重墻。正確的做法是先調用 inherited 執行父類的創建流程,再在后面添加自定義形狀的代碼,就像先按標準圖紙建起框架,再切割出圓形的門窗。

5、TForm 的父代類別 TScrollingWinControl

TScrollingWinControl 是個特別的存在,它就像給窗體裝了可伸縮的陽臺。如數據一多就超出窗體范圍,這時候就可用這個父類自帶的滾動功能。它內部維護著滾動條的狀態,當控件內容超過顯示區域時自動顯示滾動條,就像陽臺根據需要伸出縮進。記得有次自定義滾動邏輯,發現即使隱藏滾動條,它依然在后臺計算內容偏移量。這種封裝特別巧妙 —— 開發者不用關心滾動條如何與內容聯動,只需設置 AutoScroll 屬性,剩下的交給這個父類處理,就像按下按鈕,陽臺自動根據需要調整長度。

6、VCL Framework 的窗口 thunk 回叫函式

InitWndProc 是 VCL 里的 "通信兵",負責把 Windows 系統的消息傳遞給 Delphi 的對象方法。這個 thunk 技術特別精妙,能把 C 風格的回調函數轉換成面向對象的方法調用。系統發送的 WM_PAINT 消息,正是通過 InitWndProc 這個中轉站,最終變成了 TForm 的 OnPaint 事件。它就像跨國電話的轉接站,把系統的 "信號" 翻譯成 VCL 能理解的 "語言",確保消息準確送達對應的處理方法。當年為了理解這個機制,我對著匯編代碼啃了三天,才明白這層轉換背后的匠心。

7、TForm.CreateParams

每次創建窗體前,CreateParams 都會先畫好 "施工藍圖"。它設置的參數決定了窗體的樣式 —— 是對話框還是主窗口,有沒有邊框,能否最大化。如果你做一個控制界面,需要去掉標題欄。修改 CreateParams 里的 Style 參數,把 WS_CAPTION 去掉,窗體立刻變成了無邊框的樣子,就像按圖紙拆掉了屋頂的房檐。這個函數的神奇之處在于,它把復雜的窗口樣式常量,用面向對象的方式組織起來,開發者不用記住那些晦澀的常量值,只需設置 BorderStyle 等屬性,CreateParams 會自動翻譯成對應的樣式參數。

8、TCustomForm.CreateWindowHandle

經過前面這么多步驟,最后由 TCustomForm.CreateWindowHandle 完成 "最后一千米" 的施工。它拿著 CreateParams 生成的 "圖紙",調用系統 API 真正創建窗口。例如:你開發中窗體創建后總是在屏幕外。跟蹤到 CreateWindowHandle 會發現,是自定義的位置參數計算錯誤。這個函數就像施工隊的最后一道工序,把所有設計參數落實到實際的窗口創建中,最終讓窗體在屏幕上顯現出具體的樣子。看著調試器里它返回的 True 值,就像看到建筑竣工驗收合格的證書。

二、窗體的 “通信系統”:窗口訊息處理機制

窗體處理窗口訊息的機制,宛如一個高效的郵局,能夠精準地分揀和處理各種信息。

想象一下,當你點擊窗體上的關閉按鈕時,一個 “關閉” 訊息就像一封信件被發送到窗體的 “郵局”。窗體內部的訊息處理機制會迅速接收這封信,并按照既定的規則進行處理。

這里有個有趣的例子,我們可以通過攔截窗口訊息來改變窗體的屬性。比如,我們可以攔截 WM_CLOSE 訊息,讓窗體在收到關閉指令時不立即關閉,而是彈出一個提示框詢問用戶是否真的要關閉。就像郵局收到一封加急信件,我們可以先檢查信件內容,再決定是否投遞。

以下是一個簡單的代碼示例,用于攔截 WM_CLOSE 訊息:


unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)procedure FormCreate(Sender: TObject);private{ Private declarations }procedure WMCLOSE(var Msg: TWMClose); message WM_CLOSE;public{ Public declarations }end;varForm1: TForm1;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);beginend;procedure TForm1.WMCLOSE(var Msg: TWMClose);beginif MessageDlg('確定要關閉窗體嗎?', mtConfirmation, [mbYes, mbNo], 0) = mrYes thenbegininherited; // 調用父類的處理方法,執行關閉操作end;end;end.

在這個例子中,我們通過定義 WMCLOSE 方法并指定它處理 WM_CLOSE 訊息,實現了對關閉訊息的攔截和自定義處理。

三、TApplication 的設計思想

總的來說,TApplication 就像一位指揮家,在整個應用程序中發揮著統籌協調的作用。它負責管理應用程序的生命周期,協調各個窗體和組件之間的工作,確保整個程序能夠有條不紊地運行。在桌面開發時代,Delphi 的 TApplication 設計展現出了巨大的優勢。它將復雜的底層操作封裝起來,讓開發者能夠專注于業務邏輯的實現,就像指揮家不需要親自演奏每一種樂器,卻能讓整個樂隊奏出和諧的樂章。

最后小結

從 web1.0 到移動互聯網時代,技術在不斷變遷,但 TApplication 所體現的封裝、協調的設計思想卻一直影響著后來的開發框架。它告訴我們,一個優秀的框架應該像一位貼心的助手,為開發者屏蔽復雜的細節,讓開發過程變得更加輕松高效。回顧 Delphi VCL Application開發的這些技術點,就像翻開一本記錄著數字建筑歷史的相冊。2如今都成了技術成長的注腳。每一個函數、每一個機制都承載著開發者的智慧和汗水,它們共同構建了 Delphi 輝煌的過去,也為我們今天的技術發展提供了寶貴的借鑒,不能忘記,還要繼續前行。未完待續............

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91637.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91637.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91637.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

cesium FBO(四)自定義相機渲染到Canvas(離屏渲染)

前面幾節的例子是將Cesium默認的相機渲染到紋理(RTT)或Canvas,這片文章講解如何將自定義的一個camera的畫面渲染到Canvas上,有了前面幾篇的基礎了,也能將自定義的畫面渲染紋理、也可以灰度處理,原理是一樣的…

雙機并聯無功環流抑制虛擬阻抗VSG控制【simulink仿真模型實現】

雙機并聯虛擬同步發電機(VSG)系統中,因線路阻抗不匹配及參數差異,易引發無功環流。本方案在傳統VSG控制基礎上,引入自適應虛擬阻抗環節。其核心在于:實時檢測兩機間無功環流分量,據此動態調節各…

python測試總結

測試題的基礎知識點總結 1.循環求和 for循環步長(range(2,101,2)) while循環條件判斷(i%20) 生成器表達式(sum(i for i in range )) 所以:sum(range(1,101,2))(奇數和)和…

識別和分類惡意軟件樣本的工具YARA

YARA 是一個用于識別和分類惡意軟件樣本的工具,廣泛應用于惡意軟件分析、威脅情報、入侵檢測等領域。它通過編寫規則(YARA Rules)來匹配文件中的特定字符串、十六進制模式、正則表達式等特征。 一、YARA 的基本使用方法 1. 安裝 YARA Linux(Ubuntu/Debian) sudo apt-ge…

GaussDB 約束的語法

1 約束的作用約束是作用于數據表中列上的規則,用于限制表中數據的類型。約束的存在保證了數據庫中數據的精確性和可靠性。約束有列級和表級之分,列級約束作用于單一的列,而表級約束作用于整張數據表。下面是 GaussDB SQL 中常用的約束。NOT …

SecurityContextHolder 管理安全上下文的核心組件詳解

SecurityContextHolder 管理安全上下文的核心組件詳解在 Spring Security 中,SecurityContextHolder 是??安全上下文(Security Context)的核心存儲容器??,其核心作用是??在當前線程中保存當前用戶的認證信息(如用…

c++詳解系列(引用指針)

目錄 1.什么是引用 2.引用的定義 3.引用的特性 4.引用的使用 4.1引用傳參 4.2傳引用返回 5.const引用(在引用的定義前用const修飾) 5.1對于引用 5.2對于指針 6.引用&指針 總結 1.什么是引用 引用就是給變量起別名,一個變量可以…

深度學習loss總結(二)

對于目前深度學習主流任務學習,loss的設置至關重要。下面就不同任務的loss設置進行如下總結: (1)目標檢測 2D/3D目標檢測中的 Loss(損失函數)是訓練模型時優化目標的核心,通常包括位置、類別、尺寸、方向等多個方面。以下是目前 常見的 2D 和 3D 目標檢測 Loss 分類與…

【Linux網絡】netstat 的 -anptu 各個參數各自表示什么意思?

netstat 是一個網絡統計工具,它可以顯示網絡連接、路由表、接口統計、偽裝連接和多播成員資格。在 netstat 命令中,不同的參數可以用來定制輸出的內容。 你提到的 -anptu 參數組合各自的功能如下: -a (all): 顯示所有活動的連接和監聽端口。它…

[硬件電路-115]:模擬電路 - 信號處理電路 - 功能放大器工作分類、工作原理、常見芯片

功能放大器是以特定功能為核心的集成化放大電路,通過將運算放大器與外圍電阻、電容等元件集成在單一芯片中,實現標準化、高性能的信號放大功能。其核心優勢在于簡化設計流程、提升系統穩定性,并針對特定應用場景優化性能參數。以下從定義、分…

雙網卡UDP廣播通信機制詳解

UDP廣播通信機制詳解 一、通信流程分析 發送階段 通過Client.Bind(192.168.0.3, 60000)將UDP套接字綁定到指定網卡和端口設置RemoteHost "255.255.255.255"實現全網段廣播數據流向:192.168.0.3:60000 → 255.255.255.255:50000 接收階段 設備響應數據應返…

從遮擋難題到精準測量:激光頻率梳技術如何實現深孔 3D 輪廓的 2um 級重復精度?

一、深孔 3D 輪廓測量的遮擋困境深孔結構(如航空發動機燃油噴嘴孔、模具冷卻孔)因孔深大(常超 100mm)、深徑比高(>10:1),其 3D 輪廓測量長期受限于光學遮擋難題。傳統光學測量技術&a…

.NET 依賴注入(DI)全面解析

文章目錄一、依賴注入核心原理1. 控制反轉(IoC)與DI關系2. .NET DI核心組件二、服務生命周期1. 三種生命周期類型三、DI容器實現原理1. 服務注冊流程2. 服務解析流程四、高級實現方法1. 工廠模式注冊2. 泛型服務注冊3. 多實現解決方案五、ASP.NET Core中的DI集成1. 控制器注入2…

K8S部署ELK(二):部署Kafka消息隊列

目錄 1. Kafka 簡介 1.1 Kafka 核心概念 (1)消息系統 vs. 流處理平臺 (2)核心組件 1.2 Kafka 核心特性 (1)高吞吐 & 低延遲 (2)持久化存儲 (3)分…

Rust進階-part1-智能指針概述-box指針

Rust進階[part1]_智能指針概述&box指針 智能指針概述 在Rust中,智能指針是一類特殊的數據結構,它們不僅像普通指針一樣可以引用數據,還帶有額外的元數據和功能。與普通指針不同,智能指針通常使用結構體實現,并且會實現 Deref 和 Drop 等特定的trait,以提供更強大的…

C++擴展 --- 并發支持庫(補充1)

C擴展 --- 并發支持庫(下)https://blog.csdn.net/Small_entreprene/article/details/149606406?fromshareblogdetail&sharetypeblogdetail&sharerId149606406&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link atom…

在Three.js中導入和添加自定義網格的最佳實踐 - 綜合指南

探索在Three.js中導入和添加自定義網格的最佳實踐。本指南涵蓋增強 3D 項目的技術、技巧和實際示例。 添加圖片注釋,不超過 140 字(可選) 強烈建議使用 GLTF 格式來集成 3D 幾何體,提供簡化的流程,并固有地支持動畫、…

Redis知識點(1)

目錄 Redis Redis和MySQL的區別 Redis的高可用方案 Redis可以用來做什么 Redis的數據類型 字符串 列表 哈希 集合 有序集合 Bitmap Redis為什么快呢? I/O多路復用 說說select,poll,epoll,kqueue,IOCP的區別 Redis為什么早期選擇單線程? …

使用iptables封禁惡意ip異常請求

查看后端日志發現一IP(103.76.250.29)頻繁請求不存在的資源路徑??(如 /api/v1/guest/comm/config、/theme/default/assets/compoments.js 等),并伴隨對根路徑 / 的正常訪問。這種行為的可能性包括惡意掃描、自動化工…

BehaviorTree.Ros2 編譯教程

1. 源碼下載 git clone https://github.com/BehaviorTree/BehaviorTree.ROS2.git2. 編譯過程 源碼中有3個項目: btcpp_ros2_interfacesbtcpp_ros2_interfacesbtcpp_ros2_samples 2.1 編譯btcpp_ros2_interfaces: colcon --packages-select btcpp_ros2_interfaces2.2 編譯 …