深入delphi編程理解之消息(一)WINDOWS原生視窗編寫及消息處理過程

来源:https://www.cnblogs.com/LifeStartPoint/archive/2020/01/17/12206975.html
-Advertisement-
Play Games

通過以sdk方式編製windows視窗程式,對理解windows消息驅動機制和delphi消息編程有很大的幫助。 sdk編製windows視窗程式的步驟: 1、對TWndClass對象進行賦值; 2、向系統註冊wndclass對象(RegisterClass); 3、CreateWindow創建視窗 ...


  通過以sdk方式編製windows視窗程式,對理解windows消息驅動機制和delphi消息編程有很大的幫助。
sdk編製windows視窗程式的步驟:
1、對TWndClass對象進行賦值;
2、向系統註冊wndclass對象(RegisterClass);
3、CreateWindow創建視窗,獲得視窗句柄Hwnd;
4、顯示視窗(ShowWindow);
5、通過GetMessage函數不斷獲取系統消息,交給程式處理,程式過通回調函數(wndproc)處理系統消息。(消息處理部分)
程式代碼如下:
program Project2;

//{$APPTYPE CONSOLE}

uses
  Windows,
  Messages;

//==============================================================================
// 定義回調函數 Wndproc(),處理windows消息;
// 參數說明:
// HWND 可以理解為視窗句柄
// AMessage 可理解為消息的編號
// Wparam 消息的高位
// Lparam 消息的低位
//==============================================================================

function Wndproc(HWND: Thandle; AMessage: LongInt; wparam: wparam; lParam: lParam): Lresult; stdcall;
begin
  case AMessage of
    WM_DESTROY:         //處理視窗退出消息
      begin
        PostQuitMessage(0);
        Result := 0;
      end
  else    {將未處理的消息交給系統處理}
    Result := DefWindowProcW(HWND, AMessage, wparam, lParam);
  end;
end;

procedure WndMain;
var
  msg: TMsg;
  Wndclass: TWndClass;
  Hwnd: THandle;
begin
  {給視窗類賦值}
  Wndclass.lpfnWndProc := @Wndproc; //視窗回調函數,處理windows消息
  Wndclass.style := CS_HREDRAW or CS_VREDRAW;
  ;              //視窗類型
  Wndclass.cbClsExtra := 0;
  Wndclass.cbWndExtra := 0;
  Wndclass.hInstance := HInstance;
  Wndclass.hIcon := LoadIcon(0, IDI_APPLICATION);
  Wndclass.hCursor := LoadCursor(0, IDC_ARROW);
  Wndclass.lpszClassName := 'MyWndClass';
  Wndclass.lpszMenuName := nil;
  Wndclass.hbrBackground := GetStockObject(GRAY_BRUSH);
  {向系統註冊視窗類}
  RegisterClass(Wndclass);
  {創建視窗}
  Hwnd := CreateWindow(Wndclass.lpszClassName, '用sdk編寫的windows視窗', WS_OVERLAPPED, 100, 100, 300, 200, 0, 0, HInstance, 0);

  if Hwnd <> 0 then
  begin
    {顯示視窗}
    ShowWindow(Hwnd, SW_SHOWNORMAL);
    UpdateWindow(Hwnd);
    {處理視窗消息}
    while GetMessage(msg, 0, 0, 0) do
    begin
      TranslateMessage(msg);
      DispatchMessage(msg);
    end;
  end;

end;

begin
  WndMain;
  { TODO -oUser -cConsole Main : Insert code here }
end.

一、運行界面如下:

二、程式函數說明:

  (一)、視窗類型 TWndClass 其實是一個結構, 是 tagWNDCLASSA 結構的重命名.

{tagWNDCLASSA 結構:}
tagWNDCLASSA = packed record
  style: UINT;              {視窗風格, 見下表}
  lpfnWndProc: TFNWndProc;  {視窗回調函數的指針, 後面要詳細分析}
  cbClsExtra: Integer;      {為視窗類分配的額外空間, 一般為 0}
  cbWndExtra: Integer;      {為視窗實例分配的額外空間, 一般為 0}
  hInstance: HINST;         {視窗所在程式實例的句柄, 就是 HInstance}
  hIcon: HICON;             {指定視窗圖標, 一般用 LoadIcon 載入; 不指定可設為 0}
  hCursor: HCURSOR;         {指定視窗游標, 一般用 LoadCursor 載入; 不指定可設為 0}
  hbrBackground: HBRUSH;    {指定視窗背景畫刷, 這需要用 GetStockObject 函數檢索; 也可以直接指定系統顏色}
  lpszMenuName: PAnsiChar;  {菜單資源名稱; 一般置為 nil, 表示視窗沒有預設菜單}
  lpszClassName: PAnsiChar; {給該視窗類命名; CreateWindow 函數將使用這個名稱}
end;

//視窗風格參數 style 可選值:
CS_VREDRAW         = DWORD(1); {視窗高度變化時將被重繪}
CS_HREDRAW         = DWORD(2); {視窗寬度變化時將被重繪}
CS_KEYCVTWINDOW    = 4;        {}
CS_DBLCLKS         = 8;        {不忽略滑鼠雙擊的消息}
CS_OWNDC           = $20;      {給用該類建立的每一個視窗分配獨立的設備 DC}
CS_CLASSDC         = $40;      {讓屬於該類的所有視窗共用一個設備 DC}
CS_PARENTDC        = $80;      {允許視窗的子視窗繼承一些共同特性}
CS_NOKEYCVT        = $100;     {}
CS_NOCLOSE         = $200;     {禁用系統菜單的 Close命令,同時視窗沒有關閉按鈕}
CS_SAVEBITS        = $800;     {當視窗被覆蓋時, 用點陣圖緩存被覆蓋區, 從而避免 WM_PAINT 消息, 一般用於菜單或對話框}
CS_BYTEALIGNCLIENT = $1000;    {通過位元組對齊, 增強客戶區的繪製性能}
CS_BYTEALIGNWINDOW = $2000;    {通過位元組對齊, 增強視窗的繪製性能}
CS_GLOBALCLASS     = $4000;    {全局視窗類, 一般用於 DLL; 沒有此選項, 視窗類和視窗建立函數中指定的實例句柄須相同}

//關於視窗背景畫刷:
{系統預定義了一些畫刷, 需要用 GetStockObject 根據指定的常數檢索;}
{但 GetStockObject 返回的句柄有可能是畫刷、畫筆、調色板或系統字體的句柄,}
{所以還需要把 GetStockObject 返回的句柄進行類型轉換, 譬如:  HBRUSH(GetStockObject(常數))}
//下麵是 GetStockObject 函數參數的可選值:
WHITE_BRUSH         = 0;
LTGRAY_BRUSH        = 1;
GRAY_BRUSH          = 2;
DKGRAY_BRUSH        = 3;
BLACK_BRUSH         = 4;
NULL_BRUSH          = 5;
HOLLOW_BRUSH        = NULL_BRUSH;
WHITE_PEN           = 6;
BLACK_PEN           = 7;
NULL_PEN            = 8;
OEM_FIXED_FONT      = 10;
ANSI_FIXED_FONT     = 11;
ANSI_VAR_FONT       = 12;
SYSTEM_FONT         = 13;
DEVICE_DEFAULT_FONT = 14;
DEFAULT_PALETTE     = 15;
SYSTEM_FIXED_FONT   = $10;
DEFAULT_GUI_FONT    = 17;
DC_BRUSH            = 18;
DC_PEN              = 19;
STOCK_LAST          = 19;

{另外背景畫刷還可以使用 Windows 定義系統顏色常量, 譬如: HBRUSH(COLOR_WINDOW + 1) }
COLOR_SCROLLBAR               = 0;
COLOR_BACKGROUND              = 1;
COLOR_ACTIVECAPTION           = 2;
COLOR_INACTIVECAPTION         = 3;
COLOR_MENU                    = 4;
COLOR_WINDOW                  = 5;
COLOR_WINDOWFRAME             = 6;
COLOR_MENUTEXT                = 7;
COLOR_WINDOWTEXT              = 8;
COLOR_CAPTIONTEXT             = 9;
COLOR_ACTIVEBORDER            = 10;
COLOR_INACTIVEBORDER          = 11;
COLOR_APPWORKSPACE            = 12;
COLOR_HIGHLIGHT               = 13;
COLOR_HIGHLIGHTTEXT           = 14;
COLOR_BTNFACE                 = 15;
COLOR_BTNSHADOW               = $10;
COLOR_GRAYTEXT                = 17;
COLOR_BTNTEXT                 = 18;
COLOR_INACTIVECAPTIONTEXT     = 19;
COLOR_BTNHIGHLIGHT            = 20;
COLOR_3DDKSHADOW              = 21;
COLOR_3DLIGHT                 = 22;
COLOR_INFOTEXT                = 23;
COLOR_INFOBK                  = 24;
COLOR_HOTLIGHT                = 26;
COLOR_GRADIENTACTIVECAPTION   = 27;
COLOR_GRADIENTINACTIVECAPTION = 28;
COLOR_MENUHILIGHT             = 29;
COLOR_MENUBAR                 = 30;
COLOR_ENDCOLORS               = COLOR_MENUBAR;
COLOR_DESKTOP                 = COLOR_BACKGROUND;
COLOR_3DFACE                  = COLOR_BTNFACE;
COLOR_3DSHADOW                = COLOR_BTNSHADOW;
COLOR_3DHIGHLIGHT             = COLOR_BTNHIGHLIGHT;
COLOR_3DHILIGHT               = COLOR_BTNHIGHLIGHT;
COLOR_BTNHILIGHT              = COLOR_BTNHIGHLIGHT;
(二)、認識 CreateWindow 函數



CreateWindow(
  lpClassName: PChar;     {視窗類的名字}
  lpWindowName: PChar;    {視窗標題}
  dwStyle: DWORD;         {視窗樣式, 參加下表}
  X,Y: Integer;           {位置; 預設的X,Y可以指定為: Integer(CW_USEDEFAULT)}
  nWidth,nHeight: Integer;{大小; 預設的寬度、高度可以指定為: Integer(CW_USEDEFAULT)}}
  hWndParent: HWND;       {父視窗句柄}
  hMenu: HMENU;           {主菜單句柄}
  hInstance: HINST;       {模塊實例句柄, 也就是當前 exe 的句柄}
  lpParam: Pointer        {附加參數, 創建多文檔界面時才用到, 一般設為 nil}
): HWND;                  {返回所創建的視窗的句柄}

//dwStyle 視窗樣式參數可選值:
WS_OVERLAPPED       = 0;                {重疊式視窗, 應帶標題欄和邊框}
WS_POPUP            = DWORD($80000000); {彈出式視窗, 不能與 WS_CHILD 一起使用}
WS_CHILD            = $40000000;        {子視窗, 不能與 WS_POPUP 一起使用}
WS_MINIMIZE         = $20000000;        {最小化視窗}
WS_VISIBLE          = $10000000;        {初始時可見}
WS_DISABLED         = $8000000;         {禁止輸入}
WS_CLIPSIBLINGS     = $4000000;         {裁剪子視窗, 也就是子視窗重繪不影響重疊的其他子視窗, 應與 WS_CHILD 一起使用}
WS_CLIPCHILDREN     = $2000000;         {在父視窗中繪圖時繞開子視窗區域, 創建父視窗是使用}
WS_MAXIMIZE         = $1000000;         {最大化視窗}
WS_CAPTION          = $C00000;          {有標題欄}
WS_BORDER           = $800000;          {有細線邊框}
WS_DLGFRAME         = $400000;          {對話框視窗}
WS_VSCROLL          = $200000;          {有垂直滾動條}
WS_HSCROLL          = $100000;          {有水平滾動條}
WS_SYSMENU          = $80000;           {帶系統標題欄, 須同時指定 WS_CAPTION}
WS_THICKFRAME       = $40000;           {帶寬邊框, 寬邊框用於改變視窗大小}
WS_GROUP            = $20000;           {能用方向鍵轉移焦點}
WS_TABSTOP          = $10000;           {能用 TAB 轉移焦點}
WS_MINIMIZEBOX      = $20000;           {有最小化按鈕}
WS_MAXIMIZEBOX      = $10000;           {有最大化按鈕}
WS_TILED            = WS_OVERLAPPED;
WS_ICONIC           = WS_MINIMIZE;
WS_SIZEBOX          = WS_THICKFRAME;
WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_THICKFRAME or WS_MINIMIZEBOX or WS_MAXIMIZEBOX);
WS_TILEDWINDOW      = WS_OVERLAPPEDWINDOW;
WS_POPUPWINDOW      = (WS_POPUP or WS_BORDER or WS_SYSMENU);
WS_CHILDWINDOW      = (WS_CHILD);

//另外還有一些擴展樣式:
WS_EX_DLGMODALFRAME    = 1;      {指定雙邊界視窗; 藉此指定 WS_CAPTION 創建標題欄}
WS_EX_NOPARENTNOTIFY   = 4;      {在視窗創建或取消時不向父視窗發送 WM_PARENTNOTIFY 消息}
WS_EX_TOPMOST          = 8;      {在所有非最頂層視窗的上面}
WS_EX_ACCEPTFILES      = $10;    {可接受拖放文件}
WS_EX_TRANSPARENT      = $20;    {透明樣式, 在同屬視窗已重畫時該視窗才可重畫}
WS_EX_MDICHILD         = $40;    {創建一個 MDI 子視窗}
WS_EX_TOOLWINDOW       = $80;    {工具視窗}
WS_EX_WINDOWEDGE       = $100;   {帶立體的邊框}
WS_EX_CLIENTEDGE       = $200;   {帶陰影的邊界}
WS_EX_CONTEXTHELP      = $400;   {標題包含一個問號標誌, 不能與 WS_MAXIMIZEBOX 和 WS_MINIMIZEBOX 同時使用}
WS_EX_RIGHT            = $1000;  {視窗具有右對齊屬性}
WS_EX_LEFT             = 0;      {視窗具有左對齊屬性, WS_EX_LEFT 是預設設置}
WS_EX_RTLREADING       = $2000;  {視窗文本從右到左}
WS_EX_LTRREADING       = 0;      {視窗文本從左到右, WS_EX_LTRREADING 是預設設置}
WS_EX_LEFTSCROLLBAR    = $4000;  {垂直滾動條在左邊界, 只用於特殊語言環境}
WS_EX_RIGHTSCROLLBAR   = 0;      {垂直滾動條在右邊界, WS_EX_RIGHTSCROLLBAR 是預設設置}
WS_EX_CONTROLPARENT    = $10000; {允許用戶使用 Tab 鍵在視窗的子視窗間搜索}
WS_EX_STATICEDGE       = $20000; {視窗不可用時創建一個三維邊界}
WS_EX_APPWINDOW        = $40000; {當視窗可見時, 將一個頂層視窗放置到任務條上}
WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE); {立體邊框並帶陰影}
WS_EX_PALETTEWINDOW    = (WS_EX_WINDOWEDGE or WS_EX_TOOLWINDOW or WS_EX_TOPMOST); {立體邊框、工具條視窗樣式、在頂層}
WS_EX_LAYERED          = $00080000; {分層或透明視窗, 該樣式可使用混合特效}
WS_EX_NOINHERITLAYOUT  = $00100000; {子視窗不繼承父視窗的佈局}
WS_EX_LAYOUTRTL        = $00400000; {從右到左的佈局}
WS_EX_COMPOSITED       = $02000000; {用雙緩衝從下到上繪製視窗的所有子孫}
WS_EX_NOACTIVATE       = $08000000; {處於頂層但不激活}
分析:  
  首先要用 CreateWindow 創建視窗, 才能用 ShowWindow 顯示視窗; 因為 ShowWindow 需要 CreateWindow 返回的句柄.
  在 CreateWindow 的參數中, 位置與大小與視窗標題無須多說;
  父視窗與菜單, 暫時都不需要, 先可置為 0;
  程式實例的句柄, Delphi 已經為我們準備好了: HInstance; (參見原來的說明)
  視窗樣式在前面的例子中我們使用了: WS_OVERLAPPEDWINDOW, 它代表幾種特點的組合, 表示了常規視窗.
  CreateWindow 還有一個重要參數(第一個參數 lpClassName): 視窗類的名字.
  Windows 要求我們要登記並註冊一個視窗類以後, 才可以用 CreateWindow 建立視窗!

(三)認識消息機制函數
  一個程式會有一個或多個線程, 系統有一個線程隊列(就是個鏈表)管理所有這些線程, 併為每個線程建立一個消息隊列.  
  當消息產生時(譬如點擊了視窗), 系統會把該消息放到視窗所在的消息隊列, 等待視窗處理.
  視窗應該時刻待命, 準備從所在的線程隊列中取出消息並處理! 
  從消息隊列中取出消息, 一般用 GetMessage 函數; 要隨時取出消息, 需要用個迴圈, 譬如:



while(GetMessage(Msg, 0, 0, 0)) do
begin
  ...
end;

  取出消息怎麼處理? 用 DispatchMessage 函數將消息交給(前面提到的)視窗回調函數, 一般是這樣:



while(GetMessage(Msg, 0, 0, 0)) do
begin
  TranslateMessage(Msg); {有些鍵盤消息需要用 TranslateMessage 函數併發出系統需要的更具體的鍵盤消息}
  DispatchMessage(Msg);
end;

  如果 GetMessage 函數不返回 0 ; 這個消息迴圈就是死迴圈, 什麼時候 GetMessage 可以返回 0 呢?
  只有當 GetMessage 收到 WM_QUIT 消息時才返回 0. 
  我們可在需要的時候, 在回調函數中通過 PostQuitMessage 函數向線程的消息隊列中發送一條 WM_QUIT 消息, 以關閉線程.
  PostQuitMessage 函數的參數是給應用程式的退出碼, PostQuitMessage(0) 中的 0 就是我們指定的退出碼, 它將作為 WM_QUIT 消息的 wParam 參數. 譬如:

function WndProc(wnd: HWND; msg: UINT; wParam: Integer; lParam: Integer): LResult; stdcall;
begin
  Result := 0;
  if msg = WM_DESTROY then
    PostQuitMessage(0)
  else
    Result := DefWindowProc(wnd, msg, wParam, lParam);
end;

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 對於這場比賽,我真的是有點划水了,做了倆題,做第三題的時候實在是不知道什麼地方卡住了,然後我家來了客人,被帶出去吃飯去了,ε=(´ο`*)))唉!!! B - Just Eat It! 這道題是個經典的DP題,我對於遞推還不是特別熟悉,得找到題目的狀態轉移方程。 B[ i ] = max{ A[ i ...
  • 一、字元串相關的常用方法簡介​ package com.bjpowernode.java_learning; ​ public class D74_1_StringCommonMethod { public static void main(String[] args) { //1.轉為大寫 Sys ...
  • import wave r = r"D:\沫沫醬 - 舊傷口.wav" # 一個.wav格式文件 with wave.open(r, "rb") as f: # 讀取文件格式等 params = f.getparams() nchannels, sampwidth, framerate, nfram ...
  • 一、寫在前面 作為一名測試,有時候經常會遇到需要錄屏記錄自己操作,方便後續開發同學定位。以前都是用ScreenToGif來錄屏製作成動態圖,偶爾的機會看到python也能實現。那就趕緊學習下。 二、效果展示 三、知識串講 這次要講的東西可能比較多了,涉及到pyqt5 GUI軟體的製作、QThread ...
  • 學Python的很多,不只是程式員學Python,運營、產品、測試都在學Python 除了互聯網,辦公一族都開始學Python了,學Python的理由千萬條。 因為人生苦短 這句話最初出自《Java 編程思想》作者Bruce Eckel。 有因為Python牛到飛起的 再看看Python鳥怎麼喝水的 ...
  • \ ​ GNE(GeneralNewsExtractor)是一個通用新聞網站正文抽取模塊,輸入一篇新聞網頁的 HTML, 輸出正文內容、標題、作者、發佈時間、正文中的圖片地址和正文所在的標簽源代碼。GNE在提取今日頭條、網易新聞、游民星空、 觀察者網、鳳凰網、騰訊新聞、ReadHub、新浪新聞等數百 ...
  • 收集的Android測試或者開發中常用的aadb命令,可以使用Ctrl+F快速搜索### ADB命令集錦: adb --help //adb幫助 adb start-server //啟動adb server adb kill-server //關閉adb server adb devices // ...
  • Python 3.8 已於前兩周正式發佈,不過目前大多數開發者主要使用的仍是 Python 3.7.x 或更舊的版本。 ! 事實上,立刻切換到使用 Python 3.8 可能會引發一些問題。想知道何時切換至 Python 3.8?下文將簡要概述切換主要的 Python 版本以及進行切換時可能會遇到的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...