Що таке __stdcall?


151

Я дізнаюся про програмування Win32, і WinMainпрототип виглядає так:

int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show )

Мене збентежило те, для чого потрібен цей WINAPIідентифікатор:

#define WINAPI      __stdcall

Що це робить? Мене бентежить те, що взагалі щось має після повернення. Для чого __stdcall? Що це означає, коли між типом повернення та назвою функції є щось?


2
@AndrewProck У цьому випадку зміна "манекена" була нормальною, але загалом ви можете використовувати  s, щоб обійти дурний (і контрпродуктивний - випадок у точці) мінімум 6 символів.
Аді Інбар

Відповіді:


170

__stdcallє умовою виклику, що використовується для функції. Це повідомляє компілятору правила, які застосовуються для налаштування стека, висунення аргументів та отримання зворотного значення.

Є цілий ряд інших угод про виклики, __cdecl, __thiscall, __fastcallі чудово названі __declspec(naked). __stdcallє стандартною умовою виклику для системних викликів Win32.

Вікіпедія охоплює деталі .

Це в першу чергу важливо, коли ви викликаєте функцію поза кодом (наприклад, API OS) або ОС викликає вас (як це відбувається у випадку WinMain). Якщо компілятор не знає правильного режиму виклику, то, швидше за все, ви отримаєте дуже дивні збої, оскільки стеком не буде керовано правильно.


8
Дивіться це питання на прикладі дуже starnge Crashes stackoverflow.com/questions/696306 / ...
Sharptooth

Якщо я не помиляюся, тоді ці умовні вимоги контролюють те, як компілятор виробляє код складання. При взаємодії з кодом складання важливо враховувати конвенцію виклику, щоб запобігти виникненню проблем зі стеком. Тут є приємна таблиця, в якій зафіксовані деякі умовності: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller

Чи можна сказати, що це відключить певні незаконні оптимізації?
кесарі

40

Самі C або C ++ не визначають цих ідентифікаторів. Вони є розширеннями компіляторів і є певними умовами виклику. Це визначає, куди поставити аргументи, в якому порядку, де викликана функція знайде зворотну адресу тощо. Наприклад, __fastcall означає, що аргументи функцій передаються через регістри.

Вікіпедія Стаття являє собою огляд різних угод про виклики , знайдених там.


18

Поки що відповіді висвітлювали деталі, але якщо ви не збираєтесь переходити до складання, то все, що вам потрібно знати, - це те, що абонент, і виклик повинні використовувати одне і те ж правило виклику, інакше ви отримаєте помилки, які важко знайти.


12

Я погоджуюсь, що всі відповіді поки що є правильними, але ось причина. Компілятори C і C ++ від Microsoft надають різні умови виклику щодо (передбачуваної) швидкості функціональних дзвінків у межах функцій C та C ++ програми. У кожному конкретному випадку абонент і абонент повинні домовитись про те, який режим виклику використовувати. Тепер сам Windows надає функції (API), і ті вже складені, тому, коли ви викликаєте їх, ви повинні відповідати їм. Будь-які дзвінки в API API та зворотні виклики з API API повинні використовувати конвенцію __stdcall.


3
і його не слід плутати з _standard_call тим, що це стандарт-c! можна подумати, що це був би сенс __stdcall, якщо хтось не знає краще
Йоганнес Шауб - ліб

3
Невеликий нітпік: є декілька API API, які використовують __cdecl замість __stdcall - зазвичай такі, які приймають змінну кількість параметрів, наприклад wsprintf ().
Майкл Берр

Ти маєш рацію. Він названий так, щоб виглядати як функція CRT, але це API. За будь-якого випадку ви знаєте, як P / викликати це з C #?
програміст Windows

Я цього не перевіряв, але pinvoke.net дає такий підпис: "статичний зовнішній int wsprintf ([Out] StringBuilder lpOut, рядок lpFmt, ...);"
Майкл Берр

Моя інтуїція говорить, що компілятор C # не знав би використовувати конвенцію __cdecl для цього.
Програміст Windows



4

__stdcall використовується для розміщення аргументів функції у стеці. Після завершення функції він автоматично передає пам'ять. Це використовується для фіксованих аргументів.

void __stdcall fnname ( int, int* )
{
    ...
}

int main()
{
    CreateThread ( NULL, 0, fnname, int, int*...... )
}

Тут у назви fnname є аргументи, які він прямо натискає на стек.


1

Мені ніколи не довелося цим користуватися до сьогодні. Це тому, що в моєму коді я використовую багатопотокову передачу і багатопотоковий API, який я використовую, це Windows one (_beginthreadex).

Для початку теми:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0);

Функція ExecuteCommand ОБОВ'ЯЗКОВО використовувати ключове слово __stdcall у підписі методу, щоб надіслати його викликом Beginthreadex:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.