作者:刘铁猛
日期:2005-12-13
关键字: Win32 API 入门
声明:如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向[email][email protected][/email]发一个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
int main(int argc, char* argv[])
{
     return 0;
}
解说:添加了#include这句预编译指令,注意:这是一句指令,而不是语句,所以没有分号结尾。
[五级]
#include
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
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

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

[
二级]
#include
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
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…
后记:本来后面的都已经写出来了,不过我打算编进我的书里去.
声明:如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向[email][email protected][/email]发一个Mail,我很想知道我的文章都去哪里了,谢谢:)

职场 休闲 C++语言

0

收藏

上一篇:C++创始人大名的发音 下一篇:Windows数据类型探幽——千...
一个Win32程序的进化_第2张图片
水之真谛

172篇文章,279W+人气,38粉丝

关注
一个Win32程序的进化_第3张图片

Ctrl+Enter 发布

发布

取消

推荐专栏更多

一个Win32程序的进化_第4张图片
VMware vSAN中小企业应用案例

掌握VMware超融合技术

共41章 | 王春海

¥51.00 400人订阅
订   阅
一个Win32程序的进化_第5张图片
带你玩转高可用

前百度高级工程师的架构高可用实战

共15章 | 曹林华

¥51.00 495人订阅
订   阅
一个Win32程序的进化_第6张图片
基于Kubernetes企业级容器云平台落地与实践

容器私有云平台实践之路

共15章 | 李振良OK

¥51.00 661人订阅
订   阅
一个Win32程序的进化_第7张图片
网工2.0晋级攻略 ——零基础入门Python/Ansible

网络工程师2.0进阶指南

共30章 | 姜汁啤酒

¥51.00 1862人订阅
订   阅
一个Win32程序的进化_第8张图片
负载均衡高手炼成记

高并发架构之路

共15章 | sery

¥51.00 550人订阅
订   阅

猜你喜欢

我的友情链接 深入浅出WPF——如何实现类的Property与标签的Attribute映射 我们不得不面对的中年职场危机 Java线程:线程的调度-休眠 用光影魔术手制作一寸照片(8张一寸) 职场终极密籍--记我的职业生涯 我的IT职场生涯: 毕业4年,月薪过万 Linux关闭休眠和屏保模式 年薪从0到10万-我的IT职场经验总结 Windows7删除休眠文件hiberfil.sys节省大量C盘空间 致IT同仁 — IT人士常犯的17个职场错误 “跳槽加薪”现象,无奈的职场规则 实例讲解vsan分布式架构虚拟磁盘文件丢失的解决方法 exchange灾难恢复篇——环境准备 在Outlook中通讯簿中隐藏指定的列 个别用户outlook中无法显示对话历史记录 混合部署环境本地邮件被云端传输规则当成外部邮件处理 利用Exchange Server CVE-2018-8581+HASH传递玩爆AD Exchange 2013/2016监控邮箱的一些介绍 SFB 项目经验-49-耗时5天修复某上市企业2000人邮箱数据库实录(五一巨献)
一个Win32程序的进化_第9张图片
一个Win32程序的进化_第10张图片

扫一扫,领取大礼包

0

分享
一个Win32程序的进化_第11张图片
关注
水之真谛
一个Win32程序的进化_第12张图片