API入門系列之五 -一個正兒八經的SDK程序

原創文章,轉載請註明作者及出處。

首發

http://blog.csdn.net/beyondcode

http://www.cnblogs.com/beyond-code/

http://hi.baidu.com/beyondcode

 

SDK編程羣號:81543028 歡迎加入

上一篇,講了一個簡單的SDK程序的多種版本的編寫,彈出了一個窗口,顯示了我們計算1到10的結果,計算的程序不是重點,重點在於,一:讓大家認識到Unicode版本的程序和ASCII版本的程序在編程方面的區別,以及怎麼樣編寫出通用代碼的程序。二:怎麼樣運用API或者c++庫函數格式化非字符數據到一個字符串中顯示出來。

不過,那個相當簡單的程序,還算不上是一個正兒八經的SDK程序,也就是說還不是一個純爺們兒,因爲我們並親自完成一個SDK程序的經典步驟。而是調用了一個MessageBox API函數,這個函數雖然使用簡單,但是在它的內部,那可是相當複雜啊~~~。怎麼個複雜法,具體的我不知道,但是我知道的是一個SDK程序的經典步驟它是都用到了的,什麼是編寫SDK程序的經典步驟呢?新手朋友們聽好了哦,現在我就告訴你。

第一步:註冊窗口類

第二步:創建窗口

第三步:消息循環

第四步:編寫窗口消息處理函數

上面我所說的,聽起來都比較專業,下面我就解釋一下,什麼是註冊窗口類呢?註冊窗口類就是使用一個 窗口類結構體(WNDCLASSEX) 來描述一類窗口,這類窗口具有相同的屬性,也就是你在結構體WNDCLASSEX中指定的那些值。只要是用這個窗口類創建的窗口都具有這些特性。至於WNDCLASS能描述哪些特性,下面會具體講,這裏你只要瞭解是用一個名叫WNDCLASSEX的結構體來描述一個窗口的類別。

創建窗口應該比較好理解吧,就是創建一個具體的窗口,好像是一句廢話嘛。也就是說這個窗口是根據一個窗口類而創建的,不是憑空而造的。意思是你要創建一個窗口,那麼必須要有一個已經註冊的窗口類。

對於前兩步,我打一個比方,就好比你要造一輛車,那們第一步首先是幹什麼? 當然是設計圖紙啦,圖紙上就有說明這種車有哪些特性。然後第二步纔是根據這個圖紙來創建一個具體的看得見的車。所以我上面說的註冊窗口類就好比設計窗口的圖紙,然後就是根據這個窗口的圖紙來創建一個具體的窗口。都說成這樣了,應該明瞭了吧~~

至於消息循環,就是創建的窗口隨時都有可能發生很多事情,那麼發生的這些事情怎麼通知你呢?比如窗口最小化了,窗口大小改變了,怎麼通知你呢?  其實就就是通過消息循環不斷的取得窗口所發生的事情,然後以消息的形式發送給我們後面要介紹的窗口消息處理函數。

消息處理函數呢就是我們程序員負責編寫代碼對具體的消息進行具體的處理,當然你也可以不處理,交給系統的默認處理函數來處理。

對於這兩步,我也打一個比方。消息循環就好比汽車的一個總傳感器,它源源不斷的將汽車內部所發生的事情以消息的形式通過儀表板傳達給開車的人,開車的人根據具體的事情而採取具體的操作,當然你也可以不操作,無動於衷,對於windows消息來說,不操作倒沒有什麼,而對於開車的人來說,不操作的後果就不好說了。 在這裏,這個總傳感器就相當於SDK程序的消息循環,不斷的發送消息,而開車的人就相當於窗口消息處理函數,負責處理各種消息。明白了吧,還不明白的話就看看下面的具體的程序吧,也需還有最後一絲希望可以讓你恍然大悟。

講了正兒八經的SDK程序的經典步驟後,我們進入正式的代碼階段,通過代碼結合上面所講理論進一步鞏固知識。我講逐步講解並逐步編寫一個自己註冊窗口類,創建窗口,帶消息循環,並自己編寫消息處理過程的程序。

首先給出程序框架

/* BY beyondcode */

#include <windows.h>

#include <tchar.h>

LRESULT CALLBACK WinMessageProcHWND hwndUINT msgWPARAM wParamLPARAM lParam );

int WINAPI _tWinMainHINSTANCE hInstanceHINSTANCE hPrevInstanceLPTSTR lpCmdLineint nShowCmd )

{

return 0;

}

第一個函數聲明,返回類型爲LRESULT,本質經查看是long,然後函數調用約定CALLBACK和WINAPI是一樣的,都是__stdcall,說明函數調用相關的約定,不必深究。至於爲什麼用CALLBACK是爲了意思顯而易見,表示是回調函數,什麼是回調函數?也就是系統負責調用的,不必你親自調用的函數,所以你在你的程序裏是看不到調用WinMessageProc這個函數的代碼的,你只負責編寫它的代碼,至於調用,系統會在有消息的時候自動調用它。WinMessageProc的參數類型和個數是規定好了的,不然系統怎麼知道怎麼掉用,所以不能更改。

再解釋一下這四個參數吧,第一個參數是一個窗口的句柄,也就是告訴你,是哪個窗口的消息,第二個參數是消息的類型,告訴你是什麼消息,第三個和第四個參數是這個消息所帶的一些額外的但是必須的數據。你在窗口消息處理函數中只使用他們就可以了,他們的值都是系統傳遞進來的。你只是根據他們來判斷是哪個窗口的什麼消息,並且獲取該消息的額外參數信息。

有了程序框架,我們來第一步,註冊一個窗口類

   //註冊一個名叫MyWindowClass的窗口類

WNDCLASSEX wc;

wc.cbSize sizeofwc );

wc.style CS_VREDRAW | CS_HREDRAW;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

wc.hCursor LoadCursorNULLIDC_ARROW );

wc.hIcon LoadIconNULLIDI_APPLICATION );

wc.hIconSm LoadIconNULLIDI_APPLICATION );

wc.hInstance hInstance;

wc.lpfnWndProc WinMessageProc;

wc.lpszMenuName NULL;

wc.lpszClassName _T("MyWindowClass");

if( !RegisterClassEx( &wc ) )

{

MessageBoxNULL_T("註冊窗口類出錯"), _T("出錯"), MB_OK );

return 0;

}

上面的WNDCLASS的各個成員值我就不一一介紹是什麼含義,MSDN上面講的非常清楚,我只講一兩個比較重點的,第一個lpszClassName這個成員,我們給它指定的是_T("MyWindowClass")這個值,這是指定這個窗口類的名字是什麼,因爲下面的創建窗口會用到這個名字。

lpfnWndProc這個成員是WinMessageProc這個函數,這是指定這個窗口類所創建的窗口的消息處理函數是哪一個,我們這裏指定的是WinMessageProc。其他的參數我就不囉嗦了,各位不懂的MSDN一下或者在羣裏來交流一下。

指定了這個窗口類有哪些特性後就完了?當然沒有,沒有註冊怎麼使用啊,所以還需要註冊,註冊調用RegisterClassEx這個API函數,將剛纔的WNDCLASS變量的地址傳給它就可以進行註冊了,如果註冊失敗,返回值爲零,成功的話返回值爲非零。

註冊了窗口類,我們來第二步,創建一個窗口。代碼如下:

// 根據上面註冊的一個名叫MyWindowClass 的窗口類創建窗口

HWND newWind = CreateWindowEx( 0L, _T("MyWindowClass"), _T("beyondcode"), WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, NULLNULLhInstanceNULL );

ifNULL==newWind )

{

MessageBoxNULL_T("創建窗口出錯"), _T("出錯"), MB_OK );

return 0;

}

ShowWindownewWindnShowCmd );

UpdateWindownewWind );

可見,創建窗口用CreateWindowEx這個API函數,它的第一個參數是擴展樣式,我們這裏不設置擴展樣式,所以傳遞0L,第二個參數就是窗口類的名字,我們這裏指定我們上面已經註冊了的那個名叫MyWindowClass的窗口類,第三個參數是窗口的標題,隨便設置,第四個參數是窗口的樣式,我們這裏設置的是WS_OVERLAPPEDWINDOW,一般主窗口都用這個樣式,就是有最大化,最小化框,有標題欄,有系統菜單。。具體的可以參見MSDN,第五個,六個,七個,八個參數分別指定窗口的初始座標和長寬,第九個參數指定父窗口是哪個,這裏沒有父窗口,所以傳遞NULL,第十個參數指定菜單的句柄,我們這裏不設置菜單,所以傳遞NULL,第十一個是應用程序句柄,用WinMain傳遞進來的那個hInstance參數,第十二個參數表示額外數據,不設置,所以爲NULL。

這個API函數有點複雜,不過用熟悉了也就不覺得了。這樣我們就創建了一個窗口,返回值是一個窗口的句柄,如果是NULL的話,說明創建窗口失敗了,如果不是NULL的話,說明成功了。

光創建成功了還不行,如果你不顯示和更新它,你還是看不到它,所以需要調用2個API函數,ShowWindow和UpdateWindow,參數就是剛纔創建成功的那個窗口的句柄,至於ShowWindow的第二個參數是指顯示的類型,是最大化顯示呢還是最小化顯示呢,不過在程序中第一次調用ShowWindow必須使用WinMain所傳遞進來的參數的第四個參數的值。這是MSDN上說的~

窗口創建成功了,下面一步是消息循環了,消息循環說起來複雜,其實代碼挺簡單的,而且基本格式固定,如下:

//消息循環

MSG msg;

whileGetMessage( &msgNULL, 0, 0 ) )

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

看到了嗎? 一直在一個循環裏面,一直調用GetMessage,只要GetMessage所取得的消息不是WM_QUIT的話,那麼GetMessage的返回值就不是0,那麼循環就一直進行。在循環內部,將GetMessage取得的消息傳遞給TranslateMessage和DispatchMessage兩個API函數進行處理.其中DispatchMessage就是將消息發送給了對應的窗口的窗口消息處理函數進行處理。至於TranslateMessage呢,則進行一些消息的轉換,可以先不深究。

最後就是編寫窗口消息處理函數的代碼了,你需要處理那些消息,那麼你就編寫處理那些消息的代碼,對於你不處理的消息,則統統交給一個叫DefWindowProc的API函數進行默認的處理。

LRESULT CALLBACK WinMessageProcHWND hwndUINT msgWPARAM wParamLPARAM lParam )

{

switch ( msg )

{

case WM_DESTROY:

{

PostQuitMessage( 0 );

break;

}

default:

return DefWindowProchwndmsgwParamlParam );

}

return 0;

}

這裏我們只處理了WM_DESTROY這個消息,這個消息是在窗口被銷燬的時候發送給窗口消息處理函數的,在窗口處理函數中,我們判斷這個消息是不是WM_DESTROY,如果是,就調用PostQuitMessage這個API函數,如果是其他消息,我們就不管,將參數全部傳遞給DefWindowProc這個函數進行處理。

而PostQuitMessage這個API函數的功能就是發送一個WM_QUIT的消息。而我們前面說到過, 在消息循環中GetMessage一旦取得WM_QUIT這個消息,就返回值爲0,那麼消息循環也就結束了,進而整個程序也就結束了,如果在這裏我們處理WM_DESTORY函數,但是不調用PostQuitMessage,那麼結果會怎樣呢,讀者朋友們思考一下~~

好了,到這裏,這個什麼功能也沒有的SDK程序也就完了,它只顯示一個帶有標題欄的可最大化,最小化的窗口,除了能夠關閉它,你幾乎不能進行其他任何操作,因爲我們除了處理窗口銷燬這個消息,其他任何消息我們都沒處理。

這篇文章中的源代碼在VS2008,windows 7平臺下編寫並完成編譯運行,我會將源代碼上傳到羣空間或其他地方,以方便新手朋友們對比學習~ 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章