Форум программистов, компьютерный форум, киберфорум
Visual Basic
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.52/64: Рейтинг темы: голосов - 64, средняя оценка - 4.52
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79

Вызов любых функций по указателю

17.04.2014, 19:11. Показов 13755. Ответов 57

Студворк — интернет-сервис помощи студентам
Пока не работал форум, ковырялся в рантайме VB .
Иследуя функции VBA6 придумал способ вызова функций по указателю.
Все просто. Объявляем прототип функции (пустую функцию), где дополнительно первым параметром будет передаваться адрес функции. Далее пропатчиваем его, таким образом чтобы он перекидывал нас по адресу заданному первым параметром. Таким образом можно вызывать функции в стандартных модулях, модулях класса, формы, API-функции (например полученные через LoadLibrary и GetProcAddress).
Одно замечание, пока не выяснил причину, желательно запускать проект через Ctrl+F5, т.к. иногда может не работать указатель или же вообще происходить вылет. А так работает и в IDE и в скомпилированном виде.

Пример вызова по указателю обычных функций модуля.
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
Private Declare Function GetMem4 Lib "msvbvm60" (src As Any, dst As Any) As Long
Private Declare Function VirtualProtect Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Private Declare Sub EbGetExecutingProj Lib "vba6" (hProject As Long)
Private Declare Function TipGetFunctionId Lib "vba6" (ByVal hProj As Long, ByVal bstrName As Long, ByRef bstrId As Long) As Long
Private Declare Function TipGetLpfnOfFunctionId Lib "vba6" (ByVal hProject As Long, ByVal bstrId As Long, ByRef lpAddress As Long) As Long
Private Declare Sub SysFreeString Lib "oleaut32" (ByVal lpbstr As Long)
 
Private Const PAGE_EXECUTE_READWRITE = &H40
 
' Пример вызова функции по указатею
Public Sub Main()
 
    ' Пропатчиваем функции, перед первым вызовом
    PatchFunc "Proto1", AddressOf Proto1
    PatchFunc "Proto2", AddressOf Proto2
 
    MsgBox Proto1(AddressOf Func1, 1, "Вызов")
    MsgBox Proto1(AddressOf Func2, 2, "По указателю")
    MsgBox Proto1(AddressOf Func3, 3, ";)")
 
    Call Proto2(AddressOf Sub1)
    Call Proto2(AddressOf Sub2)
End Sub
 
' Прототип функций
Private Function Proto1(ByVal Addr As Long, ByVal x As Long, y As String) As String
End Function
Private Sub Proto2(ByVal Addr As Long)
End Sub
' Функции
Private Function Func1(ByVal x As Long, y As String) As String
    Func1 = "Func1_" & y
End Function
Private Function Func2(ByVal x As Long, y As String) As String
    Func2 = "Func2_" & y
End Function
Private Function Func3(ByVal x As Long, y As String) As String
    Func3 = "Func3_" & y
End Function
Private Sub Sub1()
    MsgBox "Sub1"
End Sub
Private Sub Sub2()
    MsgBox "Sub2"
End Sub
 
' Вспомогательные функции
Private Sub PatchFunc(FuncName As String, ByVal Addr As Long)
    Dim lpAddr As Long, hProj As Long, sId As Long, InIDE As Boolean
 
    Debug.Assert MakeTrue(InIDE)
 
    ' Получаем адрес функции
    If InIDE Then
        EbGetExecutingProj hProj
        TipGetFunctionId hProj, StrPtr(FuncName), sId
        TipGetLpfnOfFunctionId hProj, sId, lpAddr
        SysFreeString sId
    Else
        lpAddr = GetAddr(Addr)
        VirtualProtect lpAddr, 8, PAGE_EXECUTE_READWRITE, 0
    End If
 
    ' Записываем вставку
    ' Запускать только по Ctrl+F5!!
    ' pop eax
    ' pop ecx
    ' push eax
    ' jmp ecx
 
    GetMem4 &HFF505958, ByVal lpAddr
    GetMem4 &HE1, ByVal lpAddr + 4
End Sub
 
Private Function GetAddr(ByVal Addr As Long) As Long
    GetAddr = Addr
End Function
Public Function MakeTrue(ByRef bvar As Boolean) As Boolean
    bvar = True: MakeTrue = True
End Function

Вызов метода класса (как вычислить адреса напишу позже).
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
' Пример вызова метода по указатею
Public Sub Main()
    Dim lpFunc As Long, Obj As Class1, ret As Long
 
    Set Obj = New Class1
 
    GetMem4 ByVal ObjPtr(Obj), lpFunc
    GetMem4 ByVal lpFunc + &H1C, lpFunc
 
    PatchFunc "Class1_ZZZ", AddressOf Class1_ZZZ
 
    Call Class1_ZZZ(lpFunc, Obj, 123, 567, ret)
End Sub
 
' Прототип функций
Private Function Class1_ZZZ(ByVal Addr As Long, ByVal Obj As Class1, ByVal o As Long, ByVal b As Long, ret As Long) As Long
End Function
Класс:
Visual Basic
1
2
3
4
Public Function ZZZ(ByVal o As Long, ByVal b As Long) As Long
    MsgBox o & " " & b
    ZZZ = 5
End Function

Вызов API функций:
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
' Пример вызова метода по указатею
Public Sub Main()
    Dim hUser As Long, hGDI As Long
    Dim DC As Long
 
    hUser = LoadLibrary("user32")
    hGDI = LoadLibrary("gdi32")
 
    PatchFunc "GetDC", AddressOf GetDC
    PatchFunc "ReleaseDC", AddressOf ReleaseDC
    PatchFunc "Ellipse", AddressOf Ellipse
 
    DC = GetDC(GetProcAddress(hUser, "GetDC"), 0)
    Ellipse GetProcAddress(hGDI, "Ellipse"), DC, 0, 0, 500, 500
    ReleaseDC GetProcAddress(hUser, "ReleaseDC"), 0, DC
End Sub
 
' Прототип функций
Private Function GetDC(ByVal Addr As Long, ByVal hWnd As Long) As Long
End Function
Private Function ReleaseDC(ByVal Addr As Long, ByVal hWnd As Long, ByVal hdc As Long) As Long
End Function
Private Function Ellipse(ByVal Addr As Long, ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
End Function
5
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
17.04.2014, 19:11
Ответы с готовыми решениями:

Вызов функций по указателю
Введение Как известно, прямой работы с указателями в VB нет. Однако есть функции для почти прямой работы с ними – частично скрытые...

Вызов функции по указателю с параметрами
Здравствуйте, видел пример как это можно сделать, но примеры не рабочие, либо я не так что - то делаю, как передать параметры и выполнить...

Вызов функции по указателю из класса
Такой расклад. Допустим имеем код: #include <iostream> using namespace std; template <class _Tp> class my_mem_fun_t { ...

57
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
26.04.2014, 00:33  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Dragokas Посмотреть сообщение
например с zipfldr.dll означает, что у нее нет доступных интерфейсов?
Нет библиотеки типов внутри.
Цитата Сообщение от Dragokas Посмотреть сообщение
Можно ли доработать программу, чтобы вначале списка еще и показывала список импортируемых/экспортируемых API?
Было бы вообще замечательно.
Конечно можно, как-нибудь сделаю.
Цитата Сообщение от FelixMacintosh Посмотреть сообщение
почему не отображаются аргументы в функциях и свойствах
если и такое можно будет сделать то вообще можно отказаться от среды IDE
Можно, это вообще просто делается.
2
 Аватар для Антихакер32
1201 / 473 / 46
Регистрация: 06.01.2014
Сообщений: 1,797
Записей в блоге: 19
26.04.2014, 00:59
Если уж реч зашла о пожеланиях...
то картинка Event в наборе классов выглядит как картинка процедуры...
хотелось бы чтоб там была привычная молния

Добавлено через 9 минут
а так... выглядет впечатляюще...
я уже говорил что вполне можно использовать и как отдельное приложение
и как встроенный компонент
0
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
01.06.2014, 16:24  [ТС]
Сделал небольшую модификацию. Теперь можно просматривать импорт, экспорт и отложенный импорт. Также небольшим модификациям подвергся и COM-просмотрщик.
Library info 2
Миниатюры
Вызов любых функций по указателю  
2
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18031 / 7734 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
23.11.2014, 00:31
Вот еще интересный код: http://www.freevbcode.com/ShowCode.asp?ID=1863

Вопрос к посту № 1:
- как в этом случае получить код ошибки API функции ?
0
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
24.11.2014, 01:43  [ТС]
Цитата Сообщение от Dragokas Посмотреть сообщение
Вот еще интересный код: http://www.freevbcode.com/ShowCode.asp?ID=1863
Автор видимо не знал тогда еще про DEP. Код не будет работать на новых системах.
Цитата Сообщение от Dragokas Посмотреть сообщение
- как в этом случае получить код ошибки API функции ?
Либо вызвать по указателю, но не должно быть никаких промежуточных вызовов API устанавливающих код последней ошибки. Также можно вызывать через Declare, но нужно иметь в виду что при первом вызове функции происходит вызов дополнительных функций LoadLibrary, GetProcAddress и других, которые изменяют код последней ошибки. Можно просто вызвать вначале GetLastError и далее вызывать ее для получения ошибки. Также можно вызывать через декларированную в tlb GetLastError. Самое главное чтобы не было никаких вызовов API функций устанавливающих код ошибки между функцией генерирующей ошибку и GetLastError, так что нельзя делать пошаговое выполнение между такими вызовами, т.к. код и IDE работают в одном потоке и всякие неочевидные вызовы (например перерисовка интерфейса или что-то еще) вызывают кучу API функций. Также есть ньюансы вызовов методов класса, очищения переменных и может что-то еще. Если что-то не будет получаться - пиши сюда, я скажу как правильно делать.
1
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
09.10.2015, 18:14  [ТС]
Я немного модифицировал модуль (теперь он стал совсем маленький):
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Option Explicit
 
Private Declare Function GetMem4 Lib "msvbvm60" ( _
                         ByRef src As Any, _
                         ByRef dst As Any) As Long
Private Declare Function VirtualProtect Lib "kernel32" ( _
                         ByVal lpAddress As Long, _
                         ByVal dwSize As Long, _
                         ByVal flNewProtect As Long, _
                         ByRef lpflOldProtect As Long) As Long
 
Private Const PAGE_EXECUTE_READWRITE = &H40
 
' // Helpers functions
Public Sub PatchFunc(ByVal Addr As Long)
    Dim InIDE As Boolean
 
    Debug.Assert MakeTrue(InIDE)
 
    If InIDE Then
        GetMem4 ByVal Addr + &H16, Addr
    Else
        VirtualProtect Addr, 8, PAGE_EXECUTE_READWRITE, 0
    End If
 
    GetMem4 &HFF505958, ByVal Addr
    GetMem4 &HE1, ByVal Addr + 4
End Sub
 
Public Function MakeTrue(ByRef bvar As Boolean) As Boolean
    bvar = True: MakeTrue = True
End Function
Также добавил один пример - аналог С++ функции qSort, где в качестве аргумента сравнения передается пользовательская функция:
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
Option Explicit
 
Private Type Vector2D
    posX As Single
    posY As Single
End Type
 
Private Declare Sub memcpy Lib "kernel32" _
                    Alias "RtlMoveMemory" ( _
                    ByRef Destination As Any, _
                    ByRef Source As Any, _
                    ByVal Length As Long)
                    
' // Buffer for exchanging
Dim buffer()    As Byte
Dim isInit      As Boolean
 
' // Calling of the standard functions using the pointers
Public Sub Main()
    Dim lngArray()  As Long
    Dim index       As Long
    
    ' // We're testing the function that sorts the long-array
    ReDim lngArray(99)
    
    For index = 0 To UBound(lngArray)
        lngArray(index) = Rnd * 100
    Next
    
    ' // Magic of the function pointers
    QuickSort VarPtr(lngArray(0)), UBound(lngArray) + 1, Len(lngArray(0)), AddressOf ComparatorLong
    
    ' // Now we're testing the function that sorts the string-array
    Dim strArray()  As String
    
    ReDim strArray(5)
    
    strArray(0) = "Calling"
    strArray(1) = "of the standard functions"
    strArray(2) = "using the pointers"
    strArray(3) = "on VB6"
    strArray(4) = "by The trick"
    strArray(5) = "2015"
    
    ' // We're calling same function using the magic of pointers
    QuickSort VarPtr(strArray(0)), UBound(strArray) + 1, 4, AddressOf ComparatorString
    
    ' // Now we're testing the function that sorts the UDT-array (2D-vectors)
    ' // For example we'll sorting the array by vector length
    Dim vecArray() As Vector2D
    
    ReDim vecArray(99)
    
    For index = 0 To UBound(vecArray)
        vecArray(index).posX = Rnd * 10
        vecArray(index).posY = Rnd * 10
    Next
    
    ' // We're calling same function for the sorting of the UDT-array
    QuickSort VarPtr(vecArray(0)), UBound(vecArray) + 1, LenB(vecArray(0)), AddressOf ComparatorVector2D
    
    ' // Test length
    For index = 0 To UBound(vecArray)
        Debug.Print Sqr(vecArray(index).posX ^ 2 + vecArray(index).posY ^ 2)
    Next
    
End Sub
 
' // This callback function which sorts two long values
Public Function ComparatorLong( _
                ByRef lItem1 As Long, _
                ByRef lItem2 As Long) As Long
    ComparatorLong = Sgn(lItem1 - lItem2)
End Function
 
' // This callback function which sorts two string values
Public Function ComparatorString( _
                ByRef lItem1 As String, _
                ByRef lItem2 As String) As Long
    ComparatorString = StrComp(lItem1, lItem2, vbTextCompare)
End Function
 
' // This callback function which sorts two 2D-vectors values by length
Public Function ComparatorVector2D( _
                ByRef lItem1 As Vector2D, _
                ByRef lItem2 As Vector2D) As Long
    ' // Optimize sqr
    ComparatorVector2D = Sgn((lItem1.posX * lItem1.posX + lItem1.posY * lItem1.posY) - _
                             (lItem2.posX * lItem2.posX + lItem2.posY * lItem2.posY))
End Function
 
' // Quick-sort using the callback function for a comparing
' // This function uses callback function (lpfnComparator)
Public Sub QuickSort( _
           ByVal lpFirstPtr As Long, _
           ByVal lNumOfItems As Long, _
           ByVal lSizeElement As Long, _
           ByVal lpfnComparator As Long)
           
    Dim lpI     As Long
    Dim lpJ     As Long
    Dim lpM     As Long
    Dim lpLast  As Long
    
    If Not isInit Then
        ' // Initialize patching and buffer for exchanging
        ReDim buffer(lSizeElement - 1)
        PatchFunc AddressOf MainComparator
        isInit = True
        
    End If
    
    lpLast = lpFirstPtr + (lNumOfItems - 1) * lSizeElement
    lpI = lpFirstPtr
    lpJ = lpLast
    lpM = lpFirstPtr + ((lNumOfItems - 1) \ 2) * lSizeElement
 
    Do Until lpI > lpJ
        
        ' // Call function that being passed into the lpfnComparator parameter
        Do While MainComparator(lpfnComparator, lpI, lpM) = -1
            lpI = lpI + lSizeElement
        Loop
        
        ' // Call function that being passed into the lpfnComparator parameter
        Do While MainComparator(lpfnComparator, lpJ, lpM) = 1
            lpJ = lpJ - lSizeElement
        Loop
        
        ' // Exchanging
        If (lpI <= lpJ) Then
            
            If lpI = lpM Then
                lpM = lpJ
            ElseIf lpJ = lpM Then
                lpM = lpI
            End If
            
            If lSizeElement > UBound(buffer) + 1 Then
                ReDim buffer(lSizeElement - 1)
            End If
            
            memcpy buffer(0), ByVal lpI, lSizeElement
            memcpy ByVal lpI, ByVal lpJ, lSizeElement
            memcpy ByVal lpJ, buffer(0), lSizeElement
  
            lpI = lpI + lSizeElement
            lpJ = lpJ - lSizeElement
            
        End If
        
    Loop
 
    If lpFirstPtr < lpJ Then
        QuickSort lpFirstPtr, (lpJ - lpFirstPtr) \ lSizeElement + 1, lSizeElement, lpfnComparator
    End If
    
    If lpI < lpLast Then
        QuickSort lpI, (lpLast - lpI) \ lSizeElement + 1, lSizeElement, lpfnComparator
    End If
    
End Sub
 
' // Prototype for comparator function
' // If lpItem1 > lpItem2 then function return 1
' // If lpItem1 = lpItem2 then function return 0
' // If lpItem1 < lpItem2 then function return -1
Public Function MainComparator( _
                ByVal lpAddressOfFunction As Long, _
                ByVal lpItem1 As Long, _
                ByVal lpItem2 As Long) As Long
End Function
3
0 / 0 / 0
Регистрация: 23.07.2020
Сообщений: 5
24.12.2021, 16:34
Анатолий, скажите возможно ли пропатчить прототип функции находящейся в модуле класса чтобы далее из класса вызывать функцию по заданному в свойстве адресу.

Вычисляю адрес от vTable, передаю PatchFunc, но получаю вылет при попытке вызвать по указателю.
0
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
24.12.2021, 19:00  [ТС]
Цитата Сообщение от kasimm Посмотреть сообщение
Анатолий, скажите возможно ли пропатчить прототип функции находящейся в модуле класса чтобы далее из класса вызывать функцию по заданному в свойстве адресу.
Не совсем понял как это должно выглядеть. Если задача сделать универсальный вызыватель, то просто используй DispCallFunc, тут уже неоднократно были примеры.
0
0 / 0 / 0
Регистрация: 23.07.2020
Сообщений: 5
12.01.2022, 17:42
Цитата Сообщение от The trick Посмотреть сообщение
Если задача сделать универсальный вызыватель
Такой задачи не стоит, нужна функция обратного вызова для объекта класса, хотел использовать ваш метод вызова любых функций по указателю, но там предполагается, что прототип функции (функция-пустышка) лежит в общем модуле bas и PatchFunc передаётся её адрес полученный AddressOf после чего она делает jamp на переданный ей адрес функции при вызове. Я же хотел положить прототип функции в модуль класса, в Class_Initialize вычисляю адрес прототипа от vTable по вашему же методу и вызываю PatchFunc, но фокус не работает. На сам патч ошибки нет, а при вызове функции по указателю приложение крашится.
0
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
12.01.2022, 17:54  [ТС]
Цитата Сообщение от kasimm Посмотреть сообщение
Такой задачи не стоит, нужна функция обратного вызова для объекта класса, хотел использовать ваш метод вызова любых функций по указателю, но там предполагается, что прототип функции (функция-пустышка) лежит в общем модуле bas и PatchFunc передаётся её адрес полученный AddressOf после чего она делает jamp на переданный ей адрес функции при вызове. Я же хотел положить прототип функции в модуль класса, в Class_Initialize вычисляю адрес прототипа от vTable по вашему же методу и вызываю PatchFunc, но фокус не работает. На сам патч ошибки нет, а при вызове функции по указателю приложение крашится.
А зачем все эти сложности, почему бы не использовать интерфейсную ссылку для этого? Просто чтобы вызвать метод класса нужна как-минимум ссылка на объект у которого будет вызываться метод (статических методов в вб нет).
0
0 / 0 / 0
Регистрация: 23.07.2020
Сообщений: 5
13.01.2022, 19:25
The trick, спасибо что оперативно отвечаете.
А вот я видимо не умею объяснять.
Цитата Сообщение от The trick Посмотреть сообщение
почему бы не использовать интерфейсную ссылку для этого
Я так понимаю, что интерфейсная ссылка здесь не подойдёт (ни разу не использовал их), тело функции, которую нужно вызвать по указателю может быть разным и находится вне объекта класса (как раз в общем модуле), а прототип является приватным методом, вызов производится из объекта и передать себя (Me) не проблема, хотя этой функции в общем случае объект не нужен. Класс является универсальным, может использоваться в разных приложениях, а функция может быть реализована по разному для каждого или нескольких объектов.
0
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
13.01.2022, 19:56  [ТС]
Цитата Сообщение от kasimm Посмотреть сообщение
Я так понимаю, что интерфейсная ссылка здесь не подойдёт (ни разу не использовал их), тело функции, которую нужно вызвать по указателю может быть разным и находится вне объекта класса (как раз в общем модуле), а прототип является приватным методом, вызов производится из объекта и передать себя (Me) не проблема, хотя этой функции в общем случае объект не нужен. Класс является универсальным, может использоваться в разных приложениях, а функция может быть реализована по разному для каждого или нескольких объектов.
Честно, я не понял. Поясни как гипотетически это должно выглядеть в коде, я дам правильное решение.
0
0 / 0 / 0
Регистрация: 23.07.2020
Сообщений: 5
14.01.2022, 11:16
Имеем модуль класса (выдержка из clsTrickSubclass):

Кликните здесь для просмотра всего текста

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
Public Property Let AddrWndProc(adr As Long) ' адрес процедуры - обработчика сообщений (вместо события WndProc)
    lpAdrWnd = adr
End Property
 
Public Property Get hWnd() As Long
    hWnd = mhWnd
End Property
' Subclassing state (True - subclassing on)
Public Property Get IsSubclassed() As Boolean
    IsSubclassed = mIsSubclassed
End Property
' Pause subclassing
Public Function PauseSubclass() As Boolean
    If mIsSubclassed And Not mIsPaused Then mIsPaused = True: PauseSubclass = True
End Function
' Resume
Public Function ResumeSubclass() As Boolean
    If mIsSubclassed And mIsPaused Then mIsPaused = False: ResumeSubclass = True
End Function
' If pause then return True
Public Property Get IsPaused() As Boolean
    IsPaused = mIsPaused
End Property
' Set subclassing to window (if subclassing already enabled then remove it)
Public Function Hook(ByVal hWnd As Long) As Boolean
 
    If mIsSubclassed Then
        If Not UnHook Then Exit Function
    End If
    
    If CreateAsm Then
        
        Debug.Print Hex(lpAsm)
        
        mIsSubclassed = SetWindowSubclass(hWnd, lpAsm, ObjPtr(Me), 0)
        
        If mIsSubclassed Then
            Hook = True
            mhWnd = hWnd
        End If
        
    End If
    
End Function
' Remove subclassing
Public Function UnHook() As Boolean
    If Not mIsSubclassed Then Exit Function
    UnHook = RemoveWindowSubclass(mhWnd, lpAsm, ObjPtr(Me))
    If UnHook Then mhWnd = 0: mIsSubclassed = False
End Function
' Call default procedure
Public Function CallDef(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long, ByRef Status As Boolean) As Long
    If Not mIsSubclassed Then Exit Function
    CallDef = DefSubclassProc(hWnd, Msg, wParam, lParam)
    Status = True
End Function
 
' --------------------------------------------------------------------------------------------------------------------------------------
Private Function SUBCLASSPROC(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
    Dim inIDE   As Boolean
    Dim retAddr As Long
    Dim addr    As Long
    
    mDepth = mDepth + 1
    
    If mIsPaused Then
        SUBCLASSPROC = DefSubclassProc(hWnd, Msg, wParam, lParam)
    Else
        Dim DefCall As Boolean
        DefCall = True
        RaiseEvent WndProc(hWnd, Msg, wParam, lParam, SUBCLASSPROC, DefCall)
        If lpAdrWnd Then
            ' если есть адрес - вызываем WndProcAny по адресу (когда нужно для нескольких/всех форм обработку разместить в одной процедуре модуля bas)
            Call WndProcAny(lpAdrWnd, hWnd, Msg, wParam, lParam, DefCall, SUBCLASSPROC)
        Else
            RaiseEvent WndProc(hwnd, Msg, wParam, lParam, SUBCLASSPROC, DefCall)
        End If
        If DefCall Then SUBCLASSPROC = DefSubclassProc(hWnd, Msg, wParam, lParam)
    End If
     
    mDepth = mDepth - 1
    
    Debug.Assert MakeTrue(inIDE)
    
    If inIDE Then
        Dim refDat  As Long
        GetMem4 ByVal ObjPtr(Me) + 8, refDat
        GetMem4 ByVal refDat + 4, refDat
        If refDat = 1 Then
            addr = VarPtr(hWnd) + &H20
            GetMem4 ByVal addr, ByVal addr - &H28
        End If
    Else
        If mTerminateFlag And mDepth = 0 Then
            addr = VarPtr(hWnd) + &H20
            GetMem4 ByVal addr, ByVal addr - &H28
            ' // Clean
            Call Class_Terminate
        End If
    End If
    
End Function
 
' прототип функции
' <offset> #11 (WNDPROCINDEX + 2)
Private Function WndProcAny(ByVal Addr As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long, DefCall As Boolean, ret As Long) As Long 
 
End Function
 
Private Sub Class_Initialize() 
    Dim vTable      As Long
    Dim Addr        As Long
    
    GetMem4 ByVal ObjPtr(Me), vTable
    GetMem4 ByVal vTable + (WNDPROCINDEX + 1) * 4 + &H1C, Addr
    
    PatchFunc Addr   
End Sub


В общем модуле имеем функцию для обработки сообщений (прокрутка мышью):
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Public Function All_WndProc( _
            ByVal hwnd As Long, _
            ByVal Msg As Long, _
            ByVal wParam As Long, _
            ByVal lParam As Long, _
            ByRef DefCall As Boolean) As Long
        
    Dim CurPos As POINTAPI  ' координаты курсора мыши
    Dim hw&
    
    Select Case Msg
 
    Case WM_MOUSEWHEEL
        GetCursorPos CurPos  ' получаем координаты курсора
 
        hw = WindowFromPoint(CurPos.X, CurPos.y)  'Получаем Handle контрола под мышью
        
        If hw Then
            Dim dir As Long
            dir = (wParam And &HFFFF0000) \ &H780000  ' крутим вверх dir=1, крутим вниз dir=-1
                
            If dir > 0 Then SendMessageByNum hw, WM_VSCROLL, SB_LINEUP, ByVal 0& Else SendMessageByNum hw, WM_VSCROLL, SB_LINEDOWN, ByVal 0&
        End If
        
        DefCall = False  
        
    Case Else
        DefCall = True
    End Select
 
End Function


Далее используем на формах.
Форма 1:
Visual Basic
1
2
3
4
5
6
7
Dim FormHook   As clsTrickSubclass
 
Private Sub Form_Load()
    Set FormHook = New clsTrickSubclass
    FormHook.Hook Me.hwnd
    FormHook.AddrWndProc = L_(AddressOf All_WndProc)
End Sub
Форма 2 идентично форме 1:
Кликните здесь для просмотра всего текста

Visual Basic
1
2
3
4
5
6
7
Dim FormHook   As clsTrickSubclass
 
Private Sub Form_Load()
    Set FormHook = New clsTrickSubclass
    FormHook.Hook Me.hwnd
    FormHook.AddrWndProc = L_(AddressOf All_WndProc)
End Sub


А на форме 3 логика обработки (прокрутки) отличается, поэтому используем механизм события формы:
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Dim WithEvents FormHook  As clsTrickSubclass
 
Private Sub FormHook_WndProc( _
            ByVal hwnd As Long, _
            ByVal Msg As Long, _
            ByVal wParam As Long, _
            ByVal lParam As Long, _
            ByRef ret As Long, _
            ByRef DefCall As Boolean)
    
 
    Select Case Msg
 
    Case WM_MOUSEWHEEL
        GetCursorPos CurPos  ' получаем координаты курсора
 
        hw = WindowFromPoint(CurPos.X, CurPos.y)  'Получаем Handle контрола под мышью
        hwp = GetParent(hw)  ' получаем Handle его родителя
        
        SystemParametersInfo SPI_GETWHEELSCROLLLINES, 0, lLines2Scroll, 0
        
        Delta = wParam / &H10000
        dScroll = -lLines2Scroll * (Delta \ WHEEL_DELTA) * (10 + 9 * (Not (wParam = -7864316 Or wParam = 7864324)))
            
        Delta = VScroll.Value + dScroll * 300 * zz: If Delta < 0 Then Delta = 0
        VScroll.Value = Delta 
        
        DefCall = False 
 
    Case Else
        DefCall = True
    End Select
 
End Sub
 
Private Sub Form_Load()
    Set FormHook = New clsTrickSubclass
    FormHook.Hook Me.hwnd
End Sub


Как-то так должно работать.
В модуле класса прототип функции WndProcAny, с его помощью нужные объекты класса будут по указателю вызывать функцию All_WndProc из общего модуля.
0
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
14.01.2022, 12:45  [ТС]
Ну вообще тут можно просто вызвать DispCallFunc для этого. А вообще так делать неправильно. Нужно просто создать один класс обработчик для общих событий и присваивать его вместо адреса. К примеру:
Создаешь интерфейс IHandler который олицетворяет обработчик:
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
Option Explicit
 
Public Function ProcessWndProc( _
                ByVal hWnd As Long, _
                ByVal Msg As Long, _
                ByVal wParam As Long, _
                ByVal lParam As Long, _
                ByRef DefCall As Boolean, _
                ByRef ret As Long) As Long
 
End Function
Далее создаешь класс общего обработчика для прокрутки CCommonHdlr (тут вообще говоря можно применить паттерн декоратор тогда можно обработчики вызывать один из другого для того чтобы можно было "собирать" обработку из нескольких реализаций - намного более мощная штука чем даже наследование реализации):
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Option Explicit
 
Implements IHandler
 
' // В этом классе реализация для колеса мыши
Private Function IHandler_ProcessWndProc( _
                 ByVal hWnd As Long, _
                 ByVal Msg As Long, _
                 ByVal wParam As Long, _
                 ByVal lParam As Long, _
                 ByRef DefCall As Boolean, _
                 ByRef ret As Long) As Long
 
'    Dim CurPos As POINTAPI  ' координаты курсора мыши
'    Dim hw&
'
    Select Case Msg
 
'    Case WM_MOUSEWHEEL
'        GetCursorPos CurPos  ' получаем координаты курсора
'
'        hw = WindowFromPoint(CurPos.X, CurPos.y)  'Получаем Handle контрола под мышью
'
'        If hw Then
'            Dim dir As Long
'            dir = (wParam And &HFFFF0000) \ &H780000  ' крутим вверх dir=1, крутим вниз dir=-1
'
'            If dir > 0 Then SendMessageByNum hw, WM_VSCROLL, SB_LINEUP, ByVal 0& Else SendMessageByNum hw, WM_VSCROLL, SB_LINEDOWN, ByVal 0&
'        End If
'
'        DefCall = False
'
    Case Else
        DefCall = True
    End Select
    
End Function
В классе сабкласса (CSubclass) вместо адреса "плоской" функции, используешь интерфейсную ссылку:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Option Explicit
 
Private m_cHandler  As IHandler
 
Public Property Set WndHandler( _
                    ByVal cValue As IHandler)
 
    Set cValue = m_cHandler
    
End Property
 
Public Sub Hook( _
                ByVal hWnd As Long)
    ' // ...
End Sub
 
' --------------------------------------------------------------------------------------------------------------------------------------
Private Function SUBCLASSPROC( _
                 ByVal hWnd As Long, _
                 ByVal Msg As Long, _
                 ByVal wParam As Long, _
                 ByVal lParam As Long, _
                 ByVal uIdSubclass As Long, _
                 ByVal dwRefData As Long) As Long
    
    ' // ...
    
        If Not m_cHandler Is Nothing Then
            ' // Если есть обработчик вызываем его метод вместо события
            m_cHandler.ProcessWndProc hWnd, Msg, wParam, lParam, DefCall, SUBCLASSPROC
        Else
            ' // Иначе дергаем событие
            ' // RaiseEvent WndProc(hWnd, Msg, wParam, lParam, SUBCLASSPROC, DefCall)
        End If
 
    ' // ...
    
End Function
 
 
Private Sub Class_Initialize()
    
    ' // Здесь можно повесить обработчик по умолчанию если нужно
    ' // Set m_cHandler = New CCommonHdlr
    
End Sub
В первой форме и второй форме делаешь что-то типа:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
Option Explicit
 
Dim FormHook   As CSubclass
 
Private Sub Form_Load()
 
    Set FormHook = New CSubclass
    FormHook.Hook Me.hWnd
    ' // Используем обработчик по умолчанию для первой формы
    ' // Тут лучше использовать глобальную переменную или Property
    Set FormHook.WndHandler = New CCommonHdlr
 
End Sub
Для третьей "ловишь" событие.
Можно завести несколько общих реализаций к примеру класс CSpecificHdlr делает что-то другое:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Option Explicit
 
Implements IHandler
 
' // В этом классе реализация для какой-то другой задачи
Private Function IHandler_ProcessWndProc( _
                 ByVal hWnd As Long, _
                 ByVal Msg As Long, _
                 ByVal wParam As Long, _
                 ByVal lParam As Long, _
                 ByRef DefCall As Boolean, _
                 ByRef ret As Long) As Long
 
    Select Case Msg
 
    ' // Case WM_ANY
        
        ' // DefCall = False
        
    Case Else
        DefCall = True
    End Select
    
End Function
В нужной форме просто пишешь:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
Option Explicit
 
Dim FormHook   As CSubclass
 
Private Sub Form_Load()
 
    Set FormHook = New CSubclass
    FormHook.Hook Me.hWnd
    ' // Используем другой обработчик
    ' // Тут лучше использовать глобальную переменную или Property
    Set FormHook.WndHandler = New CSpecificHdlr
 
End Sub
А используя как я уже сказал паттерн-декоратор можно сделать так чтобы "навешать" обработчиков на нужную реализацию и каждый обработчик будет "проталкивать" сообщение следующему, а тот может обработать его как ему хочеться. Благодаря общему интерфейсу как раз и достигается полиморфизм. Ну и этот способ более безопасный т.к. используются ссылки вместо "сырых" адресов - указателей.
4
0 / 0 / 0
Регистрация: 23.07.2020
Сообщений: 5
17.01.2022, 17:09
The trick, спасибо, буду пробовать.
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,586
Записей в блоге: 1
23.06.2024, 15:47
Зачетная вещь, работат даже на VBA, правда после первого запуска колбек сбрасывается. Использовал WriteProcessMemory с флагом -1 вместо GetMem4, который увидел в трюке под названием "трамплин", так вроде не сбрасывается.
Visual Basic
1
2
    WriteProcessMemory -1, Addr, &HFF505958, 4, 0
    WriteProcessMemory -1, Addr + ptrSz, cLng(&HE1), 4, 0
Трамплин тоже зачетный, он удобнее тем, что можно использовать возможности Api вызовов для функций VB (as Any, опциональны ByVal), но при том нужно отказаться от какой-то ненужной api-функции.. В данном случае можно сделать сколько угодно прототипов функций, но меньше универсальности в способах передачи аргументов.
2
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
27.06.2024, 21:40
testuser2, молодец
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,586
Записей в блоге: 1
28.06.2024, 01:58
[HackerVlad, почти, но не совсем. Я вообще-то там был не прав. Эта область памяти обычная, перезаписываемая, но она восстанавливается почему-то после 1го использования функции. Но если не перезаписать второй раз, то уже не восстанавливается. Это верно для vba x86, нужно 1 раз пропатчить ф-ю, выполнить ее и 2й раз пропатчить. Можно, кстати тупо скопировав эти 8 байт из одной функции в другую подменить одну другой, во второй можно задать другие типы аргументов.. На vba x64 нет указателей на перезаписываемые участки памяти подобно как в x86, и трамплин там хз как делать, нверное надо знать 64й asm..
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
28.06.2024, 01:58
Помогаю со студенческими работами здесь

Вызов виртуальной функции по указателю
Суть в том, что преподаватель дал задание на защиту курсовой: вызов по указателю виртуальной функции из ТВР, искал в интернете, наткнулся...

Вызов функции по указателю со смещением
Добрый день. Есть dll открытая декомпилятором. В ней есть интересующая меня функция, но она вызываться по указателю со смещением: if (...

вызов конструктора, по указателю на объект
class qwe { public: qwe() {} qwe(char * name, int a, int b):_name(strdup(name)), _a(a), _b(b) {} private: char * _name; ...

Вызов функции по указателю, расположенному в структуре
Господа, помогите с синтаксисом: не могу понять как мне вызвать функцию по указателю, расположенному внутри структуры. Вся эта писанина...

Вызов виртуальной функции по нулевому указателю
struct A { int sum1(int a, int b) { return a+b; } virtual int sum2(int a, int b) { return a+b; } }; int main() { ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
58
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru