八宝书库 > 文学其他电子书 > 深入浅出MFC第2版(PDF格式) >

第14部分

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

小说: 深入浅出MFC第2版(PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




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



    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 _messageEntries'' = 



          { 



              WM_CREATE; OnCreate; 



              WM_PAINT; OnPaint; 



              WM_SIZE; OnSize; 



              WM_MAND; Onmand; 



              WM_SETFOCUS; OnSetFocus; 



              WM_CLOSE; OnClose; 



              WM_DESTROY; OnDestroy; 



          } ;      ↑         ↑ 



                这是消息    这是消息处理例程 



                                                                                20 


…………………………………………………………Page 83……………………………………………………………

     // mand…ID  

                    与处理例程之对照表格 

    struct MSGMAP_ENTRY _mandEntries = 

    { 

        IDM_ABOUT;      OnAbout; 

        IDM_FILEOPEN;   OnFileOpen; 

        IDM_SAVEAS;     OnSaveAs; 

    } ;    ↑                ↑ 

     这是WM_MAND 命令项这是命令处理例程 



 于是窗口函数可以这么设计: 



//………………………………………………………………………………………………………………………………………………………………………………………

// 窗口函数 

//………………………………………………………………………………………………………………………………………………………………………………………

LRESULT CALLBACK WndProc(HWND hWnd;     UINT message; 

                             WPARAM wParam; LPARAM lParam) 

{ 

int i; 



  for(i=0; i 《 dim(_messageEntries); i++) {  //  

                                                   消息对照表 

      if (message == _messageEntries'i'。nMessage) 

          return((*_messageEntries'i'。pfn)(hWnd; message; wParam; lParam)); 

  } 

  return(DefWindowProc(hWnd; message; wParam; lParam)); 

} 

//………………………………………………………………………………………………………………………………………………………………………………………

// Onmand … 

                专门处理 WM_MAND 

//………………………………………………………………………………………………………………………………………………………………………………………

LONG Onmand(HWND hWnd; UINT message; 

                  WPARAM wParam; LPARAM lParam) 

{ 

int i; 



  for(i=0; i 《 dim(_mandEntries); i++) {  //  

                                                   命令项目对照表 

      if (LOWORD(wParam) == _mandEntries'i'。nMessage) 

          return((*_mandEntries'i'。pfn)(hWnd; message; wParam; lParam)); 

  } 

  return(DefWindowProc(hWnd; message; wParam; lParam)); 

} 

//………………………………………………………………………………………………………………………………………………………………………………………

LONG OnCreate(HWND hWnd; UINT wMsg; UINT wParam; LONG lParam) 

{ 

   。。。 

} 



                                                                                         21 


…………………………………………………………Page 84……………………………………………………………

    //………………………………………………………………………………………………………………………………………………………………………………………



    LONG OnAbout (HWND hWnd; UINT wMsg; UINT wParam; LONG lParam) 



    { 



    。。。 



    } 



    //………………………………………………………………………………………………………………………………………………………………………………………



    这么一来,WndProc 和Onmand 永远不必改变,每有新要处理的消息,只要在 



    _messageEntries ' ' 和_mandEntries ' ' 两个数组中加上新元素,并针对新消息撰写新 



    的处理例程即可。 



    这种观念以及作法就是MFC  的Message Map  的雏形。MFC 把其中的动作包装得更好 



    更精致(当然因此也就更复杂得多),成为一张庞大的消息地图;程序一旦获得消息, 



    就可以按图上溯,直到被处理为止。我将在第3章简单仿真MFC  的Message Map ,并在 



    第9章「消息映射与绕行」中详细探索其完整内容。 



对话框的运作 



    Windows  的对话框依其与父窗口的关系,分为两类: 



                                               modal  

    1。  「令其父窗口除能,直到对话框结束」,这种称为                      对话框。 



                                       modeless  

    2。  「父窗口与对话框共同运行」,这种称为                     对话框。 



               modal             Generic  ! §About ! ¨  

    比较常用的是          对话框。我就以             的         对话框做为说明范例。 



    为了做出一个对话框,程序员必须准备两样东西: 



                 dialog template      RC  

    1。 对话框模板(                )。这是在       文件中定义的一个对话框外貌,以各 



                                                             。。。 

    种方式决定对话框的大小、字形、内部有哪些控制组件、各在什么位置  等等。 



    2。 对话框函数(dialog procedure )。其类型非常类似窗口函数,但是它通常只处 



      WM_INITDIAL OG   WM  MAND  

                         _ 

    理                和              两个消息。对话框中的各个控制组件也 



    都是小小窗口,各有自己的窗口函数,它们以消息与其管理者(父窗口,也就 



                                               WM  MAND 

                                                 _ 

    是对话框)沟通。而所有的控制组件传来的消息都是                                 ,再由其 



    参数分辨哪一种控制组件以及哪一种通告(notification )。 



                                                                                22 


…………………………………………………………Page 85……………………………………………………………

    Modal 对话框的激活与结束,靠的是DialogBox 和EndDialog 两个API 函数。请看 



    图1…4。 



                                            

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的