探索MFC读书笔记——WinMain函数的优化

来源:互联网 发布:中国红十字会知乎 编辑:程序博客网 时间:2024/04/30 16:42

一、WinMain函数的优化

1、windows程序两个非常重要的函数:WinMain函数和WinProc函数。

     WinMain函数作为Windows程序的入口点函数,主要完成以下功能:设计窗口类别、注册窗口类、创建窗口、显示窗口、更新窗口、消息循环。

     WinProc是回调函数,由开发者自己实现,它是程序运行的中心,是窗口的生命中枢,主要实现区分不同消息,做出不同回应。

2、对WinMain函数的优化

     int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,  LPSTR lpCmdline, int nCmdShow)

    {

             if(!hPrevInstance)

             {

                      if(!InitApplication(hInstance))

                            return false;

             }

            if(!InitInstance(hInstance, nCmdShow))

                   return false;

           ...

    }

   //---------------------------------------------------------------------------------------

   BOOL InitApplication(HINSTANCE hInstance)

   {

           WNDCLASS  wc;

           ....//填充窗口类的成员

          return(RegisterClass(&wc));

   }

   //----------------------------------------------------------------------------------------

   BOOL InitInstance(HINSTANCE int nCmdShow)

   {

          _hwnd = CreateWindow(...);

          ...

   }

3、实际上MFC就是这样做的,为什么要用这种方式?

        在Windows3.x时代,所有进程共用同一个地址空间,同一个窗口类别只需要注册一次,即可以供同一个程序后续的每一个执行实例(Instance)使用,WinMain的传人参数hPreInstance可以用来判断本次进程是否是该程序的第一个运行实例(hPreInstance是前一个运行实例的句柄,如果没有传入0),所以InitApplication函数只能被应用程序的第一个实例调用,在其中完成窗口类的设计和注册是非常合适的。

       但是,在WindowsNT和Windows95之后,情况发生了变化,不同进程不再共用地址空间,每一个进程都有独立的地址空间,共享窗口类别已经不可能了,因此注册窗口类在每一个实例中都要执行,值得注意的是,Win32系统令hPreInstance永远为0,所以以上设计并不会带来问题,即符合了新环境的要求,又兼顾到旧代码的兼容,所以一直延用至今。

4、注:MFC将上述的InitApplication函数和InitInstance函数封装为CWinApp类的两个虚拟成员方法。

 

二、消息循环的优化

1、消息循环的原型

    while(GetMessage(&msg)) {

          TranslateMessage(&msg);//转换键盘消息

          DispatchMessage(&msg);//分派消息

    }

       DispatchMessage函数,没有指定函数名称,就把消息送给了窗口过程函数,它是怎么做到的?

       因为:消息发生之时,操作系统已经根据当时状态,为消息标明了所属的窗口,而且窗口所属的窗口类别又已经标明了窗口函数(在设计窗口类别的时候),所以DispatchMessage再经过USER模块的协助,就可把消息送到窗口函数手中。

2、GetMessage的由来,为什么消息循环要使用GetMessage?

      这里先讲一下,非抢占式多任务,在以前的Windows系统中,多任务方式是“协作式多任务”,意思是说,一个任务得到CPU时间后,除非它自己放弃使用CPU,否则将完全霸占CPU,所以任务之间需要协作——使用一段时间的CPU,然后程序主动放弃使用,供其他程序使用(其他程序也如此),这样才能保证系统的正常运行。

      而消息循环中的GetMessage就是这种“非抢占式多任务”的实现关键,应用程序藉由此动作,提供了释放控制权的机会:如果消息队列中没有我的消息,我就把机会让给别人。许多资料里说GetMessage函数的特点是:”直到从消息队列中取得一个有效消息,才返回“,就是这个原因,当消息队列中没有该窗口的消息时,实际上是系统让该程序实例挂起,运行其他程序去了,等到队列中再次出现该窗口的消息并且其他程序释放了控制权,系统再把控制权交给该程序,让它继续运行。

     然而,WindowsNT以后,Windows系统具备强制性(preemptive)多任务能力,不再非靠GetMessage释放CPU控制权不可,但是,因为应用程序仍然需要靠消息驱动,还要抓取消息,因此程序写法仍不变。

3、空闲时间的利用

       所谓空闲时间(idle time),是指”系统中没有任何消息等待处理“的时间,实际上计算机的空闲时间是非常多的。”背景工作“如屏保程序,最适合在空闲时间完成,传统的SDK程序如果要处理空闲时间,可以以以下的循环代替WinMain的传统消息循环。

       while(true)

       {

            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

            {

                    if(msg.message == WM_QUIT)

                          break;

                   TranslateMssage(&msg);

                   DispatchMessage(&msg);

            }

            else

            {

                   OnIdle();//空闲时间处理函数

            }

       }

4、PeekMessage与GetMessage的比较

      它们都是从消息队列中抓取消息,如果抓不到,程序的主执行线程(primary thread,是一个UI执行线程)会被操作系统”虚悬住“,当操作系统再次回来照顾此一执行线程时,而发现消息队列中仍然为空,这时候两个API函数的行为就有不同了:

       GetMessage会过门不入,于是操作系统再去照顾其他线程;

       PeekMessage会取回控制权,使程序得以执行一段时间,于是上述消息循环进入都OnIdle函数中执行。

 

0 0