最近工作有点紧张,学习SDK的心思也下来了,但回到家里还是坚持的打开了Vs2008学习魅族sdk。突然间脑海里有了个奇怪的想法,我这是为什么呢?一方面我没有购买魅族的手机,因为暂时更想要一台3G手机,并不是那种有钱烧的人,我一台手机要用上几年。另一方面写魅族sdk学习系列更是让人费解,一个没有M8手机的人工作也跟移动开发没有关系,写这么多边缘文章图什么呢?
想了一会儿,也许是为了能给我的这个博客式的论坛带来点流量吧,呵呵(苦笑)!昨天和魅族论坛里的版主联系了下想加入官方群,结果无 功而返,看了官方的置顶贴后才知道,需要一个作品发布。那就算了本想有个好的学习环境能够学习的更好些。无所谓,就如前面说的,我图什么呢?
回到正题,应该算是昨天凌晨的时候发一个关于消息的问题贴,很高兴有一位朋友回答了我的问题。我应该谢谢这位朋友。今天晚上下班后开始习惯性的打开电脑,看过了phenix9999回复,打开程序自己调试。
不知道有多少朋友在写魅族sdk应用时会用到消息?也不知道朋友们对消息处理了解多深?而我在工作很少会去处理消息,所以在C++环境中这是个非常头痛的问题。在魅族的sdk讨论版本仅仅只有版主的一篇关于多点的消息处理,遗憾的是我并没有M8手机无法尝试。但是并不影响,从这篇开始简单了解一些消息方面的内容。
消息处理是Windows系统中用于处理各种异步响应而形成一种机制,比如点击、双击、刷新重绘等等系统都会产生相应的消息,而消息在产生后,windows会根据句柄将消息发送到相应的窗口中,供应用程序处理。基本的流程如下:
1、用户操作
2、系统捕获后产生消息
3、将消息送入消息队列
4、应用程序从消息队列中摘取消息
5、应用程序分发消息到相应窗口
6、处理消息
可能描述的并不是很专业,大概就是这个意思。在Vs2008中 建立一个 WinCE应用程序会有一个完整的应用程序代码,按F5就能运行了,就用这个代码来学习吧。
在代码中开始部分有WinMain函数,这个是必须的,一切从它开始。
WinMain函数第一行就能看到 MSG msg;这样的申明,MSG是一个结构,这就是Windows的消息结构了,看看它都有些什么东西:
- typedef struct tagMSG {
- HWND hwnd; 处理消息的窗口句柄
- UINT message;这就是消息
- WPARAM wParam; 消息的一个附加参数,非常有用
- LPARAM lParam; 同上,是个指针类型
- DWORD time; 发送消息的时间
- POINT pt; 产生消息鼠标所在位置
- }
复制代码这里面还有许多学问,并不是我所简述的一样,深入的还是要自己去学习的。
看完了消息的结构后回到WinMain函数,看下其完整的代码:
- int WINAPI WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow)
- {
- MSG msg;
- // 执行应用程序初始化:
- if (!InitInstance(hInstance, nCmdShow))
- {
- return FALSE;
- }
- HACCEL hAccelTable;
- hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY));
- // 主消息循环:
- while (GetMessage(&msg, NULL, 0, 0))
- {
- if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- return (int) msg.wParam;
- }
复制代码在执行完了MSG msg后运行了InitInstance,这个函数是用于初始化一个应用程序进程,在这个函数里会做很多事情,参数的意思很明确第一个参数是应用程序的进程句柄。选中这个函数使用F12跳转到实现里去。这个函数很复杂,在这里主要就讲一下跟消息处理相关的内容,看下面这句代码:
if (!MyRegisterClass(hInstance, szWindowClass))注册一个类?这个注册很重要,我们打开MyRegisterClass函数的实现代码:
- //
- // 函数: MyRegisterClass()
- //
- // 目的: 注册窗口类。
- //
- // 注释:
- //
- ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
- {
- WNDCLASS wc;
- wc.style = CS_HREDRAW | CS_VREDRAW; 定义窗体样式
- wc.lpfnWndProc = WndProc; windows回调函数
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hInstance = hInstance; 进程句柄
- wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY));图标
- wc.hCursor = 0;鼠标样式
- wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);背景
- wc.lpszMenuName = 0;菜单指针
- wc.lpszClassName = szWindowClass;窗体类名
- return RegisterClass(&wc);
- }
复制代码注释部分已经说的很清楚,注册窗口类的。因为windows程序嘛,就得有窗口,于是就将创建的第一个窗口作为主窗口,而创建窗口就必须调用MyRegisterClass来注册窗口类。写了这么多就是想说明这个MyRegisterClass的作用,在代码中可以后到一个WNDCLASS,这就是windows的最基本的窗体类属性,如果想要创建一个窗口就必须做上面MyRegisterClass中代码类似的处理,具体的作用我已经在边上注明。
回到InitInstance函数中,接下来看到下面的代码:
hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);看函娄名就明白了创建窗口,因为刚才已经向windows注册了窗口,当然你想知道他通过什么来知道这个窗口类的?就是szWindowClass,这就是窗口类的类名。通过这个类名CreateWindow就能找到注册的类,从而创建窗口实例。到此要说明一个重要的东西,就是在MyRegisterClass中的一行代码:
wc.lpfnWndProc = WndProc; windows回调函数这个WndProc可以在vs创建工程时自动生成的代码里找找,如下:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
这就是传说中的回调,这个函数在CreateWindow之后将从windows系统里接收发过来的消息,以此来达到处理消息的目的。Ok,InitInstance最后几行代码干的话就是显示这个窗口。
结束了InitInstance函数回到WinMain函数中,看看接下来在干啥,看到LoadAccelerators这个函数是用来加载快捷键表的不用太关心,接着看:
-
- // 主消息循环:
- while (GetMessage(&msg, NULL, 0, 0))
- {
- if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
复制代码这就来到 了传说中的消息循环了,看到没,这基本就是个死循环,因为所有的应用程序要运行就必须处于一个循环中,就好比一个结构化的程序,从第一行开始到最后一行执行完了就退出,除非你把它中断在一个位置比如DOS里中断出来让用户输入一样,或者就让他一直循环,程序不停的在哪转呀转。在Windows里的程序就是使用循环,这个循环就是消息循环。只有收到退出消息循环时才会中止,所以在写程序中退出应用程序会使用PostQuitMessage(0);这个就是向发送一个退出的消息,从而中断了消息循环,程序没有一个循环于是就运行到了WinMain函数最后一行了,结果就是88.否则的话,就会不断的 GetMessage,这里就要说明一下这几个API的具体含义。
GetMessage: 该函数从调用线程的消息队列里取得一个消息并将其放于指定的结构。这里的结构就是C++里定义的消息结构MSG。现在明白了吧,GetMessage就是可以让各种语言获取到Windows消息后转换成各自可识别的结构。
TranslateAccelerator: 翻译加速键表。该函数处理菜单命令中的加速键。该函数将一个WM_KEYDOWN或WM_SYSKEYDOWN消息翻译成一个WM_COMMAND或 WM_SYSCOMMAND消息(如果在给定的加速键表中有该键的入口),然后将WM_COMMAND或WM_SYSCOMMAND消息直接送到相应的窗 口处理过程。所以除这些消息外都会失败。这就是为什么会有这个判断 ! TranslateAccelerator
TranslateMessage: 该函数将虚拟键消息转换为字符消息。字符消息被寄送到调用线程的消息队列里,当下一次线程调用函数GetMessage或PeekMessage时被读出。
DispatchMessage: 该函数调度一个消息给窗口程序。通常调度从GetMessage取得的消息。消息被调度到的窗口程序即是MainProc()函数。就是前现注册窗口类时的WndProc啦。
整个的创建流程已经了解了,接下来了解一下消息的分发。这个可以看WndProc的处理代码就清楚了,看看用vs创建的工程默认生成的代码吧,接下来就很简单了,根据传过过来的message使用switch进行分发处理就行了。只要要注意在switch代码结构中处理的default,看看:
default: return DefWindowProc(hWnd, message, wParam, lParam);DefWindowProc: 该函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理。调用DefWindowProc函数时使用窗口过程接收的相同参数。
下一篇于聊关于魅族sdk中的关于消息处理的内容