<2017年12月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

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位变量的高历位数值。

posted @ 2018-05-28 09:59  史D芬周  阅读(1752)  评论(0编辑  收藏  举报