Russian English
   Basic.net
Среда, 23.09.2020, 03:14
Меню сайта
Категории раздела
Basic [44]
Помощь [0]
Облако тегов
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Форма входа
Главная » Статьи » Basic

Вызов функций по указателю в Visual Basic (часть 5)
Share |
Теперь поговорим немного <о Сусанине>. Если по каким-либо причинам переадресация, предложенная мною в откомпилированном виде кажется нежелательной (например, требуется большая скорость и не хочется терять даже десятка полтора тактов процессора на приведенный выше вариант, которые при частом обращении (например, в цикле!) могут сыграть значительную роль).
Тогда можно пойти как тем поправленным вариантом предложенным выше мною, так и авторским. И всё же. Если уж многократное обращение происходит в цикле (а <потерянное> время бывает критичным только в этом случае), тогда можно извратиться несколько больше, чем предложил автор.

Дело в том, что для обращения к какой-либо <собственной> функции используется стандартная инструкция:
Код ASM
1
call rel32
Если взять авторский вариант, то у нас получится последовательность вызовов
Код ASM
1
2
call rel32=Offset PlaceholderFor1ParamFunction
jmp  rel32=Offset <Вызываемая функция>
или в поправленном мною выше варианте
Код ASM
1
2
call rel32=Offset PlaceholderFor1ParamFunction
jmp  dword ptr ds:[imm32=VarPtr(vCallAddress)] функция>
Ну, почему бы нам вообще не избавиться от <лишнего> jmp, коли уж так сильно поджимает нас время? И избавимся! Да ещё и будем ходить по условному адресу.
Для этого немного напишем функцию переключателя
Код Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
#If ReleaseBuild Then
Private Ra() as Byte
Public Function ReDirect(Optional ByVal vP0 As Long, Optional ByVal vP1 As Long) As Long
 vESP=VarPtr(vP0)-4
 CopyMem VarPtr(pRet), vESP, 4
 ReDim Ra(1 to 7)
 'call      dword ptr ds:[imm32=VarPtr(vCallAddress]
 Ra(1)=&H2E: Ra(2)=&HFF: Ra(3)=&H15: PutMem VarPtr(Ra(4)),
VarPtr(vCallAddress)
 WriteProcessMemory GetCurProcess, pRet-7, VarPtr(Ra(1)), 7, 0&
 PutMem4 vESP, pRD
End Function
#End If
а обращаться к этой функции будем
Код Visual Basic
1
2
vCallAddress=vNewAddress
vReturnValue=ReDirect(,vParamValue)
<концовка> подобного обращения (из любого места VB-программы!) в маш. кодах выглядит
Код ASM
1
2
push imm8=0
call rel32
что занимает семь байт. Но инструкция
Код ASM
1
call dwrod ptr ds:[imm32]
На самом деле занимает шесть байт. Что делать? Не вставлять же nop'ы в конце? Ведь это опять получится дополнительная, хотя и пустая (один такт) операция процессора. Есть простое решение указанная выше инструкция по умолчанию адресуется относительно регистра ds, достаточно приписать перед этой инструкцией <уточняющий> префикс &H2E, который будет делать тоже самое, но в явном виде. Что в итоге? В итоге имеем те самые семь байт.

Что же на самом деле делает функция ReDirect? При обращении к этой функции, она вычисляет адрес возврата уже описанным выше способом. Затем меняет предшествующие адресу возврата семь байт. Таким образом, если обращение из этого места происходит либо в цикле, либо просто часто, то в следующий раз обращение по нужному адресу (vCallAddress) будет происходить прямо оттуда!
Кстати говоря, несмотря на то, что функция ReDirect, вроде бы имеет два параметра, при повторных обращениях задержки из-за пустого параметра всё равно не происходит, так как
Код ASM
1
push imm8=0
мы затираем при первом обращении.

Есть ещё один не менее изящный, на мой взгляд, вариант.
Код Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#If ReleaseBuild Then
Private REa() as Byte
Public Function REx(ByVal vP0 As Long, ByVal vP1 As Long) As Long
 vESP=VarPtr(vP0)-4
 CopyMem VarPtr(pRet), vESP, 4
 ReDim REa(1 to 5)
 'pop       eax
 REa(1)=&H67: REa(2)=&H28
 'call      eax
 REa(3)=&H67: REa(4)=&HFF: REa(5)=&HD0
 WriteProcessMemory GetCurProcess, pRet-5, VarPtr(REa(1)), 5, 0&
 PutMem4 vESP, pRD
End Function
#End If
В этом случае обращение к функции REx должно выглядеть следующим образом:
Код Visual Basic
1
vReturnValue=REx(vCallAddress,vParamValue)
То есть теперь инструкция
Код ASM
1
call rel32
заменяется инструкциями
Код ASM
1
2
pop  eax
call eax
Как мы помним инструкция
Код ASM
1
call rel32
занимает пять байт, в то время, как наша замена - всего три. Как и выше, <недостающие байты> мы <добираем> за счёт <уточняющих> префиксов. В данном случае это префикс &H67. Его <безболезненно> можно ставить перед любой из наших инструкций. В итоге получаем искомые пять байт. Заметьте, что последний вариант при передаче управления по условному адресу выполняется максимально быстро. Если же, мы знаем, что обращение будет происходить на один и тот же адрес, то максимально быстро будет работать вот такой вариант
Код Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#If ReleaseBuild Then
Public vNewAddress as Long
Private Function GetPA(ByVal vProcAddress as Long)as Long
 GetPA=vProcAddress
End Function
Public Function ReD(ByVal vP1 As Long) As Long
 vESP=VarPtr(vP0)-4
 CopyMem VarPtr(pRet), vESP, 4
 'до обращения в vNewAddress должен содержаться <абсолютный> адрес
 'после вычисления там будет содержаться относительный
 vNewAddress=pRet-GetPA(AddressOf ReD)+vNewAddress
 WriteProcessMemory GetCurProcess, pRet-4, VarPtr(vNewAddress), 4, 0&
 PutMem4 vESP, pRD
End Function
#End If
Теперь обращение к этой функции обычное.
Код Visual Basic
1
2
3
vNewAddress=<адрес вызываемой процедуры>
:
vReturnValue=ReD(vParamValue)
А что происходит в функции ReD? Просто в инструкции
Код ASM
1
call rel32
перезаписывается само значение rel32.

Вот, собственно говоря, и все замечания. В прочих случаях, не требующих столь изощрённых методов оптимизации предлагаю пользоваться переадресацией альтернативной (если честно, то не очень-то поворачиваются пальцы на клавиатуре их сравнивать) CallWindowProc.


Автор: Approximator
Источник: vbnet.ru
Категория: Basic | Добавил: Admin (14.06.2012)
Просмотров: 1810 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск
Наш опрос
Какую версию Basic вы предпочитаете?
Всего ответов: 2028

© Basic.ucoz.net, 2020