友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

深入浅出MFC第2版(PDF格式)-第103章

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






                         图6…6 LineDDA 函数说明 



                                                                                          399 


…………………………………………………………Page 462……………………………………………………………

                  第篇    湷觥 FC  程式設計 



                   LineDDA  并不属于任何一个MFC 类别,因此调用它必须使用C++  的〃scope operator〃 



                    (也就是::): 



                     void CMyFrameWnd::OnPaint() 

                      { 

                      CPaintDC dc(this); 

                      CRect rect; 



                        GetClientRect(rect); 



                        dc。SetTextAlign(TA_BOTTOM | TA_CENTER); 



                        ::LineDDA(rect。right/2; 0; rect。right/2; rect。bottom/2; 

                            (LINEDDAPROC) LineDDACallback; (LPARAM) (LPVOID) &dc); 

                      } 



                     其中LineDDACallback  是我们准备的callback 函数,必须在类别中先有声明: 



                      class CMyFrameWnd : public CFrameWnd 

                      { 

                      。。。 

                     private: 

                        static VOID CALLBACK LineDDACallback(int;int;LPARAM); 

                      }; 



                     请注意,如果类别的成员函数是一个callback 函数, 你必须声明它为〃static〃,才能把 



                     C++ 编译器加诸于函数的一个隐藏参数this 去掉(请看方块批注) 。 



                              以类别的成员函数作为 Windows callback 函数 



                      虽然现在来讲这个题目,对初学者而言恐怕是过于艰深,但我想毕竟还是个好机会 



                     ……我可以在介绍如何使用callback 函数的场合,顺便介绍一些C++  的重要观念。 



                     首先我要很快地解释一下什么是callback 函数。凡是由你设计而却由Windows 系 



                      统调用的函数,统称为callback 函数。这些函数都有一定的类型,以配合Windows 



                      的调用动作。 



                     某些Windows API  函数会要求以callback 函数作为其参数之一,这些API 例如 



400 


…………………………………………………………Page 463……………………………………………………………

                                                    第6章    MFC 程式的生死因果 



 SetTimer 、LineDDA、EnumObjects 。通常这种API 会在进行某种行为之后或满足某种 



 状态之时调用该callback 函数。图6…6  已解释过LineDDA调用callback 函数的时机; 



 下面即将示范的EnumObjects 则是在发现某个Device Context 的GDI object 符合我们 



 的指定类型时,调用callback 函数。 



 好,现在我们要讨论的是,什么函数有资格在C++ 程序中做为callback 函数?这个 



 问题的背后是:C++ 程序中的callback  函数有什么特别的吗?为什么要特别提出讨论? 



是的,特别之处在于,C++ 编译器为类别成员函数多准备了一个隐藏参数(程序代码 



中看不到),这使得函数类型与Windows callback  函数的预设类型不符。 



假设我们有一个CMyclass 如下: 



class CMyclass { 

  private : 

    int nCount; 

    int CALLBACK _export 

        EnumObjectsProc(LPSTR lpLogObject; LPSTR lpData); 

  public : 

    void enumIt(CDC& dc); 

} 

void CMyclass::enumIt(CDC& dc) 

{ 



  // 注册callback 函数 

  dc。EnumObjects(OBJ_BRUSH; EnumObjectsProc; NULL); 



} 



C++ 编译器针对CMyclass::enumIt 实际做出来的码相当于: 

void CMyclass::enumIt(CDC& dc) 

{ 



  // 注册callback 函数 

  CDC::EnumObjects(OBJ_BRUSH; EnumObjectsProc; 

                      NULL; (CDC *)&dc); 

   

} 



 你所看到的最后一个参数,(CDC *)&dc,其实就是this 指针。类别成员函数靠着this 



                                                                                  401 


…………………………………………………………Page 464……………………………………………………………

              第篇    湷觥 FC  程式設計 



                指针才得以抓到正确对象的资料。你要知道,内存中只会有一份类别成员函数, 



                但却可能有许多份类别成员变量……每个对象拥有一份。 



                C++  以隐晦的this 指针指出正确的对象。当你这么做: 



                nCount = 0; 



                其实是: 



                this…》nCount = 0; 



                基于相同的道理,上例中的EnumObjectsProc  既然是一个成员函数,C++ 编译器也 



                会为它多准备一个隐藏参数。 



                好,问题就出在这个隐藏参数。callback 函数是给Windows 调用用的,Windows 并 



                不经由任何对象调用这个函数,也就无由传递this 指针给callback 函数,于是导至 



                堆栈中有一个随机变量会成为this 指针,而其结果当然是程序的崩溃了。 



                要把某个函数用作callback 函数,就必须告诉C++ 编译器,不要放this 指针作为 



                该函数的最后一个参数。两个方法可以做到这一点: 



                1。 不要使用类别的成员函数(也就是说,要使用全域函数)做为callback 函数。 



                2。 使用static 成员函数。也就是在函数前面加上static 修饰词。 



                第一种作法相当于在C 语言中使用callback 函数。第二种作法比较接近OO  的精神。 



                我想更进一步提醒你的是,C++  中的static 成员函数特性是,即使对象还没有产生, 



                static 成员也已经存在(函数或变量都如此) 。换句话说对象还没有产生之前你已经 



                可以调用类别的static 函数或使用类别的static 变量了。请参阅第二章。 



                也就是说,凡声明为static 的东西(不管函数或变量)都并不和对象结合在一起, 



                它们是类别的一部份,不属于对象。 



402 


…………………………………………………………Page 465……………………………………………………………

                                                            第6章    MFC 程式的生死因果 



空闲时间(idle time)的处理:OnIdle 



        为了让Hello 程序更具体而微地表现一个MFC 应用程序的水准,我打算为它加上空闲 



        时间(idle time )的处理。 



        我已经在第1章介绍过了空闲时间,也简介了Win32 程序如何以PeekMessage                       「偷闲」。 



        Microsoft 业已把这个观念落实到CWinApp  (不,应该是CWinThread)中。请你回头看 



        看本章的稍早的「CWinApp::Run 程序生命的活水源头」一节,那一节已经揭露了MFC 



        消息循环的秘密: 



        int CWinThread::Run() 

         { 

            。。。 

            for (;;) 

            { 

                while (bIdle && 

                        !::PeekMessage(&m_msgCur; NULL; NULL; NULL; PM_NOREMOVE)) 

                { 

                    // call OnIdle while in bIdle state 

                    if (!OnIdle(lIdleCount++)) 

                        bIdle = FALSE; // assume 〃no idle〃 state 

                } 

                。。。 // msg loop 

            } 

         } 



        CThread::OnIdle 做些什么事情呢?CWinApp 改写了OnIdle  函数,CWinApp::OnIdle 又 



       做些什么事情呢?你可以从THRDCORE。CPP 和APPCORE。CPP  中找到这两个函数的 



       源代码,源代码可以说明一切。当然基本上我们可以猜测OnIdle  函数中大概是做一些 



        系统(指的是MFC 本身)的维护工作。这一部份的功能可以说日趋式微,因为低优先 



       权的执行线程可以替代其角色。 



        如果你的MFC 程序也想处理idle time,只要改写CWinApp 衍生类别的OnIdle  函数即 



        可。这个函数的类型如下: 



              virtual BOOL OnIdle(LONG lCount); 



                                                                                         403 


…………………………………………………………Page 466……………………………………………………………

                  第篇    湷觥 FC  程式設計 



                   lCount 是系统传进来的一个值,表示自从上次有消息进来,到现在,OnIdle  已经被调用 



                   了多少次。稍后我将改写Hello 程序,把这个值输出到窗口上,你就可以知道空闲时间 



                   是多么地频繁。lCount 会持续累增,直到CWinThread::Run  的消息循环又获得了一个讯 



                   息,此值才重置为0 。 



                   注意:Jeff Prosise 在他的Programming Windows 95 with MFC 一书第7章谈到OnIdle 



                   函数时,曾经说过有几个消息并不会重置lCount 为0,包括鼠标消息、WM_SYSTIMER 、 



                   WM_PAINT 。不过根据我实测的结果,至少鼠标消息是会的。稍后你可在新版的Hello 程 



                   序移动鼠标,看看lCount 会不会重设为0 。 



                  我如何改写Hel
返回目录 上一页 下一页 回到顶部 0 1
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!