Столкнулся с одной довольно интересной задачей. А именно: передача неопределенного кол-ва аргументов для stdcall функции. т.е. вот какая сетуация: 1) если есть тип typedef UINT (__stdcall * PROC_CALL)(void* Param1); то всё нормально вызывается, но тока 1 параметр можно передать. 2) если есть тип typedef UINT (__stdcall * PROC_CALL)(void* Param1, ...); то странным образом вызов данной функции ведет себя как __cdecl, т.е. после вызова функции, вызывающая сторона очищает стек, при этом она это делает всегда, несмотря даже на наличие __stdcall в прототипе. Толи это бага MS VC 2008, толи фишка языка. А юзать разный прототип для выполнения функций с разным кол-вом параметров, как-то неохото. Потому что может быть параметров от 1 до 8 Кто что предложит? Единственно что в голову пришло, это юзать промежуточную функцию, которая через ASM будет генерить нормальный __stdcall вызов, но както не оч хочется такое юзать
stdcall не может быть в функциях неопределенного количества аргументов, т.к. стек отчищает сама вызываемая функция, сто пудов это не баг, MSVC сама меняет эту дерективу, хотя странно...лучще бы ошибку выдавала, т.к. деректива stdcall и ... в парметрах как антонимы. Да и "функцию-обертку" сделать не выйдет, да даже если и выйдет(что то сверхестественное), то какой от нее профит?
stdcall игнорируется при объявлении функции с переменным числом параметров, так как этот формат вызова жестко указывает размер стека, и для таких функций он неприменим.
2 Jingo Bo профит есть. Допустим у меня есть адреса десяти функций в массиве (не важно каких), все они объявлены как __stdcall и имеют разное кол-во параметров. И мне из програмки в зависимости от ситуации надо вызывать эти функции. Хранить прототипы для каждой функции - не рационально, потому что на перед не знаешь какие функции будут юзайться.
Портотип можно объявить как : typedef UINT (__stdcall * PROC_CALL)(uint8 narg); в ручную вызывать функцию, то есть через asm до вызова кидать в стек аргументы, и в аргумент функции количество параметров, потом call, а в функции в начале вытаскивать из стека все аргументы так же через asm, думаю это единственный вариант.
забавно, недавно с похожей задачей разбирался. если функция накед, неважно определен ли стдколл явно или нет, то компилер генерит цдекл код, поскольку только вызывающая процедура может определить кол-во аргументов в стеке.
Можно объявить функцию например так - typedef UINT (__stdcall * PROC_CALL)(void* Param1, void* Param2, void* Param3, void* Param4, void* Param5, void* Param6, void* Param7, void* Param8); при вызове передавать в функцию требуемые параметры, в качестве остальных 0 Либо еще вариант собрать параметры в структуру и передавать ее в функцию. Например: Code: struct stParams { void* Param1; void* Param2; ... void Param8; }; typedef UINT (__stdcall * PROC_CALL)(stParams* pParams); Правда не знаю на сколько это удобно в данном случае.
2 Jingo Bo всё делается не для усложнения а для облегчения. потому что если уже вообще захочется универсальности то достаточно сразу писать Code: push ... push ... push ... push ... call [адрес переменной хранящей адрес функции] Былобы оч хорошо еслибы вообще замутить в виде макроса такое
Да это понятно. Code: push ... push ... push ... push ... call [адрес переменной хранящей адрес функции] Ну собственно да, но ток это не очень хорошо, т.к если вылезет где нить эксцепшн, то в стеке может что то остаться(по мере взятия аргументов), но это все от реализации уже зависит. Как нех. stdarg.h через va_list
вот еще такая тема Code: ////////////////////////////////////////////////////////////////////////// __declspec(naked) NTSTATUS __cdecl ZwService(ULONG n, ...) { __asm { mov eax, [esp+4] lea edx, [esp+8] int 0x2e ret } } ////////////////////////////////////////////////////////////////////////// int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DWORD startaddr, retlen; char aaa[128]; ZwService(0xEC, /* win7 x86 ZwQueryInformationThread syscall # */ GetCurrentThread(), ThreadQuerySetWin32StartAddress, (PVOID)&startaddr, 4, &retlen); wsprintf(aaa, "%08X", startaddr); MessageBoxA(0,aaa,0,0); ExitProcess(0); }
2 sn0w ну так это тока системные вызовы так вызывать и то, на Win 7 этот код вроде как уже не будет пахать потому что из юзаермода наглухо убили int2e (хотя могу и ошибаться) Конечно можно просто подсосывать sysenter а не int2e А если допустим мне надо вызнать какуюнить функцию у которой большинство обраотки идет в юзермоде, то тут такой код будет бесполезным
2slesh попробуй на первый взгляд работает Code: #include <stdio.h> #include <string.h> #include <windows.h> DWORD TlsApiCall; __declspec(naked) int __cdecl coll(void*a,...){ __asm{ push dword ptr[TlsApiCall]; call dword ptr[TlsGetValue]; mov dword ptr[eax],esp; push dword ptr[esp]; pop dword ptr[eax+4]; lea esp, [esp+8]; call dword ptr[esp-4]; push eax; push dword ptr[TlsApiCall]; call dword ptr[TlsGetValue]; pop dword ptr[eax+8]; mov esp, dword ptr[eax]; push dword ptr[eax+4]; pop dword ptr[esp]; mov eax, dword ptr[eax+8]; ret; } } int main(){ char dup[12]; // гденить начале TlsApiCall = TlsAlloc(); // для каждого потока 12 байт TlsSetValue(TlsApiCall, &dup); // имеем требуемое поведение coll(addr, bla, bla, bla, ...); } да и просто typedef UINT (__stdcall * PROC_CALL)(void* Param1, ...); не так а так typedef UINT (__stdcall * PROC_CALL)(); сгенерирует variadic __stdcall вызов и стек чистить не будет это для Си а в Си++ так не будет работать в нем можно например что то типа coll юзать или на шаблонах можно сделать переходник это все применительно к MSVC/C++