天天看點

第一個windows程式(windows程式設計筆記)

以下内容大多由《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 主事件循環

第一個windows程式(windows程式設計筆記)

主事件循環函數

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程式(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;

}

繼續閱讀