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 :

Passage d'objet dans une DLL


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut Passage d'objet dans une DLL
    Bonjour,

    Dans mon programme, je suis en train de créer un système de plugins. Histoire de vérifier que ça fonctionne, j'ai codé quelques fonctions dans ma DLL genre "GetAuthor" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    //HEADER (dans une classe)
    typedef void* (__stdcall *DllPtyType)(void);
    DllPtyType AuthorFc;
     
    // FICHIER CPP
    AuthorFc = LoadFunction<DllPtyType>("GetAuthor");
    La fonction LoadFunction étant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    template<class T>
    	T LoadFunction(const string& _FuncName)
    	{
    		void* Func = GetProcAddress(m_Library, _FuncName.c_str() );
     
    		return reinterpret_cast<T>(Func);
    	}
    (j'ai enlevé le check sur "Func" puisque je le gère ailleurs). Et la fonction GetAuthor :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    extern "C" __declspec(dllexport) void* GetAuthor()
    {
    	return (void*)(&g_Author);
    }
    (g_Author étant de type string). Puis je l'utilise comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    void* l_Ret;
     
    	if( AuthorFc )
    	{
    		l_Ret = AuthorFc();
    		memcpy((void*)&m_Author,l_Ret,sizeof(string));
    	}
    	else
    		m_Author = "Not specified";
    Ca fonctionne... presque. Dans la suite du programme, j'obtiens bien l'auteur. Par contre, j'ai un joli plantage (type segfault) à la fermeture. Je ne vois pas du tout d'où il vient (enfin... le memcpy qui passe mal ?).

    Et puis ce code est relativement moche : je suis obligé de passer par un castage tout moche en void* à la C. En effet, si je définie DllPtyType comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    typedef string (__stdcall *DllPtyType)(void);
    où que je passe un argument de type string (mais, plus généralement, un quelconque type non standard du C/C++), le code compile, mais plante à l'exécution en me disant que le registre "ESP was not saved properly...".
    Pourquoi ? Alors que je reste définitivement en C++ partout, je ne peux pas passer d'objet autrement que par un cast "sauvage" en void* ?

    merci

    Cordialement
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    Je n'ai plus d'erreur de mémoire à la fermeture de mon programme si, au lieu de passer des string castées en void*, je passe directement des char* (qui ne peuvent d'ailleurs pas être "const").

    On va faire avec... n'empêche que ça m'étonne de pas pouvoir passer d'objet à des DLL... Je dois me tromper. Savez-vous comment faire ?

    Merci.

    Cordialement
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  3. #3
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 537
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 537
    Points : 2 548
    Points
    2 548
    Par défaut
    Il n'y a rien de particulier a faire : les objet apparaissent en C comme des structures.

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    Alors pourquoi ce plantage ?
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  5. #5
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 537
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 537
    Points : 2 548
    Points
    2 548
    Par défaut
    Tout ce qui est alloué dans la DLL doit être désaloué dans la DLL et vice versa. A mon avis le plantage vient de la.

    Essaye de passer l'application au debuggeur histoire de voir précisément ouc a coince

  6. #6
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    Ou alors vérifie que tes projets sont bien configurés correctement concernant la mémoire partagée entre exe et dll...

  7. #7
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    deadalnix > Tu as probablement raison, je vais regarder de ce pas.

    Klaim > Peux-tu être un peu plus explicite s'il-te-plaît ? Où peut-on régler ces paramètres (sachant que j'utilise visual studio)?

    Mat007 > Merci pour cette doc ! Du coups, si j'ai bien compris, il me semble que pour un système de plugins tel que celui que je met en place, j'ai tout intérêt à oublier les passages d'objets "user-defined" genre std::string, std::vector ou encore même des types perso : si je distribue mon programme compilé avec une version x de la STL et que quelqu'un veut se faire son plugin 2 ans plus tard, donc avec une version x+1 de la STL, y'a une forte probabilité pour que ça plante. Est-ce correct ? Vaut-il donc mieux que je reste avec mes types standards ?

    Merci

    Cordialement
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  9. #9
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Citation Envoyé par delire8 Voir le message
    il me semble que pour un système de plugins tel que celui que je met en place, j'ai tout intérêt à oublier les passages d'objets "user-defined" genre std::string, std::vector ou encore même des types perso : si je distribue mon programme compilé avec une version x de la STL et que quelqu'un veut se faire son plugin 2 ans plus tard, donc avec une version x+1 de la STL, y'a une forte probabilité pour que ça plante. Est-ce correct ? Vaut-il donc mieux que je reste avec mes types standards ?
    Oui c'est en gros le principe.
    Tu dois quand même pouvoir exporter des types à toi en faisant un peu gaffe (genre aux données membre notamment, mais tu peux toujours les masquer avec un Pimpl par exemple).

    MAT.

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    Bon, après pas mal de temps passé sur google à lire des articles dont tu m'as suggéré l'existence, comme : http://www.deez.info/sengelha/2008/0...ries/#comments

    je pense que je vais abandonner l'idée de passer des objets issus de la STL d'un module à un autre... Reste à voir pour mes objets persos.

    Justement, en parlant de ça. Il est clairement dit que pour que ça marche, il faut que la DLL et l'EXE partagent le même allocator sous windows. Ok, mais comment on s'en assure ? Quelques articles suggèrent de regarder la "runtime library" (option /MD , /MDd, etc.. ) et de s'assurer que c'est la même. Dans mon projet, elles sont bien toutes les deux à /MDd (debug oblige), et pourtant, il semble qu'elles ne partagent pas le même allocator. Qu'y a-t-il à vérifier en plus ?

    Merci

    Cordialement

    PS : la technique pimpl que tu m'as recommandé est totalement nouvelle pour moi. Il semble qu'elle induise de fortes réductions de performances... or je cours après celles-ci (sans compter qu'il faut ré-écrire une bonne partie du code). Merci tout de même, je la garde comme dernier recours.
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  11. #11
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    Un petit truc que je viens de voir (eh oui... il est tard, je tourne au ralenti) :

    si dans ma DLL je transforme ma fonction en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    extern "C" __declspec(dllexport) string* GetAuthor()
    {
    	return &g_Author;
    }
    g_Author étant de type string. Puis que je redéfinie le pointeur de fonction de AuthorFc pour recevoir un string*, et que je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    m_Author = *(AuthorFc());
    Ben ça marche.
    Avec ce que vous m'avez dit, c'est logique : en raisonnant ainsi, je retourne d'abord un pointeur vers un std::string (donc pas de constructeur), puis une fois que mon EXE l'a "capturé", j'en fais une copie. L'allocation se fait avec le bon allocator.

    Mais bon, si ça fonctionne, l'intérêt est quasiment nul ici : on est obligé de faire des copies partout, alors que l'idée de base était de passer un maximum de références pour éviter ces copies...

    Bref, je vais continuer à réfléchir sur la question.

    Merci

    Cordialement
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  12. #12
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 537
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 537
    Points : 2 548
    Points
    2 548
    Par défaut
    Si ton bazard est open source, alors il n'y a pas vraiment de soucis a passer des elemnts de la stl : il n'y a qu'a recompiler pour que ca marches.

    Pour tes objets persos, Tu peux inclure les .h des deux cotés et spécifier que ta class est a exporter. Dans ce cas tu pourra passer tes objets sans soucis d'un bout a l'autre. Par contre, assure toi que la définition de tes objets ets stable, et en depend pas par exemple d'un std::string.

  13. #13
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    delire8> Désolé j'ai pas été clair.
    Je ne me souviens pas exactement du champs en question dans les propriétés de projets, je suis en train d'essayer de le retrouver là. Je me souviens juste que depuis VS2005 la valeur par défaut du champs permet aux objets créés par les modules d'être accessibles entre les modules (ce qui t'evite a avoir a faire une DLL boite noir).
    Peut être était-ce C++/Code Generation/RuntimeCheck , actuellement mon projet exe est réglé en Multi-Threaded Dll (et Debug en mode Debug). A vrai dire les dll qu'il utilise aussi.

    Désolé ma mémoire est imprécise là dessus, j'ai plus eu ce problème depuis des années

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    En fait, ma DLL et mon EXE utilisent bien la même runtime library. C'est "juste" que ma DLL n'est pas liée à l'exe au moment de la compilation, mais utilisée via LoadLibrary / GetProcAdress. Ca doit vouloir dire que l'exe et la dll fonctionnent comme deux programmes relativement indépendants (du point de vue du memory manager), et donc le passage d'objets perso (plus généralement, l'allocation dans l'un, la destruction dans l'autre) ne fonctionne pas.

    En gros, j'aurais besoin d'une zone de mémoire commune dans laquelle l'exe et la dll peuvent "déposer" des données. Ca existe ?

    Merci

    cordialement
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  15. #15
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    Il semblerait que l'article suivant :
    http://www.tweakbits.com/articles/dll/index.html

    réponde à ma question... je vais tester
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  16. #16
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 537
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 537
    Points : 2 548
    Points
    2 548
    Par défaut
    Citation Envoyé par delire8 Voir le message
    et donc le passage d'objets perso (plus généralement, l'allocation dans l'un, la destruction dans l'autre) ne fonctionne pas.
    La passage d'objet si. Il faut que la définition de l'objet (le .h) soit dans les deux projets et ils pourront se comprendre. A condition que tu spécifie au compilo que ta classe est exportable/importable.

    Et non, il n'y a pas moyen de créer un objet dans l'un et le détruire dans l'autre, ca poserait de gros problèmes de sécurité. Tu peut par contre accéder aux zone mémoire de l'un ou l'autre en t'envoyant des pointeurs par exemple Le passage se fait en C style donc pas de références.

  17. #17
    screetch
    Invité(e)
    Par défaut
    tu as essayé en virant le __stdcall ? car ton probleme est un probleme de fausse declaration de fonction : tu appelles avec une convention d'appel mais tu definis avec une autre. alors soit tu rajoutes __stdcall dans ta DLL soit tu le retires dns ton projet.

  18. #18
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par delire8 Voir le message
    Ca fonctionne... presque. Dans la suite du programme, j'obtiens bien l'auteur. Par contre, j'ai un joli plantage (type segfault) à la fermeture. Je ne vois pas du tout d'où il vient (enfin... le memcpy qui passe mal ?).
    Du tout... sauf que ton std::string dans le main va détruire la chaine...
    Et le unload de la DLL va faire de même....

    En même temps... si c'est pour faire du statique, autant faire un truc genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    Dans la dll:
     
    const char* getAuthor()
    {
       return "Author";
    }
     
    Dans le main:
     
    typedef const char* (*getAuthorProc)();
    getAuthorProc proc = ....;
    std::string toto = proc();
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    typedef string (__stdcall *DllPtyType)(void);
    .... le code compile, mais plante à l'exécution en me disant que le registre "ESP was not saved properly...".
    Pourquoi ?
    Tu es sur que la fonction de ta DLL est bien en __stdcall ? En général ce genre d'erreur ca vient de là.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    extern "C" __declspec(dllexport) string* GetAuthor()
    {
    	return &g_Author;
    }
    Il est ou le __stdcall ?
    Normalement, par défaut, c'est _cdecl qui est utilisé.
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  19. #19
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    Désolé pour le retard, j'étais assez occupé ces derniers jours.

    Merci pour vos réponses, je vais essayer... bientôt. Eh oui, en voulant sauvegarder mes projets, j'ai simplement formatté. Si si, c'est possible...

    Cordialement
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

  20. #20
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    256
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Juin 2002
    Messages : 256
    Points : 121
    Points
    121
    Par défaut
    J'ai refait un projet vite-fait pour tester ta suggestion. Ca marche presque.
    Si je corrige et que je met bien __stdcall dans le prototype des fonctions exportées, eh bien mon pointeur sur fonction (initialisé par GetProcAddress) reste à NULL.

    En revanche, si j'enlève les conventions d'appel partout, tout fonctionne (y compris le retour de std::string et la modification d'un argument de type string& .

    Pourquoi cela plante avec les __stdcall et __fastcall ? D'ailleurs, qu'est-ce que changent ces préfixes ?

    Merci

    Cordialement
    OS : WinXP
    Outils : VC++ 8 (Visual Studio 2005)

Discussions similaires

  1. Problème de survol en création d'objet dans une DLL
    Par KlausGunther dans le forum Composants VCL
    Réponses: 20
    Dernier message: 07/01/2015, 12h19
  2. Réponses: 1
    Dernier message: 07/01/2010, 16h16
  3. Passage de structure dans une DLL
    Par nikoko34 dans le forum VB.NET
    Réponses: 1
    Dernier message: 18/07/2008, 10h11
  4. Réponses: 20
    Dernier message: 23/09/2005, 13h50
  5. [VB6]Passage d'un tableau dans une DLL écrite en delphi
    Par flash dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 20/09/2002, 10h15

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