PctGL SERIES  
http://pctgl.cnblogs.com
看此文的朋友们, 请在看完这篇文章后,继续看下一篇,地址如下,

 http://www.cnblogs.com/pctgl/articles/1586841.html

这是例子:  https://files.cnblogs.com/pctgl/iSubClass.rar 

 

上一篇文章发出来之后,没想到很多人都注意到了,而且也用到了

但上次的代码还存有稍许遗憾就是不能进行子类化,这次我改了下,做了一个子类化专用函数

GetWndProcAddress
此函数专用于子类化接口的获取

Private Function GetWndProcAddress(ByVal SinceCount As Long) As Long
'   地址指针 = GetWndProcAddress( 取第 N 个公共函数(属性)  =或= 所有公共函数个数 + 第 N 个私有函数的函数地址)
    Dim mePtr As Long
    Dim jmpAddress As Long
    mePtr = ObjPtr(Me)
    CopyMemory jmpAddress, ByVal mePtr, 4
    CopyMemory jmpAddress, ByVal jmpAddress + (SinceCount - 1) * 4 + &H1C, 4

    ReDim LinkProc(10)
    LinkProc(0) = &H83EC8B55
    LinkProc(1) = &HFC8B14EC
    LinkProc(2) = &H56FC758D
    LinkProc(3) = &H3308758D
    LinkProc(4) = &HFC04B1C9
    LinkProc(5) = &HFF68A5F3
    LinkProc(6) = &HB8FFFFFF
    LinkProc(7) = &HFFFFFFFF
    LinkProc(8) = &H48BD0FF
    LinkProc(9) = &H10C2C924
    
    CopyMemory ByVal VarPtr(LinkProc(5)) + 3, mePtr
    CopyMemory ByVal VarPtr(LinkProc(7)), jmpAddress
    GetWndProcAddress = VarPtr(LinkProc(0))
End Function

参数 SinceCount 和上次文章介绍的用法相同,取消了参数部分的设置,于是乎接口函数的形式也就被固化了

接口函数
Private Sub MsgHook(Result As Long, ByVal cHwnd As Long, ByVal Message As Long, ByVal wParam As Long, ByVal lParam As Long)
  ‘这里可以写你的处理过程,比如 if message = 100 then msgbox “xx” 之后可以决定是否调用 callwindowproc
     ’ Result 必不可少, Result 参数保存回调函数的返回值。
    Result = CallWindowProc(〔getwindowlong的返回值〕, cHwnd, Message, wParam, lParam)
End Sub

说明:
接口函数请务必按照如上形式声明和使用
1. 必须是 Sub 的函数形式,不能为 Function
2. Result 参数必须存在,所有参数传值,传址方式,参数类型,顺序都不允许改变
3.   刚才写了一堆,把问题详细的说了一遍,突然之间 FireFox 崩溃了

   我也不写了,谁对这个问题有兴趣,对他的应用,和原理想更进一步了解的请来我Q群讨论吧   18403077


GetWndProcAddress 函数的中转汇编代码:
'    00151BEA    55              PUSH EBP
'    00151BEB    8BEC            MOV EBP,ESP
'    00151BED    83EC 10         SUB ESP,14
'    00151BF0    8BFC            MOV EDI,ESP
'    00151BF2    8D75 FC         LEA ESI,DWORD PTR SS:[EBP-4]
'    00151BF5    56              PUSH ESI
'    00151BF6    8D75 08         LEA ESI,DWORD PTR SS:[EBP+8]
'    00151BF9    33C9            XOR ECX,ECX
'    00151BFB    B1 04           MOV CL,4
'    00151BFD    FC              CLD
'    00151BFE    F3:A5           REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
'    00151C00    68 00100000     PUSH 1000
'    00151C05    B8 00200000     MOV EAX,2000
'    00151C0A    FFD0            CALL EAX
'    00401234    8B0424          MOV EAX,DWORD PTR SS:[ESP]
'    00151C10    C9              LEAVE
'    00151C11    C2 1000         RETN 10

----------------------------------------

发一个例子,演示一下
新建 标准 exe 工程, 添加一个 Form1 , 一个 Class1
Class1:  类模块代码:
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, Optional ByVal Length As Long = 4)
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 Type ThisClassSet
    s_srcWndProcAddress     As Long
    s_Hwnd                  As Long
    ‘ Made By PctGL
End Type
Dim PG              As ThisClassSet
Dim LinkProc()      As Long
Private Const WM_LBUTTONDBLCLK = &H203

Private Sub MsgHook(Result As Long, ByVal cHwnd As Long, ByVal Message As Long, ByVal wParam As Long, ByVal lParam As Long)
    If Message = WM_LBUTTONDBLCLK Then
        MsgBox "WM_LBUTTONDBLCLK"
        
    End If
    
    Result = CallWindowProc(PG.s_srcWndProcAddress, cHwnd, Message, wParam, lParam)
End Sub


Private Function GetWndProcAddress(ByVal SinceCount As Long) As Long
'   地址指针 = GetWndProcAddress( 取第 N 个公共函数(属性)  =或= 所有公共函数个数 + 第 N 个私有函数的函数地址)
    Dim mePtr As Long
    Dim jmpAddress As Long
    mePtr = ObjPtr(Me)
    CopyMemory jmpAddress, ByVal mePtr, 4
    CopyMemory jmpAddress, ByVal jmpAddress + (SinceCount - 1) * 4 + &H1C, 4

    ReDim LinkProc(10)
    LinkProc(0) = &H83EC8B55
    LinkProc(1) = &HFC8B14EC
    LinkProc(2) = &H56FC758D
    LinkProc(3) = &H3308758D
    LinkProc(4) = &HFC04B1C9
    LinkProc(5) = &HFF68A5F3
    LinkProc(6) = &HB8FFFFFF
    LinkProc(7) = &HFFFFFFFF
    LinkProc(8) = &H48BD0FF
    LinkProc(9) = &H10C2C924
    
    CopyMemory ByVal VarPtr(LinkProc(5)) + 3, mePtr
    CopyMemory ByVal VarPtr(LinkProc(7)), jmpAddress
    GetWndProcAddress = VarPtr(LinkProc(0))
End Function

Function SetMsgHook(ByVal cHwnd As Long) As Long
    If cHwnd <> PG.s_Hwnd Then
        PG.s_srcWndProcAddress = SetWindowLong(cHwnd, -4&, GetWndProcAddress(3))
        PG.s_Hwnd = cHwnd
        SetMsgHook = PG.s_srcWndProcAddress
    End If
End Function

Sub SetMsgUnHook(Optional WndProcAddress As Long)
    Dim i As Long
    i = WndProcAddress
    If WndProcAddress = 0 Then i = PG.s_srcWndProcAddress
    SetWindowLong PG.s_Hwnd, -4&, i
End Sub

Form1:
Option Explicit

Dim m  As New Class1

Private Sub Form_Load()
    '设置子类化
    m.SetMsgHook hWnd

End Sub

Private Sub Form_Unload(Cancel As Integer)
    '取消子类化
    m.SetMsgUnHook
End Sub

直接运行就可以了, 双击窗口客户区,可以看到测试的 MsgBox 提示

更多功能大家可以进一步开发,比如加个事件,将消息转发给窗口,就可以在窗口代码里面直接处理了

无论  GetWndProcAddress 或是 GetClassProcAddress 都只适用于类模块。
标准模块,窗口,控件都不能直接用的。
posted on 2009-09-15 01:16  PctGL  阅读(1547)  评论(1编辑  收藏  举报