1.1 了解窗口
在圖形化基於視窗的應用程序裡,窗口就是屏幕上一塊可視區域,應用程序的窗口包括標題欄,菜單欄,Windows系統菜單,最小化按鈕,最大化按鈕,恢復按鈕,關閉按鈕,可改變大小的邊框,窗口客戶區,垂直滾動條,水平滾動條.
窗口客戶區是應用程序顯示輸出部分
1.2 窗口的創建
1.21 創建Win32工程和MessageBox函數
Windows 應用程序的主函數是:
int APIENTRY WinMain(HINSTANCE hInstance, //應用程序的實例句柄
HINSTANCE hPrevInstance, //Win16留下的廢物,不用參數
LPSTR lpCmdLine, 命令行參數
int nCmdShow) //主窗口初始化的顯示方式
Windows 彈出對話框的函數是:
::MessageBox(HWND hwnd, //一個窗口句柄,指定的對話框屬於哪一個窗口所有
//設為NULL,表示沒有窗口擁有該對話框
LPCSTR lpText, //對話框的正文
LPCSTR lpCaption, //對話框的標題
UINT uType) //
對話框顯示按鈕種類和行為nType 指示了對話框顯示按鈕的種類和行為,可以設成如下的值:
1: MB_OK 確定按鈕
2: MB_OKCANCEL 包括兩個按鈕:確定和取消按鈕
3: MB_ABORTRETRYIGNORE 包括三個按鈕:終止,調試和忽略
4: MB_YESNOCANCEL 包括三個按鈕:是,否和取消
5: MB_YESNO 是和否按鈕
6: MB_RETRYCANCEL 重試和取消按鈕
7: MB_ICONHAND 一個停止標志圖標
8: MB_ICONQUESTION 一個詢問標志圖標
9: MB_ICONEXCLAMATION 一個感歎號圖標
10: MB_DEFBUTTON1 第一個按鈕是選中按鈕
11: MB_DEFBUTTON2 第二個按鈕是選中按鈕
12: MB_DEFBUTTON3 第三個按鈕是選中按鈕
13: MB_DEFBUTTON4 第四個按鈕是選中按鈕
MessageBox 函數返回被選中按鈕的ID號,ID號為一個整形數值,這個宏在WINUSER.H中定義:
ID號 = ID + 按鈕單詞
如果確定按鈕被選中,將返回:IDOK
示例代碼:
#include <Windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
int nSet = ::MessageBox(NULL,"Hello world","標題",MB_YESNOCANCEL | MB_ICONEXCLAMATION | MB_DEFBUTTON3);
if(nSet == IDYES)
{
::MessageBox(NULL,"用戶選擇了是按鈕","標題",MB_OK);
}
else if(nSet == IDNO)
{
::MessageBox(NULL,"用戶選擇了否按鈕","標題",MB_OK);
}
else if(nSet == IDCANCEL)
{
::MessageBox(NULL,"用戶選擇了取消按鈕","標題",MB_OK);
}
return 0;
}
1.22 Windows 的消息驅動
創建窗口後,就要對窗口的行為負責,比如用戶拖動窗口時,應跟隨鼠標移動這個窗口.如果不這麼做,程序將失去窗口界面的友好性,但是,如何知道用戶在窗口上的動作呢
是操作系統告訴程序的,Windows不斷向程序發送消息,通知用戶在窗口上的動作
當 Windows向程序發送消息時,它調用程序中的一個函數,這個函數稱為消息處理函數
LRESULT CALLBACK WinProc(HWND hwnd, //窗口句柄
UINT message, //消息ID號
WPARAM wParam, //消息參數,其值取決於消息//的類型
LPARAM lParam) //消息參數,其值取決於消息
//的類型
例如,一般的應程序接受到 WM_CLOSE 消息時會試圖銷毀自己的窗口,下面代碼為了關閉了記事本程序,向它的主窗口發送了 WM_CLOSE 消息,運行這個程序之前請先新建一個文本文檔,然後再打開這個文本文檔.
#include <Windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
HWND hwnd;
BOOL bStart = FALSE;
while(bStart == FALSE)
{
//查找標題為"新建文本文檔.txt - 記事本"的窗口
hwnd = ::FindWindow("Notepad","新建文本文檔.txt - 記事本");
if(hwnd != NULL)
{
//向窗口發送WM_CLOSE 消息
::SendMessage(hwnd,WM_CLOSE,0,0);
::MessageBox(NULL,"新建文本文檔.txt - 記事本窗口已關閉","消息提示",MB_OK);
bStart = TRUE;
}
else {
::MessageBox(NULL,"請先啟動新建文本文檔.txt - 記事本窗口","消息提示",MB_OK);
}
}
return 0;
}
以上是一個應用程序向另一個應用程序發送窗口的過程,系統向應用程序發送消息的過程是類似的,系統為應用程序傳遞所有輸入到它的各個窗口,每個窗口都關聯一個消息處理函數,消息處理函數處理輸入,然後再將控制權交給系統
下面是一個最簡單的窗口程序源代碼,它的作用是彈出一個典型的 Windows 程序窗口,這些代碼可以做為今後用 API 寫 Windows 程序的基本框架
#include <Windows.h>
//消息處理函數原形
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
char *szClassName = "WndClass";
WNDCLASSEX wndclass;
//用描述主窗口的參數填充WNDCLASSEX 結構
wndclass.cbSize = sizeof(wndclass); //定義結構的大小
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; //定義窗口樣式
wndclass.lpfnWndProc = WndProc; //指定本窗口的消息處理函數
wndclass.cbClsExtra = 0; //沒有額外的類內存
wndclass.cbWndExtra = 0; //沒有額外的窗口內存
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION); //使用默認的圖標
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); //使用默認的光標
wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); //定義窗口的背景顏色為灰色
wndclass.hInstance = hInstance; //實例句柄
wndclass.lpszClassName = szClassName; //窗口類名稱
wndclass.lpszMenuName = NULL; //不使用菜單
wndclass.hIconSm = NULL; //沒有類的小圖標
//注冊窗口類
::RegisterClassEx(&wndclass);
//創建主窗口
HWND hwnd = ::CreateWindowEx(0, //不定義擴展樣式
szClassName, //類名
"Hello world", //窗口標題
WS_OVERLAPPEDWINDOW, //窗口風格
CW_USEDEFAULT, //默認的窗口X 軸坐標
CW_USEDEFAULT, //默認的窗口Y 軸坐標
CW_USEDEFAULT, //默認的窗口寬度
CW_USEDEFAULT, //默認的窗口高度
NULL, //沒有父窗口句柄
NULL, //沒有菜單句柄
hInstance, //程序實例句柄
NULL); //沒有用戶數據
if(hwnd == NULL)
{
::MessageBox(NULL,"創建窗口出錯","error",MB_ICONHAND);
}
//顯示窗口
::ShowWindow(hwnd,nCmdShow);
//刷新窗口客戶區
::UpdateWindow(hwnd);
//從消息隊列中取出消息,交給消息處理函數處理,直到GetMessage 函數返回FALSE ,結束消息循環
MSG msg;
while(::GetMessage(&msg,NULL,0,0))
{
//轉化鍵盤消息
::TranslateMessage(&msg);
//將消息發送給消息處理函數
::DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
char *szText = "My first window process ";
HDC hdc; //聲明設備環境句柄
PAINTSTRUCT ps;
switch(msg)
{
case WM_PAINT: //窗口客戶區需要重畫
{
//使無效的客戶區變得有效,並取得設備環境句柄
hdc = ::BeginPaint(hwnd,&ps);
::TextOut(hdc,0,0,szText,strlen(szText));
return 0;
}
case WM_DESTROY: //正在銷毀窗口
{
//向消息隊列投遞一個WM_QUIT 消息,促使GetMessage 函數返回0,結束消息循環
::PostQuitMessage(0);
return 0;
}
}
// 將我們不處理的消息交給系統做默認處理
return ::DefWindowProc(hwnd,msg,wParam,lParam);
}
分析以上程序,可以得出在桌面上創建一個窗口的具體步驟
(1) 注冊窗口類(RegisterClassEx)
(2) 創建窗口(CeateWindowEx)
(3) 在桌面上顯示窗口(ShowWindow)
(4) 更新窗口客戶區(UpdateWindow)
(5) 進入無限的消息獲取和處理消息的循環,首先是獲取消息(GetMessage),如果有消息到達,則通過 DispatchMessage 函數將消息發送到指定的消息處理函數 WndProc;
創建窗口的知識要點:
(1).指定窗口類的樣式:
wndclass.style = CS_HREDRAW | CS_VREDRAW;
這句代碼指定了如果窗口寬度或高度改變了就重畫整個窗口,在 WINUSER.H 中定義了窗口類的全部可用樣式,可以組合使用各種樣式
(2)指定窗口客戶區重畫時使用的畫刷
wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
這句代碼指定使用 Windows 預定義的白色背景畫刷,也可以自己創建一個畫刷對象以便指定喜歡的顏色為窗口背景色,下面的代碼將窗口的背景色改為了黃.色
Wndclass.hbrBackground = (HBRUSH)::GetSolidBrush(RGB(255,255,0));
…………………
………………….
::DeleteObject(wndclass.hbrBackground); //最後別忘記在程序結束之前刪除創建的刷子釋放資源
(3)指定窗口的風格
CreateWindows 第四個參數指定了窗口的風格,下面列出了一些最常見的風格定義,它們是以 WS 為前辍的預定義的值
WS_BORDER 創建一個單邊框的窗口
WS_CAPTION 創建一個有標題框的窗口(包括WS_BORDER風格)
WS_CHILD 創建一個子窗口,這個風格不能與 WS_POPVP 風格合用
WS_DISABLED 創建一個初始狀態為禁止的子窗口,一個禁止狀態的窗口不能接受來自用戶的輸入信息
WS_DLGFRAME 創建一個帶對話框邊框風格的窗口,這種風格的窗口不能帶有標題條
WS_HSCROLL 創建一個有水平滾動條的窗口
WS_VSCROOL 創建一個有垂直滾動條的窗口
WS_ICONIC 創建一個初始狀態為最小化的窗口,與 WS_MINIMIZE 風格相同
WS_MAXIMIZE 創建一個初始狀態為最大化的窗口,該風格不能與 WS_EX_CONTEXTHELP 風格同時出現,同時必須指定 WS_SYSMENU 風格
WS_OVERLAPPED 產生一個層疊窗口,一個層疊的窗口有一個標題條和一個邊框,與 WS_TILED 風格相同
WS_OVERLAPPEDWINDOW 創建一個具有 WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU,WS_THICKFRAME, WS_MINIMIZEBOX,WS_MAXMIZEBOX 風格的層疊窗口
WS_POPUP 創建一個彈出式窗口,該風格不能與WS_CHLD 風格同時使用
WS_POPUPWINDOW 創建一個具有 WS_BORDER, WS_POPUP, WS_SYSMENU, 風格的窗口,WS_CAPTION 和 WS_POPUPWINDOW 必須同時使用才能使窗口可見
WS_SIZEBOX 創建一個可調邊框的窗口,與 WS_THICKFRAME 風格相同
WS_SYSMENU 創建一個在標題條上帶有窗口菜單的窗口,必須同時設定 WS_CAPTION 風格
WS_THICKFRAME 創建一個具有可調邊框的窗口,與 WS_SIZEBOX 風格相同
WS_VISBLE 創建一個初始狀態為可見的窗口
(4)消息隊列 MSG 結構
Windows 為每個線程維護了一個消息隊列,每當有消息進入時,Windows 就利用GetMessage 函數調用線程的消息隊列來填充 MSG 結構,這個結構定義了消息的所有屬性
Typedef struct tagMSG
{
HWND hwnd; //消息要發往的窗口句柄
UINT message; //消息標識符,以WM_開頭的預定義值
WPARAM wParam; //消息參數之一
LPARAM lParam; //消息參數之二
DWord time; //消息放入消息隊列的時間
POINT pt; //這是一個POINT 數據結構,表示消息放入消息隊列時的鼠標位置
}MSG, &PMSG;v