#c #windows #programming-languages #inline-assembly
#c #Windows #языки программирования #встроенная сборка
Вопрос:
Я нахожусь в ситуации, когда я должен создать макет функции _stdcall, используя C и встроенный ASM, но который использует переменное количество аргументов. Обычно он не будет знать, сколько аргументов извлекать из стека, когда он возвращает управление своему родителю, поэтому не будет работать, но я надеюсь сообщить ему через глобальную переменную, сколько параметров у него должно быть, а затем заставить его выводить их подобным образом.
Возможно ли это на самом деле? Если да, может ли кто-нибудь направить меня в правильном направлении? Я специально застрял с кодом epilog, который мне понадобится.
Моя цель — создать функцию, которая может использоваться в качестве обратного вызова для любой функции, которая требует ее (например, EnumWindows), при условии, что пользователь сообщает ей во время выполнения, какой длины должен быть список аргументов. Идея заключается в том, чтобы он интегрировался с некоторым кодом в другом месте, поэтому он в основном запускает триггер каждый раз, когда вызывается обратный вызов, и предоставляет ссылку на место, где возвращенные переменные могут быть прочитаны и просмотрены пользователем.
Имеет ли это смысл?
Комментарии:
1. Глобальная переменная сделает ее потокобезопасной.
2. Боюсь, это не имеет смысла, по крайней мере, для меня.
3. Разве вы не можете упаковать значения (или указатели на значения) в массив и передать указатель на этот массив плюс другую соответствующую информацию (типы, размер) вместо того, чтобы пытаться имитировать что-то с __stdcall, для которого это не предназначено?!
4. С чем вы пытаетесь взаимодействовать? Большинство разработчиков рассматривают
__stdcall
функции с переменными аргументами как несовместимые именно по тем причинам, которые вы выделили. Из документации для __stdcall : «Вызываемый объект очищает стек, поэтому компилятор преобразует функции vararg в __cdecl».5. Спасибо всем. Моя ситуация в основном такова, что я должен создать функцию, адрес которой будет передан такой функции, как EnumWindows() или DirectSoundEnumerate (), чтобы она вызывалась как обратный вызов. Но функция, выполняющая это, может меняться, поэтому в один момент это может быть EnumWindows, а в следующий DirectSoundEnumerate. Вызывающий предполагает, что моей функцией является _stdcall , но разве невозможно просто подготовить голую функцию, которая просто очищает после себя, как если бы это был stdcall? Я только хочу, чтобы он имитировал _stdcall достаточно близко, чтобы быть стабильным, если это возможно.
Ответ №1:
Не имеет смысла. __stdcall
не допускает переменных параметров, поскольку общий размер всех параметров указан в имени функции (из msdn):
Соглашение об оформлении имен
К имени добавляется символ подчеркивания (_). За именем следует знак at (@), за которым следует количество байтов (в десятичной системе счисления) в списке аргументов. Следовательно, функция, объявленная как
int func( int a, double b )
, оформлена следующим образом:_func@12
В этой цитате рассказывается о том, как реализуются переменные __stdcall
функции:
Соглашение о вызове __stdcall используется для вызова функций Win32 API. Вызываемый объект очищает стек, поэтому компилятор преобразует функции vararg в __cdecl. Для функций, использующих это соглашение о вызовах, требуется прототип функции.
(выделено мной)
Итак, нет __stdcall
функций с переменными параметрами, они автоматически изменяются на __cdecl
. 🙂
Ответ №2:
Вы можете сделать что-то вроде следующего (взломанный код):
static int NumberOfParameters = 0;
__declspec(naked) void GenericCallback()
{
// prologue
__asm push ebp
__asm mov ebp, esp
// TODO: do something with parameters on stack
// manual stack unwinding for 2 parameters
// obviously you would adjust for the appropriate number of parameters
// (e.g. NumberOfParameters) instead of hard-coding it for 2
// fixup frame pointer
__asm mov eax, [ebp 0]
__asm mov [ebp 8], eax // NumberOfParameters * 4 (assuming dword-sized parameters)
// fixup return address
__asm mov eax, [ebp 4]
__asm mov [ebp 12], eax // (NumberOfParameters 1) * 4
// return TRUE
__asm mov eax, 1
// epilogue
__asm mov esp, ebp
__asm pop ebp
// fixup stack pointer
__asm add esp, 8 // NumberOfParameters * 4
__asm ret 0
}
int main(int argc, _TCHAR* argv[])
{
NumberOfParameters = 2;
EnumWindows((WNDENUMPROC)GenericCallback, NULL);
return 0;
}