一个Win32程序的进化

来源:互联网 发布:云计算的技术构成 编辑:程序博客网 时间:2024/04/30 23:37
2005年12月13日 18:01:00

一个Win32程序的进化

作者:刘铁猛

日期:2005-12-13

关键字: Win32 API 入门

声明:如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向bladey@tom.com发一个Mail,我很想知道我的文章都去哪里了.谢谢.


追忆:亲爱的同胞们,今天是12月13日,是"南京大屠杀"68周年记念日.让我们永远铭记国耻,珍惜我们现在的和平生活.在您阅读这篇文章前,我真诚地请您关上屏幕,为我们死难的同胞和牺牲的烈士们默哀1分钟.为了让我们的祖国更加强胜,就让我们从自己做起--珍惜我们的工作机会,珍惜我们的学习时间,做一个够格的中国人!


乱弹:

在学习的时候,人们对结构陌生而且复杂的东西总是心存恐惧。比如学习《植物学》,如果一上来就让你研究被子植物雌花的构造,那你肯定当场晕菜;又或如学习《有机化学》,不由分说先让你分析一下"高级脂肪酸钠"分子中的共价键/离子键结构,我保证你下半辈子都不再打算学化学。然而,雌花结构之美,大分子结构之玄妙是我们无法想象的--在电子显微镜下,一个一个细胞如冰雕般晶莹,如玉石般温润,如晚霞般五彩缤纷;在原子级别上,大分子中的电子云会在不同的能量级下显现出各种奇幻的形状来..为什么这些美轮美奂的知识却少人有问津呢?原因就在于他们的复杂,复杂就代表着难度。其实,难度并不是问题,问题在学学习的方法,如果方法不正确那么就会"难上加难"。真不知道有多少科学天才正是因为学习方法不正确,而在一次一次失败的打击下丧失了热情与爱好,与攀登科学的最高峰、摘取科学皇冠上的明珠失之交臂。

那么,学习复杂知识最好的办法是什么呢?其实答案很简单:从它的发展/演变历史入手开始学习。想了解被子植物雌花的结构,那就要从单细胞植物(比如蓝藻)学起,然后是多细胞植物,然后是细胞的分化,细胞分化为器官,器官中有"叶",叶上长了"孢子"就有了裸子植物,叶子卷曲起来保护这些孢子就成了"花"的雏形,花(为了吸引虫虫们来Happy)形成了之后孢子演变成种子--被子植物诞生了。研究有机大分子也一样,必须从最最简单的有机分子--甲烷分子--的一个C和四个H开始学习,然后你会看到乙烷..然后去掉几个H,再加上O,你就得到酒精(乙醇)了,有意思吧!不过,酒,并不是这么造滴~~~~酒精是可以燃烧滴,燃烧是剧烈的氧化反应(比燃烧更剧烈的是爆炸),我们身体里的有机燃料(比如血糖和脂肪)也会氧化,不过这种氧化非常缓慢,既保证了我们可以从中获得足够的热量维持生命、运动和新陈代谢,又保证了我们不至于成为烤全羊或者"肉弹"..血糖氧化不完全就可能产生乳酸,存留在你的肌肉里就会让你"酸疼酸疼"的,直到它们被你的血液从肌肉中洗干净..在这些有机小分子上加上一个一个的C原子,就会形成有机大分子,比如脂肪酸分子。脂肪酸分子大了,就称之为"高级脂肪酸分子",高级脂肪酸分子与钠盐反应就生成了高级脂肪酸钠,有了高级脂肪酸钠,我们就造出了--肥皂,没错,是肥皂。所以,如果你看到肥皂盒上印有"使用高级脂肪酸钠制造"一定要心知肚明"此高级非彼高级",这个"高级"是指C链的长度,跟肥皂的质量没关系,只要是肥皂就肯定是用"高级脂肪酸钠"制造的,否则的话那是洗衣粉。

推而广之,要想学习微积分,你必须从1+1开始;要想研究社会现状,你必须从"夏商与西周,东周分两段.."开始;要想论证"奇点"的存在或者"时间旅行"的可能性,你必须从v=s/t开始..摸清一个事物的来龙去脉才能领略它的复杂之美。从简单到复杂,每个环节都不能缺失,大概考古学、天文学、地质学的魅力就在于此吧。

正文:

一.为什么要学Win32

要回答这个问题,我们就要先搞清楚我们是站在Windows程序开发历史的哪个阶段。当红的C#.NET平台技术是建立在"程序集"(Assembly)模块上的,这是一种比COM更加高级的封装形式,据说一开始叫"COM3"来着,可能是Bill不太乐意他的他的Windows老在COM上打转转,于是就叫".NET Framework"了吧。在Assembly之前的封装形式是"COM+"(COM2乎?),在COM+之前自然就是COM封装了,COM前是OLE封装,OLE之前..呃..就没有封装了,只有赤裸裸的Win16/Win32函数可以调用。前面提到的所谓"封装"就是人们发现有些Win32函数总是一起使用或总是按一定的结构使用(称之为"复用"),于是就把它们"攒"起来,形成一个模块。拜C++语言的OO能力所赐,C语言形式的Win32函数被封装在称为"类"的模块里,形成了MFCMicrosoft)及OWLBorland)等类库,并以COM组件的形式安装在用户的计算机里供用户和开发人员使用。在COM的基础上又发展出了COM+,其本质仍然是对Win32函数的封装。COM+之后就是.NET FrameworkAssembly了,它是迄今为止对Win32函数最高级别的封装了..你基本已经看不到Win32函数的影子了,这就是为什么我们说.NET/C#开发不是底层开发的原因。

OK,我们暂且称基于.NET Framework的程序开发为"第三代Windows程序开发",基于COM的程序开发(如VC/VB)为"第二代Windows程序开发",基于Win32函数的程序开发为"第一代Windows程序开发"。

由此可见,无论是想掌握COM程序设计还是.NET Framework程序设计的真谛,你迟早是要回来学《Windows程序考古学》的。因为,有些问题由于封装的太厚了,你可能找不到答案--你只可能在Win32级别上去找答案。只有透彻地学习了Win32程序设计之后,你方能体验到脚踏实地、豁然开朗的感觉,放能体验那种恍然间的开悟。

Now, let's go.去剖析一下一个最简单的Win32程序。

二.热身运动

一上来就直接看Win32程序,我怕会吓到你,所以我们先从一个命令行程序开始。以这个程序来演示一下一个Program是如何进化的。

[一级]

main()
{
}

解说:这恐怕是最简单的C语言程序了--只有一个main入口点函数,当然,它什么也不做。

[二级A]

void main()
{
}

解说:在[一级]的基础上,明确地指出了主函数没有返回值。没有返回值对程序的运行结果不好把握,所以这一支进化到此为止。

[二级B]

int main()
{
return 0;
}

解说:其实这是[一级]的完整形式,就算你不写,计算机也会隐式为你添加int返回类型和在执行完之后return一个零。注意哦!不写返回值类型的C语言函数默认是返回int型值,而不是无返回值的void型函数。详细信息你可以去ISO-C90/C99里去查。不过值得注意的是:C++语言不支持默认的int型返回值和return 0,这就意味着,如果你的源文件是以.c作为扩展名,加不加intreturn 0都没有关系,若是以.cpp为扩展名,你将有可能收到一个warning,不过,程序应该能继续运行。

[X]

int main(int argc, char* argv[])
{
return 0;
}

解说:在[二级]的基础上添加了main函数的参数。一个非常重要的而且你必须要知道的一点就是:main入口点函数的参数不像程序内成员函数的参数,成员函数的参数是由设计程序的程序员"手动"传递进去的,也就是程序员调用函数则程序员负责向函数传递参数。而main函数不是由程序员调用的,而是程序编译完成并交付用户后,用户通过操作系统来调用的(比如双击程序的图标或者在命令行里输入程序的名字),因此,main函数的参数不是程序员在设计期能传递的,只能在main函数被系统调用时,由系统传递给它。简言之就是:谁调用,谁传参

[四级]

#include >stdio.h<
int main(int argc, char* argv[])
{
return 0;
}

解说:添加了#include>stdio.h<这句预编译指令,注意:这是一句指令,而不是语句,所以没有分号结尾。

[五级]

#include >stdio.h<
int main(int argc, char* argv[])
{
//声明了一些变量
int a=100,b=200,x=300,y=400,temp=0;

//交换a,b的值
temp=a;
a=b;
b=temp;
//交换x,y的值
temp=x;
x=y;
y=temp;

//输出结果
printf("a=%d,b=%d,x=%d,y=%d/n", a,b,x,y);

return 0;
}
解说:用同样的算法分别交换了abxy的值。

[六级]

#include >stdio.h<
void Exchange(int* arg1, int* arg2)
{
int temp=0;
temp = *arg1;
*arg1 = *arg2;
*arg2=temp;
}

int main(int argc, char* argv[])
{
//声明了一些变量
int a=100,b=200,x=300,y=400;

//用函数交换值
Exchange(&a,&b);
Exchange(&x,&y);

//输出结果
printf("a=%d,b=%d,x=%d,y=%d/n", a,b,x,y);

return 0;
}
解说:有操作复用的地方,就会有函数的出现


[
七级]

#include >stdio.h<

//前置函数声明
void Exchange(int*, int*);

int main(int argc, char* argv[])
{
//声明了一些变量
int a=100,b=200,x=300,y=400;

//用函数交换值
Exchange(&a,&b);
Exchange(&x,&y);

//输出结果
printf("a=%d,b=%d,x=%d,y=%d/n", a,b,x,y);
return 0;
}

//函数实现
void Exchange(int* arg1, int* arg2)
{
int temp=0;
temp = *arg1;
*arg1 = *arg2;
*arg2=temp;
}

解说:为了避免过多的子函数出现在main前而将main"埋没",采取了函数的"前置声明"和"后置实现"。特别注意:前置声明函数的时候,甚至可以只给出参数的类型而不必给出参数的名称

[总结]

至此,一个结构美观,功能实用的小程序就进化完成了--从仅仅8个字符进化到十几行。之所以给大家展示这样一个程序,就是因为我们下面要看的Win32程序虽然复杂,但也是这样一点一点进化来的。

三.正式开始

热身运动结束之后,我们就要正式剖析一个Win32的程序了。Win32的程序远比命令行程序复杂,而且变量名和函数名也要长得多,入口点函数的参数也比较多也比较复杂..呃..入门的门槛比较高,做好心理准备哦!

[一级]:一个什么都不干的Win32程序

#include >windows.h<
WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
}

解说:比起命令行下那个只有8个字符的最简单程序来,Win32最简单的程序也足够复杂了。首先,#include>windows.h<指令是绝不能缺少的(就算以后你在程序中没有直接include这个windows.h文件,那么也一定是通过别的.h文件间接地包含了它),不要指望编译器会自动为你添加这一句。其次,入口点函数的名称也不再是main而是WinMain,而且WinMain也不像main那样能够支持有参数和无参数两种形式,WinMain函数只有一种形式,那就是接收4个参数(参数的数据类型怪怪的,如果想知道具体是什么类型,可以参见本人的另一篇掘作《Windows数据类型探幽--千回百转你是谁?》)。目前,最重要的是你要盯紧那第一个参数,也就是HINSTANCE类型的hInstance变量。


[
二级]

#include >windows.h<
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
return 0 ;
}

解说:在[一级]的基础上,除了像main一样添加了int返回值类型和return 0之外,还添加了一个WINAPI修饰符。这个宏(如果还不了解什么是"宏",请学习C/C++语言基础知识)的实际值是__stdcall__stdcallMicrosoft公司对C/C++语言扩充时添加的Keywork,这个Keywork是专门用于呼叫Win32 API时使用的(所以宏的名字叫"WINAPI"),而且在出现这个Keywork的时候,被修饰函数的参数传递顺序是从右向左,被修饰函数被调用完后,还要负责清理自己所占用过的栈内存--这些不理解不要紧,并不影响我们的入门学习。


[X
]

#include >windows.h<
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
MessageBox(NULL, TEXT("Hello, Win32!"), TEXT("问候"), MB_OK) ;
MessageBox(NULL, L"Hello, Win32!",L"问候",0);

return 0 ;
}

解说:这次是添加了核心代码(上下两句其实是完全一样的,只是上面一句使用了预先定义的宏方便了记忆,而下面一句是"原始面貌")。MessageBox函数会让程序弹出一个消息框,第一个参数是指出哪个窗体拥有这个消息框,我们的程序还没有窗体,所以只能用一个NULL值,接下来的两个不说你也应该看出来,一个是内容,一个是标题。不过要注意,由于是32位程序设计,所以要用L(即TEXT()宏的原形)来把16位字符串转换成32位字符串。最后一个参数是消息框的按钮数量--MB_OK就是只有一个OK按钮,对应的值是0MB_YESNO就是有YesNo两个按钮,对应的值是4..总之,用记宏比你记没有形象的整数值要方便多了。

TO BE CONTINUE.

后记:本来后面的都已经写出来了,不过我打算编进我的书里去.

声明:如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向bladey@tom.com发一个Mail,我很想知道我的文章都去哪里了,谢谢:)



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=551511


原创粉丝点击