web-dev-qa-db-fra.com

Un appel à la fonction PInvoke '[...]' a déséquilibré la pile

Je reçois cette erreur étrange sur certaines choses que j'utilise depuis un certain temps. C'est peut-être une nouvelle chose dans Visual Studio 2010 mais je ne suis pas sûr.
J'essaie d'appeler une fonction non modifiée écrite en C++ à partir de C #.
D'après ce que j'ai lu sur Internet et le message d'erreur lui-même, cela a quelque chose à voir avec le fait que la signature dans mon fichier C # n'est pas la même que celle de C++ mais je ne peux vraiment pas voir il.
Tout d'abord, voici ma fonction non modifiée ci-dessous:

TEngine GCreateEngine(int width,int height,int depth,int deviceType);

Et voici ma fonction en C #:

[DllImport("Engine.dll", EntryPoint = "GCreateEngine", CallingConvention = CallingConvention.StdCall)]  
        public static extern IntPtr CreateEngine(int width,int height,int depth,int device);

Lorsque je débogue en C++, je vois très bien tous les arguments, donc je ne peux que penser que cela a quelque chose à voir avec la transformation de TEngine (qui est un pointeur vers une classe nommée CEngine) en IntPtr. J'ai déjà utilisé cela dans VS2008 sans problème.

45
Sanctus2099

Peut-être que le problème réside dans la convention d'appel. Êtes-vous sûr que la fonction non managée a été compilée en tant que stdcall et pas autre chose (je suppose que fastcall)?

22
PeterK

J'ai eu une DLL c ++ _cdecl que j'ai appelée sans problème à partir de Visual Studio 2008, puis le code identique dans Visual Studio 2010 ne fonctionnerait pas. J'ai le même PInvoke ... a également déséquilibré l'erreur de pile.

La solution pour moi était de spécifier la convention d'appel dans l'attribut DllImport (...): De:

[DllImport(CudaLibDir)] 

À:

[DllImport(CudaLibDir, CallingConvention = CallingConvention.Cdecl)]

Je suppose qu'ils ont changé la convention d'appel par défaut pour DLLImport entre .NET 3.5 et .NET 4.0?

86
Scott Morken

Il se peut également que dans le .NET Framework version 3.5, le MDA pInvokeStackImbalance soit désactivé par défaut. Sous 4.0 (ou peut-être VS2010), c'est activé par défaut .

Oui. Techniquement, le code était toujours erroné et les versions précédentes du framework le corrigeaient en silence.

Pour citer le document .NET Framework 4 Migration Issues : "Pour améliorer les performances d'interopérabilité avec du code non managé, des conventions d'appel incorrectes dans une plate-forme invoquée entraînent désormais l'échec de l'application. Dans les versions précédentes, la couche de marshaling était résolue ces erreurs dans la pile ... Si vous avez des binaires qui ne peuvent pas être mis à jour, vous pouvez inclure l'élément < NetFx40_PInvokeStackResilience > dans le fichier de configuration de votre application pour permettre de résoudre les erreurs d'appel dans la pile comme précédemment. Cependant, cela peut affecter les performances de votre application. "

Un moyen simple de résoudre ce problème consiste à spécifier la convention d'appel et à vérifier qu'elle est identique à celle de la DLL. Une __declspec(dllexport) devrait produire un format cdecl .

[DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl)]
49
Keith Vinson

Utilisez le code suivant, si par exemple votre DLL a le nom MyDLL.dll et que vous souhaitez utiliser la fonction MyFunction dans la DLL

[DllImport("MyDLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunction();

cela a fonctionné pour moi.

5
user3610819

Dans mon cas (VB 2010 et DLL compilé avec Intel Fortran 2011 XE), le problème existe lorsque mon application cible .NET Framework 4. Si je change le cadre ciblé en version 3.5, alors tout fonctionne bien comme Donc, je suppose que la raison est quelque chose introduite dans .Net Framework 4 mais je n'ai aucune idée pour le moment

Mise à jour: le problème a été résolu en recompilant Fortran DLL et en spécifiant explicitement STDCALL comme convention d'appel pour les noms d'exportation dans la DLL.

2
southern_snowman