zoukankan      html  css  js  c++  java
  • 一个Win32程序的进化------转载

    一个Win32程序的进化

    一.为什么要学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 ", 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 ", 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 ", 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……总之,用记宏比你记没有形象的整数值要方便多了。
     
     
     
     
     
     
     
     
     
     
     
    本文转自 水之真谛 51CTO博客,原文链接:http://blog.51cto.com/liutiemeng/18969,如需转载请自行联系原作者
  • 相关阅读:
    C#中使用Oracle存储过程返回结果集
    微信公众平台开发教程(九)微信公众平台通用开发框架
    微信公众平台开发教程(八)Session处理
    微信公众平台开发教程(七)安全策略
    微信公众平台开发教程(六)获取个性二维码
    微信公众平台开发教程(五)自定义菜单(含实例源码)
    微信公众平台开发教程(三) 基础框架搭建
    微信公众平台开发教程(一) 微信公众账号注册流程
    当"唐僧"没那么容易
    C#编程总结(六)异步编程
  • 原文地址:https://www.cnblogs.com/bedfly/p/12147448.html
Copyright © 2011-2022 走看看