windows API普通函数跟回调函数有何区别
通俗点讲:
1、普通函数(假设我们都是函数)
你卖电脑,我买电脑,我给你钱(调用你)后,你给我电脑(得到返回值)。这种情况下,我给钱后就不能走开,必须等你把电脑给我,否则你交货的时候可能找不到人。
2、回调函数(假设我们还是函数)
你还卖电脑,我也还买电脑,我给你钱(调用你)后,你发现没存货了,要去进货,不能马上给我电脑(返回值),我愤怒了,不想一直等下去,就给你留个电话号码,让你进完货后再打电话叫我(这个电话就是回调函数)。 不知道说明白没...回调函数一般都是在多线程编程中使用。在多线程的框框下理解就很容易了,无非是来来往往的信息交流而已 MSG结构
typedef struct tagMSG {
HWND hwnd; //这个消息所在的窗口句柄
UINT message; //消息标识符,如WM_SIZE、WM_COMMAND、WM_QUIT等等
WPARAM wParam; //32位消息的特定附加信息
LPARAM lParam; // 32位消息的特定附加信息
DWORD time; // /消息创建时的时间
POINT pt; //消息创建时的鼠标位置
} MSG, *PMSG;
GetMessage()
BOOL GetMessage(
//从消息队列中“摘取”一个消息信息放到lpMsg所指的变量里;如果所取窗口的消息队列中没有消息,则程序会暂停在GetMessage 函数里,不会返回。
LPMSG lpMsg, //传出参数、函数执行成功,从消息队列中“摘”取的一个消息信息会放入lpMsg所指的MSG结构变量中。
HWND hWnd , //传入参数。你要获取你程序中哪个窗口的消息,那就把相应的窗口句柄代入其中。
UINT wMsgFilterMin,//
UINT wMsgFilterMax);
//返回值:如果取的是WM_QUIT消息,则返回值为0,如果取的是其它消息,返回值为非0值。
//WM_QUIT消息是退出程序的消息。当我们想让程序退出时,我们就可以发一个WM_QUIT消息,让GetMessage返回0值。
TranslateMessage()
BOOL TranslateMessage( CONST MSG *lpMsg);
//对GetMessage取得的MSG消息结构中的信息进行必要的预处理。
//GetMessage函数取得的消息,要经过TranslateMessage处理一下,才可以传给DispatchMessage函数 。
//因此,TranslateMessage必放在GetMessage与DispatchMessage之间。
DispatchMessage()
LONG DispatchMessage( CONST MSG *lpMsg);
//用来完成调用WinPro回调函数并把由GetMessage取得的消息结构MSG变量中的信息传递给WinPro回调函数。
鼠标消息
随着 Windows 操作系统的流行,鼠标因为其精确定位和操作方便的优点而成为计算机不可缺少的输入设备。
鼠标的基础知识
1 .鼠标操作和鼠标消息用户在使用鼠标操作的过程中,经常会使用的主要方式有五种 ,如表所示。
操作名称 | 描述 |
单击(Click) | 按下并迅速释放鼠标按钮 |
双击(Double Click) | 连续快速完成两次单击操作 |
移动(Move) | 鼠标光标移动 |
拖动(Drag) | 按下鼠标一键不放,同时执行鼠标移动操作 |
与键盘的特殊键组合 | 在按下Ctrl键或Shift键的同时执行鼠标单击操作 |
其中,前三种操作是最为基本的操作,可以产生Windows内部定义的消息,并通过这些消息来判断用户具体执行了哪种操作。
Windows定义的鼠标消息共有20条,其中非编辑区的鼠标消息一般交由系统处理,程序只处理编辑区内的鼠标消息。编辑区内的鼠标消息共有10条,如表所示。
消息常量 | 操作描述 | 消息常量 | 操作描述 | 消息常量 | 操作描述 |
WM_MOUSEMOVE | 移动鼠标 | ||||
WM_LVBUTTONDOWN | 按下鼠标左键 | WM_RVBUTTONDBLCLK | 按下鼠标右键 | WM_MVBUTTONDOWM | 按下鼠标中键 |
WM_LBUTTONUP | 释放鼠标左键 | WM_RBUTTONUP | 释放鼠标右键 | WM_MBUTTONUP | 释放鼠标中键 |
WM_LBUTTONDBLCLK | 双击鼠标左键 | WM_RBUTTONDBLCLK | 双击鼠标右键 | WM_MBUTTONDBLCLK | 双击鼠标中键 |
对于前表所列的鼠标操作中的最后两种,不能直接使用Windows定义的消息来判断,只能通过编程,将多种消息和数据组合之后判断。
例如,判断用户是否按下鼠标左键之后进行拖动操作可以通过case语句来实现:
case WM_MOUSEMOVE:
if (wParam&MK_LBUTTON) //只处理鼠标拖动的消息
{ …… // 处理程序
}
wParam参数中保存了在消息产生时其他操作进行的状态;用户可以通过位屏蔽操作来判断在该消息产生的同时,其余操作是否正在进行。这正是在程序中判断复杂鼠标操作的基本方法。例如,上面判断拖动操作的程序段就用了位操作 wParam& MK_LBUTTON, 判断在鼠标移动(WM_MOUSEMOVE)的同时鼠标左键是否同时被接下。如果,鼠标左键同时按下,则位操作的结果为TRUE,说明当前操作为拖动操作,程序可以继续进行下一步处理。又如需要判断单击鼠标左键时是否同时按下了Ctrl键或Shift键,可以用以下程序段来处理:
case WM_ LBUTTONDOWN:
if(wParam& MK_CTROL)
{//Ctrl键同时按下
if (wParam&MK_ SHIFT)
{// Ctrl 键和Shift键都同时按下
…… // 处理程序
}
else { // Ctrl健同时按下,但 Shift键没有被按下
……. // 处理程序
}
}
else if(wParam&MK_ SHIFT)
{ // Shift键同时按下,但 Ctrl键没有被接下
…… // 处理程序
}
else
{// Shift 键和Ctrl键都未按下
…… // 处理程序
}
lParam参数保存了消息产生时鼠标所在点的坐标,其中低16位为X坐标,高16位为Y坐标。
在处理鼠标消息的时候,如果需要处理鼠标双击消息,则在注册窗口类时,窗口的风格必须包括CS_DBCLCKS。否则即使执行了双击操作,窗口也只能收到两条WM_ BUTTONUP和 WM_BUTTONDOWN消息。区分双击操作和两次单击操作是以两次击键的时间间隔为标准的。当两次击键的时间间隔小于 500毫秒时, Windows将其视为双击操作:如果两次击键的时间间隔大于500毫秒,Windows将其视为两次单击操作。500毫秒为默认的时间间隔,用户可以通过调用SetDoubleClickTime函数来修改这一时间间隔。
SetDoubleClickTime函数的原型定义如下:
BOOL SetDoubleClickTime(UINT uInterval // 新的击键时间间隔)
2.鼠标捕捉
在通常情况下,只有当鼠标位于窗体内时,窗体才能接收到鼠标的消息。如果需要接收所有的鼠标消息而不论鼠标是否在窗口内,这时可以调用SetCapture函数来实现。
SetCapture函数的原型定义如下:
HWND SetCapture (
HWND hwnd // 窗口句柄
);
调用SetCapture函数后,所有鼠标操作所产生的消息都直接发送到指定窗口。因为此时鼠标可能位于窗口之外,所以鼠标的坐标可能为负值。由于调用该函数会使其他窗口不能接收到键盘和鼠标的消息,因此在完成操作后应及时调用ReleaseCapture 函数释放鼠标捕获。
ReleaseCapture函数的原型定义如下:
BOOL ReleaseCapture(VOID);
键盘基础
Windows程序获得键盘输入的方式:键盘输入以消息的形式传递给程序的窗口过程。实际上,第一次学习消息时,键盘就是一个明显的例子:消息应该传递给应用程序的信息类型。
Windows用8种不同的消息来传递不同的键盘事件。程序可以忽略其中至少一半的消息而不会有任何问题。在大多数情况下,这些消息中包含的键盘信息会多于程序所需要的。处理键盘的部分工作就是识别出哪些消息是重要的,哪些是不重要的。
键盘基础知识:
用键盘当作输入设备,每当用户按下或释放某一个键时,会产生一个中断,该中断激活键盘驱动程序KEYBOARD.DRV来对键盘中断进行处理。 KEYBOARD.DRV程序会根据用户的不同操作进行编码,然后调用Windows用户模块USER.EXE生成键盘消息,并将该消息发送到消息队列中等候处理。
1.扫描码和虚拟码
扫描码对应着键盘上的不同键,每一个键被按下或释放时,都会产生一个唯一的扫描码作为本身的标识。扫描码依赖于具体的硬件设备,即当相同的键被按下或释放时,在不同的机器上可能产生不同的扫描码。在程序中通常使用由Windows系统定义的与具体设备无关的虚拟码。在击键产生扫描码的同时,键盘驱动程序KEYBOARD.DRV截取键的扫描码,然后将其翻译成对应的虚拟码,再将扫描码和虚拟码一齐编码形成键盘消息。所以,最后发送到消息队列的键盘消息中,既包含了扫描码又包含了虚拟码。
2.输入焦点
同一时刻,Windows中可能有多个不同的程序在运行,也就是说有多个窗口同时存在。这时,键盘由多个窗口共享,但只有一个窗口能够接收到键盘消息,这个能够接收键盘消息的窗口被称为拥有输入焦点的窗口。拥有输入焦点的窗口应该是当前的活动窗口,或者是活动窗口的子窗口,其标题和边框会以高亮度显示,以区别于其他窗口。拥有输入焦点的也可以是图标而不是窗口,此时,Windows也将消息发送给图标,只是消息的格式略有不同。
窗口过程可以通过发送WM_SETFOCUS和 WM_KILLFOCUS消息使窗体获得或失去输入焦点。程序也可以通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来判断窗体何时获得或失去输入焦点。其中WM_SETFOCUS消息表示窗口正获得输入焦点,WM_ KILLFOCUS消息表示窗口正失去输入焦点。
3.键盘消息
键盘消息分为系统键消息和非系统键消息。系统键消息是指由Aft键和其他键组合而产生的按键消息。当系统键被按下时产生WM_ SYSKEYDOWN消息,当系统键被释放时产生WM_SYSKEYUP消息。 Aft键与其他键形成的组合键通常用于对程序菜单和系统菜单进行选择,或用于在不同的程序之间进行切换。因此,系统键消息应该交由Windows进行处理,用户所编制的程序一般不处理系统键消息,而是将这些消息交由DefWindowProc函数进行处理。如果用户想对系统键消息进行处理,应该在处理完这些消息后,再将其发送给DefWindowProc函数,使得Windows系统能够正常工作。
某些击键消息可以被转换成字符消息,例如字母键、数字键等。而有些键只能产生按键消息而没有字符消息,例如 Shift键、Insert键等。消息循环中的 TranslateMessage函数可以实现从击键消息向字符消息的转化。当GetMessage函数捕获一个WM_SYSKEYDOWN消息或 WM_KEYDOWN消息后,TranslateMessage函数判断产生该消息的键是否能够被转换成字符消息,如果能,就将该消息转换成字符消息,再通过DispatchMessape函数将转换后的字符消息发送到消息队列中去。字符消息共有以下四种,如表所示。
字符 | 系统字符 | 非系统字符 |
普通字符 | WM_SYSCHAR | WM_CHAR |
死字符 | WM_SYSDEADCHAR | WM_DEADCHAR |
其中死字符是由某些特殊键盘上的按键所造成的,Windows一般忽略死字符所产生的消息。
Windows的消息一般是通过一个MSG结构体变量传送给消息处理函数的。对于键盘消息, MSG结构体变量的各个域中较重要的是lParam域和 wParam域。
wParam域:对于非字符消息,wParam域保存按键的虚拟健代码;对于字符消息, wParam域保存字符的ASCII码。
lParam域:32位的lParam变量被分为六部分,记录了以下相关信息:重复次数、OEM扫描码、扩展键标志、关联键标志、前一击键状态和转换状态。
lParam域各位的含义如表所示。
位数 | 含义 |
0-15 | 击键重复次数累加 |
16-23 | OEM扫描码 |
24 | 是否为扩展键 |
25-28 | 未定义 |
29 | 是否便用关联键,及Alt键是否同时按下 |
30 | 前一次击键状态,0表示该键前一次状态为抬起,1表示前一次状态为按下 |
31 | 转换状态 |
按键的次序不同,产生的消息也不相同。例如,按下并释放1键,读过程依次产生如表所示三条消息。按下1键所产生的消息和wParam的取值
消息??????? wParam变量取值
WM_KEYDOWN??虚拟码1
WM_CHAR????ASCII码“1”
WM_KEYUP ????虚拟码1
如果按下Shift键后再按下1键并释放,则依次产生如表所示的消息。按下 Shift键后按 1健所产生的消息和 wParam的取值
消息??????? wParam变量取值
WM_KEYDOWN ??虚拟码 VK_SHIFT
WM_KEYDOWN ??虚拟码 VK_1
WM_CHAR ASCII??码 “1”
WM_KEYUP ????虚拟码 VK_1
WM_KEYUP???? 虚拟码 VK_SHIFT
WM_SIZE消息
wParam域:保存了窗体新尺寸的左上角坐标,变量的32位分为两个部分,低16位保存X坐标,高16位保存Y坐标。
lParam域:保存了窗体新尺寸的右下角坐标,变量的32位分为两个部分,低16位保存X坐标,高16位保存Y坐标。
在编程过程中,通常通过LOWORD宏定义来获得32位变量的低16位数值,通过HIWORD宏定义来获得32位变量的高历位数值。
windows API普通函数跟回调函数有何区别的更多相关文章
- C++中 线程函数为静态函数 及 类成员函数作为回调函数
线程函数为静态函数: 线程控制函数和是不是静态函数没关系,静态函数是在构造中分配的地址空间,只有在析构时才释放也就是全局的东西,不管线程是否运行,静态函数的地址是不变的,并不在线程堆栈中static只 ...
- C++中类成员函数作为回调函数
注:与tr1::function对象结合使用,能获得更好的效果,详情见http://blog.csdn.net/this_capslock/article/details/38564719 回调函数是 ...
- C++中的Thunk技术 / 非静态类成员函数作为回调函数 的实现方法
原文:https://blog.twofei.com/616/ 用我的理解通俗地解释一下什么是C++中的Thunk技术吧! Thunk技术就是申请一段可执行的内存, 并通过手动构造CPU指令的形式来生 ...
- php变量函数,回调函数
一,变量可以直接传递函数 <?php function demo($num , $n )//$n是个函数 { for($i=0;$i<$num;++$i) { if($n($i)) { e ...
- Go基础系列:函数(2)——回调函数和闭包
回调函数和闭包 当函数具备以下两种特性的时候,就可以称之为高阶函数(high order functions): 函数可以作为另一个函数的参数(典型用法是回调函数) 函数可以返回另一个函数,即让另一个 ...
- php自定义函数之回调函数
回调函数,可以配合匿名函数和变量函数实现更加优美.复杂的一种函数结构.大理石平台价格 回调函数,就是在处理一个功能的时候,我让让这个功能自定义能力再强一些,我准许调用这个函数的时候,还可以传入一个函数 ...
- 【知识点】inline函数、回调函数、普通函数
目录 一.inline内联函数 1.1 使用 1.2 编译器对 inline 函数处理步骤 1.3 优缺点 1.3.1 优点 1.3.2 慎用内联 1.3.3 不宜使用内联 1.4 虚函数(virtu ...
- 使用匿名函数在回调函数中正确访问JS循环变量
有时候, 需要以不同的参数调用某个URL,并且在回调函数中仍然可以访问正在使用的参数, 这时候, 需要使用闭包保存当前参数, 否则, 当回调函数执行时, 之前的参数很可能早已被修改为最后一个参数了. ...
- 上传图片的回调函数,callback作为一个函数针对回调函数
Tool.ImageUpload = function (selector, callback) { /// <summary>图片上传</summary> /// <p ...
随机推荐
- 拷贝地图 CopyAndOverwriteMap()
private void CopyAndOverwriteMap() { //Get IObjectCopy interface IObjectCopy objectCopy = new Object ...
- Hexo搭建个人blog
Hexo搭建 现在只想说心累... 前几天看了几个牛人的blog,感觉他们的风格很舒服,然后就发现了Hexo这个好东西!激动的想马上自己也弄一个,昨天晚上开始看资料特别是:潘柏信写了两篇 HEXO搭建 ...
- 7.1 itertools--高效循环的创建函数
7. 函数式编程库 本库主要提供了支持函数式编程的函数和类,以及提供通用调用对象. 7.1 itertools--高效循环的创建函数 本模块主要提供了迭代器方面的操作函数,跟语言API.Haskell ...
- hdu 4950 Monster(数学题,多校8)
题目链接:pid=4950http://acm.hdu.edu.cn/showproblem.php?pid=4950">http://acm.hdu.edu.cn/showprobl ...
- js获取get传递的值
<script language="javascript" src="js/jquery-1.9.0.min.js"></script> ...
- 模仿猫眼电影App一个动画效果
看真正的猫眼效果图 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzIxMDYyMA==/font/5a6L5L2T/fontsize/400/f ...
- DRF的认证,频率,权限
1,DRF的认证 初识认证:浏览器是无状态的,一次导致每次发的请求都是新的请求,所以每次请求,服务器都会进行校验,这样就很繁琐,这趟我们就需要给每一个用户登录后一个新的标识,浏览器每次都会带着这个唯一 ...
- C编程中fread 、fwrite 用法总结
在C语言中进行文件操作时,我们经常用到fread()和fwrite(),用它们来对文件进行读写操作.下面详细绍一下这两个函数的用法. 我们在用C语言编写程序时,一般使用标准文件系统,即缓冲文件系统 ...
- Test redis
单机测试: public class RedisClient{ private Jedis jedis; private JedisPool jedisPool; private ShardedJed ...
- 解决Linux环境Oracle显示乱码
首先查看当前的编码格式 select userenv('language') from dual; 解决方法: 一.临时解决方法 切换到Oracle用户,执行 export NLS_LAN ...