前面一篇
《M8SDK学习之了解一下消息处理》中简单的了解了Wince中的消息机制,如果朋友还不是很清楚那就只能去查看专业的书籍资料了,《Windows CE 程序设计》是本不错的书。这一篇中将学习下M8sdk中对消息机制的管理及应用。
写过或者看过M8sdk程序的朋友应该知道CMzApp这个类,此类是M8sdk的应用程序类,与传统的程序开发不同的是,这个类只要一个申明就能使程序运行起来,很方便。那些初始化进程实例、注册窗口类、启动消息循环的代码难道不用写了吗?是的,这些代码通过CMzApp这个类都已经封装了。这一点如果使用过C# 、DELPHI之类的体会比较深,因为写C#程序时一般就不用管程序的入口,更不用提自己建立消息循环了。好啦,来分析下CMzApp吧。由于看不到源代码,只能打开MzApp.h来看看。
- class MZFC_API CMzApp
- {
- public:
- CMzApp(void);
- virtual ~CMzApp(void);
- public:
- /// called by the framework to init the application. ( inside you can do such as creating window, sizing window, etc.)
- virtual BOOL Init();
- // run the application
- virtual int Run();
- /// called by the framework to release resources when the application is going to end
- virtual int Done();
- protected:
- /// override to do your own processing of the posted message.
- virtual BOOL PreTranslateMessage(MSG* pMsg);
- public:
- /// get the module file name of the process
- CMzString GetModuleFileName(HMODULE hModule);
- public:
- /// Enable/Disable NeverQuit functionality for the application. (when the home key is pressed,the app will not exit if enabled)
- void EnableNeverQuit(bool bNeverQuit);
- MZFC_INLINE bool IsEnableNeverQuit();
- private:
- bool m_bNeverQuit;
- };
复制代码类成员中也看不太出有什么特别的,因为在写程序时仅仅是对MzApp进行了实例化就能运行了,从这一点判断出CMzApp的构造函数就已经完成了整个过程,而我们写程序时又是如何来定制自己的内容的呢?比如加入主窗口?这一点从上面的代码中可以看到public区域有几个虚方法,分别是:
virtual BOOL Init();这个方法很好理解就是初始化的意思,以前的文章中写的程序中都会重写它,以达到初始化主窗口的目的,CMzApp已经封装了应用程序初始化的主体工作,所以通过公布一个虚方法供开发者能自定义初始化过程,这里运用到了面向对象的手法。一方面可以可以封装繁杂的初始化过程,另一方面又达到了自定义的目的。
virtual int Run();这个方法从来没有用过,就函数名的意思和注释来看就是让应用程序运行。可能光看这样的代码会很抽象,其实如果没有猜错的话,这一步会将代码真正的推入到消息循环中。当然没有我说的这么简单,里面要做许多的处理,具体请咨询魅族吧。
virtual int Done();这个嘛从注释的意思就是讲当应用程序结束时释放资源。
上面三个方法基本上说明了CMzApp给开发人员所提供的用于创建应用程序所能做的事情,其他的一些重复劳动都已经被封装。另外除了上面三个函数外再一下EnableNeverQuit:
void EnableNeverQuit(bool bNeverQuit);这个函数,这个函数比较有意思,估计有蛮多朋友会使用到。其注释的说明是开启/关闭从不退出程序功能,啥意思?呵呵,就是说说程序不真正的退出,而是驻留在内存而不退出,比如按下M键后。简单的理解就是启用这个属性后应用程序将不能通过发WM_QUIT消息来退出。如果有朋友需要写常驻留的程序就可以使用它咯。
看过CMzApp的头文件后,大概了解了程序的入口了,接着就会想那程序中的窗口和控件又是如何来做到消息的响应呢?这可能就会提到整个SDK的设计了,因为一套windows framework封装的好坏其对消息的处理是第一要点,因为如果消息能够完好的分发将使整个framework非常的容易开发和扩展。这就是用到很多的面向对象、设计模式等知识了,我也没有哪个本事就不好讨论太多,点到为止吧。还是看看已经接触到sdk的消息处理技巧吧。
按照常理,魅族在设计sdk的组件都是从几个非常简单的类开始继承的,《Meizu M8 MZFC Documentation (CHS).chm》里对开发直接接触的最末枝的类进行了划分,可以看到继承关系,这些类都是通过sdk中的DLL封装后引出的类了,再上层已经无法看到。所以消息的派发更是无从着手了,我只是根据自己的一点点经验,大概的猜一下。
先猜猜sdk的类继承关系吧。sdk中的控件类(可视化)和窗口类都应该是同一个根继承下来的。
之所以这么说,从面向对象开发来说,将一些相同的代码抽象成父类是一种常用的手法,在windows中控件和窗口其实都是同根同母的,都是根窗口的一种特殊形式。因此魅族应该会将这些共同的东西进行抽象封装成若干个基类。这样做好处是大大的,由于没有sdk的继承图,也不是非常的确定,但魅族的sdk至少有3层以上的继承关系。
第一层 根类
就像C#中的Object类一样,在C++中好像没有根类,所以魅族肯定会自己封装一个根类,供子类派生,这样就可以将一些最最根本的对象方法都底层化。
第二层 分支基类
叫分支基类不太好听,我也不知道应该叫什么好。分支就意味着在这一层将对类分门别类,如用于可视类继承的,还有用于绘画类继承的,用于非可视类继承的。
为什么要分支?分支可以让同一类的对象更好的抽象,比如对于一些不用处理界面的类和需要处理界面的类就能分开来继承,否则当类继承到一定程度时就会出现各种乱七八糟的问题,这里面有很多面向对象设计原则和设计模式的东西就不聊了。
另外注意,虽然我将分支基类定为一层,并不是说只有一层继承关系,在分支基类中也许会有继承关系。原因说过了就是把一些共同的东西抽象,抽象!
第三层 偶们看见的类了
分支基类已经确定,接下来要做的就是把所有要实现的类设计 实现了,要一个窗口就从可视控件基类中继承下来增加窗口的一些特性呗,要写一个按钮也是同样的道理。sdk到类关系这一步就算是差不多了,接下来就是第三方开发人员干的咯。
猜了下类结构后,再来说明消息机制在这个类结构的工作机制。前面提到类已经分支了,同一类型的类都有着很多相同的特点,这样的话处理的消息也都类似咯。OK,将这些消息都放在一基类中,这样从这个基类派生的类不就都拥有了这些消息的处理能力了吗?而一些不相干的消息就不会处理了。另外利用类的多态性就能让派生类处理各自的消息时有着不同的能力,比如按钮1和按钮2都有MZ_MN_LBUTTONDOWN的消息处理,而在两个按钮基类中统一处理了这个消息,并且通过调用一个虚方法OnLButtonDown进行处理,如何?哈哈,按钮1重写这个方法后可以显示一个对话框,铵钮2重 写这个方法后可以实现退出程序。哈哈,效果来了吧。
为了说明sdk的消息机制,还是要了解下整体的派发机制。先举个例子,如果要用sdk写一个带按钮窗口,通过这个按钮可以退出程序,需要做哪些事情?
1、继承窗口类CMzWnd或者CMzWndE
2、在类中申明按钮成员并创建它
3、重写MzDefWndProc方法处理按钮点击消息
4、处理消息并通过应用程序退出
这个过程做完后,会不会有一个疑问?这个按钮的点击消息是怎么到达这个窗口类的?系统又是如何将这个消息发送过来的呢?在上一篇关于windows消息文章时已经说过,系统会通过消息产生的句柄发送到相应应用程序的消息队例中,应用程序再从消息队列中取出消息发送到产生消息的窗口里,然后再由窗口分发这个消息到具体的控件里,进而处理消息。
这一整个流程,M8sdk要如何实现呢?回到最前面说的CMzApp类,是的,它封装了这些操作。看下面的代码:
- class CM8NoteApp : public CMzApp
- {
- public:
- // The main window of the app.
- CM8NoteMainWnd m_MainWnd;
- virtual BOOL Init()
- {
- CoInitializeEx(0, COINIT_MULTITHREADED);
- RECT rcWork = MzGetWorkArea();
- m_MainWnd.Create(rcWork.left,rcWork.top,RECT_WIDTH(rcWork),RECT_HEIGHT(rcWork), 0, 0, 0);
- m_MainWnd.SetBgColor(MzGetThemeColor(TCI_WINDOW_BG));
- m_MainWnd.Show();
- //this->EnableNeverQuit(true);
- return TRUE;
- }
- };
- CM8NoteApp noteApp;
复制代码继承了CmzApp类从而创建了一个自己的应用程序类,在这个类中申明了一个窗口成员,并重写了Init方法用来初始化应用程序同时创建主窗口,最后将类实例化成对象,程序就运行了。
在应用程序类中做了下面的几步操作:
1、初始化应用程序,向系统申请内存支
2、调用Init,初始化各种资源
3、Run应用程序,使应用程序过入消息循环
分析到这可以发现CMzApp中应该是实现了一个隐藏的窗口,从而调用了系统的消息回调函数。通过这个隐藏的窗口调用回调函数再将消息分发。嗯,是的,所以的消息都要通过窗口才能分发。
说到分发就要看窗口中的一个重要函数MzDefWndProc,这个方法就是魅族sdk用于分发消息的方法,具体的道理和上篇中看到的WndProc差不多,进而使窗口能够使用多态的方法分发消息。
至于MzDefWndProc干了什么?在CMzWnd的基类肯定有对这个方法的根实现,会对所有发生在CMzWnd及派生类上的控件的消息进行接收及分发。这个类应该就是CMzApp中隐藏的类咯。
再前面说到类结构时说到类进行分支后都会实现各自需要的消息,这样一来,通过一个基础窗口类进行消息派发,只要是创建在这个基础窗口类或者它的派生类上的控件就都能处理消息了。OK,流程完毕。
我的这个小文所涉及的类结构等内容都是猜测,在没有拿到魅族官方的sdk详细类结构及框架说明前,不敢保证是正确的。希望朋友能交流。