A veces es necesario realizar un desarrollo de una librería en .NET que realiza llamadas a librerías de C/C++, porque vienen de funcionalidades más antiguas o simplemente porque en C/C++ corre más en las que se necesita hacer cálculo intensivo.
Si es así, al realizar las pruebas de unidad (unit test), te puede aparecer el problema PInvokeStackImbalance:
Una llamada a la función PInvoke ‘MiDll!<Module>::miFuncionC’ impidió la correspondencia de la pila. Es posible que la razón sea que la firma PInvoke administrada no coincida con la firma de destino no administrada. Compruebe que la convención y los parámetros de llamada de la firma PInvoke coinciden con la firma no administrada de destino.
Lo que viene a significar PInvokeStackImbalance es que la firma de la función a la que se llama no es la esperada. En mi caso estaba seguro que la firma de la función era correcta puesto que en el uso de la función, fuera de las pruebas de unidad, funcionaba perfectamente.
Por lo que el problema es como .NET llama a las funciones de código nativo. Desde .NET tenemos estos distintos tipos de convenios de llamadas a través del enumerado CallingConvention:
Members
Member name | Description |
---|---|
Cdecl | The caller cleans the stack. This enables calling functions with varargs, which makes it appropriate to use for methods that accept a variable number of parameters, such as Printf. |
FastCall | This calling convention is not supported. |
StdCall | The callee cleans the stack. This is the default convention for calling unmanaged functions with platform invoke. |
ThisCall | The first parameter is the this pointer and is stored in register ECX. Other parameters are pushed on the stack. This calling convention is used to call methods on classes exported from an unmanaged DLL. |
Winapi Supported by the .NET Compact Framework. | This member is not actually a calling convention, but instead uses the default platform calling convention. For example, on Windows the default is StdCall and on Windows CE .NET it is Cdecl. |
Más info: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention%28v=vs.71%29.aspx
.NET por defecto usa StdCall, pero mi librería usa Cdecl.
Evitar PInvokeStackImbalance: Definir convenios de llamadas a funciones
Para especificar el convenido de llamada usaremos los atributos de DllImport.
Si en un inicio tenemos definida nuestra función de esta manera:
[DllImport(«MiDll», CharSet=CharSet::Ansi)]
extern «C» void *MiFuncionC(int id);
pasamos a especificar el convenio de llamada de esta otra manera:
[DllImport(«MiDll», CharSet=CharSet::Ansi,CallingConvention=CallingConvention::Cdecl)]
extern «C» void *MiFuncionC(int id);
De esta manera en las pruebas de unidad se fuerza a usar el convenio Cdecl evitando la excepción PInvokeStackImbalance.