Windows SDK编程 API入门系列(转)
之一?-那‘煩人’的Windows數據類型
原創文章,轉載請注明作者及出處。
?
首發
http://blog.csdn.net/beyondcode
http://www.cnblogs.com/beyond-code/
http://hi.baidu.com/beyondcode
Baidu文章地址:http://hi.baidu.com/beyondcode/blog/item/09370f24526d6b6835a80f54.html
CSDN文章地址:?http://blog.csdn.net/beyondcode/archive/2009/03/23/4015769.aspx
?
?
Hello Everybody This is beyondcode
?
?
?
大家好 再次自我介紹一下 我是beyondcode, 這次心血來潮, 計劃著做一系列關于Windows API 編程的教程,用于幫助一些在Windows API編程上有疑惑的,納悶的,迷惑的新手朋友們。
?
先解釋一些術語或名詞吧?? SDK是Software Development Kit的簡寫,也就是軟件開發包的意思,其中就包含了我們寫程序要用到的一些頭文件,庫,工具,幫助文檔之類的。
Windows API編程是指調用Windows的接口函數來進行程序的編寫,例如MessageBox就是一個API函數或者說接口函數。怎么說都可以,自己理解就行。如果你連這個都不太懂,我想也不會搜到這篇文章了吧~·
?
?
?
為什么做這個系列教程呢,請聽我一一道來先,最近遇到一些事一些人,讓我真的感覺在這方面的引導入門文章真的很是匱乏,加上Windows SDK頭文件中那些復雜,龐大,'煩人'的宏定義與數據類型定義,對于一個新手來說(我所說的新手不單只剛接觸編程的,還特指那些在其他語言領域有比較高造詣的朋友) 一個純SDK寫的helloworld程序都算是一個有些困難和挑戰的任務了吧。 本著幫助別人,高興自己的原則,我有了這個打算,當然對自己以前所學,所經歷做一次回憶,也是這次計劃的一部分。
?
?
?
聲明一下,本系列教程是面向廣大初次接觸WIN32 SDK程序編寫的新手朋友們的,如果你是高手,一笑而過吧~當然,除了一笑而過,也多謝你們提出指正文章中的錯誤,以免我誤人子弟啊~~謝謝
?
?
?
?
?
Ok 廢話不多說,進入正題,今天第一篇,講什么??? 對于一個新人來說,第一次接觸SDK編程或者說API編程,什么最迷惑你們的,我們講它,我覺得Windows SDK中那'煩人'的數據類型定義和宏定義應該算這個很角色吧。。
?
?
其實微軟的本意也是善良的,為了減輕程序員的負擔,和為了編程的方便,才花了那么多心思與精力定義出了這么一大套數據類型與宏定義,這也是我為什么在之前說它煩人都是加上引號的原因,因為他不是真的煩人,熟練了,你不但不覺得它煩,反而離不開它了,呵呵,日久深情也就是這么來的。
呵呵 先看幾個數據類型定義吧
typedef float FLOAT;
typedef long LONG;
typedef short SHORT
typedef int INT;
typedef char CHAR;
float, long, short, int, char 這幾個數據類型都是大家熟悉的C/C++的數據類型吧,微軟將他們重新定義了一下,很簡單,就是改變名字為大寫了,這樣做的目的大概是微軟為了編碼的方便吧,輸入法大小寫都不用切換了,多人性化呀 呵呵。。
?
再看幾個數據類型定義的例子
typedef unsigned int UINT;
typedef unsigned int UINT32;
typedef signed int INT32;
typedef unsigned long DWORD;
typedef unsigned short WORD;
?
這些數據類型的定義就稍微有實質性作用一些了,注意觀察,他們都比較短了,不用寫那么長了,而且也還比較直觀,如果我要定義一個無符號整形, 我就不用寫 unsigned int a;
這么長了,只需UINT a; 多簡單, 多明了,所以我說其實不煩人吧。
其中DWORD 算是SDK程序中可以經常看見的一個數據類型了,經常被使用,很多新手也就不明白,這是什么數據類型啊,現在看到了吧,其實就是無符號長整形unsigned long,給他取了個外號而已··沒什么技術含量,所以不用怕,程序中究竟是寫unsigned long 還是DWORD都看你自己心情,因為他們都代表同一種數據類型。
?
?
?
下面再介紹2個很重要的,經常被使用到的,無處不在的數據類型WPARAM,LPARAM
先看看他們定義吧
typedef LONG_PTR LPARAM;
typedef UINT_PTR WPARAM;
先告訴你,這2個數據類型很重要,不是危言聳聽,以后你寫SDK程序就知道了,看他們的定義如上,有些迷糊? 別,我們一步一步分析,我們分析LPARAM。首先定義LPARAM 為LONG_PTR也就是用LPARAM的地方也就可以寫成LONG_PTR,LONG_PTR又是被定義成什么的呢???
typedef long LONG_PTR;
看到了嗎??? 也就是long 所以歸根結底,LPARAM 就是long型,所有LPARAM型的變量,你都可以直接使用long數據類型代替。不過不推薦這樣,至于為什么,各位思考思考呢~~
以上這些數據類型是參考MSDN中的說明,或者可以查看WinDef.h這個頭文件查看這些Windows數據類型的定義,那么也請各位自己推推看LARAM和WPARAM的真面目吧~
?
各位朋友在推導的過程中可能發現LONG_PTR的定義是這樣寫的
#if defined(_WIN64)
typedef __int64 LONG_PTR;
#else
typedef long LONG_PTR;
#endif
這是什么意思呢,能看懂英文都能知道這在定義些什么,如果定義了 _WIN64這個宏 那么就定義 LONG_PTR 為 __int64,否則定義LONG_PTR為long。 很簡單吧 也就是說如果_WIN64這個宏在前面被定義了,那么這里的LONG_PTR就被定義為__int64這個在64位編程下的數據類型,否則就定義為long型,這樣說應該比較好理解了吧。在這里,各位就不必深究__int64了, 在目前的主流32位編程下很少使用它啦。理解就ok了。這樣定義是微軟為了程序員編寫的程序能在32位與64位下都能編譯而采用的伎倆。
有關這些Windows的數據類型,想查看他們的真面目,其實很簡單,在VC6.0,VS2008 這些集成開發環境里面,你只需要在一個數據類型上面點擊右鍵,在彈出菜單中選擇‘Goto Defination’ 或者是 ‘查看定義’就可以看到了,如果看到的還不是最終面目,在繼續上面步驟。直到看到它的本質數據類型為止。通過這樣,新手對于Windows的這些復雜的數據類型定義也就有了根本的認識,不再是迷迷糊糊,在以后的編程中也就不會出現不知道用哪種數據類型或者哪些數據類型之間可以相互轉換的情況了。不過還需要多多觀察與練習才是啊~~
?
?
?
?
下面再來看一看windows中定義的一些宏
#define?? VOID void
#define CONST?? const
2個最簡單的宏,也是只變成大寫而已,難道又是為了方便程序員不切換輸入法?還真的人性化呀。
Windows SDK中的宏定義是最龐大的,最復雜的,但也是最靈活的,為什么這樣說,先不告訴你,我會在以后的系列文章中一點一點的講解,累積,因為太多了,也比較復雜,我們就采取在需要用到的時候才講解它,目前看來還沒這個必要了解那么多,就了解上面2個很簡單的好了,像其他如:WINAPI??? CALLBACK??? GetWindowText 這些宏現在講了不但記不住還會增加你們的負擔。,我們就在以后要用到的時候再做講解。
?
到這里第一篇系列文章的內容也就差不多了。新手朋友們哪些地方迷惑的,提出來,我可以考慮是否加在后續的文章中進行解說。本SDK系列入門教程需要你們的支持。謝謝。
?
?
?
之二 -Unicode還是ASCII
今天,開始第二篇文章,這章我準備介紹一下Windows平臺下編程中Unicode編碼和ASCII編碼的相關問題。
不知道各位新手朋友們遇到這樣的問題沒有呢,新建一個Windows應用程序,調用MessageBox這個函數,準備讓它彈出一段提示文本,可是編譯器在編譯的時候卻報錯說,不能將?const?char*?或者?const?char[]?轉換為?const?wchar_t*?之類的提示呢,很多剛接觸Windows?API編程的朋友們在這里可能就卡住了,不知如何下手解決了,其實,這就是Unicode編碼和ASCII編碼的問題了。我下面就會一一道來
?
關于Unicode和ASCII具體的編碼是怎么的,我這里就不詳細介紹了,也介紹不了,如果需要深入了解,網上有很多這方面的專門文章,我這里就只對Unicode編碼和ASCII編碼在Windows平臺下的編程相關的內容進行介紹。
我們都知道Unicode和ASCII最大的區別就是Unicode采用2個字節來存儲一個字符,不管是英文,漢字,還是其他國家的文字,都有能用2個字節來進行編碼,而ASCII采用一個字節存儲一個字符,所以對于英文的編碼,那是足夠的了,可是對于漢字的編碼,則必須采用一些特殊的方法,用2個ASCII字符來表示一個漢字。
?
?
我們在寫程序的過程中,勢必要和字符打交道,要輸入,獲取,顯示字符,到底是選用Unicode字符呢還是ASCII字符呢,這都是各位自己的權利。但為了程序的通用性和符合目前操作系統的主流趨勢,Unicode編碼是被推薦的。由于Unicode字符要比ASCII字符占用的空間大一倍,編譯出來的程序在體積上和占用的內存上必定要大一些,不過這并不是什么很大的問題。所以微軟目前的SDK中保留了2套API,一套用于采用Unicode編碼處理字符的程序的編寫,一套用于采用ASCII編碼處理字符的程序的編寫。?例如,我們上面提到的MessageBox,它其實不是一個函數名,而是一個宏定義,我們先來看看它是怎么被定義的,再來討論它。
?
#ifdef?UNICODE
#define?MessageBox??MessageBoxW
#else
#define?MessageBox??MessageBoxA
#endif?
?
看到了嗎???很簡單是不是,?如果定義了UNICODE?這個宏?那么就定義MessageBox為MessageBoxW,如果沒有定義UNICODE這個宏,?那么就定義MessageBox?為MessageBoxA,MessageBox后面的W和A?就是代表寬字節(Unicode)和ASCII,這樣,其實存在于SDK中的函數是MessageBoxW和MessageBoxA這兩個函數.
MessageBox只是一個宏而已。所以在程序中,這3個名字你都可以使用,只不過需要注意的是,使用MessageBoxA的話,那么你要注意傳給它的參數,字符都必須是單字節,也就是ASCII,?在程序中就是char,如果使用MessageBoxW的話,那么,字符都必須使用Unicode,程序中就是?wchar_t。?但是這樣有個非常不方便的地方那就是,如果你使用W后綴系列的函數的話,那么你的程序使用的字符就是Unicode字符編碼的,但是如果你需要用這個程序的源代碼編譯出字符采用ASCII編碼的程序,那么需要改動的地方就太大了。凡是涉及到字符操作的地方都需要改變。那么?,有沒有比較好的辦法不做更改就可以用同樣的代碼編譯出ASCII版本的程序呢。??
當然有,就是我們在編程的時候盡量使用不帶后綴的宏定義,如上例,就使用MessageBox,其中的參數也不明確使用char?還是wchar_t?而是使用微軟給我們定義的TCHAR字符數據類型,它的定義和上面MessageBox函數的定義差不多,都是根據是否定義了UNICODE這個宏來判斷是將TCHAR定義為char還是wchar_t,所以這樣一來,這個TCHAR的數據類型就是可變的了,它根據工程的設置而定義為相應的最終字符類型,這樣我們的程序就可以不做任何更改就可以輕松的編譯出另外一個版本的了。是不是非常方便。
前面2篇文章純文字的介紹比較多,因為很多是概念性的,需要理解,后面的文章我準備配合一些小示例程序,使用一些簡單的API函數,遇到的相關的概念在一并介紹的方法進行。所以,前2篇文章如果各位朋友不是很能理解,不用擔心,影響不是很大,經過后面的學習,你就會慢慢的理解前面所說的內容了。
?
?
?
之三 -那迷惑人的Windows字符和字符指針類型
大家好,通過前面兩篇打頭文章,我也看了留言,感謝那些給我提意見的人和指出錯誤之處的人。再次謝謝你們的支持。?另外,Windows?SDK編程交流群已經建立了,歡迎各位志同道合者加入進行交流(?群號:81543028?)
?
本打算通過前面兩篇文章的講解,后來的系列就可以通過使用一些簡單的,常用的API寫一些示例程序的講解進行,但是發現還有一個不得不先講一講的要點,Windows下和字符串操作有關的數據類型。我看留言中也有幾位朋友提到了,那我就在這篇中講它吧。不會很枯燥的,各位慢慢看下去就是了。
?
下面我羅列一些我們在Windows平臺下編程經常使用到的和字符或字符串有關的數據類型。
?
char??和??wchar_t?
這兩個類型大家絕對不會陌生吧,一個是單字節的字符類型,一個是寬字節的字符類型(也就是Unicode字符)。
char???c?=?'b';
wcha_t??wc?=?L'b';
上面我就分別定義了2個變量c和wc?,相信第一個定義大家都看的懂,就是定一個字符變量c,其中保存了'b'這個字符。?那么第二個呢???我相信還是很多人都看的懂,要是你看不懂也沒關系,現在就告訴你,也是定義一個字符變量wc,?只不過這個字符變量是Unicode字符變量,用2個字節來保存一個字符,而上面的c這個字符變量只有一個字節來保存,那么在'b'前面的L又是什么意思呢,它就表示這里的'b'這個字符是一個Unicode字符,所以第二個定義的意思就是將L'b'這個Unicode字符保存到wc這個Unicode字符變量中。
?
如果我要定義一個字符數組怎么定義呢??用分別用單字節的char和寬字節的wchar_t來定義就應該是:
char? c[10];
wchar_t wc[10];
如果是要帶初始化的字符數組的聲明,我們來看看怎么寫
char c[]?=?"beyondcode";
wchar_t wc[]?=?L"beyondcode";
看到了嗎,寬字節的操作其實和單字節的字符操作一樣吧,只是在前面加上L表示是寬字節的字符或者字符串。
?
上面都是屬于C/C++中的知識,并沒有涉及太多Windows中的數據類型,那么各位朋友們在Windows編程中看到的滿到處都是的?TCHAR,LPSTR,?LPCSTR,?LPWSTR,?LPCWSTR,?LPTSTR,?LPCTSTR?這些數據類型又是怎么回事呢??別急,我們一步一步的來,最后我會聯系到那上面去的。
?
上面的你都知道或者是理解了的話,那我們繼續,除了可以聲明一個字符數組,我還可以定義一個字符指針變量來指向一個字符數組,當然這個字符數組可以是Unicode的寬字節字符數組,也可以是單字節字符數組,如下:
char? c[]?=?"hello?beyondcode"; //定義一個字符數組
wchar_t??wc[]?=?L"hello?beyondcode"; //定義一個寬字節字符數組
?
char ??*p?=?c; //定義一個字符指針,指向剛才的字符數組
wchar_t *wp?=?wc; //定義一個寬字節字符指針,指向剛才的寬字節字符數組
?
這樣之后,我就可以通過指針來改變剛才我們定義的2個數組,例如:
?
p[0]?=?'H';
wp[0]?=?L'H';
把上面2個數組的第一個字符通過指針改變成大寫。這里是可以通過指針來修改的,因為我沒有定義指針為常量指針,也就是沒有加const?修飾符。如果我像下面這樣定義的話,那么就不能通過這些指針來改變他們所指向的數據了,而是只有讀取他們。
const??char??*p?=?c;
const??wchar_t??*wp?=?wc;
?
上面將的都是C/C++的基礎知識,有點啰嗦,為了照顧新手朋友們嘛,下面我們就來看看Windows是怎么定義它的數據類型的
?
首先,定義了CHAR,?WCHAR的這2個字符數據類型,就是我們上面討論的兩個字符數據類型改了一下名字而已。現在你還不昏吧··
typedef?char? CHAR;
typedef?wchar_t? WCHAR;
然后,用剛才定義的?CHAR,?WCHAR這2個字符數據類型去定義了一系列其他字符指針類型。
typedef??CHAR??*LPSTR;
typedef??WCHAR??*LPWSTR;
這樣一定義之后,LPSTR的就是?CHAR*,?而CHAR?又是char,?所以LPSTR的本質就是?char*,也就是我們上面熟悉的不能再熟悉的字符指針,??那LPWSTR不用我推導,相信你也推導出來了吧。不過我還是推導一下,LPWSTR是?WCHAR?*?,?WCHAR是wchar_t,這樣LPWSTR就是?wchar_t*?,也就是我們上面討論的寬字節字符指針。上面這些定義都是在WinNT.h這個頭文件中定義的,讀者朋友們有興趣在這個頭文件里面去挖掘挖掘吧,上面2個定義我只是提取了重要的部分,其實在里面他還定義了其他很多別名.
?
看了LPSTR,?LPWSTR是怎么一回事之后,我們再接再厲,看看LPCSTR,LPCWSTR這2個數據類型又是怎么一回事呢,?老規矩,先看windows的定義。
typedef??CONST??CHAR??*LPCSTR;
typedef??CONST??WCHAR?*LPCWSTR;
和上面的比較,名字中就多了一個大寫的C,這個C的含義就代表是const修飾符,也就是我們上面所說的常量指針,指向的內容不能通過這個指針被改變,但可以讀取。定義中的大寫的CONST也是一個宏,我在第一篇文章中就講過了,代換出來也就是const,?所以請讀者自己推導一下這兩個數據類型的本質是什么。
?
所以,在windows平臺下的編程過程中,凡是可以使用char*?的地方,你都可以使用LPSTR來代替,凡是可以使用wchar_t*的地方,你都可以使用LPWSTR來代替,至于怎么用,還是那句老話,看你個人心情,只不過Windows的API函數中關于字符串的都是使用LP這種數據類型。但是你還是可以給他傳遞char*?或者?wchar_t*?,只要他們的本質是一樣的,那怎么不可以呢~~
下面,我們來看一看一些示例。
char ?c?=?'c';??和?CHAR?c?=?'c';????是一樣的
?
wchar_t wc?=?L'w';?和?WCHAR?wc?=?L'w';????是一樣的
?
char*?p??和?LPSTR?p?是一樣的
wchar_t*?wp??和?LPWSTR?wp????是一樣的
?
再來看看動態內存分配怎么寫的呢
char*?p?=?new?char[10]; //動態分配了十個字符
也可以寫成
CHAR*?p?=?new?CHAR[10];
LPSTR?p?=?new?CHAR[10];
LPSTR?p?=?new?char[10];
?
寬字節的再來一次
wchar_t*?wp?=?new?wchar_t[10];
也可以寫成下面這些形式
WCHAR*??wp?=?new?WCHAR[10];
LPWSTR??wp?=?new?WCHAR[10];
LPWSTR??wp?=?new?wchar_t[10];
?
上面定義的這些字符指針?p?,?wp都沒有用const修飾符,所以可以通過他們來修改他們所指向的內容。這里留給讀者一個問題,怎么定義有const修飾符的字符指針呢,都可以用什么形式來寫呢,寫得越多越好喲。。
?
?
通過上面這些,我想你大概已經了解了LPSTR,?LPCSTR,?LPWSTR,?LPCWSTR這四個數據類型了,他們無非就是:
LPSTR? -------????char*
LPCSTR ------- ?const?char*
LPWSTR ------- ?wchar_t*
LPCWSTR --------? ?const?wchar_t*?
?
下面我提一個問題,如果你在你的程序中使用的字符串都是通過LPWSTR,LPCWSTR這種寬字節(Unicode)字符指針來進行操作的,那么在Unicode環境下編譯,完全沒有問題,如果這時你需要編譯一套ASCII版本的程序,那你會怎么辦呢????你說將用LPWSTR?和LPCWSTR的地方全部換成LPSTR和LPCSTR,再將字符串前面的L去掉就可以了,對,這是一種方法,但是!!所有人在這里都應該知道我要說但是,這也太麻煩了吧。難道沒有通用點的方法嗎????有!!??所有人在這里也都知道我會說有,呵呵。??那就是使用微軟的通用數據類型,說通用數據類型有點太專業了,其實也就那樣,請聽我慢慢分析來。我在上一篇文章中說過,凡是涉及字符串操作的API函數有2套,一個A系列的,一套W系列的,還有一套宏,能根據不同的工程環境定義成不同的API函數名。那么在字符類型上微軟也使用幾乎同樣的技術,定義了一套宏能根據不同的工程環境定義成不同的字符數據類型。我上面就提到過的TCHAR,LPTSTR,?LPCTSTR就是這樣的類型。
首先說說TCHAR,它是被這樣定義的:
#ifdef??UNICODE
typedef??WCHAR??TCHAR;
#else
typedef??char TCHAR
?
看到了嗎??它也是根據UNICODE這個宏被定義沒有,如果被定義了,那么TCHAR代表的數據類型就是WCHAR,?也就是wchar_t,?如果沒被定義,那么TCHAR?就代表的是char
?
同樣LPTSTR,LPCTSTR也是這樣的,考慮到篇幅,我就只列出LPTSTR來給大家看看了
?
#ifdef???UNICODE
typedef??LPWSTR?? LPTSTR;
#else
typedef??LPSTR LPTSTR;
這個是我簡化了的定義,真實面目有些復雜,不過意思也是如此,有興趣可以自己看看,在WinNT.h這個頭文件中。下面再次解釋一下上面這個LPTSTR的定義,?還是老樣子,根據UNICODE這個宏被定義與否來決定怎么定義LPTSTR?,如果是定義了UNICODE這個宏,表示當前工程環境是Unicode環境,那么LPTSTR就被定義為了LPWSTR,?LPWSTR就是我們前面所講的wchar_t*?,所以此時LPTSTR代表的數據類型就是wchar_t*?,??如果這時的工程沒有定義UNICODE這個宏,那么就定義LPTSTR為LPSTR,而LPSTR就是我們前面所說的char*?,所以這是的LPTSTR就代表char*。懂了嗎?各位,我都覺得自己有些啰嗦了··不好意思···
?
然后還有一個宏需要講一下,由于我們使用通用數據類型,那么我事先就不知道我的源代碼需要在Unicode下編譯還是在ASCII環境下編譯,所以如下這種情況
TCHAR?tc?=?'a';??或者是?TCHAR?tc?=?L'a';??是否合適呢??前面我已經說過了字符或字符串常量前面加L代表這是寬字節的字符或字符串,將一個寬字節字符賦值給一個TCHAR數據類型的變量tc,什么情況下是正確的呢???各位思考一下呢???
如果當前工程是Unicode環境,那么TCHAR數據類型就是wchar_t的寬字節類型,所以tc就是寬字節字符變量,那么上面第二個賦值語句就是正確的,而第一個就是錯誤的。
如果反過來,當前的工程是ASCII環境,那么TCHAR代表的是char這種數據類型,那么第一個賦值語句就是正確的,而第二個就是錯誤的了。
分析了這么多,我就是要講一個宏?_T(),?只要將字符或者字符串常量放在_T()這個宏里面,那么這個宏就能根據當前的環境決定是否在字符或字符串前面加L,如下面:
TCHAR?tc?=?_T('A');
如果這么寫,在不需要改寫源代碼的情況下,就可以編譯出Unicode和ASCII兩套程序
而只需要改變工程的環境而已。
?
?
?
這篇文章的內容大概就這么多了,關于后續文章的內容安排,我會適當采納各位朋友的留言來進行安排。
?
在這里我介紹的是Windows平臺下的和字符串操作有關的數據類型,至于MFC中的CString類,c++標準庫中的string,我就不做講解了。
?
還有,前兩篇的文章中我說微軟定義這些數據類型為大寫是為了編碼的方便,不需要切換輸入法,很多朋友都留言給我指出了,其實我是開了一個小玩笑,畢竟面對的是初學者,我覺得在目前這種環境下,沒有必要給他們講什么代碼移植性啊,兼容性這些概念,那些在達到一定程度之后就會慢慢理解的,所以我自己想了一個小小的優點來說明那個問題,不過還是感謝你們的補充說明,謝謝··
?
?
?
之四 -一個相當簡單的SDK程序
大家好,還是我beyondcode,再次見面,前面介紹的那么多'理論知識',你們都懂了嗎??就算還沒有徹底領悟,但至少還是有那么一點意識了吧,知道有那么一回事了吧。這一篇我打算通過一個小小小例子,來回憶一下我們以前介紹的相關知識,如Windows的數據類型,特別是和字符和字符串操作相關的數據類型,還有就是Unicode和ASCII在API函數上的具體體現。?
另外,SDK編程交流群已經建立,很多朋友踴躍參加,系列文章和群的發展離不開你們。群號:81543028。
?
Ok,我們正式開始,我打算從一個簡單的SDK程序開始,別怕,就幾行代碼而已··
?
/*?BY?beyondcode?*/
#include?<windows.h>
?
int?WINAPI?WinMain(?HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,?LPSTR?lpCmdLine,?int?nShowCmd)
{
MessageBoxA(?NULL,?"Hello?beyondcode",?"Title",?MB_OK?);
return?0;
}
程序你已經看到了,這恐怕就是一個最簡單的帶窗口的SDK程序了吧,如果你能寫出代碼行數比這個還少,又帶窗口顯示字符串的SDK程序,歡迎交流,呵呵,開個玩笑。
程序倒是簡單,可是我還是要問一問,這個程序,你通過觀察我在字符串的處理,還是在API函數的調用,還是主函數的參數寫法,你能看出什么問題呢?.....................................對,就是我全部明確指出是單字節版本的,WinMain的第三個參數是LPSTR類型,調用的MessageBox是帶A后綴的單字節版本,字符串常量"Hello?beyondcode"和"Title"都沒有使用L前綴。那么第二個問題來了,?如果我告訴你我現在的工程環境是?使用Unicode字符集?(工程使用的字符集可以在?【項目】->工程屬性?彈出的屬性頁中的?【配置屬性】中的【常規】左邊的【字符集】中設置),那么我上面的程序能正常通過編譯嗎??當然能,因為我已經試過了,不信你也可以試試,可是為什么呢??這是因為我指定的參數和函數需要的參數都是單字節版本的,也就是說他們相互匹配。要是我這里將MessageBoxA改成MessageBoxW呢??就會出錯吧,因為MessageBoxW的第二個,和第三個參數是需要LPCWSTR,通過上一篇學習,我們知道也就是const?wchar_t*,?而我給出的兩個字符串常量卻沒有用L前綴.也就是說他們是單字節的,傳給寬字節版本的MessageBoxW當然就類型不匹配了啊,所以就通不過編譯了吧。
通過上面的學習,我再出一個問題,如果我此時的工程環境是使用Unicode字符集,而這里我不用MessageBoxA,也不用MessageBoxW,而是用MessageBox,其他的都不變,結果會怎么樣呢????不能理解的可以加群討論喲~~~
?
?
好了,單字節版本的程序,我們已經看到了,我們再來看看我們怎么才能把它改成寬字節版本的呢?
其實需要改的地方不多,也就5處WinMain改成wWinMain,?WinMain的第三個參數改成LPWSTR,MessageBoxA改成W,兩個字符串常量加L就ok了。
/*?BY?beyondcode?*/
#include?<windows.h>
?
int?WINAPI?wWinMain(?HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,?LPWSTR?lpCmdLine,?int?nShowCmd?)
{
MessageBoxW(?NULL,?L"Hello?Beyondcode",?L"Title",?MB_OK?);
return?0;
}
?
如果我想寫一個代碼比較通用的版本,也就是可以不用改動代碼,就能編譯出Unicode和ASCII的兩個版本的程序,我應該怎么寫呢???其實就是我上一篇重點討論的,凡是涉及到字符串的都不明確指出是Unicode還是ASCII版本的,調用的API函數凡是涉及到字符串參數的都不明確指出調用是A后綴的還是W后綴的函數,而是調用沒有后綴的函數,如上面的MessageBox,這樣就能寫出代碼比較通用的程序了。那么我們現在來把我們上面的程序改一改,讓它通用
/*?BY?beyondcode?*/
#include?<windows.h>
#include?<tchar.h>
?
int?WINAPI?_tWinMain(?HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,?LPTSTR?lpCmdLine,?int?nShowCmd?)
{
MessageBox(?NULL,?_T("Hello?Beyondcode"),?_T("Title"),?MB_OK?);
return?0;
}
?
WinMain被改成了_tWinMain?,_tWinMain也是一個宏,根據UNICODE這個宏被設置與否而被定義成WinMain或wWinMain,和LPTSTR是一樣的,這里還需要注意的是要包含tchar.h這個頭文件,因為_tWinMain和_T()這些宏是被定義在里面的。經過上面我們就寫出了第一個SDK的可以編譯出兩個版本的比較通用的程序代碼了。是不是有點成就感了呢。。
?
下面,我們繼續在上面的程序中加一些功能,讓它計算1到10的和,然后把結果顯示給我們看,這個地方,很多SDK初學者就不知所措了,因為一個和是一個整數,怎么顯示這個整數給我們呢,通過對話框??MessageBox,可是MessageBox顯示的是字符串。而我們這里又不是控制臺程序可以使用printf之類的格式化輸出函數來輸出數字,也不能使用cout之類的C++對象來輸出,那我們怎么辦呢??通過對話框來顯示結果是不錯的選擇,但是對話框需要的是字符串,那我們就把我們的結果格式化到一個字符串里面,然后傳送給MessageBox讓它顯示出來。那么就需要用到格式化字符串函數,下面我們就介紹wsprintf這個函數
#ifdef?UNICODE
#define?wsprintf??wsprintfW
#else
#define?wsprintf??wsprintfA
#endif?//?!UNICODE
說它是函數,是不確切的。因為它實際是一個宏,根據環境被定義成不同的函數名wsprintfW或者wsprintfA,?而我們為了程序的通用性,直接使用wsprintf,傳遞的參數凡是涉及到字符串常量的我們都是用_T()宏,字符串指針的我們都使用LPTSTR和LPCTSTR。
下面我就先貼出添加了功能的程序代碼,然后在做分析,你可以先不看分析,自己看一看代碼,不懂的猜一猜它的意思。
?
/*?BY?beyondcode?*/
#include?<windows.h>
#include?<tchar.h>
?
int?WINAPI?_tWinMain(?HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,?LPTSTR?lpCmdLine,?int?nShowCmd?)
{
int?sum?=?0;
for(?int?i?=?1;?i<=10;?i++?)
sum?+=?i;
?
TCHAR?strSum[256]?=?{?0?};
wsprintf(?strSum,?_T("%d"),?sum?);
MessageBox(?NULL,?strSum,?_T("Title"),?MB_OK?);
return?0;
}
?
怎么樣,也還不算復雜吧,計算1到10的那部分不用我講了吧,最后的結果存放在sum這個變量里,我們現在的目的就是要讓它顯示在MessageBox彈出的對話框上面。
首先我們定義一個字符數組,我使用的是通用類型TCHAR,然后把它全部初始化為0。接著調用格式化字符函數wsprintf,它的第一個參數是LPTSTR類型的,指定經過格式化的字符串存放的地方,第二個參數是指定以什么格式來格式化后面的數據,這里我們要格式化一個整數,所以指定%d,這個和printf這些函數是一樣的,?后面的參數就是我們要格式化的數據了。
這里還有一點可能有些新手朋友們不太懂,那就是,wsprintf要求的第一個參數是LPTSTR,而我傳遞的是一個TCHAR的數組名,這里我就在啰嗦一次咯,我們假設我們的環境是UNICODE的,那么LPTSTR相當于什么類型呢??上一篇就講過的啊,就是wchar_t*?就是寬字符指針,而我們知道數組名就是代表這個數組元素類型的指針,那么這里TCHAR數組的元素類型就是TCHAR,在Unicode環境下,TCHAR就是wchar_t也就是說strSum代表的是wchar_t類型的指針,也就是wchar_t*,所以看到了嗎,他們是一樣的類型。
通過上面的wsprintf函數的調用strSum這個字符數組中就包含了計算結果的字符串表示,然后我們通過MessageBox講這個字符數組中的內容顯示出來。在這里MessageBox的第二個參數類型是LPCTSTR,也就是const?wchar_t*,?而我們上面說過我們的strSum代表的是?wchar_t*,這里同樣可以傳遞給它又是為什么呢?這是因為阿,這里strSum在傳遞給MessageBox的時候就隱式轉換成了const?wchar_t*了。
?
有關格式化字符串的函數還有如下,詳細用法各位可以查看MSDN,和上面所介紹的都差不多
sprintf?單字節版本的C/C++庫函數
swprintf?寬字節版本的C/C++庫函數
?
而我們上面的wsprintf和上面兩個函數看起來很相似,大家不要搞混淆了啊,wsprintf最前面的w不是代表Wide,寬字節的意思了,而是Windows的W,代表是windows的API函數了,其實它是一個宏這在上面已經說過了,真正的API函數其實是wsprintfA和wsprintfW這兩個,在不嚴格的情況下通常我們也說wsprintf是函數,只要大家懂就行了~
?
OK,?這一篇文章就到這里了,源代碼就這么點,我也不上傳了,各位有興趣自己敲一下。下一篇,我講會就怎么自己創建窗口進行介紹,謝謝大家的支持。
總結
以上是生活随笔為你收集整理的Windows SDK编程 API入门系列(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每个计算机系的学生都学离散数学,离散数学
- 下一篇: ap接口 php_2018年小米高级 P