一、关于起因

之前发过一篇博文,是关于VB6中跟踪鼠标移出事件的示例(http://www.cnblogs.com/alexywt/p/5891827.html)

随着业务状况的不断发展,提出了更多的挑战和问题.

其一:子类化在VB6的IDE调试阶段会出现崩溃情况,需要实现子类化的无崩溃调试;

其一:我的窗体或自定义控件中可能有很多控件需要跟踪鼠标移出事件;甚至可能会通过代码来动态添加控件,要监听移出事件,通常是用WithEvents,但VB6的该关键字不支持数组对象的事件跟踪.也就是说要找到一种方式来批量处理大量控件的鼠标移出事件.

二、修改CHooker类使子类化在IDE调试时不崩溃

我对原来的Hooker类进行了改进,使其能适应调试模式,而不至于造成IDE崩溃:废话不多说,直接上修改过之后的类模块代码如下,其中关键部分在GetWndProcAddress过程中对LogMode分类进行处理,该过程的代码源自"嗷嗷叫的老马"http://www.cnblogs.com/pctgl/articles/1586841.html

 Option Explicit

 Private Const WM_MOUSELEAVE = &H2A3&
Private Const WM_MOUSEMOVE = &H200
Private Const TME_LEAVE = &H2& Private Type TRACKMOUSEEVENTTYPE
cbSize As Long
dwFlags As Long
hwndTrack As Long
dwHoverTime As Long
End Type Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, Optional ByVal Length As Long = )
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcessHeap Lib "kernel32" () As Long
Private Declare Function HeapAlloc Lib "kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function HeapFree Lib "kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal lpMem As Long) As Long
Private Declare Function TrackMouseEvent Lib "user32" (lpEventTrack As TRACKMOUSEEVENTTYPE) As Long Private Type ThisClassSet
s_srcWndProcAddress As Long
s_Hwnd As Long
s_BlockProtect As Long
n_heapAlloc As Long
End Type Private LinkProc() As Long
Private PG As ThisClassSet
Private mMouseLeaveTracking As Boolean
Private mHookObject As Object Public Event GetWindowMessage(Result As Long, ByVal cHwnd As Long, ByVal Message As Long, ByVal wParam As Long, ByVal lParam As Long)
Public Event MouseLeave() Private Sub HookProc(Result As Long, ByVal cHwnd As Long, ByVal Message As Long, ByVal wParam As Long, ByVal lParam As Long)
Dim tTrackML As TRACKMOUSEEVENTTYPE '一个移开事件结构声明
'子类化接口过程
RaiseEvent GetWindowMessage(Result, cHwnd, Message, wParam, lParam)
Select Case Message
Case WM_MOUSEMOVE
If Not mMouseLeaveTracking Then
mMouseLeaveTracking = True
'initialize structure
tTrackML.cbSize = Len(tTrackML)
tTrackML.hwndTrack = cHwnd
tTrackML.dwFlags = TME_LEAVE
'start the tracking
TrackMouseEvent tTrackML
End If
Case WM_MOUSELEAVE
RaiseEvent MouseLeave
mMouseLeaveTracking = False
End Select
Result = CallWindowProc(PG.s_srcWndProcAddress, ByVal cHwnd&, ByVal Message&, ByVal wParam&, ByVal lParam&)
End Sub Private Function GetWndProcAddress(ByVal OrgWindowProc As Long, ByVal SinceCount As Long) As Long
' 地址指针 = GetWndProcAddress( 取第 N 个公共函数(属性) =或= 所有公共函数个数 + 第 N 个私有函数的函数地址)
Dim mePtr As Long
Dim jmpAddress As Long
Dim i As Long mePtr = ObjPtr(Me)
CopyMemory jmpAddress, ByVal mePtr,
CopyMemory jmpAddress, ByVal jmpAddress + (SinceCount - ) * + &H1C, If App.LogMode = Then ReDim LinkProc() As Long LinkProc() = &H83EC8B55
LinkProc() = &H75FFFCC4
LinkProc() = &H1075FF14
LinkProc() = &HFF0C75FF
LinkProc() = &HB90875
LinkProc() = &HFF000010
LinkProc() = &H1F883D1
LinkProc() = &H4D8D1575
LinkProc() = &H6851FC
LinkProc() = &HB8000020
LinkProc() = &H3000
LinkProc() = &H458BD0FF
LinkProc() = &HB807EBFC
LinkProc() = &H4000
LinkProc() = &HC2C9D0FF
LinkProc() = &H10 CopyMemory ByVal VarPtr(LinkProc()) + , GetProcAddress(GetModuleHandle("vba6.dll"), "EbMode"), & ' Label Sign: 0100000
CopyMemory ByVal VarPtr(LinkProc()) + , ObjPtr(Me), & ' Label Sign: 0200000
LinkProc() = jmpAddress ' Label Sign: 0300000
LinkProc() = PG.s_srcWndProcAddress ' Label Sign: 0400000 PG.n_heapAlloc = HeapAlloc(GetProcessHeap, &H8, &)
CopyMemory ByVal PG.n_heapAlloc, LinkProc(), &
VirtualProtect ByVal PG.n_heapAlloc, ByVal &, ByVal &H40&, PG.s_BlockProtect
GetWndProcAddress = PG.n_heapAlloc Else
ReDim LinkProc()
LinkProc() = &H83EC8B55
LinkProc() = &H75FFFCC4
LinkProc() = &H1075FF14
LinkProc() = &HFF0C75FF
LinkProc() = &H458D0875
LinkProc() = &H6850FC
LinkProc() = &HB8000010
LinkProc() = &H2000
LinkProc() = &H458BD0FF
LinkProc() = &H10C2C9FC CopyMemory ByVal VarPtr(LinkProc()) + , ObjPtr(Me), & ' Label Sign: 0100000
LinkProc() = jmpAddress ' Label Sign: 0200000
VirtualProtect ByVal VarPtr(LinkProc()), ByVal &, ByVal &H40&, PG.s_BlockProtect
GetWndProcAddress = VarPtr(LinkProc()) End If End Function Public Function StartHook(HookObject As Object) As Long
Dim cHwnd As Long
Set mHookObject = HookObject
cHwnd = HookObject.hWnd
'设置指定窗口的子类化
PG.s_Hwnd = cHwnd
PG.s_srcWndProcAddress = GetWindowLong(cHwnd, ByVal -&)
SetWindowLong ByVal cHwnd, ByVal -&, ByVal GetWndProcAddress(PG.s_srcWndProcAddress, )
StartHook = PG.s_srcWndProcAddress
End Function Public Property Get HookObject() As Object
Set HookObject = mHookObject
End Property Public Sub UnHook()
'取消窗口子类化
SetWindowLong ByVal PG.s_Hwnd&, ByVal -&, ByVal PG.s_srcWndProcAddress&
Set mHookObject = Nothing
End Sub Private Sub Class_Terminate()
If PG.n_heapAlloc Then
UnHook
VirtualProtect ByVal PG.n_heapAlloc, ByVal &, ByVal PG.s_BlockProtect, PG.s_BlockProtect
HeapFree GetProcessHeap, ByVal &, PG.n_heapAlloc
PG.n_heapAlloc =
End If
End Sub
三、实现批量跟踪一个窗体上多个控件的某事件

实现批量跟踪事件的思路是:在窗体上建立一个函数,来处理多个控件的指定事件

那么问题来了,我们子类化只能针对一个控件实施,假如我们直接在窗体上一个一个控件去子类化并写事件处理函数的话,也就背离了我的目的,因为这样的话有多少个控件就有很多个事件处理函数要写,我们的目的是只要一个函数来处理.

解决的方案是再建立一个包装器类,该类要完成2个事情,其一将需子类化的对象完成子类化,其二将调用的那个窗体中的事件处理函数与子类化关联,使之子类化回调函数即为该窗体中的自定义函数.

那么问题又来了,包装器类的第一个功能很容易实现,我们可以直接在该包装器类内实例化一个CHooker对象,然后调用其StartHook即可,那么要如何关联窗体中的自定义函数了?

因为该自定义函数在窗体中,是不可能通过AddressOf来取得其地址调用的,我们这里通过建立代理类实现

在VB.net中我们用代理用的很频繁,那么在VB6中要怎么做了?

首先我们建立一个接口类IMouseLeaveCallBack,代码如下

 Public Sub MouseLeave(Hooker As CHooker)

 End Sub

然后在建立代理类CMouseLeaveDelegate,代码如下所示,其关键是在初始化类过程中传递一个类型为IMouseLeaveCallBack的对象进去,随后在mHooker的MouseLeave事件回调中执行该IMouseLeaveCallBack对象的MouseLeave方法,并将mHooker传递给它,以便实现IMouseLeaveCallBack接口的类通过mHooker的HookObject得到发生鼠标移出事件的控件是谁.

 Private WithEvents mHooker As CHooker
Private mDelegate As IMouseLeaveCallBack Public Sub InitClass(HookedObject As Object, DelegateObject As IMouseLeaveCallBack)
Set mHooker = New CHooker
mHooker.StartHook HookedObject
Set mDelegate = DelegateObject
End Sub Private Sub Class_Terminate()
If mHooker Is Nothing Then Exit Sub
mHooker.UnHook
Set mHooker = Nothing
End Sub Private Sub mHooker_MouseLeave()
mDelegate.MouseLeave mHooker
End Sub
三、在窗体中实现接口函数

通过前面的处理之后,我们就可以在窗体上实现IMouseLeaveCallBack接口,并写该接口MouseLeave过程的实现代码了.

我的示例很简单,目的是为窗体上的按钮及图片框设置鼠标移动时背景色及鼠标离开后的背景色

其中鼠标移动背景色我是一个一个控件处理,这种方式非常麻烦,如果你想一个函数搞定所有控件的鼠标移动事件,可以参考鼠标移出事件的处理方式.这里我就是用来对比批量处理与单个单个处理的应用效果.

建立一个窗体,在窗体上建立一个按钮,一个PictureBox,我就只用了2个控件,注意按钮的Style要设置成Graphical,否则设置背景色无效.

在窗体中输入如下所示代码,这里我通过一个数组保存所有需要进行Hook的控件,以便能通过一个For循环遍历实施Hook.

Implements IMouseLeaveCallBack

Private mHookedObjects( To ) As Object
Private mMouseLeaveHandles( To ) As CMouseLeaveDelegate Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Command1.BackColor = RGB(, , )
End Sub Private Sub Form_Load()
Set mHookedObjects() = Me.Command1
Set mHookedObjects() = Me.Picture1
For i = To
Set mMouseLeaveHandles(i) = New CMouseLeaveDelegate
mMouseLeaveHandles(i).InitClass mHookedObjects(i), Me
Next
End Sub Private Sub IMouseLeaveCallBack_MouseLeave(Hooker As CHooker)
Hooker.HookObject.BackColor = RGB(, , )
End Sub Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Picture1.BackColor = RGB(, , )
End Sub

从以往子类化跟踪MouseLeave深入讨论VB6的自定义Hook类的更多相关文章

  1. 窗口的子类化与超类化——子类化是窗口实例级别的,超类化是在窗口类(WNDCLASS)级别的

    1. 子类化 理论:子类化是这样一种技术,它允许一个应用程序截获发往另一个窗口的消息.一个应用程序通过截获属于另一个窗口的消息,从而实现增加.监视或者修改那个窗口的缺省行为.子类化是用来改变或者扩展一 ...

  2. Win32中安全的子类化(翻译)

    关于子类化的话题虽然有些旧,但它至今仍然不失为一种开发Windows的强有力技术,在MFC的内核.甚至.NET的内核中都离不开它,希望本连载能对Windows开发的爱好者有所帮助. 原文标题:Safe ...

  3. C++ 中超类化和子类化

    超类化和子类化没有具体的代码,其实是一种编程技巧,在MFC和WTL中可以有不同的实现方法. 窗口子类化: 原理就是改变一个已创建窗口类的窗口过程函数.通过截获已创建窗口的消息,从而实现监视或修改已创建 ...

  4. MFC_1.3 控件子类化 消息反射

    控件子类化 如果想要在默认的控件类中添加一些功能,就需要子类化一个控件类 在类内可以响应控件所有的消息,并且可以添加自己的函数和数据 通过类向导子类化控件的步骤 打开类向导,创建一个 MFC 类,不要 ...

  5. 眼见为实(2):介绍Windows的窗口、消息、子类化和超类化

    眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和 ...

  6. VB6史无前例的子类化之透明按钮

    [原创文章,转发请保留版权信息] 作者:mezstd 文章地址:http://www.cnblogs.com/imez/p/3299728.html 效果图: 请原谅笔者无耻地称之为史无前例,至少在笔 ...

  7. C++ 中超类化和子类化常用API

    在windows平台上,使用C++实现子类化和超类化常用的API并不多,由于这些API函数的详解和使用方法,网上一大把.本文仅作为笔记,简单的记录一下. 子类化:SetWindowLong,GetWi ...

  8. 窗口 超类化 子类化 HOOK

    body { font-family: Bitstream Vera Sans Mono; font-size: 11pt; line-height: 1.5; } html, body { colo ...

  9. 深入理解MFC子类化

    子类化,通俗来讲就是用自己的窗口处理函数来处理特定消息,并将自己其他消息还给标准(默认)窗口处理函数.在SDK中,通过SetWindowLong来指定一个自定义窗口处理函数:SetWindowLong ...

随机推荐

  1. [MongoDB] - Shell基本命令

    在这里,我简单的记录一下MongoDB在shell客户端中使用的基本命令,比如创建/显示数据库,创建集合,删除集合等. 一.启动MongoDB并连接shell客户端 使用mongod.exe和mong ...

  2. Linux内存中的 buffer 和 cache 到底是个什么东东?

    Linux 中的 free 命令,会输出: total 总量 used  已使用 free 空闲 shared 共享内存 buffers cached 前面四项都比较好理解,一看我也就知道啥意思了.但 ...

  3. 本地计算机上的XXX服务启动后停止,某些服务在未由其它服务或程序使用时将自动停止

    创建WindowsService,以及安装和卸载网上的资料一搜一大堆,在这里就不再做演示,只说明下博主在工作中使用WindowsService服务出现的错误,以及最终的结局方案. 1.启动window ...

  4. perl open函数

    open(DATA, "<file.txt"); # <只读打开file.txt ,DATA作为句柄 open(DATA, ">file.txt&quo ...

  5. Java设计模式之《观察者模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6513651.html 观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监 ...

  6. 去掉Dedecms幻灯片的标题文字和绿色背景

    在操作dedecms站群中的站,因为看到首页的幻灯片Flash显示标题和文字,显得有点突兀,觉得应该去除dedecms 幻灯片默认的文字标题和背景,这样会感觉舒服些,下面教大家如何在模板当中去除ded ...

  7. 使用SSM框架 搭建属于自己的APP二维码合成、解析、下载

    最近公司的app上线了,在推广APP的时候出现了一个问题,因为Android和IOS的下载地址不一样,那么在推广的时候就要推广两个二维码,这样比较麻烦,如何简化我们的推广,让IOS用户扫描二维码的时候 ...

  8. python服务器环境搭建(1)——本地服务器准备

    去年十月底到新公司上班,由于公司旧系统存在各种问题同时不便于扩展,而公司领导对17年寄予很大的期望,用户量.收入要上一个新台阶,我经过全面评估后,决定全部用python重构过,开发一个基于微服务架构的 ...

  9. I3D Next-Gen Game Development with Unity3D Vol I学习笔记(上)

    这部教程主题是讲述如何在Unity中安放模型以及对场景进行优化(面对的是次世代平台).第二章看到一半,看不下,索性直接凭借Maya和Torque的经验自己操作. 看教程总结的原则如下(or tips) ...

  10. Yii2.0修改默认控制器

    设置默认控制器有两种方法 1.在/vendor/yiisoft/yii2/web/Application.PHP的第28行左右 public $defaultRoute = 'site';      ...