Что такое __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-интерфейс ОС) или ОС вызывает вас (как в случае с WinMain). Если компилятор не знает правильного соглашения о вызовах, вы, вероятно, получите очень странные сбои, так как стек не будет правильно управляться.

Роб Уокер
источник
8
Смотрите этот вопрос на примере очень starnge Crashes stackoverflow.com/questions/696306/...
Sharptooth
Если я не ошибаюсь, тогда эти соглашения о вызовах управляют тем, как компилятор создает код сборки. При взаимодействии с ассемблерным кодом очень важно учитывать соглашение о вызовах, чтобы избежать проблем со стеком. Здесь есть хорошая таблица, документирующая некоторые условные обозначения: msdn.microsoft.com/en-us/library/984x0h58.aspx
Николас Миллер,
Можно ли сказать, что это отключило бы некоторые незаконные оптимизации?
Кесари
40

Сам C или C ++ не определяют эти идентификаторы. Они являются расширениями компилятора и обозначают определенные соглашения о вызовах. Это определяет, куда поместить аргументы, в каком порядке, где вызываемая функция найдет адрес возврата и так далее. Например, __fastcall означает, что аргументы функций передаются через регистры.

Википедия Статья представляет собой обзор различных соглашений о вызовах , найденных там.

Йоханнес Шауб - Литб
источник
18

Ответы до сих пор охватывали детали, но если вы не собираетесь переходить к сборке, то все, что вам нужно знать, это то, что и вызывающий, и вызываемый должны использовать одно и то же соглашение о вызовах, в противном случае вы получите ошибки, которые трудно найти.

MovEaxEsp
источник
12

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

Программист Windows
источник
3
и его не следует путать с _standard_call в том смысле, что это стандарт-c! Кто-то может подумать, что в этом будет смысл __stdcall, если кто-то не знает лучше
Йоханнес Шауб -
3
Небольшая мелочь: есть несколько API-интерфейсов Windows, которые используют __cdecl вместо __stdcall - обычно те, которые принимают переменное количество параметров, например wsprintf ().
Майкл Берр
Ты прав. Он назван так, чтобы выглядеть как функция CRT, но это API. Случайно ли вы знаете, как P / Invoke это из C #?
Windows программист
Я не проверял это, но pinvoke.net дает такую ​​подпись: «static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);»;
Майкл Берр
Моя интуиция говорит, что компилятор C # не будет знать, как использовать на этом соглашение __cdecl.
Windows программист
5

Это связано с тем, как вызывается функция - в основном порядок, в котором вещи помещаются в стек, и кто отвечает за очистку.

Вот документация, но она мало что значит, если вы не понимаете первую часть:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

Джоэл Коухорн
источник
4

__stdcall используется для помещения аргументов функции в стек. После завершения функции он автоматически освобождает память. Это используется для фиксированных аргументов.

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

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

Здесь у fnname есть аргументы, которые он непосредственно помещает в стек.

Филипп Ларди
источник
1

Я никогда не использовал это раньше, до сегодняшнего дня. Это потому, что в моем коде я использую многопоточность, а многопоточность API, которую я использую, является windows (_beginthreadex).

Чтобы начать тему:

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

Функция ExecuteCommand ДОЛЖНА использовать ключевое слово __stdcall в сигнатуре метода, чтобы beginthreadex мог вызвать его:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Katianie
источник