c++ - ¿Por qué a veces funciona el uso de la convención de llamadas incorrecta?
winapi callback (4)
Esto es muy específico de Windows, no estamos hablando de C ++ estándar aquí.
Verificando la documentación de StartServiceDispatcher
tiene solo un argumento, y se declara como WINAPI
que a su vez significa __stcall
llamada de convención de llamadas.
Para funciones independientes, __stdcall
es una de las dos principales convenciones de llamadas. El otro es __cdecl
. La diferencia de nivel de código de máquina es simplemente quién restaura el puntero de pila: con __stdcall
es la función en sí, mientras que con __cdecl
es el código de llamada.
Cuando la función en realidad es __stdcall
pero se invoca como si fuera __cdecl
, la situación es que hay dos intentos para restaurar el puntero de la pila: uno a la salida de la función y otro en el código de llamada. El de la función tendrá éxito. Dependiendo de cómo se realice el intento en el código de llamada, puede ensuciar completamente (por ejemplo, si solo se agrega el desplazamiento requerido, tratando el puntero de la pila como relativo), o puede no tener ningún efecto perjudicial. Pero es muy probable que cree un lío, ya que la suposición sobre el valor del puntero de la pila al regresar de la función es incorrecta.
Cuando la función en realidad es __cdecl
, no restaurará el puntero de pila, ya que esa es la responsabilidad del código de llamada. Y si el código de llamada lo trata como __stdcall
entonces el código de llamada tampoco lo restaurará, ya que desde la vista del código de llamada la función lo está haciendo. El resultado, si no se produce un bloqueo anticipado (debido a suposiciones rotas), debería ser que las llamadas repetidas, por ejemplo en un ciclo, consumirán espacio en la pila.
Es todo un comportamiento indefinido.
Y una de las propiedades de Undefined Behavior es que puede hacer cualquier cosa, incluso aparentemente trabajando ...
Saludos y hth.,
Utilicé la función "StartServiceCtrlDispatcher" para registrar una función de devolución de llamada (llamada ServiceMain) en Windows, pero la función de devolución de llamada que declare se compiló con una convención de llamadas incorrecta.
El problema es que en algunas computadoras , cuando la aplicación regresaba de la función de devolución de llamada, la aplicación se bloqueaba, pero en otras computadoras la aplicación no se bloqueaba .
Ahora, una vez que encontré el error, todo funcionó, pero simplemente no entiendo por qué en algunas computadoras funcionó correctamente sin fallar.
¡Gracias! :-)
Las computadoras donde la aplicación se bloqueó podrían haber estado utilizando .NET Framework versión 4.
Eche un vistazo al siguiente artículo: http://msdn.microsoft.com/en-us/library/ee941656.aspx
Enuncia lo siguiente en Interoperabilidad - Invocación de plataforma:
"Para mejorar el rendimiento en la interoperabilidad con el código no administrado, las convenciones de llamadas incorrectas en una invocación de plataforma ahora causan que la aplicación falle. En versiones anteriores, la capa de clasificación resolvía estos errores en la pila".
Las convenciones de llamada difieren en los detalles, como qué registros se conservan. Si por casualidad no almacenaba algo que todavía necesitara en esos registros, no importaba que se borraran cuando no era necesario. Del mismo modo, si su convención de llamadas difiere sobre cómo se trata con los valores de retorno, si no devuelve nada, entonces no importa.
Afortunadamente, x64 solo tiene una convención de llamadas y todo este lío estará en el pasado.
Todo esto está relacionado con lo que está vigente en la memoria. Supongamos que tiene dos funciones como esta:
void stdcall f1(...) { ... }
void cdecl f2(...) { ... }
stdcall
es la convención de llamadas de Windows, mientras que cdecl
es utilizado por la mayoría de los compiladores. La diferencia entre ellos es quién es el responsable de limpiar la pila después de la llamada. En stdcall
, el callee ( f1
, o f2
) lo hace, en cdecl
, lo hace la persona que llama.
La pila, después de todo, está llena de valores desconocidos. Por lo tanto, cuando se limpia (incorrectamente), el siguiente valor al que accedes en la pila no está determinado. Podría ser un valor aceptable, o podría ser muy malo. Esto es, en principio, cómo funciona el desbordamiento de pila (el error, no el sitio).