一个Win32程序的进化

作者:刘铁猛
日期:2005-12-13
关键字: Win32 API 入门
声明:如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向[email protected]发一个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 函数被封装在称为“类”的模块里,形成了 MFC Microsoft )及 OWL Borland )等类库,并以 COM 组件的形式安装在用户的计算机里供用户和开发人员使用。在 COM 的基础上又发展出了 COM+ ,其本质仍然是对 Win32 函数的封装。 COM+ 之后就是 .NET Framework Assembly 了,它是迄今为止对 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 作为扩展名,加不加 int return 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;
}
解说:用同样的算法分别交换了 a b x y 的值。
[ 六级 ]
#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 __stdcall Microsoft 公司对 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 按钮,对应的值是 0 MB_YESNO 就是有 Yes No 两个按钮,对应的值是 4 ……总之,用记宏比你记没有形象的整数值要方便多了。
 
 
 
TO BE CONTINUE…
后记:本来后面的都已经写出来了,不过我打算编进我的书里去.
声明:如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向[email protected]发一个Mail,我很想知道我的文章都去哪里了,谢谢:)

本文出自 “上善若水 润物无声” 博客,转载请与作者联系!

分享至
一键收藏,随时查看,分享好友!
0人
了这篇文章
类别: C++语言┆阅读( 0)┆评论( 0) ┆ 返回博主首页┆ 返回博客首页
上一篇 五笔王朝的终结 下一篇 Windows数据类型探幽――千回百转你是谁?(1)

职位推荐

  • Java工程师
  • Java高级工程师
  • JAVA工程师
  • JAVA工程师
  • web前端
  • 量化交易系统IT软件开发工程师
  • 前端开发工程师
  • C#高级工程师
  • 高级PHP开发工程师
  • web前端开发工程师

文章评论

 
 

发表评论            

昵  称:
登录  快速注册
验证码:

点击图片可刷新验证码请点击后输入验证码博客过2级,无需填写验证码

内  容:

同时赞一个

你可能感兴趣的:(职场,休闲)