API入門系列之一個(gè)相當(dāng)簡(jiǎn)單的SDK程序

字號(hào):

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