1. 程式人生 > >Windows API入門系列之五 -一個正兒八經的SDK程式

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

上一篇,講了一個簡單的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>

LRESULTCALLBACKWinMessageProcHWNDhwndUINTmsgWPARAMwParamLPARAMlParam );

intWINAPI_tWinMainHINSTANCEhInstanceHINSTANCEhPrevInstanceLPTSTRlpCmdLineintnShowCmd )

{

return 0;

}

第一個函式宣告,返回型別為LRESULT,本質經檢視是long,然後函式呼叫約定CALLBACK和WINAPI是一樣的,都是__stdcall,說明函式呼叫相關的約定,不必深究。至於為什麼用CALLBACK是為了意思顯而易見,表示是回撥函式,什麼是回撥函式?也就是系統負責呼叫的,不必你親自呼叫的函式,所以你在你的程式裡是看不到呼叫WinMessageProc這個函式的程式碼的,你只負責編寫它的程式碼,至於呼叫,系統會在有訊息的時候自動呼叫它。WinMessageProc的引數型別和個數是規定好了的,不然系統怎麼知道怎麼掉用,所以不能更改。

再解釋一下這四個引數吧,第一個引數是一個視窗的控制代碼,也就是告訴你,是哪個視窗的訊息,第二個引數是訊息的型別,告訴你是什麼訊息,第三個和第四個引數是這個訊息所帶的一些額外的但是必須的資料。你在視窗訊息處理函式中只使用他們就可以了,他們的值都是系統傳遞進來的。你只是根據他們來判斷是哪個視窗的什麼訊息,並且獲取該訊息的額外引數資訊。

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

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

WNDCLASSEXwc;

wc.cbSizesizeofwc );

wc.styleCS_VREDRAW | CS_HREDRAW;

wc.cbClsExtra= 0;

wc.cbWndExtra= 0;

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

wc.hCursorLoadCursorNULLIDC_ARROW );

wc.hIconLoadIconNULLIDI_APPLICATION );

wc.hIconSmLoadIconNULLIDI_APPLICATION );

wc.hInstancehInstance;

wc.lpfnWndProcWinMessageProc;

wc.lpszMenuNameNULL;

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 的視窗類建立視窗

HWNDnewWind = 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上說的~

視窗建立成功了,下面一步是訊息迴圈了,訊息迴圈說起來複雜,其實程式碼挺簡單的,而且基本格式固定,如下:

//訊息迴圈

MSGmsg;

whileGetMessage( &msgNULL, 0, 0 ) )

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

看到了嗎? 一直在一個迴圈裡面,一直呼叫GetMessage,只要GetMessage所取得的訊息不是WM_QUIT的話,那麼GetMessage的返回值就不是0,那麼迴圈就一直進行。在迴圈內部,將GetMessage取得的訊息傳遞給TranslateMessage和DispatchMessage兩個API函式進行處理.其中DispatchMessage就是將訊息傳送給了對應的視窗的視窗訊息處理函式進行處理。至於TranslateMessage呢,則進行一些訊息的轉換,可以先不深究。

最後就是編寫視窗訊息處理函式的程式碼了,你需要處理那些訊息,那麼你就編寫處理那些訊息的程式碼,對於你不處理的訊息,則統統交給一個叫DefWindowProc的API函式進行預設的處理。

LRESULTCALLBACKWinMessageProcHWNDhwndUINTmsgWPARAMwParamLPARAMlParam )

{

switch ( msg )

{

caseWM_DESTROY:

{

PostQuitMessage( 0 );

break;

}

default:

returnDefWindowProchwndmsgwParamlParam );

}

return 0;

}

這裡我們只處理了WM_DESTROY這個訊息,這個訊息是在視窗被銷燬的時候傳送給視窗訊息處理函式的,在視窗處理函式中,我們判斷這個訊息是不是WM_DESTROY,如果是,就呼叫PostQuitMessage這個API函式,如果是其他訊息,我們就不管,將引數全部傳遞給DefWindowProc這個函式進行處理。

而PostQuitMessage這個API函式的功能就是傳送一個WM_QUIT的訊息。而我們前面說到過, 在訊息迴圈中GetMessage一旦取得WM_QUIT這個訊息,就返回值為0,那麼訊息迴圈也就結束了,進而整個程式也就結束了,如果在這裡我們處理WM_DESTORY函式,但是不呼叫PostQuitMessage,那麼結果會怎樣呢,讀者朋友們思考一下~~

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

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

相關推薦

Windows API入門系列 -一個正兒八經SDK程式

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

【轉載】API入門系列三 -那迷惑人的Windows字符和字符指針類型

asc blog char* 讀者 兼容性 部分 lpcstr 意思 wchar_t 原創文章,轉載請註明作者及出處。 首發 http://blog.csdn.net/beyondcode http://www.cnblogs.com/beyond-code/ http:/

[unreal4入門系列] 熟悉關卡編輯器介面

一. 標籤欄標籤欄就是最頂部看起來像這樣的東西:在最右方就是專案的名稱,如這裡的test1。前面的對話氣泡是反饋按鈕,你可以給Epic公司提供任何意見。而接著的帽子圖示,是顯示虛幻4內建的教程。 二. 選單欄 在標籤欄下方就是選單欄,它提供了大部分應用程式常用的命令

MongoDB最簡單的入門教程-通過Restful API訪問MongoDB

rest creat 操作 database internal 進行 作用 shu 公眾 通過前面四篇的學習,我們已經在本地安裝了一個MongoDB數據庫,並且通過一個簡單的Spring boot應用的單元測試,插入了幾條記錄到MongoDB中,並通過MongoDB Com

osgi.net從入門到精通系列

完整 我們 問題 eid aps 加載 .net 周期 switch 宿主模塊 宿主模塊是能夠向外界獨立暴露它提供的模塊。它能夠被UIOSP安裝、啟動、停止和卸載,具有 獨立的類型空間以及相應的類加載器,擁有完整的生命周期。我們可以在模塊清單文件給一個宿主模塊 定義起始狀

Windows SDK程式設計 API入門系列

之一 -那‘煩人’的Windows資料型別 原創文章,轉載請註明作者及出處。 首發 Hello Everybody This is beyondcode 大家好 再次自我介紹一下 我是beyondcode, 這次心血來潮, 計劃著做一系列關於Windows API

Java入門系列執行緒池ThreadPoolExecutor原理分析思考(十

前言 關於執行緒池原理分析請參看《http://objcoding.com/2019/04/25/threadpool-running/》,建議對原理不太瞭解的童鞋先看下此文然後再來看本文,這裡通過對原理的學習我談談對執行緒池的理解,若有錯誤之處,還望批評指正。 執行緒池思考 執行緒池我們可認為是準備好執行應

提升HTML5的性能體驗系列 webview啟動速度優化及事件順序解析

執行時間 很快 runt 代碼 模式 本地 技術 apk loaded webview加載時有5個事件。觸發順序為loading、titleUpdate、rendering、rendered、loaded。webview開始載入頁面時觸發loading,載入過程中如果&am

R語言數據分析系列

r語 來看 tab barplot code 繪制 ber map lib R語言數據分析系列之五 —— by comaple.zhang 本節來討論一下R語言的基本圖形展示,先來看一張效果圖吧。 這是一張用R語言生成的,虛擬的wordcloud雲圖,詳細

深入理解Tomcat系列:Context容器和Wrapper容器

ssa stream servlet實例 可用 igel sse ould rip alt 前言 Context容器是一個Web項目的代表,主要管理Servlet實例,在Tomcat中Servlet實例是以Wrapper出現的。如今問題是怎樣才幹通過C

數據庫面試系列:mysql的存儲引擎

當前 查看 efault 表鎖 blog 如果 show 事務 測試 mysql的默認存儲引擎是innoDB,是唯一一個支持事務和支持外鍵的存儲引擎, 可以通過:show variables like ‘default_storage_engine‘;查看當前數據庫到默認引

Jenkins入門系列——01第一章 Jenkins是什麽?

source 每次 servlet容器 生命 .com .net 優點 指紋 順序 第一章 Jenkins是什麽? Jenkins 是一個可擴展的持續集成引擎。 主要用於: l 持續、自動地構建/測試軟件項目。 l 監控一些定時執行的任務。 Jenkins擁有

Vue入門系列)Vue實例詳解與生命周期

auto res context mode parent all from bool silent 【入門系列】 【本文轉自】   http://www.cnblogs.com/fly_dragon Vue的實例是Vue框架的入口,其實也就是前端的ViewM

Office 365 系列:創建新用戶

office365 創建賬號、分配許可 本章節跟大家分享怎麽去創建新用戶、分配許可以及修改用戶後綴和電子郵件地址。 登錄 Office 365 管理中心,點擊“用戶”--“活動用戶”--“+添加用戶”; 參照截圖,輸入對應的姓名、顯示名、登錄名、密碼等信息點擊“添加”

mongo 3.4分片集群系列:詳解平衡器

大致 mongos 搭建 一次 相對 時間 表示 部分 man 這個系列大致想跟大家分享以下篇章(我會持續更新的↖(^ω^)↗): 1、mongo 3.4分片集群系列之一:淺談分片集群 2、mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3、mongo 3.4

Scala入門系列):面向對象之類

important ica back ember const 就會 out 不用 spa // 定義類,包含field以及method class HelloWorld { private var name = "Leo" def sayHello() { prin

大數據學習系列 ----- Hive整合HBase圖文詳解

scala direct ont 增加 pac hadoop2 exe dfs- min 引言 在上一篇 大數據學習系列之四 ----- Hadoop+Hive環境搭建圖文詳解(單機) 和之前的大數據學習系列之二 ----- HBase環境搭建(單機) 中成功搭建了Hive

python全棧系列---定義一個session類

ret self ges its redis lib coo span imp 首先:註意cookie中的get_cookie是返回字符串,而get_secure_cookie返回的是字節類型 #self.get_secure_cookie()

多線程系列:Balking 模式

實現 vat 正常 bject ret .get 完全 .data 工具 一,什麽是Balking模式 如果現在不合適執行這個操作,或者沒必要執行這個操作,就停止處理,直接返回。在Balking模式中,如果守護條件不成立,就立即中斷處理。 二,例子: 定期將當前數據內容寫入

算法入門系列排序與檢索

一個 str show tor cal else color mem 函數 UVA340 UVA10420 時間有點久遠,很早之前寫的,然後忘記總結了,這道題其實很容易,一行只取第一個字符串,然後按照字典序輸出每個字符串的個數。 這裏有個技巧就是先用scanf獲