MSVC/C++ calling convention

Тема в разделе "LANGS.C", создана пользователем osox, 2 дек 2010.

  1. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    Всех приветствую
    не первый раз сталкиваюсь с тем что
    невозможно определить указатель на
    функцию в C++ которая принимает переменное
    число аргументов и имеет соглашение
    __stdcall если пишеш func(...) то
    требует __cdecl а если func() то вообще не принимает
    в Си можно спокойно написать
    typedef void (__stdcall*)();
    и это работает для переменного числа аргументов
    и при этом стек не чистится после вызова
    как бы так сделать в C++
    вот код в котором надо передавать
    переменное число и не чистить стек после вызова
    Код (Text):
    1. typedef void(__thiscall*fn_t)(void*_this);
    2.  
    3. class test{
    4. public:
    5.     virtual void f1(){}
    6.     virtual void f2(){}
    7. };
    8.  
    9. int main(){
    10.     test o;
    11.     fn_t *func = *(fn_t**)&o;
    12.     func[0](&o);
    13.     func[1](&o);
    14.     // func[2](&o, arg1, arg2, arg3, ...);
    15. }
    надо нечто такого
    typedef void(__thiscall*fn_t)(void*_this, ...);
    и чтоб стек не корректировался после вызова
    но на это пишет error C2217: '...' requires '__cdecl'
    и вообще как в MSVC++ добится передачи переменного числа
    аргументов с соглашением __stdcall или __thiscall ?
     
  2. IceCrashLdr

    IceCrashLdr New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    193
    Извине, но думаю вы плохо предстовляете что это такое, вообщем в гугуле можно найти ...
    Argument Passing and Naming Conventions http://msdn.microsoft.com/en-us/library/984x0h58%28v=VS.80%29.aspx
    naked (C++) http://msdn.microsoft.com/en-us/library/h5w10wxs%28v=VS.80%29.aspx
     
  3. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    MSVC зло, походу так нельзя - сам сталкивался с похожей проблемой...
     
  4. qqwe

    qqwe New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2009
    Сообщения:
    2.914
    osox
    посмотрите что это такое - стдкалл и сами ответьте себе - возможно ли вообще к нему применять переменное число параметров.
     
  5. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    А теперь скажите нам, что такое "__stdcall, но с переменным числом аргументов и стек не чистится после вызова". Это и есть cdecl

    так не бывает:)
     
  6. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    ЭТО В СИ ВОЗМОЖНО
    я непонимаю чему вы удивляетесь
    потому что это уже есть и работает но в Си
    а не в Си++
    в Си я могу винапишку описать
    без инклудов как например
    void __stdcall MessageBoxA();
    и это работает компилятор
    примет любое количество аргументов
    и соглашение при вызове будет __stdcall
    из за этого это собирается компоновщиком
    в С++ я так не могу вернее могу но максимум
    это __cdecl соглашение
    но тогда линкер ругается из за несовпадения
    линкер ищет _MessageBoxA а экспортируется
    _MessageBoxA@16 это один из
    примеров что нужно неконтролируемое
    количество параметров и чтоб соглашение
    компилятор юзал stdcall в Си это ВОЗМОЖНО
    как сделать тоже самое в С++ ?
    или вот еще пример
    компилятор написал виртуальные функции
    с соглашением __thiscall
    я не хочу для каждой описывать
    сигнатуру хочу чтоб сколько при вызове указываю
    все передавал как в __cdecl но так как
    виртуальные функции сами чистят стек
    хочу чтоб после моих вызовов компилятор
    не корректировал стек
    в Си это так void __stdcall Func();
    как такое же в С++ сделать ?
     
  7. Com[e]r

    Com[e]r Com[e]r

    Публикаций:
    0
    Регистрация:
    20 апр 2007
    Сообщения:
    2.624
    Адрес:
    ого..
    у студентов сессия пошла, скоро и школьники с каникулами догонят..
    зачем ты классы юзаешь, горе, если ты на си писал? )

    напиши враппер, если тебе стёк надо сохранять, в конце-концов)
     
  8. qqwe

    qqwe New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2009
    Сообщения:
    2.914
    osox
    как много слов. и все навалены как нибудь.
    если можно, то 2мя-3мя предложениями то что вам надо. по пунктам, с отступами и форматированием. и без эмоций.

    стдкалл не примет переменное количество аргументов. вне зависимости от языка и компилятора
     
  9. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    Возможно это поможет:
    Код (Text):
    1. //////////////////////////////////////////////////////////////////////////////
    2. //
    3. //  Test a detour of a member function (member.cpp of member.exe)
    4. //
    5. //  Microsoft Research Detours Package, Version 2.1.
    6. //
    7. //  Copyright (c) Microsoft Corporation.  All rights reserved.
    8. //
    9. //  By default, C++ member functions use the __thiscall calling convention.
    10. //  In order to Detour a member function, both the trampoline and the detour
    11. //  must have exactly the same calling convention as the target function.
    12. //  Unfortunately, the VC compiler does not support a __thiscall, so the only
    13. //  way to create legal detour and trampoline functions is by making them
    14. //  class members of a "detour" class.
    15. //
    16. //  In addition, C++ does not support converting a pointer to a member
    17. //  function to an arbitrary pointer.  To get a raw pointer, the address of
    18. //  the member function must be moved into a temporrary member-function
    19. //  pointer, then passed by taking it's address, then de-referencing it.
    20. //  Fortunately, the compiler will optimize the code to remove the extra
    21. //  pointer operations.
    22. //
    23. //  If X::Target is a virtual function, the following code will *NOT* work
    24. //  because &X::Target is the address of a thunk that does a virtual call,
    25. //  not the real address of the X::Target.  You can get the real address
    26. //  of X::Target by looking directly in the VTBL for class X, but there
    27. //  is no legal way to 1) get the address of X's VTBL or 2) get the offset
    28. //  of ::Target within that VTBL.  You can of course, figure these out for
    29. //  a particular class and function, but there is no general way to do so.
    30. //
    31. #include <stdio.h>
    32.  
    33. #include <windows.h>
    34. #include <detours.h>
    35.  
    36. #include "..\slept\verify.cpp"
    37.  
    38. //////////////////////////////////////////////////////////////// Target Class.
    39. //
    40. class CMember
    41. {
    42.   public:
    43.     void Target(void);
    44. };
    45.  
    46. void CMember::Target(void)
    47. {
    48.     printf("  CMember::Target!   (this:%p)\n", this);
    49. }
    50.  
    51. //////////////////////////////////////////////////////////////// Detour Class.
    52. //
    53. class CDetour /* add ": public CMember" to enable access to member variables... */
    54. {
    55.   public:
    56.     void Mine_Target(void);
    57.     static void (CDetour::* Real_Target)(void);
    58.  
    59.     // Class shouldn't have any member variables or virtual functions.
    60. };
    61.  
    62. void CDetour::Mine_Target(void)
    63. {
    64.     printf("  CDetour::Mine_Target! (this:%p)\n", this);
    65.     (this->*Real_Target)();
    66. }
    67.  
    68. void (CDetour::* CDetour::Real_Target)(void) = (void (CDetour::*)(void))&CMember::Target;
    69.  
    70. //////////////////////////////////////////////////////////////////////////////
    71. //
    72. int main(int argc, char **argv)
    73. {
    74.     (void)argc;
    75.     (void)argv;
    76.  
    77.     //////////////////////////////////////////////////////////////////////////
    78.     //
    79.  
    80. #if (_MSC_VER < 1310)
    81.     void (CMember::* pfTarget)(void) = CMember::Target;
    82.     void (CDetour::* pfMine)(void) = CDetour::Mine_Target;
    83.  
    84.     Verify("CMember::Target", *(PBYTE*)&pfTarget);
    85.     Verify("*CDetour::Real_Target", *(PBYTE*)&CDetour::Real_Target);
    86.     Verify("CDetour::Mine_Target", *(PBYTE*)&pfMine);
    87. #else
    88.     Verify("CMember::Target", (PBYTE)(&(PBYTE&)CMember::Target));
    89.     Verify("*CDetour::Real_Target", *(&(PBYTE&)CDetour::Real_Target));
    90.     Verify("CDetour::Mine_Target", (PBYTE)(&(PBYTE&)CDetour::Mine_Target));
    91. #endif
    92.  
    93.     printf("\n");
    94.  
    95.     DetourTransactionBegin();
    96.     DetourUpdateThread(GetCurrentThread());
    97.  
    98. #if (_MSC_VER < 1310)
    99.     pfMine = CDetour::Mine_Target;
    100.  
    101.     DetourAttach(&(PVOID&)CDetour::Real_Target,
    102.                  *(PBYTE*)&pfMine);
    103. #else
    104.     DetourAttach(&(PVOID&)CDetour::Real_Target,
    105.                  (PVOID)(&(PVOID&)CDetour::Mine_Target));
    106. #endif
    107.  
    108.     LONG l = DetourTransactionCommit();
    109.     printf("DetourTransactionCommit = %d\n", l);
    110.     printf("\n");
    111.  
    112. #if (_MSC_VER < 1310)
    113.     pfTarget = CMember::Target;
    114.     pfMine = CDetour::Mine_Target;
    115.  
    116.     Verify("CMember::Target", *(PBYTE*)&pfTarget);
    117.     Verify("*CDetour::Real_Target", *(&(PBYTE&)CDetour::Real_Target));
    118.     Verify("CDetour::Mine_Target", *(PBYTE*)&pfMine);
    119. #else
    120.     Verify("CMember::Target", (PBYTE)(&(PBYTE&)CMember::Target));
    121.     Verify("*CDetour::Real_Target", *(&(PBYTE&)CDetour::Real_Target));
    122.     Verify("CDetour::Mine_Target", (PBYTE)(&(PBYTE&)CDetour::Mine_Target));
    123. #endif
    124.     printf("\n");
    125.  
    126.     //////////////////////////////////////////////////////////////////////////
    127.     //
    128.     CMember target;
    129.  
    130.     printf("Calling CMember (w/o Detour):\n");
    131.     (((CDetour*)&target)->*CDetour::Real_Target)();
    132.  
    133.     printf("Calling CMember (will be detoured):\n");
    134.     target.Target();
    135.  
    136.     return 0;
    137. }
     
  10. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    никак...

    это связано с тем, что в либ файлах имена "украшенные"... если найдете и подключите user32.lib с "неукрашенными" именами проблемы с компиляцией не будет, но будет проблема со стеком...

    вы не можете объяснить, что вам нужно... но я вам отвечу... вас спасет преобразование указателей на функцию перед вызовом... попробуйте... а когда поймете, что это не удобно, придете сюда и снова ничего не сможете объяснить... но я вам отвечу сразу, что вам помогут темплейты, или препроцессором можно сделать...
     
  11. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Ты знаешь что такое stdcall?
    При stdcall функция сама должна стэк от параметров очистить.
    Как она это сделает, если не знает сколько их там?

    И да, твой пример просто гениальный.
    В Си так можно, оно не собирается, а если собирается, то не работает, но так можно, честно. :)
     
  12. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    cppasm
    Чё вы народ путаете - единственное отличие cdecl от stdcall в том, что в stdcall стек очищает вызываемый (т.е. функция), тогда как в cdecl стек очищает вызывающий. Прядок передачи параметров через стек ОДИНАКОВЫЙ. Разберитесь сами сначала, перед тем как такие советы раздавать.

    UPD: Вот вам в догонку ссылко: http://unixwiz.net/techtips/win32-callconv.html, учите матчасть ;)
     
  13. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Спасибо дядя, когда мне надо будет - я загуглить в состоянии.

    Да, попутал с pascal.
    stdcall это по сути гибрид pascal и cdecl.
    В любом случае, вопрос по очистке стэка остаётся в силе.
    По твоей же ссылке написано:
    При чём здесь
    не понятно.
    Ты в любом другом компиляторе можешь это сделать?
     
  14. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Дык отвечаьт другим надо или правильно или никак

    osox
    в
    СИ
    как
    и
    в
    С++
    это
    невозможно
    по
    тем
    же
    причинам!
     
  15. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Кто-то спорит что-ли? Или ты никогда не ошибаешься?
    Просто есть разница между тем чтобы исправить чужую ошибку, и понтоваться типа "учите матчасть".
     
  16. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    Товарищу нужно получить указатель на начало функции, но не в виде void (*func)(), а в виде void *func. Например, чтобы потом просплайсить её. При чем здесь модель вызова? Хочется показать, что вы знаете какой код создаёт компилятор? И даже тут некоторые умудрились ошибиться.
     
  17. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    nиздоболов комеров не спрашивали завали лицоо дичь
     
  18. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Бан на месяц.