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

  經過以sdk方式編制windows窗口程序,對理解windows消息驅動機制和delphi消息編程有很大的幫助。
sdk編制windows窗口程序的步驟:
一、對TWndClass對象進行賦值;
二、向系統註冊wndclass對象(RegisterClass);
三、CreateWindow建立窗口,得到窗口句柄Hwnd;
四、顯示窗口(ShowWindow);
五、經過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.

1、運行界面以下:html

2、程序函數說明:編程

  (一)、窗口類型 TWndClass 實際上是一個結構, 是 tagWNDCLASSA 結構的重命名.windows

{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;
相關文章
相關標籤/搜索