东风破 发表于 2014-5-5 09:44:53

Autoit系统消息详解——GUI通知(整理)

本帖最后由 东风破 于 2014-5-5 11:15 编辑


原转帖地址:http://www.autoitx.com/forum.php?mod=viewthread&tid=111&fromuid=7662117

      很多人都对系统消息比较陌生,实际上,Autoit里用GUIGetMsg和GUICtrlSetOnEvent函数捕捉的GUI事件都属于系统消息的范畴。而在v3.1.0以后的版本里,我们就可以用GUIRegisterMsg函数来自行捕捉系统消息,藉此来实现对右击、双击、获取焦点等行为的判断。本文将探讨如何在Autoit里捕捉并处理GUI通知。

先看一下下面的例子,这个例子实现了对双击列表框的响应:
GUICreate("MyGUI", 359, 94, -1, -1)

$list = GUICtrlCreateListView("ddddddddddd", 10, 10, 320, 80)

Global Const $WM_NOTIFY = 0x004E
GUIRegisterMsg($WM_NOTIFY, "WM_Notify_Events")

GUISetState()
Do
Until GUIGetMsg() = -3

Func WM_Notify_Events($hWndGUI, $MsgID, $wParam, $lParam)
      #forceref $hWndGUI, $MsgID, $wParam
      Local Const $NM_FIRST = 0
      Local Const $NM_DBLCLK = ($NM_FIRST - 3)

      Local $tagNMHDR, $event, $hwndFrom, $code
      $tagNMHDR = DllStructCreate("int;int;int", $lParam) ;NMHDR (hwndFrom, idFrom, code)
      If @error Then Return
      $event = DllStructGetData($tagNMHDR, 3)

      If $event = $NM_DBLCLK Then MsgBox(0, "", "双击")

      $tagNMHDR = 0
      $event = 0
      $lParam = 0
EndFunc   ;==>WM_Notify_Events

下面对整段代码进行分析:

Global Const $WM_NOTIFY = 0x004E
这一句是对系统消息的赋值,和一些高级语言不同,Autoit的库里并不包含系统消息,如果我们想要使用某一消息就必须先对其赋值。

类似的,我们在建立GUI时都会加上一句#include <GUIConstants.au3>,而GUIConstants.au3里就是对GUI样式的赋值。

另外,你可以使用 Win32 API Constants 这个工具来查看不同系统消息的值。



GUIRegisterMsg($WM_NOTIFY, "WM_Notify_Events")
注册WM_NOTIFY这个消息ID(message),每当有新的通知(notification)产生时就会执行WM_Notify_Events这个函数。要捕捉不同的系统通知就需要注册不同的消息ID,类似的消息ID还有WM_COMMAND、WM_COMMNOTIFY等。

注意,我在描述系统消息时都没有加上$号,给变量加$号貌似是Autoit的特色,在后文提到系统消息时我都会省略掉$号。



Func WM_Notify_Events($hWndGUI, $MsgID, $wParam, $lParam)

...

EndFunc ;==>WM_Notify_Events
每当有新的通知产生时这个函数就会被执行,其中$wParam就是发送通知的控件ID,而$event就是被触发的通知。

还有一点很重要,不同的消息ID传递的参数意义也不同,后文会继续提到。



If $event = $NM_DBLCLK Then MsgBox(0, "", "双击")
如果系统通知是双击就弹出对话框,你还可以用$wParam来判断具体控件。



经过上面的阅读相信大家对系统消息处理的基本原理有了了解,下面我们将了解到如何找到并使用我们想要的系统消息。

所有的GUI通知我们都可以在MSDN的控件页面下找到,地址是:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773173(v=vs.85).aspx

GUI通知分为两类,一类是通用的,一类是专门针对某一种控件的。通用消息可以在旁边侧栏里的General Control Reference下找到,其余的通知直接往下翻,然后点开不同的控件类型就是了。

前文我已说到,不同的系统通知需要注册不同的消息ID,具体的对应关系我们也可以在MSDN上找到。

比如NM_DBLCLK通知,在MSDN上是这样描述的:


Notifies a control's parent window that the user has double-clicked the left mouse button within the control. NM_DBLCLK is sent in the form of a WM_NOTIFY message.

也就是说需要注册WM_NOTIFY这个消息ID。

再如LBN_SETFOCUS通知,我们可以找到这样几句话:


An application sends the LBN_SETFOCUS notification message when a list box receives the keyboard focus. The parent window of the list box receives this notification message through the WM_COMMAND message.

这自然是说需要注册WM_COMMAND这个消息。

最后还剩下一个问题,就是不同的消息ID传递的参数不同,具体信息需要我们在MSDN里搜索。

比如说WM_NOTIFY这个消息,我们搜索到的描述页面是http://msdn.microsoft.com/en-us/library/windows/desktop/bb775583(v=vs.85).aspx。里面有这样的描述:


lResult = SendMessage(            // returns LRESULT in lResult
         (HWND) hWndControl,            // handle to destination control
         (UINT) WM_NOTIFY,            // message ID
         (WPARAM) wParam,            // = (WPARAM) (int) idCtrl;
         (LPARAM) lParam            // = (LPARAM) (LPNMHDR) pnmh;
);

也就是说$wParam变量返回控件ID,而$lParam则包含了我们想要的通知类型。

再往下翻,有这样的话:


pnmh
Pointer to an NMHDR structure that contains the notification code and additional information. For some notification messages, this parameter points to a larger structure that has the NMHDR structure as its first member.

也就是说$lParam这个变量为NMHDR结构,我们这样分解$lParam:


$tagNMHDR = DllStructCreate("int;int;int", $lParam) ;NMHDR (hwndFrom, idFrom, code)
$event = DllStructGetData($tagNMHDR, 3)
这样我们就可以由$event得到具体的通知类型。

另外我们还需要明晰一下概念,像NM_DBLCLK、NM_RCLICK、CBN_SELCHANGE之类的属于通知(notification),而WM_COMMAND、WM_NOTIFY等则属于消息(message)。

再看到下面一个例子:
GUICreate("MyGUI", 359, 94, -1, -1)

$list = GUICtrlCreateLabel("ddddddddddd", 10, 10, 320, 80)

Global Const $WM_COMMAND = 0x0111

GUIRegisterMsg($WM_COMMAND, "MY_WM_COMMAND")

GUISetState()
While 1
      $msg = GUIGetMsg()
      Select
                Case $msg = -3
                        ExitLoop
                Case Else
                        ;;;
      EndSelect
WEnd

Func MY_WM_COMMAND($hWnd, $msg, $wParam, $lParam)
      Local $nNotifyCode = BitShift($wParam, 16)
      Local $nID = BitAND($wParam, 0xFFFF)
      Local $hCtrl = $lParam

      Local Const $STN_DBLCLK = 1

      If $nNotifyCode = $STN_DBLCLK Then MsgBox(0, "", "双击")

EndFunc   ;==>MY_WM_COMMAND
在这个例子里我想要捕捉文字控件的双击通知,在Static Controls里我得知双击通知是STN_DBLCLK,而我需要注册WM_COMMAND消息。

在WM_COMMAND里我又找到了下面的话:


WM_COMMAND wNotifyCode = HIWORD(wParam);
   wID = LOWORD(wParam);
   hwndCtl = (HWND) lParam;

即$lParam是控件ID,而$wParam包含了通知类型。再看下面的话:


wNotifyCode
Value of the high-order word of wParam. Specifies the notification code if the message is from a control. If the message is from an accelerator, this parameter is 1. If the message is from a menu, this parameter is 0.

可知我们就需要用下面的代码分解$wParam:


Local $nNotifyCode = BitShift($wParam, 16)
这样$nNotifyCode 里就储存了通知类型。

最后我们就用下面的代码来提示双击操作:


If $nNotifyCode = $STN_DBLCLK Then MsgBox(0, "", "双击")
看了上面的例子相信大家已经能够熟练处理GUI通知,最后我再给出一个例子供大家举一反三:

拖动滚动条设置窗口透明度:
$gui = GUICreate("MyGUI", 392, 86, -1, -1)

$Slider_1 = GUICtrlCreateSlider(30, 10, 340, 60)
GUICtrlSetLimit($Slider_1, 255)
GUICtrlSetData($Slider_1, 255)

Global Const $WM_HSCROLL = 0x114
Global Const $TB_LINEUP = 0
Global Const $TB_LINEDOWN = 1
Global Const $TB_PAGEUP = 2
Global Const $TB_PAGEDOWN = 3
Global Const $TB_THUMBPOSITION = 4
Global Const $TB_THUMBTRACK = 5

GUIRegisterMsg($WM_HSCROLL, "MY_WM_HSCROLL")

GUISetState()
While 1
      $msg = GUIGetMsg()
      Select
                Case $msg = -3
                        ExitLoop
                Case Else
                        ;;;
      EndSelect
WEnd

Func MY_WM_HSCROLL($hWnd, $msg, $wParam, $lParam)
      $nScrollCode = BitAND($wParam, 0x0000FFFF)
      $nPos = BitShift($wParam, 16)
      $hwndScrollBar = $lParam
      Switch $nScrollCode
                Case $TB_LINEDOWN, $TB_LINEUP, $TB_PAGEDOWN, $TB_PAGEUP, $TB_THUMBTRACK
                        WinSetTrans($gui, "", GUICtrlRead($Slider_1))
      EndSwitch
EndFunc   ;==>MY_WM_HSCROLL

ps.本来想多给几个例子的,不过再给文章长度就会超过限制...下周我还会继续讲解系统消息的其他应用。

东风破 发表于 2014-5-5 11:18:19

一直是比葫芦画瓢用GUIRegisterMsg,偶然间搜到这个帖子,终于可以好好学习下其中的原理了。原帖过去久远,看着有点费劲,特此意淫的整理下。

zhongzijie 发表于 2014-5-5 14:37:55

謝謝樓主整理分享。

lin6051 发表于 2014-5-7 08:26:29

学习了 不是很明白

xms77 发表于 2014-5-11 21:39:20

好久不用,忘了,温故一下,学到了许多新的东西。

mm22mm 发表于 2014-5-12 13:43:08

不懂,mark

f4李文杨 发表于 2014-6-17 18:28:43

楼主讲的很精P!{:face (303):}

luke 发表于 2014-6-26 10:29:41

太好了。谢谢!

ncxj 发表于 2016-6-15 23:02:04

很好,整理一下好看多了

hnfeng 发表于 2016-6-17 08:45:17

整理的太好了。谢谢!

rane 发表于 2016-6-17 09:59:21

收藏了,学习了

hncsjj 发表于 2016-6-21 23:27:03

学习收藏了

hncsjj 发表于 2016-6-21 23:27:04

学习收藏了

mysiap 发表于 2016-6-24 09:04:28

學習了,謝謝樓主的分享{:face (88):}

mHWhD 发表于 2018-2-7 20:38:29

谢谢楼主分享!!!!
页: [1]
查看完整版本: Autoit系统消息详解——GUI通知(整理)