大家好,還是我beyondcode,再次見面,前面介紹的那么多’理論知識’,你們都懂了嗎? 就算還沒有徹底領(lǐng)悟,但至少還是有那么一點意識了吧,知道有那么一回事了吧。這一篇我打算通過一個小小小例子,來回憶一下我們以前介紹的相關(guān)知識,如Windows的數(shù)據(jù)類型,特別是和字符和字符串操作相關(guān)的數(shù)據(jù)類型,還有就是Unicode和ASCII在API函數(shù)上的具體體現(xiàn)。
另外,SDK編程交流群已經(jīng)建立,很多朋友踴躍參加,系列文章和群的發(fā)展離不開你們。群號:81543028。
Ok,我們正式開始,我打算從一個簡單的SDK程序開始,別怕,就幾行代碼而已··
/* BY beyondcode */
#include
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MessageBoxA( NULL, "Hello beyondcode", "Title", MB_OK );
return 0;
}
程序你已經(jīng)看到了,這恐怕就是一個最簡單的帶窗口的SDK程序了吧,如果你能寫出代碼行數(shù)比這個還少,又帶窗口顯示字符串的SDK程序,歡迎交流,呵呵,開個玩笑。
程序倒是簡單,可是我還是要問一問,這個程序,你通過觀察我在字符串的處理,還是在API函數(shù)的調(diào)用,還是主函數(shù)的參數(shù)寫法,你能看出什么問題呢?考試大提示就是我全部明確指出是單字節(jié)版本的,WinMain的第三個參數(shù)是LPTSTR類型,調(diào)用的MessageBox是帶A后綴的單字節(jié)版本,字符串常量"Hello beyondcode"和"Title"都沒有使用L前綴。那么第二個問題來了, 如果我告訴你我現(xiàn)在的工程環(huán)境是 使用Unicode字符集 (工程使用的字符集可以在 【項目】->工程屬性 彈出的屬性頁中的 【配置屬性】中的【常規(guī)】左邊的【字符集】中設(shè)置),那么我上面的程序能正常通過編譯嗎? 當然能,因為我已經(jīng)試過了,不信你也可以試試,可是為什么呢? 這是因為我指定的參數(shù)和函數(shù)需要的參數(shù)都是單字節(jié)版本的,也就是說他們相互匹配。要是我這里將MessageBoxA改成MessageBoxW呢? 就會出錯吧,因為MessageBoxW的第二個,和第三個參數(shù)是需要LPCWSTR,通過上一篇學習,我們知道也就是const wchar_t*, 而我給出的兩個字符串常量卻沒有用L前綴.也就是說他們是單字節(jié)的,傳給寬字節(jié)版本的MessageBoxW當然就類型不匹配了啊,所以就通不過編譯了吧。
通過上面的學習,我再出一個問題,如果我此時的工程環(huán)境是使用Unicode字符集,而這里我不用MessageBoxA,也不用MessageBoxW,而是用MessageBox,其他的都不變,結(jié)果會怎么樣呢?好了,單字節(jié)版本的程序,我們已經(jīng)看到了,我們再來看看我們怎么才能把它改成寬字節(jié)版本的呢?
其實需要改的地方不多,也就5處WinMain改成wWinMain, WinMain的第三個參數(shù)改成LPWSTR,MessageBoxA改成W,兩個字符串常量加L就ok了。
/* BY beyondcode */
#include
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd )
{
MessageBoxW( NULL, L"Hello Beyondcode", L"Title", MB_OK );
return 0;
}
如果我想寫一個代碼比較通用的版本,也就是可以不用改動代碼,就能編譯出Unicode和ASCII的兩個版本的程序,我應(yīng)該怎么寫呢? 其實就是我上一篇重點討論的,凡是涉及到字符串的都不明確指出是Unicode還是ASCII版本的,調(diào)用的API函數(shù)凡是涉及到字符串參數(shù)的都不明確指出調(diào)用是A后綴的還是W后綴的函數(shù),而是調(diào)用沒有后綴的函數(shù),如上面的MessageBox,這樣就能寫出代碼比較通用的程序了。那么我們現(xiàn)在來把我們上面的程序改一改,讓它通用
/* BY beyondcode */
#include
#include
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也是一個宏,根據(jù)UNICODE這個宏被設(shè)置與否而被定義成WinMain或wWinMain,和LPTSTR是一樣的,這里還需要注意的是要包含tchar.h這個頭文件,因為_tWinMain和_T()這些宏是被定義在里面的。經(jīng)過上面我們就寫出了第一個SDK的可以編譯出兩個版本的比較通用的程序代碼了。是不是有點成就感了呢。。
下面,我們繼續(xù)在上面的程序中加一些功能,讓它計算1到10的和,然后把結(jié)果顯示給我們看,這個地方,很多SDK初學者就不知所措了,因為一個和是一個整數(shù),怎么顯示這個整數(shù)給我們呢,通過對話框? MessageBox,可是MessageBox顯示的是字符串。而我們這里又不是控制臺程序可以使用printf之類的格式化輸出函數(shù)來輸出數(shù)字,也不能使用cout之類的C++對象來輸出,那我們怎么辦呢? 通過對話框來顯示結(jié)果是不錯的選擇,但是對話框需要的是字符串,那我們就把我們的結(jié)果格式化到一個字符串里面,然后傳送給MessageBox讓它顯示出來。那么就需要用到格式化字符串函數(shù),下面我們就介紹wsprintf這個函數(shù)
#ifdef UNICODE
#define wsprintf wsprintfW
#else
#define wsprintf wsprintfA
#endif // !UNICODE
說它是函數(shù),是不確切的。因為它實際是一個宏,根據(jù)環(huán)境被定義成不同的函數(shù)名wsprintfW或者wsprintfA, 而我們?yōu)榱顺绦虻耐ㄓ眯?,直接使用wsprintf,傳遞的參數(shù)凡是涉及到字符串常量的我們都是用_T()宏,字符串指針的我們都使用LPTSTR和LPCTSTR。
下面我就先貼出添加了功能的程序代碼,然后在做分析,你可以先不看分析,自己看一看代碼,不懂的猜一猜它的意思。
/* BY beyondcode */
#include
#include
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的那部分不用我講了吧,最后的結(jié)果存放在sum這個變量里,我們現(xiàn)在的目的就是要讓它顯示在MessageBox彈出的對話框上面。
首先我們定義一個字符數(shù)組,我使用的是通用類型TCHAR,然后把它全部初始化為0。接著調(diào)用格式化字符函數(shù)wsprintf,它的第一個參數(shù)是LPTSTR類型的,指定經(jīng)過格式化的字符串存放的地方,第二個參數(shù)是指定以什么格式來格式化后面的數(shù)據(jù),這里我們要格式化一個整數(shù),所以指定%d,這個和printf這些函數(shù)是一樣的, 后面的參數(shù)就是我們要格式化的數(shù)據(jù)了。
這里還有一點可能有些新手朋友們不太懂,那就是,wsprintf要求的第一個參數(shù)是LPTSTR,而我傳遞的是一個TCHAR的數(shù)組名,這里我就在啰嗦一次咯,我們假設(shè)我們的環(huán)境是UNICODE的,那么LPTSTR相當于什么類型呢? 上一篇就講過的啊,就是wchar_t* 就是寬字符指針,而我們知道數(shù)組名就是代表這個數(shù)組元素類型的指針,那么這里TCHAR數(shù)組的元素類型就是TCHAR,在Unicode環(huán)境下,TCHAR就是wchar_t也就是說strSum代表的是wchar_t類型的指針,也就是wchar_t*,所以看到了嗎,他們是一樣的類型。
通過上面的wsprintf函數(shù)的調(diào)用strSum這個字符數(shù)組中就包含了計算結(jié)果的字符串表示,然后我們通過MessageBox講這個字符數(shù)組中的內(nèi)容顯示出來。在這里MessageBox的第二個參數(shù)類型是LPCTSTR,也就是const wchar_t*, 而我們上面說過我們的strSum代表的是 wchar_t*,這里同樣可以傳遞給它又是為什么呢?這是因為阿,這里strSum在傳遞給MessageBox的時候就隱式轉(zhuǎn)換成了const wchar_t*了。
有關(guān)格式化字符串的函數(shù)還有如下,詳細用法各位可以查看MSDN,和上面所介紹的都差不多
sprintf 單字節(jié)版本的C/C++庫函數(shù)
swprintf 寬字節(jié)版本的C/C++庫函數(shù)
而我們上面的wsprintf和上面兩個函數(shù)看起來很相似,大家不要搞混淆了啊,wsprintf最前面的w不是代表Wide,寬字節(jié)的意思了,而是Windows的W,代表是windows的API函數(shù)了,其實它是一個宏這在上面已經(jīng)說過了,真正的API函數(shù)其實是wsprintfA和wsprintfW這兩個,在不嚴格的情況下通常我們也說wsprintf是函數(shù),只要大家懂就行了~
OK, 這一篇文章就到這里了,源代碼就這么點,我也不上傳了,各位有興趣自己敲一下。下一篇,我講會就怎么自己創(chuàng)建窗口進行介紹,謝謝大家的支持。
另外,SDK編程交流群已經(jīng)建立,很多朋友踴躍參加,系列文章和群的發(fā)展離不開你們。群號:81543028。
Ok,我們正式開始,我打算從一個簡單的SDK程序開始,別怕,就幾行代碼而已··
/* BY beyondcode */
#include
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MessageBoxA( NULL, "Hello beyondcode", "Title", MB_OK );
return 0;
}
程序你已經(jīng)看到了,這恐怕就是一個最簡單的帶窗口的SDK程序了吧,如果你能寫出代碼行數(shù)比這個還少,又帶窗口顯示字符串的SDK程序,歡迎交流,呵呵,開個玩笑。
程序倒是簡單,可是我還是要問一問,這個程序,你通過觀察我在字符串的處理,還是在API函數(shù)的調(diào)用,還是主函數(shù)的參數(shù)寫法,你能看出什么問題呢?考試大提示就是我全部明確指出是單字節(jié)版本的,WinMain的第三個參數(shù)是LPTSTR類型,調(diào)用的MessageBox是帶A后綴的單字節(jié)版本,字符串常量"Hello beyondcode"和"Title"都沒有使用L前綴。那么第二個問題來了, 如果我告訴你我現(xiàn)在的工程環(huán)境是 使用Unicode字符集 (工程使用的字符集可以在 【項目】->工程屬性 彈出的屬性頁中的 【配置屬性】中的【常規(guī)】左邊的【字符集】中設(shè)置),那么我上面的程序能正常通過編譯嗎? 當然能,因為我已經(jīng)試過了,不信你也可以試試,可是為什么呢? 這是因為我指定的參數(shù)和函數(shù)需要的參數(shù)都是單字節(jié)版本的,也就是說他們相互匹配。要是我這里將MessageBoxA改成MessageBoxW呢? 就會出錯吧,因為MessageBoxW的第二個,和第三個參數(shù)是需要LPCWSTR,通過上一篇學習,我們知道也就是const wchar_t*, 而我給出的兩個字符串常量卻沒有用L前綴.也就是說他們是單字節(jié)的,傳給寬字節(jié)版本的MessageBoxW當然就類型不匹配了啊,所以就通不過編譯了吧。
通過上面的學習,我再出一個問題,如果我此時的工程環(huán)境是使用Unicode字符集,而這里我不用MessageBoxA,也不用MessageBoxW,而是用MessageBox,其他的都不變,結(jié)果會怎么樣呢?好了,單字節(jié)版本的程序,我們已經(jīng)看到了,我們再來看看我們怎么才能把它改成寬字節(jié)版本的呢?
其實需要改的地方不多,也就5處WinMain改成wWinMain, WinMain的第三個參數(shù)改成LPWSTR,MessageBoxA改成W,兩個字符串常量加L就ok了。
/* BY beyondcode */
#include
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd )
{
MessageBoxW( NULL, L"Hello Beyondcode", L"Title", MB_OK );
return 0;
}
如果我想寫一個代碼比較通用的版本,也就是可以不用改動代碼,就能編譯出Unicode和ASCII的兩個版本的程序,我應(yīng)該怎么寫呢? 其實就是我上一篇重點討論的,凡是涉及到字符串的都不明確指出是Unicode還是ASCII版本的,調(diào)用的API函數(shù)凡是涉及到字符串參數(shù)的都不明確指出調(diào)用是A后綴的還是W后綴的函數(shù),而是調(diào)用沒有后綴的函數(shù),如上面的MessageBox,這樣就能寫出代碼比較通用的程序了。那么我們現(xiàn)在來把我們上面的程序改一改,讓它通用
/* BY beyondcode */
#include
#include
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也是一個宏,根據(jù)UNICODE這個宏被設(shè)置與否而被定義成WinMain或wWinMain,和LPTSTR是一樣的,這里還需要注意的是要包含tchar.h這個頭文件,因為_tWinMain和_T()這些宏是被定義在里面的。經(jīng)過上面我們就寫出了第一個SDK的可以編譯出兩個版本的比較通用的程序代碼了。是不是有點成就感了呢。。
下面,我們繼續(xù)在上面的程序中加一些功能,讓它計算1到10的和,然后把結(jié)果顯示給我們看,這個地方,很多SDK初學者就不知所措了,因為一個和是一個整數(shù),怎么顯示這個整數(shù)給我們呢,通過對話框? MessageBox,可是MessageBox顯示的是字符串。而我們這里又不是控制臺程序可以使用printf之類的格式化輸出函數(shù)來輸出數(shù)字,也不能使用cout之類的C++對象來輸出,那我們怎么辦呢? 通過對話框來顯示結(jié)果是不錯的選擇,但是對話框需要的是字符串,那我們就把我們的結(jié)果格式化到一個字符串里面,然后傳送給MessageBox讓它顯示出來。那么就需要用到格式化字符串函數(shù),下面我們就介紹wsprintf這個函數(shù)
#ifdef UNICODE
#define wsprintf wsprintfW
#else
#define wsprintf wsprintfA
#endif // !UNICODE
說它是函數(shù),是不確切的。因為它實際是一個宏,根據(jù)環(huán)境被定義成不同的函數(shù)名wsprintfW或者wsprintfA, 而我們?yōu)榱顺绦虻耐ㄓ眯?,直接使用wsprintf,傳遞的參數(shù)凡是涉及到字符串常量的我們都是用_T()宏,字符串指針的我們都使用LPTSTR和LPCTSTR。
下面我就先貼出添加了功能的程序代碼,然后在做分析,你可以先不看分析,自己看一看代碼,不懂的猜一猜它的意思。
/* BY beyondcode */
#include
#include
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的那部分不用我講了吧,最后的結(jié)果存放在sum這個變量里,我們現(xiàn)在的目的就是要讓它顯示在MessageBox彈出的對話框上面。
首先我們定義一個字符數(shù)組,我使用的是通用類型TCHAR,然后把它全部初始化為0。接著調(diào)用格式化字符函數(shù)wsprintf,它的第一個參數(shù)是LPTSTR類型的,指定經(jīng)過格式化的字符串存放的地方,第二個參數(shù)是指定以什么格式來格式化后面的數(shù)據(jù),這里我們要格式化一個整數(shù),所以指定%d,這個和printf這些函數(shù)是一樣的, 后面的參數(shù)就是我們要格式化的數(shù)據(jù)了。
這里還有一點可能有些新手朋友們不太懂,那就是,wsprintf要求的第一個參數(shù)是LPTSTR,而我傳遞的是一個TCHAR的數(shù)組名,這里我就在啰嗦一次咯,我們假設(shè)我們的環(huán)境是UNICODE的,那么LPTSTR相當于什么類型呢? 上一篇就講過的啊,就是wchar_t* 就是寬字符指針,而我們知道數(shù)組名就是代表這個數(shù)組元素類型的指針,那么這里TCHAR數(shù)組的元素類型就是TCHAR,在Unicode環(huán)境下,TCHAR就是wchar_t也就是說strSum代表的是wchar_t類型的指針,也就是wchar_t*,所以看到了嗎,他們是一樣的類型。
通過上面的wsprintf函數(shù)的調(diào)用strSum這個字符數(shù)組中就包含了計算結(jié)果的字符串表示,然后我們通過MessageBox講這個字符數(shù)組中的內(nèi)容顯示出來。在這里MessageBox的第二個參數(shù)類型是LPCTSTR,也就是const wchar_t*, 而我們上面說過我們的strSum代表的是 wchar_t*,這里同樣可以傳遞給它又是為什么呢?這是因為阿,這里strSum在傳遞給MessageBox的時候就隱式轉(zhuǎn)換成了const wchar_t*了。
有關(guān)格式化字符串的函數(shù)還有如下,詳細用法各位可以查看MSDN,和上面所介紹的都差不多
sprintf 單字節(jié)版本的C/C++庫函數(shù)
swprintf 寬字節(jié)版本的C/C++庫函數(shù)
而我們上面的wsprintf和上面兩個函數(shù)看起來很相似,大家不要搞混淆了啊,wsprintf最前面的w不是代表Wide,寬字節(jié)的意思了,而是Windows的W,代表是windows的API函數(shù)了,其實它是一個宏這在上面已經(jīng)說過了,真正的API函數(shù)其實是wsprintfA和wsprintfW這兩個,在不嚴格的情況下通常我們也說wsprintf是函數(shù),只要大家懂就行了~
OK, 這一篇文章就到這里了,源代碼就這么點,我也不上傳了,各位有興趣自己敲一下。下一篇,我講會就怎么自己創(chuàng)建窗口進行介紹,謝謝大家的支持。

