使用HANDLE_MSG宏简化Win32应用的开发
http://blog.csdn.net/daiyutage/article/details/17241161
Win32应用中的回调函数WndProc用于接收Windows向应用程序直接发送的消息,以及响应消息。大多情况下,我们这样编写代码:
- LRESULT CALLBACK WndProc(HWND hWnd,
- UINT message,
- WPARAM wParam,
- LPARAM lParam )
- {
- int cxClient, cyClient;
- PAINTSTRUCT ps;
- HDC hdc;
- switch( message )
- {
- case WM_SIZE:
- cxClient = LOWORD(lParam);
- cyClient = HIWORD(lParam);
- break;
- case WM_PAINT:
- hdc = BeginPaint( hWnd, &ps );
- EndPaint( hWnd, &ps );
- break;<br />
- case WM_DESTROY:
- PostQuitMessage( 0 );
- break;
- }
- return DefWindowProc( hWnd, message, wParam, lParam );
- }
这种方式通过switch分支语句分理处理各个消息,结构简单明了。但有2个问题:
- 在所处理的消息多了后,全部代码都集中于一个switch语句中,变量容易相互污染,且代码很长,不容易定位代码。
- 大多数的消息都有相应的WPARAM、LPARAM参数,但对于每个消息,其WPARAM、LPARAM参数的具体内容又不一致。就像上面WM_SIZE中的代码,我们怎么知道cyClient、cyClient的信息就放在lParam而不是wParam中,且cxClient的信息在lParam的低位,cyClient的信息在lParam的高位?我们需要随时查询SDK文档以了解这两个参数中各有什么含义,以及如何恰当地将这些参数抽取出来。不管怎样,上面的代码实际上就是个让人一团雾水的魔术代码(Magic Code).
WindowsX.h定义了许多宏,可以帮助我们解决上述这些问题。其中HANDLE_MSG宏可以大大简化Win32开发。下面我们先看使用HANDLE_MSG宏后的代码:
- void OnSize(HWND hwnd, UINT state, int cx, int cy)
- {
- }
- void OnPaint(HWND hWnd)
- {
- PAINTSTRUCT ps;
- HDC hdc;
- hdc = BeginPaint( hWnd, &ps );
- EndPaint( hWnd, &ps );
- }
- void OnWinDestroy(HWND hWnd)
- {
- PostQuitMessage(0);
- }
- LRESULT CALLBACK WndProc(HWND hWnd,
- UINT message,
- WPARAM wParam,
- LPARAM lParam )
- {
- switch( message )
- {
- HANDLE_MSG(hWnd, WM_SIZE, OnSize);
- HANDLE_MSG(hWnd, WM_PAINT, OnPaint);
- HANDLE_MSG(hWnd, WM_DESTROY, OnWinDestroy);
- }
- return DefWindowProc( hWnd, message, wParam, lParam );
- }
与使用HANDLE_MSG宏之前的代码相比,我们根本不需使用LOWORD、HIWORD来提取cxCleint及cyClient的信息,OnSize函数的参数已经有cx及cy,而且还传进一个表示窗口变化类型的参数state,如SIZE_RESTORED, SIZE_MAXSHOW等。ps与hdc这两个变量移至OnPaint函数中而成为真正的局域变量。在switch分支语句中,对于每一个消息,都使用HANDLE_MSG宏清晰地将消息与消息处理函数对应起来,代码非常简洁。而无论是在消息处理函数,或是switch分支中,我们均无需显式地加上break语句。
下面我们来看看HANDLE_MSG宏是如何做到这一点的。HANDLE_MSG宏的定义如下所示:
- #define HANDLE_MSG(hwnd, message, fn) /
- case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
HANDLE_MSG(hwnd, message, fn)是宏的签名。hwnd是需处理消息的窗口句柄,message是消息,fn是指向负责消息处理的函数指针。当编译器遇到此宏,将在编译时将其转换为
- case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
的形式。##是ANSI C标准中的预处理器,用于将前后两个符号直接连接起来。因此,当message为WM_SIZE时,HANDLE_##message就变成:
- HANDLE_WM_SIZE
因此,对于
- HANDLE_MSG(hWnd, WM_SIZE, OnSize)
编译器将转换为:
- case WM_SIZE:
- return HANDLE_WM_SIZE(hwnd, wParam, lParam, OnSize)
我们注意到,尽管HANDLE_MSG宏中未使用wParam及lParam参数,但展开宏后,这两个参数均加进来了。
HANDLE_WM_SIZE也是一个在WindowsX.h中定义的宏,其原型如下:
- #define HANDLE_WM_SIZE(hwnd, wParam, lParam, fn) /
- ((fn)((hwnd), (UINT)(wParam), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)), 0L)
正是在HANDLE_WM_SIZE宏中,自动将wParam, lParam的高位及低位分别抽出,作为OnSize函数的实参进行传递。
应注意,不同消息处理函数的参数列表是不一样的。如OnSize函数,其参数列表包括HWND, UINT, int, int, 而OnPaint、OnWindDestory函数均只有一个HWND参数。那么,如何声明这些消息处理函数的签名?很简单,在WindowsX.h文件中找到定义HANDLE_WM_SIZE的地方,其上面有一行注释:
- /* void Cls_OnSize(HWND hwnd, UINT state, int cx, int cy) */
- #define HANDLE_WM_SIZE(hwnd, wParam, lParam, fn) /
- ((fn)((hwnd), (UINT)(wParam), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)), 0L)
Cls_OnSize函数已经给出了函数的签名,因此,我们只需将此签名复制过来,并将Cls_OnSize更名为自己选择的函数名称即可。
http://blog.csdn.net/daiyutage/article/details/17241161
http://blog.csdn.net/cocoasprite/article/details/43208717
使用HANDLE_MSG宏简化Win32应用的开发的更多相关文章
- 5款帮助简化的HTML5 Audio开发的Javascript类库
HTML5的audio标签提供了我们方便控制声音的功能,可是使用原生的HTML5来开发声音或者音乐相关的项目仍旧很的麻烦.在今天这篇文章中,我们将介绍5款帮助你简化开发的javascript audi ...
- 架设WIN32汇编程序的开发环境
笔者在学习Windows下的图形界面应用程序(GUI,Graphical User Interface)的时候碰到的第一个麻烦就是架设WIN32汇编程序的开发环境,在这里笔者愿意和大家分享这段经历. ...
- 第1章—Spring之旅—简化Spring的java开发
简化Spring的java开发 1.1简介 区别于EJB的特性 简化javaBean,为了降低java开发的复杂性,Spring采取了以下4种关键策略: 基于POJO的轻量级和最小入侵性编程 通过依赖 ...
- (转)可简化iOS 应用程序开发的6个Xcode小技巧
Xcode是iPhone和iPad开发者用来编码或者开发iOS app的IDE.Xcode有很多小巧但很有用的功能,很多时候我们可能没有注意到它们,也或者我们没有在合适的水平使用这些功能简化我们的iO ...
- 关于C++的宏:WIN32和DEBUG
判断平台相关,判断程序是属于debug版本还是release版本,我们会这么做. #ifdef WIN32 #else #endif #ifdef DEBUG // 如果是调试版本 #else //发 ...
- 最简化的DirectX 11开发环境的配置 VS2010
转载自:http://blog.csdn.net/zhmxy555/article/details/7672101 在编写基于DirectX 11的应用程序之前,我们当然需要在IDE中加入Direct ...
- JeeSite 4.0 简化业务逻辑层开发
2019独角兽企业重金招聘Python工程师标准>>> 引言 对于业务逻辑层的开发重复代码很多,尽管有代码生成器,但从代码量总的来说还是比较多,所以就有了以下抽象类及工具,对一些常用 ...
- Jaxb的优点与用法(bean转xml的插件,简化webservice接口的开发工作量)
一.jaxb是什么 JAXB是Java Architecture for XML Binding的缩写.可以将一个Java对象转变成为XML格式,反之亦然. 我们把对象与关系数据库之间的映射称 ...
- 用block做事件回调来简化代码,提高开发效率
我们在自定义view的时候,通常要考虑view的封装复用,所以如何把view的事件回调给Controller就是个需要好好考虑的问题, 一般来说,可选的方式主要有target-action和de ...
随机推荐
- 用CMake代替makefile进行跨平台交叉编译
在开始介绍如何使用CMake编译跨平台的静态库之前,先讲讲我在没有使用CMake之前所趟过的坑.因为很多开源的程序,比如png,都是自带编译脚本的.我们可以使用下列脚本来进行编译: 1 2 3 ./c ...
- Android自定义组件系列【1】——自定义View及ViewGroup
View类是ViewGroup的父类,ViewGroup具有View的所有特性,ViewGroup主要用来充当View的容器,将其中的View作为自己孩子,并对其进行管理,当然孩子也可以是ViewGr ...
- Gibbs 采样的应用
Gibbs 采样的最大作用在于使得对高维连续概率分布的抽样由复杂变得简单. 可能的应用: 计算高维连续概率分布函数的数学期望, Gibbs 采样得到 n 个值,再取均值: 比如用于 RBM:
- Activity“ 阻止自动弹出软键盘”的方法 -尤其是对于Tab页下的!
我的activity是Tab页签下的~! 所以应把代码加在继承于TabActivity的那个activity中!!而不是由点击Tab页启动的那个activity <activity androi ...
- 细说Oracle中NULL值
1.NULL是什么? NULL表示UNKNOW(未知),其不代表不论什么值. 比如一行中某列没有不论什么值即为NULL. ORACLE同意不论什么一种数据类型的字段为空,除了下面两种情况: 1)主键字 ...
- 线程基础:JDK1.5+(9)——线程新特性(中)
(接上文<线程基础:JDK1.5+(8)--线程新特性(上)>) 3.工作在多线程环境下的"计数器": 从这个小节開始,我们将以一个"赛跑"的样例. ...
- Android6.0动态申请权限那些坑--以及避免用户选择不再提示后无法获取权限的问题
Android 6.0 为了保护用户隐私,将一些权限的申请放在了应用运行的时候去申请, 比如以往的开发中,开发人员只需要将需要的权限在清单文件中配置即可,安装后用户可以在设置中的应用信息中看到:XX应 ...
- 【t061】游览路线
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 话说LCINF信息组来到烟台参加夏令营.一天,大家提议出去游玩,来到了烟台最繁华的地方.由于他们对烟台 ...
- erlang浅谈
优点: 1.面向并发,有成熟而且久经考验的框架可供使用,网络部分已经经过了良好封装 2.内存缓存解决方案进程字典,前者的读写速度是50NS-100Ns级别的 3.对二进制数据解析的语法是直观,简单,强 ...
- erlang OTP gen_server 图解分析
http://www.hoterran.info/otp-gen_server-sourcecode 在阅读erlang的otp源码gen_server.erl的时候,一直想写点什么,用一种最好的方式 ...