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 :

Ecrire une DLL C++ avec des parametres string en entrée


Sujet :

C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    24
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 24
    Points : 17
    Points
    17
    Par défaut Ecrire une DLL C++ avec des parametres string en entrée
    bonjour
    je suis vraiment débutant en C++, j'ai récupéré un bout de code tel que Myfunction reçois deux nombres et retourne la somme des deux nombres

    int __stdcall MyFunction(int nFirstNum, int nSecondNum)
    {
    return nFirstNum + nSecondNum;
    }

    Seulement je voudrais écrire une DLL qui y ressemble mais la fonction devra recevoir deux parametres string à peu près de cette manière

    int __stdcall MyFunction(Char FirstCHAR, Char SecondCHAR)
    {
    // ici je reçois par exemple FirstChar="CB01220" et SecondCHAR="ALA-56- 19920" et je fais mon traitement

    return (0);
    }

    merci de votre aide

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut, et bienvenue sur le forum

    Sois attentif à ce que tu écrit:

    "CB1220" et "ALA-56- 19920" ne sont pas des char (qui sont le type caractère), mais, sont à considérer, au minimum comme des "tableau de caractères", au maximum comme "des chaines de caractères".

    En C++, on préfère utiliser la classe string, disponible dans l'espace de noms std par inclusion du fichier d'en-tête <string>, pour la gestion des chaines de caractères, car elle permet de faciliter et de sécuriser énormément la tache.

    Cependant, dans le cas tout particulier des dll's, il faut comprendre que, idéalement, une dll devrait pouvoir être compilée avec un compilateur particulier, mais être utilisée dans un projet qui, lui, serait compilé avec un compilateur différent.

    Or, la norme laissant énormément de libertés aux programmeurs de compilateurs, il n'est pas impossible que la classe std::string de l'un ne soit pas implémentée de la même manière ce celle de l'autre, ce qui empêcherait dés lors cette "interopérabilité" (et qui nécessiterait dés lors de compiler la dll pour chaque compilateur particulier, réduisant dés lors à néant tout l'avantage que l'on peut en tirer).

    il est donc dés lors préférable que toute fonction accessible depuis l'extérieur de la dll n'utilise que des types priimitifs, ou des pointeurs vers des types primiftifs.

    Au final, il est fort possible de partir sur un code ressemblant à (en ayant définit par macro préprocesseur MYEXPORT à __decl(dllexport) pour la compilation de la dll)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int MYEXPORT MyFunction(const char* s1, const char* s2)
    {
        //pour manipuler les chaines plus facilement
        std::string recupstr1(s1); 
        std::string recupstr2(s2);
        /* tu fais ton traitement ici */
        return 0;
    }
    ou, si la valeur de retour doit être une chaine de caractères, cela pourrait ressembler à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const char* MYEXPORT MyFunction(const char* s1, const char* s2)
    {
     
        //pour manipuler les chaines plus facilement
        std::string recupstr1(s1); 
        std::string recupstr2(s2);
        std::string result;//chaine de résultat à renvoyer
        /* tu fais ton traitement ici */
        return result.c_str();
    }

  3. #3
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Quelques précisions sur l'export pour les fonctions et les classes :
    http://www.developpez.net/forums/sho...42&postcount=6

  4. #4
    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
    Salut,

    Citation Envoyé par koala01 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
        std::string result;//chaine de résultat à renvoyer
        /* tu fais ton traitement ici */
        return result.c_str();
    Le pointeur retourné n'est plus valide dès que result est détruit (en fait dès que result est modifié même)...

    MAT.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Mat007 Voir le message
    Salut,


    Le pointeur retourné n'est plus valide dès que result est détruit (en fait dès que result est modifié même)...

    MAT.
    De fait... cela nous oblige donc à un code du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    char *ret=new char[result.size()];
    memcpy(ret,result.c_str());
    return ret;
    avec obligation de gérer la libération de la mémoire dans les fonctions qui récupère ce pointeur

    Je comprend de mieux en mieux ce que je n'aime pas dans les DLL's

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    En fait, le problème majeur des DLLs est simple: Elles ne sont pas adaptées au C++, et il n'y a pas de standard indépendant du compilateur.

    Les DLLs MFC marchent parce qu'elles ne sont utilisées que sous Visual.

    De plus, ce qui est alloué par une DLL doit être détruit par la même DLL.
    C'est pourquoi, quand une fonction dans une DLL doit retourner une chaîne, soit elle prend un buffer et une taille en entrée, soit elle donne une fonction pour la détruire.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    24
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 24
    Points : 17
    Points
    17
    Par défaut
    Bonjour
    comme je débute en c++, j'ai repris donc "littéralement" le premier code puis le 2me de koala01 mais à la compile j'ai deux error dont voici les messages

    error C2146: syntax error : missing ';' before identifier 'MyFunction' et fatal error C1004: unexpected end of file found

    la suite des réponses à question ne m'a pas bcp aidé à avancer

    Est-il possible d'avoir un code "correct" prêt à être exécuté, cela m'aide bcp dans ma compréhension, c'est ce que je faisias à mes débuts en VB.net, je récupérais des bouts de codes valides et je travaillais dessus pour arriver au traitement final recherché.

    Merci de votre aide

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    A vrai dire, c'est parce que j'ai sans doute manqué quelque peu de précision dans mes écrits.

    Pour te permettre de comprendre l'ensemble du problème, il semble utile de t'expliquer clairement toute la problématique des bibliothèques dynamiques, du moins sous windows.

    En effet, pour qu'une fonction présente dans une bibliothèque dynamique soit utilisable de l'extérieur, il faut qu'elle soit exportée par la bibliothèque dynamique.

    Et, "fatalement", pour qu'un programme sache qu'il a affaire à une fonction venant de la bibliothèque dynamique, il faut lui demander... de l'importer.

    Cela signifie que, lorsque tu compile ta bibliothèque, la déclaration (et la définition) des fonctions doit être du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typeDeRetour __decl(dllexport) nomFonction(/*paramètres éventuels */)
    mais, que de l'autre coté, lorsque tu fournis le fichier d'en-tête à l'utilisateur de ta bibliothèque de manière à ce qu'il puisse l'utiliser de son coté, son compilateur doit trouver la déclaration (et la définition) sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typeDeRetour __decl(dllimport) nomFonction(/*paramètres éventuels */)
    La différence étant minime, autant attirer ton attention dessus...

    Dans le premier code, ce qui est se trouve entre les parenthèses qui suivent __decl est dllexport, alors que c'est dllimport dans le second code.

    Evidemment, tu me diras qu'il est tout à fait possible de prévoir deux fichiers d'en-tête différents (vu que ce sont les seuls qu'il faut fournir en plus de la dll), l'un qui sera utilisé pour compiler la dll et l'autre qui sera fournit à l'utilisateur...

    Mais, même si c'est possible, il faut quand même avouer que ça représente pas mal de travail pour pas grand chose

    L'idée est donc d'appeler saint préprocesseur à la rescousse, car il va gérer cela très facilement.

    L'idée est, tout simplement de lui dire que, si on lui indique qu'il est occupé à travailler à la compilation de la bibliothèque dynamique, il doit remplacer un terme choisi pour être unique (ici MYEXPORT) par __decl(dllexport), et que, sinon, il doit remplacer ce terme par ... __decl(dllimport).

    Il reste alors juste à savoir comment nous allons signaler au préprocesseur que nous sommes occupés à compiler la dll...

    Ce sera fait en fournissant une option -D (ou /D selon le compilateur) pour define, suivie d'un terme explicite, par exemple "BUILD_DLL", et cela prendrait donc la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    gcc -DBUILB_DLL(tout le reste de l'instruction)
    Au final, l'instruction préprocesseur serait du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #ifdef BUILD_DLL 
    #define MYEXPORT __decl(dllexport)
    #else
    #define MYEXPORT __decl(dllimport)
    #endif
    que l'on veillera à placer dans un fichier qui sera inclus partout où risque d'apparaitre le fameu MYEXPORT

    Si tu avais pris la peine de lire le lien fourni par Médinoc, tu n'aurais pas eu moins d'explications

    Tu peux aussi envisager de lire le chapitre dédié à la gestion des dll dans l'excellent article de Laurent Gomilla, sans oublier que ou la fonction rechercher sur le forum sont des amis très utiles pour les membres du forum.

    Evidemment, s'il reste quelque zones d'ombre malgré tes recherches personnelles, n'hésite pas à nous faire part de tes interrogations

    Au fait, une petite question en passant: y a-t-il une raison primordiale qui te fasse te tourner vers une DLL plutôt que vers une bibliothèque statique, dont la gestion reste malgré tout bien moins complexe

  9. #9
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par koala01 Voir le message
    De fait... cela nous oblige donc à un code du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    char *ret=new char[result.size()];
    memcpy(ret,result.c_str());
    return ret;
    avec obligation de gérer la libération de la mémoire dans les fonctions qui récupère ce pointeur

    Je comprend de mieux en mieux ce que je n'aime pas dans les DLL's
    Non - parce que tu forces l'utilisateur à libérer la mémoire. Hors, rien n'oblige l'utilisateur a utiliser le même runtime dans la DLL et dans le programme appelant. De fait, au moment de la destruction de la chaine de caractère allouée, il est probable qu'une corruption de la pile ou du tas se produise (ça arrive notamment sous Visual C++ si on mixe les modèles de compillation single-threaded debug, multithreaded debug, single-threaded release, multithreaded release, single-threaded debug DLL, multithreaded debug DLL, single-threaded release DLL, et multithreaded release DLL (DLL == utilisant le CRT sous la forme d'une DLL). Comme on voit, la multiplicité des modèles fait qu'on a de forte chance de se retrouver dans un mauvais cas).

    C'est la raison pour laquelle les API Windows préfère passer les chaines de résultat en paramètre de la fonction appelée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error_code_type my_function(param1, ..., char* out_string, int out_string_max_len);
    De cette manière, l'appelant est responsable de l'allocation et de la désallocation. Par contre, cela oblige la fonction appelée à vérifier si
    * out_string est valide
    * la taille maximale de out_string est suffisante pour stocker le résultat de la fonction.
    Dans le premier cas, la fonction renvoie généralement un code d'erreur du type 'paramètre invalide'. Dans le second cas, elle renverra un code du type 'la taille n'est pas suffisante'.

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Comme je l'ai dit, on peut s'en sortir en retournant un pointeur, du moment qu'on expose une fonction pour supprimer.

    Exemples dans Windows:
    • Les BSTR, crées et détruites par OleAut32.dll (SysAllocString()/SysFreeString()).
      Dans le même genre, on peut compter aussi les SAFEARRAY (SafeArrayCreate()/SafeArrayDestroy()) et CoTaskMemAlloc()/CoTaskMemFree()
    • FormatMessage() avec le flag FORMAT_MESSAGE_ALLOCATE_BUFFER retourne un pointeur qu'il faut libérer avec LocalFree().
    • Un objet COM s'efface lui-même lors du dernier Release() (fonctions virtuelles/pointeurs de fonction).

    Exemples ailleurs:
    • La bibliothèque DispHelper exporte des fonctions qui retournent des chaînes, ainsi qu'une fonction dhFreeString().

  11. #11
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    24
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 24
    Points : 17
    Points
    17
    Par défaut
    je vous remercie pour vos réponses qui semble pour un vrai de vrai débutant cmme moi un peu compliquées. Pour résumer, je vous envoi ce tutorial
    http://www.programmers-corner.com/tutorial/4

    dans cette exemple la DLL reçois en entrée deux chiffres et return leur somme

    Tout simplement, j'aimerais faire la même chose sauf qu'au lieu des chiffres ce sera des chaines de caractères telles "C0001245" et "AER-02-1992" en entrée et en sortie msgbox (C0001245 AER-02-1992)

    Donc si vous pouviez m'envoyer le source en détail ( les lignes à mettre dans .h, .def et .cpp comme l'exemple du tutorial) pour que la DLL s'exécute se serait sympa et cela m'aiderai bcp à avancer dans mon projet.

    depuis mon premier message et grace notamment à vos réponses, j'ai pas mal avancé dans la compréhension des DLL mais concretement c'est encore en chantier, sans doute que la compréhension viens en s'exerçant.

    Merci de votre aide

  12. #12
    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 Serry99 Voir le message
    dans cette exemple la DLL reçois en entrée deux chiffres et return leur somme

    Tout simplement, j'aimerais faire la même chose sauf qu'au lieu des chiffres ce sera des chaines de caractères
    Il y a déjà un paramètre de type chaîne de caractères dans l'exemple, c'est szString.
    Qu'est-ce qui te bloque exactement ?

    MAT.

  13. #13
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    24
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 24
    Points : 17
    Points
    17
    Par défaut
    si tu regardes bien le programme VB le szstring est indiqué en dur alors que mes deux chaines de caratères (parmetres reçus ) sont des variables, c'est à dire je recois par exemples le couple "C14578" "ALE-04-1954" une autre fois "Bz1235" "QZE-04-4532" etc etc

    mon traitement commence par une lecture d'un fichier, je récupére les deux chaines de caractères (les fameux paramètres) que je transmet à ma DLL qui fait un traitement.

    j'ai bien essayer de remplacer dans le programme VB le szstring en dur par une vaiable qui concaténe telle C1="C14578 ALE-04-1954" mais la DLL plante

    Mon problème donc et de pouvoir écrire en gros ceci
    MyFunction(char parm1, char parm2)
    Return parm1, parm2
    {
    et dans le programme VB, j'écrirais ceci
    Private Declare Function MyFunction Lib _
    "c:\temp\debug\maDll.dll" (ByVal parm1 As String, _
    ByVal parm2 as string) As string

    Private Sub Form_Load()
    Dim lngResults As string ( au lieu de Long)
    lngResults = MyFunction(Parm1, parm2)
    MsgBox CStr(lngResults)
    Unload Me
    End Sub

  14. #14
    Membre à l'essai
    Inscrit en
    Février 2008
    Messages
    21
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 21
    Points : 22
    Points
    22
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    En fait, le problème majeur des DLLs est simple:
    De plus, ce qui est alloué par une DLL doit être détruit par la même DLL.
    Bonjour, je serais intéressé pour avoir plus de précisions sur les raisons.

    Merci

  15. #15
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par cedriclyon Voir le message
    Bonjour, je serais intéressé pour avoir plus de précisions sur les raisons.

    Merci
    cf #9 dans la même page.

    On peut ajouter que l'ABI du programme appelant peut être différente de l'ABI de la fonction appelée. Par conséquent, mieux vaux laisser l'appelé effectuer les opérations sur la mémoire qu'il a créé.

    De plus, une DLL avec une interface C peut être utilisée dans n'importe quel langage qui supporte les DLL. Question: comment désalouer une zone mémoire allouée avec l'opérateur new dans un programme écrit en VB?

Discussions similaires

  1. [RegEx] Ecrire une requete INSERT avec des variables $_POST
    Par arnaudperfect dans le forum Langage
    Réponses: 10
    Dernier message: 13/06/2007, 15h12
  2. Réponses: 3
    Dernier message: 09/08/2006, 11h58
  3. [Reflection] Retrouver une methode avec des parametres primitifs
    Par rozwel dans le forum API standards et tierces
    Réponses: 9
    Dernier message: 06/02/2006, 15h39
  4. Réponses: 8
    Dernier message: 02/02/2006, 18h13

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