O que é __stdcall?

151

Estou aprendendo sobre programação Win32, e o WinMainprotótipo se parece com:

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

Fiquei confuso sobre o que WINAPIera esse identificador e encontrei:

#define WINAPI      __stdcall

O que isso faz? Estou confuso por ter alguma coisa depois de um tipo de retorno. Para que serve __stdcall? O que significa quando há algo entre o tipo de retorno e o nome da função?

Tristan Havelick
fonte
2
@AndrewProck A alteração "fictícia" foi aceitável neste caso, mas em geral você pode usar  s para contornar o mínimo bobo (e contraproducente - neste caso) de 6 caracteres.
Adi Inbar

Respostas:

170

__stdcallé a convenção de chamada usada para a função. Isso informa ao compilador as regras que se aplicam para configurar a pilha, enviar argumentos e obter um valor de retorno.

Há uma série de outras convenções de chamada, __cdecl, __thiscall, __fastcalle maravilhosamente nomeado __declspec(naked). __stdcallé a convenção de chamada padrão para chamadas do sistema Win32.

A Wikipedia cobre os detalhes .

É importante principalmente quando você está chamando uma função fora do seu código (por exemplo, uma API do sistema operacional) ou se o sistema operacional está chamando você (como é o caso do WinMain). Se o compilador não souber a convenção de chamada correta, provavelmente ocorrerá falhas muito estranhas, pois a pilha não será gerenciada corretamente.

Rob Walker
fonte
8
Veja esta pergunta para um exemplo de acidentes muito starnge stackoverflow.com/questions/696306/...
sharptooth
Se não me engano, essas convenções de chamada controlam como o compilador produz o código do assembly. Ao fazer interface com o código do assembly, é essencial que a convenção de chamada seja observada para evitar problemas de pilha. Há uma mesa agradável aqui documentar algumas convenções: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller
Podemos dizer que isso desabilitaria certas otimizações ilegais?
kesari
40

C ou C ++ em si não definem esses identificadores. Eles são extensões do compilador e representam certas convenções de chamada. Isso determina onde colocar argumentos, em que ordem, onde a função chamada encontrará o endereço de retorno e assim por diante. Por exemplo, __fastcall significa que argumentos de funções são passados ​​por registradores.

O artigo da Wikipedia fornece uma visão geral das diferentes convenções de chamadas encontradas lá.

Johannes Schaub - litb
fonte
18

As respostas até agora cobriram os detalhes, mas se você não pretende ir para a montagem, tudo o que você precisa saber é que o chamador e o destinatário devem usar a mesma convenção de chamada; caso contrário, você receberá bugs que são difíceis de encontrar.

MovEaxEsp
fonte
12

Concordo que todas as respostas até agora estão corretas, mas aqui está a razão. Os compiladores C e C ++ da Microsoft fornecem várias convenções de chamada para a velocidade (pretendida) de chamadas de função nas funções C e C ++ de um aplicativo. Em cada caso, o chamador e o destinatário devem concordar com qual convenção de chamada usar. Agora, o próprio Windows fornece funções (APIs) e essas já foram compiladas; portanto, quando você as chama, deve estar em conformidade com elas. Quaisquer chamadas para APIs do Windows e retornos de chamada das APIs do Windows devem usar a convenção __stdcall.

Programador do Windows
fonte
3
e não deve ser confundido com _standard_call, pois é padrão-c! pode-se pensar que esse seria o objetivo de __stdcall se não se conhecesse melhor
Johannes Schaub - litb 18/11/08 -
3
Um pequeno detalhe: existem algumas APIs do Windows que usam __cdecl em vez de __stdcall - geralmente aquelas que recebem um número variável de parâmetros, como wsprintf ().
Michael Burr
Você está certo. É nomeado para parecer uma função CRT, mas é uma API. Por acaso, você sabe como invocá-lo em C #?
Programador Windows
Eu não testei isso, mas o pinvoke.net fornece esta assinatura: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr
Minha intuição diz que o compilador C # não saberia usar a convenção __cdecl nessa.
Programador Windows
5

Tem a ver com a forma como a função é chamada - basicamente a ordem em que as coisas são colocadas na pilha e quem é responsável pela limpeza.

Aqui está a documentação, mas isso não significa muito, a menos que você entenda a primeira parte:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

Joel Coehoorn
fonte
4

__stdcall é usado para colocar os argumentos da função na pilha. Após a conclusão da função, ela desaloca automaticamente a memória. Isso é usado para argumentos fixos.

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

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

Aqui o nome fn tem argumentos que ele envia diretamente para a pilha.

philippe lhardy
fonte
1

Eu nunca tive que usar isso antes até hoje. É porque no meu código eu estou usando multiencadeamento e a API de multiencadeamento que eu estou usando é a do Windows (_beginthreadex).

Para iniciar o encadeamento:

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

A função ExecuteCommand DEVE usar a palavra-chave __stdcall na assinatura do método para o beginthreadex chamá-lo:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Katianie
fonte