友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

深入浅出MFC第2版(PDF格式)-第9部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



                   #0020          MENUITEM SEPARATOR 

                   #0021          MENUITEM 〃E&xit〃;             IDM_EXIT 

                   #0022      END 

                   #0023      POPUP 〃&Edit〃 

                   #0024      BEGIN 

                   #0025          MENUITEM 〃&UndotCtrl+Z〃;     IDM_UNDO; GRAYED 

                   #0026          MENUITEM SEPARATOR 

                   #0027          MENUITEM 〃Cu&ttCtrl+X〃;      IDM_CUT; GRAYED 

                   #0028          MENUITEM 〃&CopytCtrl+C〃;     IDM_COPY; GRAYED 

                   #0029          MENUITEM 〃&PastetCtrl+V〃;    IDM_PASTE; GRAYED 

                   #0030          MENUITEM 〃Paste &Link〃;       IDM_LINK; GRAYED 

                   #0031          MENUITEM SEPARATOR 

                   #0032          MENUITEM 〃Lin&ks。。。〃;         IDM_LINKS; GRAYED 

                   #0033      END 

                   #0034      POPUP 〃&Help〃 

                   #0035      BEGIN 

                   #0036          MENUITEM 〃&Contents〃;               IDM_HELPCONTENTS; GRAYED 

                   #0037          MENUITEM 〃&Search for Help On。。。〃;  IDM_HELPSEARCH; GRAYED 

                   #0038          MENUITEM 〃&How to Use Help〃;        IDM_HELPHELP; GRAYED 

                   #0039          MENUITEM SEPARATOR 

                   #0040          MENUITEM 〃&About Generic。。。〃;       IDM_ABOUT 

                   #0041      END 



14 


…………………………………………………………Page 77……………………………………………………………

   #0042 END 



   #0043 



   #0044 AboutBox DIALOG DISCARDABLE 22; 17; 144; 75 



   #0045 STYLE DS_MODALFRAME  | WS_CAPTION  | WS_SYSMENU 



   #0046 CAPTION 〃About Generic〃 



   #0047 BEGIN 



   #0048     CTEXT 〃Windows 95〃; …1;0; 5;144;8 



   #0049     CTEXT 〃Generic Application〃;…1;0;14;144;8 



   #0050     CTEXT 〃Version 1。0〃; …1;0;34;144;8 



   #0051     DEFPUSHBUTTON 〃OK〃; IDOK;53;59;32;14;WS_GROUP 



   #0052 END 



程序进入点 WinMain 



  main       C  

       是一般  程序的进入点: 



         int main (int argc; char *argv ' '; char *envp ' '); 



         { 



         。。。 



         } 



   WinMain 则是Windows 程序的进入点: 



        int CALLBACK WinMain (HINSTANCE hInstance; HINSTANCE hPrevInstance; 



                              LPSTR lpCmdLine; int nCmdShow) 



         { 



         。。。 



         } 



        //   Win32   CALLBACK        __stdcall 

           在       中          被定义为             ,是一种函数调用习惯,关系到 



        // 参数挤压到堆栈的次序,以及处理堆栈的责任归属。其它的函数调用习惯还有 



        // _pascal   _cdecl 

                   和 



   当Windows  的「外壳」(shell,例如Windows 3。1 的程序管理员或Windows 95 的文件 



   总管)侦测到使用者意欲执行一个Windows 程序,于是调用加载器把该程序加载,然后 



  调用C startup code,后者再调用WinMain ,开始执进程序。WinMain  的四个参数由操作 



   系统传递进来。 



                                                                                    15 


…………………………………………………………Page 78……………………………………………………………

窗口类别之注册与窗口之诞生 



       一开始,Windows 程序必须做些初始化工作,为的是产生应用程序的工作舞台:窗口。 



      这没有什么困难,因为API 函数Create Window 完全包办了整个巨大的工程。但是窗口 



      产生之前,其属性必须先设定好。所谓属性包括窗口的「外貌」和「行为」,一个窗口 



       的边框、颜色、标题、位置等等就是其外貌,而窗口接收消息后的反应就是其行为(具 



                                                                                       API         RegisterClass 

       体地说就是指窗口函数本身)。程序必须在产生窗口之前先利用                                                          函数 



       设定属性(我们称此动作为注册窗口类别)。RegisterClass  需要一个大型数据结构 



       WNDCLASS 做为参数,Create Window 则另需要11 个参数。 



      WNDCLASS  wc;                            GENERIC。C 

                                                 GENERIC。C             LRESULT CALLBACK WndProc(HWND hWnd;      



      wc。style         = CS_HREDRAW | CS_VREDRAW;  窗口函数                                                                      UINT message; 

      wc。lpfnWndProc   = (WNDPROC)WndProc;                                                                                   WPARAM wParam;  

      wc。cbClsExtra    = 0;                                                                                                  LPARAM lParam) 

      wc。cbWndExtra    = 0;                                            { 

      wc。hInstance     = hInstance;                                    。。。                                       GENERIC。C 

      wc。hIcon         = LoadIcon(hInstance; 〃jjhouricon 〃);           }                                           GENERIC。C 



      wc。hCursor       = LoadCursor(NULL; IDC_ARROW); 

      wc。hbrBackground = GetStockObject(WHITE_BRUSH);              窗口图标(icon) 

      wc。lpszMenuName  = 〃GenericMenu〃; 

                                                                          jjhouricon ICON    DISCARDABLE     〃jjhour。ico〃 

      wc。lpszClassName = 〃Generic〃;                菜单(menu)               GenericMenu MENU DISCARDABLE 



                                                                          BEGIN 

      RegisterClass(&wc);                                                    POPUP 〃&File〃                      GENERIC。RC 

                                                                                                                 GENERIC。RC 

                                        窗口类別名称                               。。。 

      HWND      hWnd; 

                                                                             POPUP 〃&Edit〃 

                                                                                                  菜单(menu) 

      hWnd = CreateWindow(                                                   。。。 

                          〃Generic〃;                                         POPUP 〃&Help〃} 

                                                                             。。。 

                          〃                         〃; 

                    Generic Sample Application                            END 

                          WS_OVERLAPPEDWINDOW;        窗口标睿╟aption) 

                          CW_USEDEFAULT;  // left 

                          CW_USEDEFAULT;  // top 

                          CW_USEDEFAULT;  // width 

                          CW_USEDEFAULT;  // height 

                          NULL; 

                          NULL; 

                                                                                                                  height 

                          hInstance; 

                          NULL 

                         ); 



                                                                                       width 



                             图  1…3    RegisterClass 与 CreateWindow 



                                                                                                                              16 


…………………………………………………………Page 79……………………………………………………………

    图                                                       wc lpf nWndProc  

  从   1…3 可以清楚看出一个窗口类别牵扯的范围多么广泛,其中                            。            所指 



  定的函数就是窗口的行为中枢,也就是所谓的窗口函数。注意,Create Window 只产生窗 



  口,并不显示窗口,所以稍后我们必须再利用ShowWindow 将之显示在屏幕上。又,我 



  们希望先传送个WM_PAINT 给窗口, 以驱动窗口的绘图动作, 所以调用 

                                                              



  Updat e Window 。消息传递的观念暂且不表,稍后再提。 



  请注意,在Generic 程序中,RegisterClass 被我包装在InitAppl ication 函数之中, 



  Create Window 则被我包装在InitInstance  函数之中。这种安排虽非强制,却很普遍: 



  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 hInstance; int nCmdShow) 



  { 



  _hWnd = CreateWindow (。。。); 



  。。。 



  } 



  两个函数(InitAppl ication 和InitInstance )的名称别具意义: 



    Windows 3。x  

■ 在              时代,窗口类别只需注册一次,即可供同一程序的后续每一个 



            instance 

  执行实例 (           )使用(之所以能够如此,是因为所有进程共在一个地址空 



                     RegisterClass  

  间中),所以我们把                      这个动作安排在「只有第一个执行个体才会 



                                                                                           17 


…………………………………………………………Page 80……………………………………………………………

        进入」的InitAppl ication 函数中。至于此一进程是否是某个程序的第一个执行 



        实例,可由WinMain  的参数hPrevInstance 判断之;其值由系统传入。 



       ■ 产生窗口, 是每一个执行实例( instance  ) 都得做的动作, 所以我们把 

                                                         



           Create Window 这个动作安排在「任何执行实例都会进入」的InitInstance  函数中。 



             Windows NT  Windows 95             Win32  

    以上情况在              和          中略有变化。由于           程序的每一个执行实 



       instance                                              Win32  

    例 (      )有自己的地址空间,共享同一窗口类别已不可能。但是由于                          系统令 



    hPrevInstance   0                 RegisterClass  Create Window  

               永远为 ,所以我们仍然得以把                   和            按旧习惯 



    安排。既符合了新环境的要求,又兼顾到了旧源代码的兼容。 



    InitAppl ication 和InitInstance  只不过是两个自定函数,为什么我要对此振振有词呢?原 



    因是MFC 把这两个函数包装成CWinApp  的两个虚拟成员函数。第6章「MFC 程序的 



    生与死」对此有详细解释。 



消息循环 



    初始化工作完成后,WinMain 进入所谓的消息循环: 



       while  (GetMessage (&msg;。。。)) { 



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



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



        } 



    其中的TranslateMessage 是为了将键盘消息转化,Dispat chMessage 会将消息传给窗口函 



    数去处理。没有指定函数名称,却可以将消息传送过去,岂不是很玄?这是因为消息发 



    生之时,操作系统已根据当时状态,为它标明了所属窗口,而窗口所属之窗口类别又已 



                             wc lpf nWndProc             Dispat chMessage 

                               。                   ) 

    经明白标示了窗口函数(也就是                       所指定的函数 ,所以 



                      图   所示  Dispa tchMessage  USER  

    自有脉络可寻。请注意  1…2           ,             经过       模块的协助,才把消 



    息交到窗口函数手中。 



    消息循环中的GetMessage 是Windows 3。x 非强制性(non…preemptive )多任务的关键。应 



    用程序藉由此动作,提供了释放控制权的机会:如果消息队列上没有属于我的消息,我 



    就把机会让给别人。透过程序之间彼此协调让步的方式,达到多任务能力。Windows 95 和 


…………………………………………………………Page 81……………………………………………………………

    Windows NT 具备强制性(preemptive )多任务能力,不再非靠GetMessage 释放CPU 控 



    制权不可,但程序写法依然不变,因为应用程序仍然需要靠消息推动。它还是需要抓消 



    息! 



窗口的生命中枢 :窗口函数 



    消息循环中的Dispat chMessage 把消息分配到哪里呢?它透过USER 模块的协助,送到 



                                   switch case  

    该窗口的窗口函数去了。窗口函数通常利用                /   方式判断消息种类,以决定处置 



    方式。由于它是被Windows 系统所调用的(我们并没有在应用程序任何地方调用此函 



    数),所以这是一种call back  函数,意思是指「在你的程序中,被Windows 系统调用」 



    的函数。这些函数虽然由你设计,但是永远不会也不该被你调用,它们是为Windows 系 



    统准备的。 



    程序进行过程中,消息由输入装置,经由消息循环的抓取,源源传送给窗口并进而送到 



    窗口函数去。窗口函数的体积可能很庞大,也可能很精简,依该窗口感兴趣的消息数量 



    多寡而定。至于窗口函数的形式,相当一致,必然是: 



      LRESULT CALLBACK WndProc (HWND hWnd; 



                            UINT message; 



                            WPARAM wParam; 



                            LPARAM lParam) 



                                   switch case   def ault 

    注意,不论什么消息,都必须被处理,所以                /   指令中的       : 处必须调用 



    Def WindowProc,这是Windows  内部预设的消息处理函数。 



    窗口函数的wParam 和lParam  的意义,因消息之不同而异。wParam 在16 位环境中是 



    16 位,在32 位环境中是32 位。因此,参数内容(格式)在不同操作环境中就有 



    了变化。 



    我想很多人都会问这个问题:为什么Windows Programming Modal 要把窗口函数设计为 



    一个call back  函数?为什么不让程序在抓到消息(GetMessage )之后直接调用它就好 



    了?原因是,除了你需要调用它,有很多时候操作系统也要调用你的窗口函数(例如当 



                                                                       19 


…………………………………………………………Page 82……………………………………………………………

      某个消息产生或某个事件发生)。窗口函数设计为callback 形式,才能开放出一个接口 



      给操作系统叫用。 



消息映射 (                        )的雏形 

             Message Map 



      有没有可能把窗口函数的内容设计得更模块化、更一般化些?下面是一种作法。请注意, 



               MFC 

      以下作法是         「消息映射表格」(第9章)的雏形,我所采用的结构名称和变量名称, 



      都与MFC 相同,藉此让你先有个暖身。 



                  MSGMAP   ENTR Y         dim  

                          _ 

      首先,定义一个                    结构和一个       宏: 



      struct MSGMAP_ENTRY { 



                        UINT nMessage; 



                        LONG  (*pfn)(HWND; UINT; WPARAM; LPARAM); 



                         }; 



      #define dim (x)  (sizeof(x) / sizeof(x '0')) 



           MSGMAP_ENTR Y          pf n  

      请注意                 的第二元素      是一个函数指针,我准备以此指针所指之函 



      数处理nMessage 消息。这正是对象导向观念中把「资料」和「处理资料的方法」封装 



      起来的一种具体实现,只不过我们用的不是C++ 语言。 



      接下来,组织两个数组_messageEntries ' ' 和_mandEntries ' ',把程序中欲处理的消 



      息以及消息处理例程的关联性建立起来: 



      // 消息与处理例程之对照表格 



          struct MSGMAP_ENTRY _
返回目录 上一页 下一页 回到顶部 9 10
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!