大家好,還是我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的第三個 參數是LPTSTR類型,調用的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, 這 一篇文章就到這裡了,源代碼就這麼點,我也不上傳了,各位有興趣自己敲一下。下一篇,我講會就怎 麼自己創建窗口進行介紹,謝謝大家的支持。