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 :

Libraries windows (__cdecl, __stdcall, __declspec(dllexport), __declspec(dllimport))


Sujet :

C++

  1. #1
    Membre habitué
    Inscrit en
    Juin 2003
    Messages
    223
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Juin 2003
    Messages : 223
    Points : 145
    Points
    145
    Par défaut Libraries windows (__cdecl, __stdcall, __declspec(dllexport), __declspec(dllimport))
    Bonjour,

    je suis en train de développer des libraires pour windows (et linux), mais je me suis rendu compte que sous windows (MVC et MinGW) je dois utiliser les commande suivante.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #if (defined WIN32 || defined WIN64 || defined _WIN64)
        #define X7S_CDECL __cdecl
        #define X7S_STDCALL __stdcall
        #define X7S_EXPORT __declspec(dllexport)
        #define X7S_IMPORT __declspec(dllimport) 
    #else
        #define X7S_EXPORT
        #define X7S_CDECL
        #define X7S_STDCALL
        #define X7S_IMPORT
    #endif

    Le seul problème c'est que j'ai vu différente implémentation sur le net et je n'ai pas tres bien compris les différences entre ces dernieres.

    D'apres le tutoriel de http://www.codeguru.com/cpp/cpp/cpp_...icle.php/c9855
    et la FAQ c++ http://cpp.developpez.com/faq/vc/?page=DLL#MakeDynDll
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    #ifdef DLL_EXPORTS
       #define X7S_DECLSPEC X7S_EXPORT
    #else 
       #define X7S_DECLSPEC X7S_IMPORT
    #endif
    alors que dans la libraries de OpenCV ils utilisent:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #ifndef CVAPI
        #define CVAPI(rettype) CV_EXPORTS rettype CV_CDECL
    #endif
    je suis pas sur de comprendre la différence entre chacune et ne trouve pas de bon tutoriel sur le sujet.

    ----

    Autre questions:

    • je viens de me rendre compte que je n'utilise jamais DLL_IMPORT et pourtant mon projet marche bien. pourquoi?
    • A quoi serve X7S_CDECL et X7S_STDCALL ?
    • MinGW a l'air aussi d'avoir besoin de ces commandes.
    • Il faut utiliser X7S_DECLSPEC seulement pour les class et les functions que l'on utilise en dehors, ou il y a t'il d'autre cas ?

  2. #2
    Membre confirmé Avatar de themadmax
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    446
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2005
    Messages : 446
    Points : 496
    Points
    496
    Par défaut
    Le plus simple si tu utilise le compilo Microsoft Visual Studio, est de linker avec un fichier .def (Le rajout de définition est automatiquement fait)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    link.exe [...] /def:fichier.def
    Exemple fichier def
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    EXPORTS	
    	Function1
    	Function2
    Voir aussi http://wyw.dcweb.cn/stdcall.htm
    ________________________________________________
    http://bliquid.fr : Blog sur Android et l'Acer Liquid

  3. #3
    Membre habitué
    Inscrit en
    Juin 2003
    Messages
    223
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Juin 2003
    Messages : 223
    Points : 145
    Points
    145
    Par défaut
    Nan je n'utilise pas vraiment visual studio.
    Je développe avec cmake et j'aimerais devoir le moins possible modifier le projet mais juste le code.

    merci pour l'URL ca m'a bien aidé.

  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,

    Pour la première ligne c'est pas plutôt ça que tu veux :
    ?

    Cf. http://predef.sourceforge.net/precomp.html#sec34

    MAT.

  5. #5
    Membre habitué
    Inscrit en
    Juin 2003
    Messages
    223
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Juin 2003
    Messages : 223
    Points : 145
    Points
    145
    Par défaut
    nan car MinGW a aussi besoin de l'entete!

  6. #6
    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
    Ah pardon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #if defined __MINGW32__ || defined _MSV_VER
    MAT.

  7. #7
    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
    __cdecl = convention d'appel C
    L'appelant empile les paramètres de droite à gauche, et les dépile au retour de la fonction. C'est la 'norme' en C 'standard'.

    __stdcall = convention d'appel standard (windows)
    L'appelant empile les paramètres de droite à gauche, l'appelé les depile à la sortie de fonction... Cela permet un code plus compacte (le code de dépilage n'est présent qu'une fois), mais empêche l'utilisation de fonctions varargs.

    __fastcall = convention d'appel rapide
    L'appelant utilise deux registres pour les deux premiers paramètres, puis empile les suivant de doite à gauche. Comme en stdcall, l'appelé dépile les paramètres. Permet de considérablement améliorer la rapidité pour les fonctions à un paramètre maximum. A noter qu'un fonction membre passe toujours le 'this' comme premier paramètre.

    __thiscall = convention d'appel C++
    C'est le défaut pour les fonctions membres sans arguement. Le 'this' est passé dans un registre. A noter que __thiscall == __fastcall pour les fonctions membres sans paramètres !

    Ceci est pour la manière dont on s'interface avec une fonction. Une librairie a tendance à toujours *specifier explicitement* cette convention, sinon, elle est tributaire du choix des options de compilation. Rien n'empêche, dans une même classe, de panacher ces options... par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class Toto
    {
         virtual void __fastcall setX(int x);
         virtual int /*__fastcall*/ getX(void) const; // <= __thiscall par défaut (donc __fastcall)
         virtual void __stdcall makeRect(int x1, int x2, int x3, int x4);
         virtual void __cdecl makeUniont(int x, ...); // <= varags forcing __cdecl
    }
    Ensuite vient le __declspec(dllexport) ou __declspec(dllimport).

    Personellement, je ne vois pas comment utiliser un fichier .def pour les classes C++ Donc c'est très utile !
    Le header doit spécifier __declspec(dllexport) lors de la compilation de la DLL pour dire que la fonction/classe doit être exportée, et __declspec(dllimport) lors de l'utilisation de la DLL pour dire à l'utilisateur qu'il devra linker avec la DLL.
    Pour éviter d'avoir deux header différents, en général on a un truc du genre:

    MonFichierDeDll.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #include "MaDllDefines.h"
     
    class MADLL_API Toto
    {
         void MADLL_FASTCALL  setX(int i);
         int   MADLL_FASTCALL  getX(void) const;
    };
    MaDllDefines.h
    Code : 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
    22
    23
    24
    25
     
    /* PARTIE DEPENDANT DU COMPILATEUR */
    #if defined (_MSC_VER)
      /* on est sur Visual... on peut utiliser les specifics de visual: */
      #define MADLL_EXPORT   __declspec(dllexport)
      #define MADLL_IMPORT   __declspec(dllimport)
      #define MADLL_FASTCALL  __fastcall
      #define MADLL_STDCALL   __stdcall
      #define MADLL_CDECL      __cdecl
    #else 
      /* on n'est pas sur Visual... on sait pas faire... Perso j'y mettrai un #error... mais bon... */
      #define MADLL_EXPORT   
      #define MADLL_IMPORT   
      #define MADLL_FASTCALL  
      #define MADLL_STDCALL  
      #define MADLL_CDECL      
    #endif  
     
    #ifdef COMPILATION_DE_MADLL
       /* on est en train de compiler MaDll */
      #define MADLL_API     MADLL_EXPORT
    #else
       /* on est en train d'utiliser MaDll */
      #define MADLL_API     MADLL_IMPORT
    #endif
    Donc... la compilation de la DLL sous Visual, fera un __declspec(dllexport) de Toto, en utilisant les conventions __fastcall
    L'utilisation de la DLL sous Visual, fera un __declspec(dllimport) en utilisant les conventions __fastcall
    Sous tout autre compilateur, on aura aucun export/import, et pas de convention spécifique quant à l'appel des fonctions... Mais ca compilera quand même !
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 613
    Points : 30 616
    Points
    30 616
    Par défaut
    Salut,
    Citation Envoyé par nicroman Voir le message
    <snip>
    MaDllDefines.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    /* PARTIE DEPENDANT DU COMPILATEUR */
    #if defined (_MSC_VER)
    <sniped code>
    #else 
      /* on n'est pas sur Visual... on sait pas faire... Perso j'y mettrai un #error... mais bon... */
      #define MADLL_EXPORT   
      #define MADLL_IMPORT   
      #define MADLL_FASTCALL  
      #define MADLL_STDCALL  
      #define MADLL_CDECL      
    #endif
    <snip>
    Il faut noter que, en ce qui concerne MADLL_EXPORT et MADLL_IMPORT, le fait de ne pas travailler avec VC++ n'empêche absolument pas de les déclarer sous les formes respectives de __declspec(dllexport) et __declspec(dllimport)...

    Sous linux, __declspec n'existe pas, et c'est grâce aux option -shared -fPIC ou -fpic que tu obtiendra ta bibliothèque dynamique

    Au final, ta compilation conditionnelle pourrait être proche de
    config.h
    Code : 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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    /* je vais partir du principe que cdecl, fastcall et stdcall existent 
     * sous windows et sous linux... (bien que stdcall semble essentiellement
     * utilisé avec l'API windows et que j'aie surtout rencontré fastcall sous
     * borland) nous pourrions jouer avec les if defined() pour gérer les
     * différents cas intéressants ;)
     */
    #define CDECL_DLL __cdecl
    #define FASTCALL_DLL __fastcall
    #define STDCALL_DLL __stdcall
    /* si nous sommes surs de travailler sous windows, __declespec existe
     * quel que soit le compilateur envisagé (ca fait partie de l'api windows)
     */
    #if defined(_windows_)
        #define EXPORT_DLL __declspec(dllexport)
        #define IMPORT_DLL __declspec(dllimport)
    #else
    // sous linux, cela n'existe pas, nous les définissons donc à rien
        #define EXPORT_DLL 
        #define IMPORT_DLL
    #endif
    /* Il faut savoir si nous parlon d'une bibliothèque statique ou dynamique
     * pour une bibliothèque dynamique, il faut prévoir l'exportation et
     * l'importation
     */
    #if defined(SHARED)
        #define ABI_IMPORT IMPORT_DLL
        #define ABI_EXPORT EXPORT_DLL
    #else
        /* par contre, pour une bibliothèque statique (qui sera ici l'option
         * par défaut), il n'y a rien à importer ni à exporter
         */
        #define ABI_IMPORT
        #define ABI_EXPORT
    #endif
    /* enfin, il faut savoir si nous sommes occupés à compiler notre bibliothèque
     * ou si nous sommes occupés à l'utiliser
     * Nous allons estimer que nous signalerons la compilation de
     * la bibliothèque en définissant le symbole BUILD_MY_LIB (considéré
     * comme unique)
     */
    #if defined(BUILD_MY_LIB)
        /* si nous compilons notre bibliothèque, il faut exporter les symboles */
        #define DLL_ABI ABI_EXPORT
    #else
        /* sinon, il faut importer les symboles */
        #define DLL_ABI ABI_IMPORT
    #endif
    Au final, si tu as un fichier qui prend la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #include "config.h"
    DLL_ABI int foo();
    il te "suffira" de modifier la ligne de compilation pour obtenir les différentes possibilités:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    /* compilation de la bibliothèque statique */
    gcc -c file.cpp -DBUILD_MYLIB /*...*/ -lmylib
    /* utilisation de la bibliothèque statique */
    gcc -c file.cpp
    /* création de la bibliothèque dynamique */
    gcc -c file.cpp -DBUILD_MY_LIB  -DSHARED
    /* utilisation de la bibliothèque dynamique */
    gcc -c file.cpp -DSHARED -lmylib
    [EDIT]Sous windows, seules les fonctions préfixée de DLL_ABI (ou les fonctions membres des classes préfixées de DLL_ABI) seront exportées / importables
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    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 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Attention, __fastcall n'est pas standardisé sous Windows: Un programme écrit sous Visual ne pourra pas appeler une fonction __fastcall d'une DLL écrite sous Borland!

    Sous Windows, les fonctions libres exportées par des DLLs sont supposées être __stdcall. Pour les fonctions de classes, il n'y a aucun standard.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Membre habitué
    Inscrit en
    Juin 2003
    Messages
    223
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Juin 2003
    Messages : 223
    Points : 145
    Points
    145
    Par défaut
    Salut,

    Desolé de ne pas avoir répondu plutôt mais j'etais en week-end.
    Merci bcp pour les réponses, je commence a comprendre comment ca marche.

    Alors je résume pour les fonction libre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     
    #if defined(_windows_)
    #define X7S_FUNC __stdcall //Generalement utiliser pour windows.
    #define X7S_FUNC_VARG __cdecl
    #else
    #define X7S_FUNC __cdecl   //Sous linux
    #define X7S_FUNC_VARG __cdecl
    #endif
     
    int X7S_FUNC maFoncLibre()

    pour les classes on peut utiliser uniquement le __declspec()


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class X7S_DLL MaClasse() 
    {
    public:
        int myFunc();
        void setVar(int var);
    };
    Si l'on veut on peut spécifier pour chaque fonction mais ce n'est pas obligatoire. (Est-ce que c'est vivement conseillé)


    Dernière question, est-ce que c'est grave d'utiliser __declspec(dllimport) même si il s'agit d'une dll static, car mes compilations sont gerer avec cmake te je mélange des librairie statique et dynamique.
    Pour l'instant je n'ai pas rencontré de gros problème.

  11. #11
    Membre habitué
    Inscrit en
    Juin 2003
    Messages
    223
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Juin 2003
    Messages : 223
    Points : 145
    Points
    145
    Par défaut
    apparemment __cdecl n'est pas trop aimé sous linux et gcc 4.3.3.
    Est-ce que je peux l'enlever.

  12. #12
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    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 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Je dirais que oui: À ma connaissance, il n'y a qu'une seule convention d'appel sous nux... (sauf pour les appels système, qui sont encore différents).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  13. #13
    Membre habitué
    Inscrit en
    Juin 2003
    Messages
    223
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Juin 2003
    Messages : 223
    Points : 145
    Points
    145
    Par défaut
    Et pour mes deux autres question:

    • on peut spécifier une convention d'appel pour chaque fonction mais ce n'est pas obligatoire. Est-ce que c'est vivement conseillé ?
    • est-ce que c'est grave d'utiliser __declspec(dllimport) même si il s'agit d'une dll static, car mes compilations sont gerer avec cmake te je mélange des librairie statique et dynamique.

  14. #14
    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 elraton Voir le message
    Et pour mes deux autres question:

    • on peut spécifier une convention d'appel pour chaque fonction mais ce n'est pas obligatoire. Est-ce que c'est vivement conseillé ?
    Oui, pour les librairies (sous windows apparemment)...
    Imagines que ta librairie soit compilée avec l'option __stdcall par défaut. Et que le programme "utilisateur" compile avec l'option __cdecl par défaut. Comme la seule chose en "commun" sont les headers, le compilateur utilisera une convention __cdecl pour appeler les fonctions de la librairie... et tu te retrouvera avec une erreur de pile.

    • est-ce que c'est grave d'utiliser __declspec(dllimport) même si il s'agit d'une dll static, car mes compilations sont gerer avec cmake te je mélange des librairie statique et dynamique.
    Ca je n'en ai pas la moindre idée pour la bonne raison que je ne l'ai jamais fait... Ou bien une fonction doit être exportée par une DLL (dllexport / dllimport) ou pas (rien du tout). C'est assez simple à gérer (comme expliqué ci-dessus).
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 11
    Dernier message: 06/11/2008, 09h49
  2. static et __declspec(dllexport)
    Par squale69 dans le forum Visual C++
    Réponses: 2
    Dernier message: 16/05/2008, 00h01
  3. template et __declspec(dllexport)
    Par mister3957 dans le forum Langage
    Réponses: 15
    Dernier message: 14/03/2008, 08h45
  4. template et __declspec(dllexport) : LNK2019
    Par mister3957 dans le forum Langage
    Réponses: 14
    Dernier message: 04/01/2008, 10h43
  5. __declspec(dllexport) dans mon fichier header mais...?
    Par Jasmine dans le forum Autres éditeurs
    Réponses: 1
    Dernier message: 03/03/2004, 18h00

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