Переменные аргументы в _stdcall, C / Inline ASM

#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;
}