通過以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;