API入门系列之四 -一个相当简单的SDK程序
原创文章,转载请注明作者及出处。
首发
http://blog.csdn.net/beyondcode
http://www.cnblogs.com/beyond-code/
大家好,还是我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, 这一篇文章就到这里了,源代码就这么点,我也不上传了,各位有兴趣自己敲一下。下一篇,我讲会就怎么自己创建窗口进行介绍,谢谢大家的支持。
请记住我 beyondcode 。
SDK编程群已经建立,欢迎有共同兴趣爱好的朋友加入。群号:81543028