¿Qué es __stdcall?

151

Estoy aprendiendo sobre la programación Win32, y el WinMainprototipo se ve así:

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

Estaba confundido sobre para qué era este WINAPIidentificador y encontré:

#define WINAPI      __stdcall

Que hace esto? Estoy confundido por tener algo en absoluto después de un tipo de retorno. Lo que es __stdcallpara? ¿Qué significa cuando hay algo entre el tipo de retorno y el nombre de la función?

Tristan Havelick
fuente
2
@AndrewProck El cambio "ficticio" estuvo bien en este caso, pero en general puede usar  s para evitar el mínimo de 6 caracteres tonto (y contraproducente - caso en punto).
Adi Inbar

Respuestas:

170

__stdcalles la convención de llamada utilizada para la función. Esto le dice al compilador las reglas que se aplican para configurar la pila, generar argumentos y obtener un valor de retorno.

Hay una serie de otras convenciones de llamada, __cdecl, __thiscall, __fastcally el maravillosamente nombrados __declspec(naked). __stdcalles la convención de llamadas estándar para las llamadas al sistema Win32.

Wikipedia cubre los detalles .

Principalmente importa cuando está llamando a una función fuera de su código (por ejemplo, una API del sistema operativo) o cuando el sistema operativo lo está llamando (como es el caso aquí con WinMain). Si el compilador no conoce la convención de llamada correcta, es probable que tenga bloqueos muy extraños ya que la pila no se administrará correctamente.

Rob Walker
fuente
8
Consulte esta pregunta para ver un ejemplo de accidentes muy intensos stackoverflow.com/questions/696306/…
sharptooth
Si no me equivoco, estas convenciones de llamada controlan cómo el compilador produce el código de ensamblaje. Al interactuar con el código de ensamblaje, es fundamental que se tenga en cuenta la convención de llamada para evitar problemas de pila. Aquí hay una buena tabla que documenta algunas convenciones: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller
¿Podemos decir que esto deshabilitaría ciertas optimizaciones ilegales?
kesari
40

C o C ++ en sí no definen esos identificadores. Son extensiones del compilador y representan ciertas convenciones de llamadas. Eso determina dónde colocar argumentos, en qué orden, dónde la función llamada encontrará la dirección de retorno, y así sucesivamente. Por ejemplo, __fastcall significa que los argumentos de las funciones se pasan sobre los registros.

El artículo de Wikipedia proporciona una visión general de las diferentes convenciones de llamadas que se encuentran por ahí.

Johannes Schaub - litb
fuente
18

Las respuestas hasta ahora han cubierto los detalles, pero si no tiene la intención de desplegarse al ensamblaje, todo lo que tiene que saber es que tanto la persona que llama como la persona que llama deben usar la misma convención de llamadas, de lo contrario obtendrá errores que Son difíciles de encontrar.

MovEaxEsp
fuente
12

Estoy de acuerdo en que todas las respuestas hasta ahora son correctas, pero esta es la razón. Los compiladores C y C ++ de Microsoft proporcionan varias convenciones de llamadas para la velocidad (prevista) de las llamadas a funciones dentro de las funciones C y C ++ de una aplicación. En cada caso, la persona que llama y la persona que llama deben acordar qué convención de llamada utilizar. Ahora, Windows mismo proporciona funciones (API), y esas ya se han compilado, por lo que cuando las llame, debe cumplirlas. Cualquier llamada a las API de Windows y las devoluciones de llamada de las API de Windows deben usar la convención __stdcall.

Programador de Windows
fuente
3
y no debe confundirse con _standard_call en que es standard-c! uno podría pensar que ese sería el punto de __stdcall si no se conoce mejor
Johannes Schaub - litb
3
Un pequeño inconveniente: hay algunas API de Windows que usan __cdecl en lugar de __stdcall, generalmente las que toman un número variable de parámetros, como wsprintf ().
Michael Burr
Tienes razón. Se llama para parecerse a una función CRT pero es una API. ¿Por casualidad sabes cómo P / Invocarlo desde C #?
Programador de Windows el
No he probado esto, pero pinvoke.net da esta firma: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr el
Mi intuición dice que el compilador de C # no sabría usar la convención __cdecl en esa.
Programador de Windows el
5

Tiene que ver con cómo se llama la función, básicamente el orden en que se colocan las cosas en la pila y quién es responsable de la limpieza.

Aquí está la documentación, pero no significa mucho a menos que comprenda la primera parte:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

Joel Coehoorn
fuente
4

__stdcall se usa para poner los argumentos de la función en la pila. Después de completar la función, automáticamente desasigna la memoria. Esto se usa para argumentos fijos.

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

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

Aquí el nombre fn tiene argumentos que empuja directamente a la pila.

philippe lhardy
fuente
1

Nunca tuve que usar esto antes hasta hoy. Es porque en mi código estoy usando subprocesos múltiples y la API de subprocesos múltiples que estoy usando es la de Windows (_beginthreadex).

Para comenzar el hilo:

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

La función ExecuteCommand DEBE usar la palabra clave __stdcall en la firma del método para que beginthreadex la llame:

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