IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Erreur 'PInvokeStackImbalance' sur incompatiblilté de signature de DLL


Sujet :

C#

  1. #1
    Membre du Club
    Inscrit en
    Février 2010
    Messages
    277
    Détails du profil
    Informations forums :
    Inscription : Février 2010
    Messages : 277
    Points : 64
    Points
    64
    Par défaut Erreur 'PInvokeStackImbalance' sur incompatiblilté de signature de DLL
    Bonjour,

    Dans un projet en C#, j'utilise des fonctions qui sont dans des DLL écrites en C dont j'ai le prototype.

    J'ai un crash "'PInvokeStackImbalance' " lorsque j'utilise l'une des fonction: dbClassAttach.

    Son prototype C est

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
            HCLASS far pascal	dbClassAttach(HSESS hSess, long objclass);

    La déclaration C# de cette fonction est:

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
            [DllImport("crdbapi.dll")]
            public static extern crc.HCLASS dbClassAttach(crc.HSESS hSess, long objclass);


    HCLASS et HSESS en C/C++ (crc.HSESS crc.HSESS en C#) sont des types construits : un pointer sur un void: void* que j'ai défini en C# comme ca dans un classe nommée crc:
    (Le mode unsafe permettant au compilateur de prendre en compte les pointeurs)

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    		unsafe public struct HSESS
    		{
    			public void* hSess;
    		}
     
    		unsafe public struct HCLASS
    		{
    			public void* hClass;
    		}


    J'appelle la fonction comme ca:

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    			crc.HCLASS hClass = dbClassAttach(GlobalVar.hSess, 1);

    où le type crc.HCLASS hClass est défini plus haut. La variable GlobalVar.hSess est de type HSESS, comme défini plus haut (un pointeur sur un void). Ce GlobalVar.hSess est l'identifiant de session ouverte par une autre fonction (dbSessLoginServer) qui fonctionne, elle, parfaitement et sans aucun problème.

    Tous cela me parait cohérent et la conversion des types C vers C# me semble OK. Pourtant, j'ai l'erreur 'PInvokeStackImbalance' ".

    Le texte d'erreur exact est le suivant:
    "
    L'Assistant Débogage managé 'PInvokeStackImbalance' a détecté un problème dans 'C:\MyData\XXXX\Eng\C#\XXXX\MySNMPcConsole\bin\Debug\XXXX.vshost.exe'.

    Informations supplémentaires*: Un appel à la fonction PInvoke 'XXXX!XXXX.Main::dbClassAttach' a déséquilibré la pile. Cela peut se produire, car la signature PInvoke managée ne correspond pas à la signature cible non managée. Vérifiez que la convention d'appel et les paramètres de la signature PInvoke correspondent à la signature non managée cible.
    "

    Je comprends que la façon dont j'appelle la fonction avec ses arguments (signature/prototype) (ou en réalité la déclaration de la DLL avec le DLLImport) n'est pas la même que celle défini dans la DLL (code non managé) . Alors que j'ai respecté la définition du header.h C.

    Une idée ? Est-ce que c'est moi qui ne fait pas les choses comme il faudrait ?

    Autre question: on peut lister toutes les fonctions d'une DLL, il existe des trucs que j'ai essayés. Mais Est-ce que il existe des choses pour extraire la signature des fonctions présentes dans la DLL ? Ou si c'est impossible ?

    Merci.

  2. #2
    Expert confirmé

    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2006
    Messages
    3 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Septembre 2006
    Messages : 3 580
    Points : 5 195
    Points
    5 195
    Par défaut
    Lu mais jamais utilisé :

    http://www.pinvoker.com/Product.aspx

    L'outil te génère automatiquement ce qu'il faut (à partir de dll et .h)
    The Monz, Toulouse
    Expertise dans la logistique et le développement pour
    plateforme .Net (Windows, Windows CE, Android)

  3. #3
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Points : 9 743
    Points
    9 743
    Billets dans le blog
    3
    Par défaut
    Ca fait un bout de temps que je n'ai pas eu à faire appel à des librairies C donc il se peut que je rouille sur le sujet Mais normalement ce message ne bloque pas l'exécution du programme, c'est plus un genre d'avertissement. Si tu continues l'exécution du programme, est-ce que tu récupères ce que tu souhaites ?

    Les appels aux librairies C diffèrent des appels à l'API Windows par exemple. Donc pour supprimer ce message, il y a la possibilité de modifier la convention d'appel comme ceci :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    [DllImport("crdbapi.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern crc.HCLASS dbClassAttach(crc.HSESS hSess, long objclass);
    Ou bien tu peux aussi désactiver l'exception dans les options de Visual Studio.

    Est-ce que ca solutionne ton problème ?

    Sinon comme outil il y a celui indiqué par theMonz31 ou encore celui-ci : http://www.red-gate.com/products/dot...pment/pinvoke/
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  4. #4
    Membre du Club
    Inscrit en
    Février 2010
    Messages
    277
    Détails du profil
    Informations forums :
    Inscription : Février 2010
    Messages : 277
    Points : 64
    Points
    64
    Par défaut
    Bonsoir,

    Merci a vous deux.

    Le PInvoke.NET de RedGate est un plug-in pour Visual Studio et ne "fonctionne" que pour les DLL Windows et non les DLL propriétaires, m'a-t-il semblé comprendre. J'ai toutefois essayé de l'installer mais sans sucées. Le fichier est un .vsix qui est censé s'installer automatiquement par un double-click mais j'ai une erreur système du programme d'Install qui explique que l'extension ne peut pas être installé. Je l'ai dézippé (c'est un fichier au format Zip) pour l'installer manuellement mais ce ne sont que des DLL, aucun .exe. J'ai essayé de le mettre directement dans le répertoire des Extensions de mon VS 2012, mais il n'est pas reconnu dans le Manager de d'Extensions. Avec la Manager d'Extensions, on peut télécharger des plug-in directement sur le site de Visual Studio : il n'est pas présent dans le Manager d'Extension, y compris avec la fonction "recherche". La poisse. J'ai abandonné, au moins pour aujourd'hui.


    Pour le PInvoker.NEt de Kilapara, il exige VS C++, que je n'ai pas. D'autant que il n'y pas de plug-in pour VS12. Leur programme complet (non plug-in) demande aussi VC++ et est limité.

    Pour le DllImport("crdbapi.dll", CallingConvention = CallingConvention.Cdecl) j'ai remplacé la convention d'appel par CallingConvention.ThisCall qui ne génère pas d'erreur et fonctionne. Sauf que le résultat n'est pas total puisque le pointeur reçu est vide (adresse mémoire zéro en hexa). Je devrais avoir autre chose (j'ai des adresse mémoires avec les autres fonctions dont dbSessLoginServer).

    La structure suivante en C#, correspond t-elle à la définition C/C++ de typedef void* HSESS;

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
     
    		unsafe public struct HCLASS
    		{
    			public void* hClass;
    		}

  5. #5
    Membre du Club
    Inscrit en
    Février 2010
    Messages
    277
    Détails du profil
    Informations forums :
    Inscription : Février 2010
    Messages : 277
    Points : 64
    Points
    64
    Par défaut
    Bonjour,

    Pour apporter des nouvelles, cet appel de DLL C en C# ne fonctionne absolument pas.

    J'ai les header C suivants:

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    		HSESS far pascal	dbSessLoginServer(HWND cons_wnd, char *ipAddr, char *user, char *passwd, eventfunc_t func, long client_arg);
     
    		long far pascal		dbSessGetLevel(HSESS sess);
     
    		HSESS far pascal	dbSessLoginConsole(HWND cons_wnd, eventfunc_t func, long client_arg);
     
    		HCLASS far pascal	dbClassAttach(HSESS hSess, long objclass);
     
    + d'autres...

    Où les types HSESS et HCLASS sont:

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    		typedef void*	HSESS;	  	// session handle
    		typedef void*	HCLASS;		// class handle
    		etc...

    Je les déclares en C# (code managé) comma ca, en fonction des headers plus haut (que je reprends en commentaire entre la ligne DLLImport et la signature de la fonction en code managé):

    J'utilise les IntPtr qui sont strictement défini comme des pointeurs sur un void: void*, ce qui correspond, parfaitement au HSESS, HCLASS, etc.

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    		[DllImport("crdbapi.dll")]
    		// HSESS far pascal	dbSessLoginServer(HWND cons_wnd, char *ipAddr, char *user, char *passwd, eventfunc_t func, long client_arg);
    		public static extern IntPtr dbSessLoginServer(IntPtr HWND, String ipAddr, String userName, String password, IntPtr evfunc, uint client_arg);
     
    		// DWORD dbSessGetLevel(HSESS hSess);
    		[DllImport("crdbapi.dll")]
    		// long far pascal	dbSessGetLevel(HSESS sess);
    		public static extern uint dbSessGetLevel(IntPtr hSess);
     
    		[DllImport("crdbapi.dll")]
    		// HSESS far pascal	dbSessLoginConsole(HWND cons_wnd, eventfunc_t func, long client_arg);
    		public static extern IntPtr dbSessLoginConsole(IntPtr cons_wnd, IntPtr evfunc, long client_arg);
     
    		[DllImport("crdbapi.dll")]
    		// void far pascal dbSessLogout(HSESS sess);
    		public static extern void dbSessLogout(IntPtr hSess);
     
    		[DllImport("crdbapi.dll", CallingConvention = CallingConvention.ThisCall)]
    		// HCLASS far pascal dbClassAttach(HSESS hSess, long objclass);
    		public static extern IntPtr dbClassAttach(IntPtr hSess, long objclass);

    Les seules fonctions qui semblent fonctionner sont les fonctions dbSessLoginServer et dbSessGetLevel. Le reste (en particulier dbClassAttach, et je ne peux pas aller plus lion dans l'utilisation de l'API) fait cracher sévèrement et le programme s'arrête brutalement ou renvois un pointeur vide (0x00000000) (ce qui signifie que la fonction a échoué alors qu'elle devait réussir). J'ai essayé toutes les conventions d'appel (ThisCall, StdCall, Cdecl, etc.) ainsi que le Marhsaling. D'ailleurs c'est peut-être le Marchaling (directive [MarshalAs(UnmanagedType.xxx etc.)] ou [return: MarshalAs(UnmanagedType.xxx)]. J'ai essayé plein de Marchaling, sans succès. Une idée ?

    En C, application console, ca fonctionne parfaitement. Je vais tenter en Visual C++, bien que je voudrai le faire en C#, si je trouve un compilateur Express qui supporte les WinForm, ce qui n'est pas le cas de Express 2012. L'édition Community peut-être ?

    Merci.

    Edition:
    Le crash sévère mentionné plus haut est un problème de violation d'accès a la mémoire :
    " Une exception non gérée du type 'System.AccessViolationException' s'est produite dans xxxxxx.exe
    Informations supplémentaires*: Tentative de lecture ou d'écriture de mémoire protégée. Cela indique souvent qu'une autre mémoire est endommagée. "

    Qu'est-ce qui ne va pas dans ma manipulation des pointeurs ?

  6. #6
    Expert confirmé

    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2006
    Messages
    3 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Septembre 2006
    Messages : 3 580
    Points : 5 195
    Points
    5 195
    Par défaut
    juste une petite question:

    A priori, tu veux attaquer une base de données avec cette dll, mais quelle base de données ?

    si celà se trouve, tu as peut-etre un wrapper .Net ou librairie .net qui te permet d'accéder à cette database sans t'obliger à wrapper absolument le bazar...

    De plus, si tu as le code de ta librairie C++, peut-etre simplement la porter en C# serait plus rapide ?
    The Monz, Toulouse
    Expertise dans la logistique et le développement pour
    plateforme .Net (Windows, Windows CE, Android)

  7. #7
    Membre du Club
    Inscrit en
    Février 2010
    Messages
    277
    Détails du profil
    Informations forums :
    Inscription : Février 2010
    Messages : 277
    Points : 64
    Points
    64
    Par défaut
    Citation Envoyé par theMonz31 Voir le message
    juste une petite question:

    A priori, tu veux attaquer une base de données avec cette dll, mais quelle base de données ?

    si celà se trouve, tu as peut-etre un wrapper .Net ou librairie .net qui te permet d'accéder à cette database sans t'obliger à wrapper absolument le bazar...

    De plus, si tu as le code de ta librairie C++, peut-etre simplement la porter en C# serait plus rapide ?
    Non. pas tout a fait. "crdbapi.dll" n'a rien a voir avec une base de données. C'est une DLL pour gérer une base de données propriétaire (fichier propriétaire ou base de données non standard, à ma connaissance) qui stoke des infos. Alors oui le "db", mais ce n'est pas une base de données. D'autant que cette API ne gère pas "la base de données" mais communique avec un logiciel serveur qui, lui, accède à cette "pseudo" base de données.

    Je n'ai aucun code, sauf les headers C/C++ te les .lib pour la compilation et l'édition des liens.

  8. #8
    Membre du Club
    Inscrit en
    Février 2010
    Messages
    277
    Détails du profil
    Informations forums :
    Inscription : Février 2010
    Messages : 277
    Points : 64
    Points
    64
    Par défaut
    J'ai installé VS Community 2013 et je m'aperçois que je n'ai pas les WinForm pour Visual C++.

    Il faut une édition payante ? Ou s'il existe quelque chose que l'on peut intégrer au VS séparément ? WinForm ou WPF, pourvut que ce ne soit pas Win32. Nope ?

Discussions similaires

  1. [XL-2010] Erreur 453 sur l'utilisation de ma dll
    Par ninolo dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 01/01/2015, 18h32
  2. Réponses: 2
    Dernier message: 04/12/2014, 20h31
  3. Erreur pInvokestackImbalance sur SendMessage
    Par neojeff dans le forum VB.NET
    Réponses: 5
    Dernier message: 24/10/2013, 14h25
  4. [AC-2013] Plantage avec erreur windows sur DLL
    Par charliejo dans le forum Access
    Réponses: 3
    Dernier message: 17/10/2013, 17h19
  5. Erreur Thread sur Appel dans DLL
    Par Danyel dans le forum VB.NET
    Réponses: 10
    Dernier message: 27/10/2008, 23h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo