以下内容大多由《Windows程式設計》,《VC++深入詳解》等書籍以及MSDN,網上一些資料和本人的了解整合而來,以作筆記之用。
---------------------------------------------------------------------------------------------
1.幾個常用的術語:
API(Application Programming Interface)應用程式程式設計接口
SDK(Software Development Kit)軟體開發包
2.匈牙利符号表示法(節選)
了解匈牙利符号表示法對windows程式設計有不小的好處,但是個人認為不必強記。
3.MessageBox(消息框)
#include<windows.h>
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow)
{
MessageBox(NULL,TEXT("xfatenet"),TEXT("HelloMsg"),0);
return 0;
}
3.1逐個分析所有windows程式入口函數WinMain()的參數:
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int nCmdShow)
WINAPI (CALLBACK也是如此):
#define WINAPI _stdcall
_stdcall 差別于 _cdecl
前者(_stdcall)一般用于調用win32 api函數;函數的參數自右向左通過棧傳遞。在主調用函數中負責壓棧,被調用函數在傳回前清理傳送參數的記憶體棧。
後者是c/c++函數的預設調用方式。函數的參數自右向左通過棧傳遞。由主調用函數進行參數壓棧,由主調用函數把參數彈出棧。對于傳送參數的記憶體棧是由主調用函數來維護的(正因為如此,實作可變參數的函數隻能使用該調用約定)。
HINSTANCE hinstance:
該參數是一個windows為你的應用程式生成的執行個體句柄。執行個體是一個用來跟蹤資源的指針或數。句柄是執行個體指針的索引. 通過句柄能找到執行個體的位址.
hprevinstance:
由名字可以知道是指産生目前執行個體的應用程式執行個體,跟蹤應用程式以前(prev-)的執行個體(instance)。
szCmdLine:和int main(int argc,char **argv)函數中的指令行參數相似。
nCmdShow :在啟動過程中被傳遞給應用程式,帶有如何打開主應用程式視窗的資訊。控制應用程式如何啟動。常用的參數值:
SW_HIDE | Hides the window and activates another window.(隐藏一個視窗,激活另一個視窗) |
SW_SHOW | Activates a window and displays it in its current size and position.(激活視窗按照目前尺寸和位置顯示它。) |
SW_SHOWNA | Displays a window in its current state. The active window remains active.(以目前狀态顯示一個視窗,激活的視窗保持激活狀态) |
SW_SHOWNOACTIVATE | Displays a window in its most recent size and position. The active window remains active.(以上次的尺寸和位置顯示視窗,激活視窗保持激活狀态) |
SW_SHOWNORMAL | Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position (same as SW_RESTORE).(激活和顯示視窗,如果視窗最小化或最大化,則恢複到原始尺寸和位置,應用程式将指定該狀态) |
MessageBox的第一參數通常是視窗句柄,第二參數是顯示的内容,第三參數是消息框的标題欄的字元串。第四參數是常數組合用于顯示消息框的按鈕樣式。
#define MB_OK—— 0
#define MB_OKCANCEL—— 1
#define MB_ABORTRETRYIGNORE——2
#define MB_YESNOCANCEL——3
#define MB_YESNO——4
#define MB_RETRYCANCEL——5
可以用或運算符比如MB_OK|MB_ICONEXCLAMATION
如果想有聲音的話,最簡單的方法調用
BOOL WINAPI MessageBeep(
__in UINT uType
);
比如MessageBeep(MB_OK);
4. 建立windows應用程式
建立一個完整的windows應用程式需要:
1. 建立一個windows類
2. 建立一個winProc
3. 用windows注冊windows類
4. 用前面建立的windows類建立一個視窗
5. 建立一個能夠從事件句柄獲得是或向事件句柄傳遞windows資訊的主事件循環。
4.1消息隊列和視窗
Windows程式設計是OOP(面向對象),每一個小的可視對象都是一視窗(子視窗或是控件視窗),視窗以消息的形式接受視窗的輸入,視窗以消息與其他視窗通訊。Windows給程式發送消息是指windows調用程式中的一個函數,位于windows程式中的函數被稱為“視窗過程”。Windows通過視窗過程來給視窗發送消息。視窗過程根據此消息進行處理,然後将控制傳回給windows。
Windows程式開始執行後,windows為該程式建立一個消息隊列,這個消息隊列用來存放該程式可能建立的各個不同視窗的消息。程式中有一段代碼,叫做消息循環。用來從隊列中取出消息。并且将他們發送到相應的視窗過程。有些消息直接發送給視窗過程,不用放進消息隊列中。
4.2 建立一個windows類
控制windows類資訊的資料結構有兩個:WNDCLASS和WNDCLASSX,
看下WNDCLASSX的結構:
typedef struct {
//WNDCLASSX結構本身的大小
UINT cbSize;
//描述該視窗的一般屬性的樣式(style) 的資訊标志
UINT style;
//是一個指向事件句柄的函數指針,基本上這裡所設定的都是該類的回調函數
WNDPROC lpfnWndProc;
//cbClsExtra和cbWndExtra原是為訓示windows将附加的運作時間資訊儲存到windows類某些單元中而設計的,一般設為0
int cbClsExtra;
int cbWndExtra;
//就是啟動時傳遞給WinMain()函數的hInstance,一般是winclass.hInstance=hInstance;
HINSTANCE hInstance;
//設定表示應用程式的圖示的類型,可以使用LoadIcon(),比如winclass.hIcon=LoadIcon(NULL,IDI_APPLICATION)
HICON hIcon;
//和上面相似,隻不過是指光标句柄。可以使用LoadCursor()函數。
HCURSOR hCursor;
//用畫刷(Brush)來填充該視窗的背景。hbrBackground就是一個用于視窗重新整理的畫筆句柄,
//申請一個基本的系統畫筆來填充視窗,使用函數GetStockObject(),
//比如winclass.hbrBackground=(HBRUSN)GetStockObject(WHITE_BRUSH);
HBRUSH hbrBackground;
//菜單資源名稱的空值終止ascii字元串
LPCTSTR lpszMenuName;
//在某種程度來說,類和模闆相似,windows需要一些途徑來跟蹤和識别這些windows類,指派該字段後,
//就可以利用它的名字來引用這個新的windows類、
LPCTSTR lpszClassName;
//最小應用程式圖示,WNDCLASSX新增
HICON hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;
4.2.1.注冊windows類
當windows類已經定義并且存放在winclass中,必須将新的類注冊,可以使用RegisterClassEx()和RegisterClass()這兩個函數實作,前者對應WNDCLASSX,後者對應WNDCLASS。
函數原型:
ATOM RegisterClassEx(
CONST WNDCLASSEX *lpwcx
);
參數是一個指向新類定義的指針。
4.3 建立視窗
建立一個視窗使用CreateWIndowEx()或是CreateWindow()函數,下面來看下CreateWindowEx()的函數原型
HWND CreateWindowEx(
//視窗風格,大多情況可以設為NULL,其他值可以查msdn
DWORD dwExStyle,
//你建立的視窗的基礎類名
LPCTSTR lpClassName,
//視窗标題
LPCTSTR lpWindowName,
/*說明視窗外觀和行為的通用視窗标志
常用樣式值:
WS_POPUP:彈出式視窗
WS_OVERLAPPED:建立一個帶有标題和邊界的重疊式視窗
WS_OVERLAPPEDWINDOW:建立一個帶有WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU,
WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX 樣式的重疊式視窗
WS_VISIBLE: 開始就可見的視窗
*/
DWORD dwStyle,
//x,y是視窗左上角的像素坐标
int x,
int y,
//nWidth和nHeight是視窗寬度和高度
int nWidth,
int nHeight,
//如果有父視窗,這個就指向父視窗的句柄
HWND hWndParent,
//附屬于該視窗菜單的句柄
HMENU hMenu,
//應用程式執行個體的句柄
HINSTANCE hInstance,
//進階屬性,一般設為NULL
LPVOID lpParam
);
4.3.1.顯示和更新視窗
建立完視窗後,視窗卻不一定可見,如果dwStyle沒有設為為WS_VISIBLE,那就要手工顯示視窗:
ShowWindow(hwnd,nCmdShow)
然後讓windows更新視窗的内容
UpdateWindow();
4.4.事件句柄(event handle)| WinProc(視窗過程)
就是當事情發生時windows從主事件循環調用的回調函數。
每一個windows類都有一個獨立的事件句柄,稱為windows procedure 簡稱winproc(視窗過程)。
當運作任務時,視窗和其他應用程式視窗就會産生事件和消息,所有的消息進入一個隊列,而該視窗消息發送到該視窗的專用隊列中,然後主事件循環檢索這些消息,并且将他們發送到該視窗的winproc(視窗過程)中來處理。
視窗過程函數原型:
LRESULT CALLBACK WindowProc(
HWND hwnd, //視窗句柄
UINT uMsg, //消息辨別符
//進一步比對或分類發送到msg參數中的資訊
WPARAM wParam,
LPARAM lParam
);
例子:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
switch (message)
{
//消息辨別符:WM_CREATE等
case WM_CREATE:
return 0 ;
case WM_PAINT:
//激活視窗
hdc = BeginPaint (hwnd, &ps) ;
//....
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
//發送WM_QUIT消息
PostQuitMessage (0) ;
return 0 ;
}
//預設的視窗過程函數,對應用程式沒有提供的其他消息做預設處理
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
4.5 主事件循環
主事件循環函數
while(GetMessage(&msg,NULL,0,0))
{
//虛拟加速鍵翻譯器,把虛拟鍵消息轉為字元消息
TranslateMessage(&msg);
//分派消息到視窗過程,由視窗過程來處理消息
DispatchMessage(&msg);
}
MSG結構:
typedef struct tagMSG {
HWND hwnd; //産生消息的視窗
UINT message; //消息辨別符
//進一步比對或分類發送到msg參數中的資訊
WPARAM wParam;
LPARAM lParam;
DWORD time; //消息事件發生的時間
POINT pt; //滑鼠位置
} MSG;
GetMessage()函數原型
//GetMessage()除了WM_QUIT傳回值,其他都傳回非零值。
BOOL GetMessage(
LPMSG lpMsg, //GetMessage()從消息隊列中取出的消息
HWND hWnd, //視窗句柄,通常設為NULL,接受調用線程的所有視窗的視窗消息
UINT wMsgFilterMin, //要擷取的視窗消息的最小值
UINT wMsgFilterMax //要擷取的視窗消息的最大值,最小值和最大值設為,則為接受所有消息
);
可以看出,當程式在等待通過GetMessage()傳遞的消息的同時,主事件循環基本上是鎖定的。如果要實作實時的無等候的事件循環,可以用PeekMessage()函數
BOOL PeekMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
//控制如何從消息序列中檢索消息
//PM_NOREMOVE:PeekMessage()處理後,消息沒有從序列中取出
//PM_REMOVE:PeekMessage()處理後,消息已經從序列中去除
UINT wRemoveMsg
);
例子:
while(true)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
假設GetMessage(&msg,NULL,0,0)寫成GetMessage(&msg,hwnd,0,0)會出現什麼情況?
當點選關閉時,視窗句柄會被銷毀,此時GetMessage(&msg,hwnd,0,0)将會傳回-1值,由于在C++中,0是假其他都是真,是以條件為真,循環會繼續下去,是以會陷入死循環中。打開任務管理器會看到cpu占用率到了100%(雙核的應該是50%)
整個windows應用程式的建立 過程:
一個完整的windows應用程式如下:
#include<windowsx.h>
#include<windows.h>
#include<stdio.h>
//定義視窗過程函數
LRESULT CALLBACK WinProc(HWND hwnd,UINT uMsg,WPARAM wparam,LPARAM lparam);
//主函數
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nShowCmd)
{
WNDCLASSEX winclass;//視窗類
HWND hwnd;//視窗句柄
MSG msg;//消息
//建立視窗類
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.style = CS_HREDRAW | CS_VREDRAW;
winclass.hInstance = hInstance;
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.hCursor = LoadCursor(NULL,IDC_CROSS);
winclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
winclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
winclass.lpfnWndProc = WinProc;
winclass.lpszClassName = "xfate";
winclass.lpszMenuName = NULL;
//注冊視窗類
if(!RegisterClassEx(&winclass))
{
return 0;
}
//建立視窗
hwnd= CreateWindowEx(NULL,
"xfate",
"xfate-2010-7-26",
WS_OVERLAPPEDWINDOW,
0,0,
800,600,
NULL,NULL,
hInstance,NULL);
//顯示和重新整理視窗
ShowWindow(hwnd,nShowCmd);
UpdateWindow(hwnd);
//主事件循環
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//視窗過程函數
LRESULT CALLBACK WinProc(HWND hwnd,UINT uMsg,WPARAM wparam,LPARAM lparam)
{
switch(uMsg)
{
case WM_CHAR:
char szChar[30];
sprintf(szChar,"char is %d",wparam);
MessageBox(hwnd,szChar,"char",0);
break;
case WM_PAINT:
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd,&ps);
TextOut(hdc,0,0,"第一個windows程式",strlen("第一個windows程式"));
EndPaint(hwnd,&ps);
break;
case WM_CLOSE:
if(IDYES == MessageBox(hwnd,"Quit?","quit",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wparam,lparam);
}
return 0;
}