Windows應用程序,操作系統,計算機硬件之間的相互關系如下圖:
向下的箭頭③表示應用程序可以通知操作系統執行某個具體的動作;
向上的箭頭④表示操作系統能夠將輸入設備的變化上傳給應用程序。
每個應用程序都維護一個消息隊列(嚴格來說應該是每個GUI線程維護一個各自的消息隊列)。
大致運行流程應該是這樣的:當輸入輸出設備產生變化時(如用戶在某個程序活動時按了一下鍵盤),操縱系統會馬上感知到這一事件,並且能夠知道用戶按下的是哪一個鍵,
操作系統並不決定對這一事件如何作出反應,而是將這一事件轉交給應用程序,這時應用程序可以通知操作系統執行某個具體的動作(如操作系統能夠控制聲卡發出聲音),
但它並不知道應該何時發出何種聲音,需要應用程序告訴操作系統該發出什么樣的聲音。(順序為②―〉④―〉③―〉①)
下面貼一個win32下完整的創建窗口過程代碼:
#include <Windows.h> #include <stdio.h> /************************ typedef struct tagMSG { HWND hwnd; //代表消息所屬的窗口 UINT message; //消息代號 WPARAM wParam; //unsigned int類型,對消息進行補充說明 LPARAM lParam; //long類型,對消息進行補充說明 DWORD time; //發出消息的時間 POINT pt; //鼠標的當前位置 } MSG; ************************/ LRESULT CALLBACK //實際上是__stdcall。__stdcall與__cdecl是兩種不同的函數調用習慣,定義了參數的傳遞順序、堆棧清除等 //由於VC++程序默認的編譯選項是__cdecl,所以在VC++中調用這些__stdcall習慣的API函數,必須在聲明這些函數的原型時加上__stdcall修飾符 winPanProc( HWND hwnd, //代表消息所屬的窗口 UINT uMsg, WPARAM wParam, LPARAM lParam ); /************************************ 這個函數是應用程序的基礎,當Windows操作系統啟動一個程序時, 它調用的就是該程序的Winmain函數, 用戶的操作所產生的消息正是經過這個函數的處理派送到對應的對象中進行處理。 當WinMain函數結束或返回時,Windows應用程序結束 *************************************/ int WINAPI WinMain( HINSTANCE hInstance, // 當前運行的實例句柄 HINSTANCE hPrevInstance, // 當前實例的上一個正在運行的,由同一個應用程序所產生的實例的句柄 LPSTR lpCmdLine, // 一個字符串,里面包含有傳遞給應用程序的參數串 int nCmdShow // 程序的窗口應該如何顯示,如最大化,最小化,隱藏等 ) { /********************************************************************************************************** 1、產生並顯示程序的主窗口。 窗口創建並顯示后,用戶便可以在窗口上進行各種操作了, 用戶的操作及程序狀態的變化都以消息的形式放到了應用程序的消息隊列中。 */ /* 設計一個窗口類; window的10個屬性,缺一則窗口無法顯示*/ WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wndcls.hCursor=LoadCursor(NULL,IDC_CROSS); wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); wndcls.hInstance = hInstance; //指定提供回調函數的程序實例句柄 wndcls.lpfnWndProc = winPanProc; //指定這一類型窗口的過程函數,也稱回調函數 wndcls.lpszClassName = L"panWnd"; //指定這一類型窗口的名稱,是字符串變量。 wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; //指定這一類型窗口的樣式,針對一個大類 /*注冊窗口類;*/ RegisterClass(&wndcls); /*創建窗口;*/ HWND hwnd; hwnd = CreateWindow( L"panWnd", //WNDCLASS的lpszClassName成員里指定的名稱 L"UESTC", //指定產生的窗口實例上顯示的標題文字 WS_OVERLAPPEDWINDOW,//窗口實例的樣式,針對個別 0,0, 500, 500, //窗口的大小、寬度、高度 NULL, //設置桌面成為當前窗口的父窗口 NULL, //指定了窗口的菜單或子窗口句柄 hInstance, //窗口所屬的應用程序的句柄 NULL //窗口附加補充信息 ); /*顯示及刷新窗口.*/ ShowWindow(hwnd, SW_SHOWNORMAL); //ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); /********************************************************************************************************** 2、從消息隊列循環取走消息,並將消息派發到窗口過程函數中去處理。 當消息循環取到一條WM_QUIT消息時,將結束循環,WinMain函數返回,結束整個程序的運行。 如果WinMain在消息循環之前返回,程序沒有正常運行,返回值為0。 如果在消息循環之后返回,返回值為WM_QIUT消息的wParam參數。 */ /* Windows中有兩種類型的消息隊列: 系統消息隊列: 這是一個系統唯一的Queue,設備驅動(mouse, keyboard)會把操作輸入轉化成消息存放在系統隊列中, 然后系統會把此消息放到目標窗口所在的線程的消息隊列(thread-specific message queue)中等待處理 線程消息隊列: 每一個GUI線程都會維護這樣一個線程消息隊列。(這個隊列只有在線程調用GDI函數時才會創建,默認不創建)。 然后線程消息隊列中的消息會被送到相應的窗口過程(WndProc)處理. 注意: 線程消息隊列中WM_PAINT,WM_TIMER只有在Queue中沒有其他消息的時候才會被處理, WM_PAINT消息還會被合並以提高效率。其他所有消息以先進先出(FIFO)的方式被處理。 */ /* 隊列消息(Queued Messages):消息會先保存在消息隊列中,消息循環會從此隊列中取消息並分發到各窗口處理。 如鼠標,鍵盤消息。 非隊列消息(NonQueued Messages):消息會繞過系統消息隊列和線程消息隊列直接發送到窗口過程被處理。 如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED */ MSG msg; /* GetMessage:從隊列中直接取走消息 PeekMessage:只取消息的一個副本,並不將消息從隊列中取走 */ while(GetMessage( &msg, //接受消息的變量的指針 NULL, //指定了接收屬於哪個窗口的消息 0, 0 //指定了接受某一范圍內的消息 )) { /* TranslateMessage: 把一個virtual-key消息轉化成字符消息(character message),並放到當前線程的消息隊列中,消息循環下一次取出處理。 TranslateAccelerator: 將快捷鍵對應到相應的菜單命令。它會把WM_KEYDOWN 或 WM_SYSKEYDOWN轉化成快捷鍵表中相應的WM_COMMAND 或WM_SYSCOMMAND消息, 然后把轉化后的 WM_COMMAND或WM_SYSCOMMAND直接發送到窗口過程處理, 處理完后才會返回。 */ //將msg結構傳給Windows,對取到的消息進行轉換 TranslateMessage(&msg); //又將msg結構回傳給Windows。然后,Windows將該消息發送給適當的窗口消息處理程序,讓它進行處理。 DispatchMessage(&msg); } return 0; } LRESULT CALLBACK winPanProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { /* 消息類型: 系統定義消息:在SDK中事先定義好的消息,非用戶定義的,其范圍在[0x0000, 0x03ff]之間, 可以分為以下三類: 窗口消息:與窗口的內部運作有關,如創建窗口,繪制窗口,銷毀窗口等 命令消息:與處理用戶請求有關, 如單擊菜單項或工具欄或控件時,就會產生命令消息。 WM_COMMAND, LOWORD(wParam)表示菜單項,工具欄按鈕或控件的ID。 如果是控件, HIWORD(wParam)表示控件消息類型 控件通知:控件通知消息,這是最靈活的消息格式 其Message, wParam, lParam分別為:WM_NOTIFY, 控件ID,指向NMHDR的指針。 NMHDR包含控件通知的內容, 可以任意擴展。 程序定義消息:用戶自定義的消息,對於其范圍有如下規定: WM_USER: 0x0400-0x7FFF (ex. WM_USER+10) WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4) RegisterWindowMessage: 0xC000-0xFFFF */ switch(uMsg) { case WM_CHAR: break; case WM_LBUTTONDOWN: break; case WM_PAINT: break; case WM_CLOSE: if(IDYES == MessageBox(hwnd, L"確定退出?", L"panWnd", MB_YESNO)) { DestroyWindow(hwnd); } break; case WM_DESTROY: /* PostMessage:發送的消息是隊列消息,它會把消息直接Post到應用程序的消息隊列中,不等程序返回就退出 SendMessage:發送的消息是非隊列消息, 被直接送到窗口過程處理,應用程序處理完此消息后,它才返回 PostThreadMessage:用於向線程發送消息 */ PostQuitMessage(0); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; }