простой вызов Windows для Th64

Многие из вас знакомы с ATL thunks, например. для создания окна. Класс, который делает эту работу CStdCallThunk, нацелен на WindowProc вызов. По сути, он превращает глобальный обратный вызов в функцию-член объекта C ++.

Этот тип thunk не будет работать для SetWindowsHookEx обратный вызов который нуждается в первом параметре без изменений. Для 32-битных окон я нашел аккуратное решение в CAuxThunk, часть ATL / AUX библиотека. К сожалению, это не работает для собственного 64-битного исполняемого файла

Интересно, есть ли какой-нибудь гуру по сборке x64, который может исправить этот CAuxThunk для работы на 64-битных окнах или придумать какой-нибудь эквивалентный thunk, который бы превратил этот обратный вызов __stdcall в функцию-член?

LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)

Спасибо,
Никос

1

Решение

Я описал общий способ генерации любой код, который вы хотите в этот ответ. Давайте переделаем это для вашего случая, просто для удовольствия.

Предположим, что ваш класс определен как:

struct YourClass {
LRESULT YourMemberFunc(int nCode, WPARAM wParam, LPARAM lParam);
};

По сути, вы пишете свой блок в C ++ с заполнителями для фактических адресов:

LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
YourClass *x = reinterpret_cast<YourClass*>(0x1122112211221122);
__int64 im = 0x3344334433443344;
LRESULT (YourClass::*m)(int,WPARAM,LPARAM) = *reinterpret_cast<LRESULT (YourClass::**)(int,WPARAM,LPARAM)>(&im);
return (x->*m)(nCode, wParam, lParam);
}

И вызовите это так, чтобы компилятор не включил вызов:

int main() {
LRESULT (CALLBACK *volatile fp)(int, WPARAM, LPARAM) = CallWndProc;
fp(0, 0, 0);
}

Компиляция в выпуске и просмотр сгенерированной сборки (в Visual Studio во время отладки посмотрите окно сборки и включите «Показать байты кода»):

4D 8B C8                       mov         r9,r8
4C 8B C2                       mov         r8,rdx
8B D1                          mov         edx,ecx
48 B9 22 11 22 11 22 11 22 11  mov         rcx,1122112211221122h
48 B8 44 33 44 33 44 33 44 33  mov         rax,3344334433443344h
48 FF E0                       jmp         rax

Это будет ваш толчок, с 44 33 44 33 44 33 44 33 заменено указателем на вашего участника (&YourClass::YourMemberFunc) а также 22 11 22 11 22 11 22 11 заменяется указателем на фактический экземпляр объекта во время выполнения.

Объяснение того, что происходит в Thunk:

В соглашении о вызовах x64 (из которых только один в Windows) первые четыре параметра передаются в rcx, rdx, r8, r9 регистры, в этом порядке, слева направо. Так что, когда наш вызов называется, мы имеем

rcx = nCode, rdx = wParam, r8 = lParam

Для функций-членов существует неявное удержание первого параметра this указатель, поэтому при входе YourMemberFunc мы должны иметь

rcx = this, rdx = nCode, r8 = wParam, r9 = lParam

Сгенерированный компилятором код выполняет именно эту настройку: он сдвигается r8 -> r9, rdx -> r8, ecx -> edx, а затем назначает наш заполнитель this = 1122112211221122 в rcx, Теперь у него есть настроенные параметры, и он может продолжить косвенный переход к самой функции. rax используется для хранения возвращаемого значения, поэтому его не нужно сохранять при вызовах функций. Вот почему он используется здесь для временного удержания адреса получателя, что дает возможность оптимизации хвостового вызова (пара вызов / возврат заменена на одиночный переход).

Почему мы должны сделать косвенный звонок? Потому что в противном случае мы получим относительный скачок. Но мы не можем использовать жестко запрограммированный относительный переход, потому что раздел будет скопирован по разным адресам в памяти! Поэтому мы прибегаем к установке абсолютного адреса во время выполнения и вместо этого делаем косвенный переход.

НТН

3

Другие решения

Поскольку в 64-битном режиме низкоуровневые соглашения одинаковы для всех типов (stdcall, cdecl и thiscall), вам не нужен ассемблерный код для достижения этой цели. Просто создайте статическую функцию, которая вызывает соответствующую функцию-член. Вам нужно будет выяснить правильное this указатель для использования, например, путем связывания hwnd в lparam с объектом. Если у вас есть только один обратный вызов, это, конечно, не будет проблемой. Что-то вроде:

LRESULT CALLBACK static CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
Window w = GetWindowByHwnd(((CWPSTRUCT*)lParam)->hwnd);
return w->CallWndProc(nCode, wParam, lParam);
}
0

Обновлен Смотрите ниже

Взгляните на SetWindowLongPtr http://msdn.microsoft.com/en-us/library/windows/desktop/ms644898(v=vs.85).aspx

Это позволяет связать указатель с окном. Учитывая, что WindowProc выглядит так

LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);

Вы можете использовать параметр hwnd с GetWindowLongPtr, чтобы получить указатель this в вашем обратном вызове.

обновленный

Взгляни на Как выполнить функцию в x86 и x64? (Как std :: bind в C ++, но динамический)

Кажется, это то, что вы ищете

0
По вопросам рекламы [email protected]