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

Bibliothèques C++ Discussion :

Appel en MS VC++ d'une Fonction en DLL Delphi 6


Sujet :

Bibliothèques C++

  1. #1
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Argentine

    Informations forums :
    Inscription : Mars 2006
    Messages : 85
    Par défaut Appel en MS VC++ d'une Fonction en DLL Delphi 6
    Bonjour,

    Nous envisageons la migration d'un projet avec deux équipes: la couche d'accès DB en une DLL Delphi 6 et la couche GUI en MS VC++.

    Nous avons expérience et produits qui tournent avec DLL en MS VC++ et GUI en Delphi, et bien sur avec les deux composants (Caller + dll) dans le même language.

    Mais faire l'inverse: Appel en C++ et DLL en Delphi6 nous pose de pb.

    Nous avons bien lu:

    http://www.developpez.net/forums/d83...appel-dll-cpp/
    http://www.esanu.name/delphi/DLL/Cal...isual%20C.html

    Par contre nous ne trouvons pas:

    Je t'invite à consulter l'excellent tutoriel d'Olivier Danhan qui traite de toutes les subtilités à tenir compte pour ce besoin très précis.
    http://www.eyrolles.com/Chapitres/9782212111439/19.pdf
    Nous avons crée le *.LIB à partir du DEF (suivant MS MSDN) et aussi à partir del outils des tiers.
    Mais le LIB que nous arrivons à construire ne nous permet pas de résoudre le LINK défaillant.

    Main.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) char * __cdecl GetCurrentUserName(void)" (__imp_?GetCurrentUserName@@YAPADXZ)

    La fonction GetCurrentUserName réponds correctement appellée depuis Delphi.

    Merci pour l'aide,
    Cordialement,

    Horacio

  2. #2
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Il faut mettre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    extern "C" __declspec(dllimport) char * __stdcall GetCurrentUserName(void);
    Si GetCurrentUserName utilise la convention d'appel stdcall. Si aucune convention d'appel n'a été donnée, il faut alors remplacer __stdcall par __fastcall ("register") qui est la convention d'appel par défaut utilisée par Delphi.

  3. #3
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Argentine

    Informations forums :
    Inscription : Mars 2006
    Messages : 85
    Par défaut
    Merci Melem pour ta réponse,

    Le code VC++ (6) d'appel est:

    // This main calls GetCurrentUserName function from DLL in Delphi6
    #include <iostream>
    extern "C" __declspec(dllimport) char * __stdcall GetCurrentUserName(void);

    using namespace std;
    void main()
    {
    char* Name;
    cout << "Prosiga\n";
    Name = GetCurrentUserName();
    return;
    }
    L'erreur obtenue du Linker:

    //Main.obj : error LNK2001: unresolved external symbol __imp__GetCurrentUserName@0
    Le code Delphi de la DLL:

    library GunDll;

    uses
    SysUtils,
    Classes,
    Windows;

    {$R *.RES}

    function GetCurrentUserName() : PChar stdcall;
    const
    cnMaxUserNameLen = 254;
    var
    sUserName : string;
    dwUserNameLen : DWord;
    begin
    dwUserNameLen := cnMaxUserNameLen-1;
    SetLength( sUserName, cnMaxUserNameLen );
    GetUserNameA(
    PChar( sUserName ),
    dwUserNameLen );
    GetCurrentUserName:=PChar( sUserName );
    end;
    exports
    GetCurrentUserName;

    end.
    Cordialement,
    Horacio

  4. #4
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Bin cela signifie tout simplement que l'entrée __imp__GetCurrentUserName@0 n'existe pas dans le .lib. Comment avez-vous fait pour générer ce LIB ? Normalement, ça se fait avec l'utilitaire LIB fourni avec Visual C++. Je ne connais pas trop les outils Borland/CodeGear. Cet utilitaire suppose cependant que les fonctions dans la DLL utilisent la convention d'appel par défaut des langages C et C++, à savoir cdecl. Donc :

    - Soit tu changes la convention d'appel de la fonction en cdecl.
    - Soit tu laisses la convention d'appel en stdcall, voire en fastcall (register) si tu veux, et lu charges la DLL dynamiquement (donc plus besoin de .lib). Par exemple :
    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
    #include <iostream>
    #include <windows.h>
     
    extern "C" {
       typedef char * __stdcall MYFUNC(void);
    }
     
    int main()
    {
        HMODULE hModule = LoadLibrary("mydll.dll");
        if (hModule != NULL)
        {
            MYFUNC * GetCurrentUserName = GetProcAddress(hModule, "GetCurrentUserName");
            if (GetCurrentUserName != NULL)
            {
                std::cout << GetCurrentUserName() << std::endl;
            }
            FreeLibrary(hModule);
        }
        return 0;
    }

  5. #5
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Bonjour,
    en fait malgré l'extern "C", le [ame="http://en.wikipedia.org/wiki/Name_mangling"]"name mangling"[/ame] (ou Decorated Names - décoration de noms) n'est pas totalement effacé. Celui du C++ est bien supprimé, mais pas celui du stdcall en C.

    Si en VC++, il est possible de le supprimer complètement lors de l'export d'une fonction en utilisant un fichier de définition (.def), lors de l'importation de symbole, je ne crois pas que cela soit possible (pas à ma connaissance en tout cas ).

    Une solution consiste alors à l'exporter en Delphi avec la décoration de nom attendue par VC++ (comme expliqué dans le lien http://www.esanu.name/delphi/DLL/Cal...isual%20C.html).
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    exports
      GetCurrentUserName; // export normal
      GetCurrentUserName name "GetCurrentUserName@0"; // export pour VC++

    Le chiffre après le @ est la taille en bytes des paramètres de la fonction.
    Ce nombre est l'espace que l'appelant doit ABSOLUMENT allouer sur la pile, en poussant les arguments, car l'appelé se chargera lui même les dépiler ; si l'appelant ne respect pas cette taille, le décalage en résultant corrompra totalement la pile.
    En cdecl, c'est l'appelant qui pousse et décharge les paramètres sur la pile, ce qui n'entraine pas de corruption si trop de paramètres sont passés (et ce qui permet notamment le varags du C).

    Un bon outil pour voir ce qu'une Dll exporte est Dependency Walker.

    Sinon il est aussi possible charger dynamiquement la Dll comme l'explique Melem, mais je te déconseille d'utiliser la convention register (par défaut en Delphi) car elle n'est pas supportée en VC++ (elle correspond bien au fastcall de C++Builder, mais pas au fastcall de VC++).

  6. #6
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Petite remarque sur ton code :
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function GetCurrentUserName() : PChar stdcall;
    const cnMaxUserNameLen = 254;
    var
      sUserName : string;
      dwUserNameLen : DWord;
    begin
      dwUserNameLen := cnMaxUserNameLen-1;
      SetLength( sUserName, cnMaxUserNameLen );
      GetUserNameA( PChar( sUserName ), dwUserNameLen );
      GetCurrentUserName:=PChar( sUserName );
    end;
    sUserName est une variable locale qui sera détruite à la sortie de la fonction, donc renvoyer un pointeur dessus n'est vraiment pas conseillé .
    C'est comme renvoyer le pointeur c_str() d'une std::string locale à une fonction.

    Il existe plusieurs techniques pour contourner ce problème (variable globale modifiée à chaque appel dont renvoie l'adresse, de même mais avec une variable globale de thread, demander un buffer en entrée, ... ou ma préféré : prendre une std::string en paramètre var - si si, c'est possible).

  7. #7
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Citation Envoyé par gb_68 Voir le message
    Bonjour,
    en fait malgré l'extern "C", le "name mangling" (ou Decorated Names - décoration de noms) n'est pas totalement effacé. Celui du C++ est bien supprimé, mais pas celui du stdcall en C.

    Si en VC++, il est possible de le supprimer complètement lors de l'export d'une fonction en utilisant un fichier de définition (.def), lors de l'importation de symbole, je ne crois pas que cela soit possible (pas à ma connaissance en tout cas ).

    Une solution consiste alors à l'exporter en Delphi avec la décoration de nom attendue par VC++ (comme expliqué dans le lien http://www.esanu.name/delphi/DLL/Cal...isual%20C.html).
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    exports
      GetCurrentUserName; // export normal
      GetCurrentUserName name "GetCurrentUserName@0"; // export pour VC++

    Le chiffre après le @ est la taille en bytes des paramètres de la fonction.
    Ce nombre est l'espace que l'appelant doit ABSOLUMENT allouer sur la pile, en poussant les arguments, car l'appelé se chargera lui même les dépiler ; si l'appelant ne respect pas cette taille, le décalage en résultant corrompra totalement la pile.
    En cdecl, c'est l'appelant qui pousse et décharge les paramètres sur la pile, ce qui n'entraine pas de corruption si trop de paramètres sont passés (et ce qui permet notamment le varags du C).

    Un bon outil pour voir ce qu'une Dll exporte est Dependency Walker.

    Sinon il est aussi possible charger dynamiquement la Dll comme l'explique Melem, mais je te déconseille d'utiliser la convention register (par défaut en Delphi) car elle n'est pas supportée en VC++ (elle correspond bien au fastcall de C++Builder, mais pas au fastcall de VC++).
    C'est beau d'avoir frappé tout ça mais ça aurait encore été plus beau si tu ne racontais pas n'importe quoi ...

    1.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    extern "C" {
       typedef char * __stdcall MYFUNC(void);
    }
    Est un typedef. Ce n'est pas une déclaration de fonction. On ne parle même pas de décoration de nom ici. Même l'extern "C" est inutile. J'ai juste mis ça à titre informatif, un rappel que c'est une déclaration de fonctions à interface C.

    2. Pour la décoration des noms, les noms dans les .lib sont toujours décorés, peu importe la manière dont tu as exporté tes fonctions (fichier def ou __declspec) et on n'a aucun contrôle dessus. C'est important parce que les compilateurs doivent connaître les noms que le linkeur doit chercher dans les .lib lorsqu'on déclare une fonction importée. Ce sont les noms dans le fichier DLL qui peuvent ne pas être décorés (possible en utilisant un fichier def lors de la construction de la DLL).

    3. Même si, depuis Delphi, tu exportais la fonction sous un nom identique à ce que VC++ ferait (qui serait, en passant, _GetCurrentUserName@0 (il te manquait l'underscore)), ça ne change rien. Tu devras toujours passer à la prochaine étape : Générer un .lib à partir de la DLL et là, ta petite manip n'aura servi à rien puisque, comme je l'ai déjà dit, l'utilitaire LIB suppose que toutes les fonctions de la DLL utilisent la convention d'appel cdecl. La convention d'appel ne peut donc pas être stdcall si on veut avoir un .lib. Et même si LIB travaillait avec des fonctions stdcall, ça ne servirait toujours à rien puisque ce ne sont pas des noms dans la DLL qu'il faut s'en occuper mais des noms générés dans le .lib, pour que .lib soit soit utilisable avec Visual C++ (mais ça c'est automatique).

    4. En ce qui concerne register et __fastcall, ça c'est vrai. J'ai fait gaffe sur ce point vu que je n'utilise pas trop BCB/Delphi.

    En conclusion, c'est donc établi que :

    a. Soit on change la convention d'appel de la fonction en cdecl.
    b. Soit tu laisses la convention d'appel en stdcall et lu charges la DLL dynamiquement (donc plus besoin de .lib).

    Dans le cas a., la fonction sera importée ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    extern "C" {
       __declspec(dllimport) char * GetCurrentUserName(void);
    }
    Ca j'ai oublié de le donner en haut ...

    Citation Envoyé par gb_68
    sUserName est une variable locale qui sera détruite à la sortie de la fonction, donc renvoyer un pointeur dessus n'est vraiment pas conseillé
    +1. Changer la variable sUserName en variable globale est déjà une solution rapide. Forum Delphi pour discuter autour de ce code, pas ici svp.

  8. #8
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Argentine

    Informations forums :
    Inscription : Mars 2006
    Messages : 85
    Par défaut
    Merci Melem et GB68 pour votre temps,

    En bref, la solution dynamique de Melem me donne cette erreur:
    #include <iostream>
    #include <windows.h>

    extern "C" {
    typedef char * __stdcall MYFUNC(void);
    }

    int main()
    {
    HMODULE hModule = LoadLibrary("GunDll.dll");
    if (hModule != NULL)
    {
    MYFUNC * GetCurrentUserName = GetProcAddress(hModule, "GetCurrentUserName");
    if (GetCurrentUserName != NULL)
    {
    std::cout << GetCurrentUserName() << std::endl;
    }
    FreeLibrary(hModule);
    }
    return 0;
    }
    Ligne de l'erreur:
    MYFUNC * GetCurrentUserName = GetProcAddress(hModule, "GetCurrentUserName");
    Msg du compilo:
    C:\Estudio\MainC6\Main.cpp(13) : error C2440: 'initializing' : cannot convert from 'int (__stdcall *)(void)' to 'char *(__stdcall *)(void)'
    Mais conceptuellement Melem m'a donne la piste, et j'ai résolu l'appel dynamique avec ce code:

    #include <iostream>
    #include <windows.h>
    #include <typeinfo>

    // DLL function signature
    typedef char* (*GCUNFunc)();

    int main()
    {
    GCUNFunc fnGetCurrentUserName;

    // Load DLL file
    HINSTANCE hInstLibrary = LoadLibrary("GunDll.dll");

    if (hInstLibrary)
    {
    // Get function pointer
    fnGetCurrentUserName = (GCUNFunc)GetProcAddress(hInstLibrary, "GetCurrentUserName");

    // Call function.
    if (fnGetCurrentUserName)
    {
    std::cout << "Logged User is: " << fnGetCurrentUserName() << std::endl;
    std::cout << "Type is: " << typeid(fnGetCurrentUserName).name() << std::endl;
    }


    // Unload DLL file
    FreeLibrary(hInstLibrary);
    }
    else
    {
    std::cout << "DLL Failed To Load!" << std::endl;
    }

    std::cin.get();

    return 0;
    }

    Pour l'appel statique, c'était plus simple, et je l'ai résolu avec ce code:

    // This main calls GetCurrentUserName function from DLL in Delphi6
    #include <iostream>
    extern "C" __declspec(dllimport) char * __cdecl GetCurrentUserName(void);

    using namespace std;
    void main()
    {
    std::cout << "Logged User is: " << GetCurrentUserName() << std::endl;
    std::cin.get();
    return;
    }
    Ici le seul changement c'est dans la ligne 3 j'avais "__stdcall" et je l'ai changé par "__cdecl"

    C'est bizarre que dans le code Delphi de la DLL je garde "stdcall" et dans le code statique de VC++ je garde "__cdecl" et ca fonctionne !

    La génération de la LIB je la fais avec l'outil ConvLib.exe.

    Finalement si ceci vous semble utile, je peux préparer les deux exemples avec leur code complet et les envoyer pour publication.

    Cordialement, Horacio

  9. #9
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    En bref, la solution dynamique de Melem me donne cette erreur : (...)
    C'est vrai, il manque juste un cast qui aurait été inutile en C ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef char* (*GCUNFunc)();
    A remplacer par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef char *(__stdcall *GCUNFunc)();
    Si les termes "propreté" et "correctitude" représentent quelque chose d'important à tes yeux.

    C'est bizarre que dans le code Delphi de la DLL je garde "stdcall" et dans le code statique de VC++ je garde "__cdecl" et ca fonctionne !
    Alors dans ton code tu appelleras une fonction stdcall avec la convention cdecl, ce qui résultera en un comportement à priori imprévisible. Ici le résultat est que ça a marché sans problème ...

    La génération de la LIB je la fais avec l'outil ConvLib.exe.

    Finalement si ceci vous semble utile, je peux préparer les deux exemples avec leur code complet et les envoyer pour publication.

    Cordialement, Horacio
    Y'a pas que moi et gb_68 sur Developpez .

  10. #10
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Argentine

    Informations forums :
    Inscription : Mars 2006
    Messages : 85
    Par défaut
    Merci beaucoup Melem,

    J'ai pas mal appris!

    J'enverrai les sources finaux, tant pour l'appel dynamique que pour le link statique.

    Cordialement, Horacio

  11. #11
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Citation Envoyé par Melem Voir le message
    C'est beau d'avoir frappé tout ça mais ça aurait encore été plus beau si tu ne racontais pas n'importe quoi ...
    Ça fait toujours plaisir ...

    Pour reprendre tes points :
    1. Oui pour un typedef, il n'y a aucun symbole exporté/importé ; mon explication concernait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    extern "C" __declspec(dllimport) char * __stdcall GetCurrentUserName(void);
    ainsi que le http://www.esanu.name/delphi/DLL/Calling delphi DLL from MS Visual C.html fourni par FDR2006 (très bon didacticiel au demeurant ).

    2. Les noms dans la lib peuvent être entièrement manipulés ; il n'y a pas de sécurité à la base (si le symbole que tu importes n'est pas du bon type, personne ne te le dira). La décoration de nom permet justement de rajouter des informations en les codant dans le texte de la déclaration ; mais sans standard commun, chaque compilateur peut avoir le sien.

    3. Non pas besoin de l'underscore en fait. Le tutoriel du lien montre comment générer la lib.

    J'ai testé le tutoriel du site esanu.name, qui utilise l'outil lib.exe de Microsoft, avec VC++ 2005 et Delphi 2006, et ça marche même avec l'export de variables globales :
    Dll en Delphi
    Code Delphi : 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
    library DllDelphi;
     
    uses
      SysUtils, Dialogs, Classes;
     
    {$R *.res}
    var IntExport : integer;
     
    procedure Init;
    begin
      IntExport := 321;
    end;
     
    procedure ProcExport( Text : PAnsiChar ); stdcall;
    var Msg : String;
    begin
      Msg := 'ProcExport : Hello from Dll '+Text+ ' '+IntToStr(IntExport);
      ShowMessage( Msg );
    end;
     
    procedure ProcExportCdecl( Text : PAnsiChar ); cdecl;
    var Msg : String;
    begin
      Msg := 'ProcExportCdecl : Hello from Dll '+Text+ ' '+IntToStr(IntExport);
      ShowMessage( Msg );
    end;
     
    exports
       IntExport,
       ProcExport,
       ProcExport name 'ProcExport@4',
       ProcExportCdecl;
     
    begin
      Init;
    end.
    fichier .def
    Code def : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    LIBRARY DllDelphi
     
    EXPORTS
       IntExport
       ProcExport@4
       ProcExportCdecl
    Ligne de commande pour générer la lib
    Code cmd : Sélectionner tout - Visualiser dans une fenêtre à part
    Lib.exe /MACHINE:x86 /DEF:DllDelphi.def /OUT:DllDelphi.lib /NAME:DllDelphi.dll
    Code C++
    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
    #include "stdafx.h"
     
    #include <iostream>
    #include <string>
     
    using namespace std;
     
    extern "C"
    {
        extern __declspec(dllimport) int IntExport;
        void __declspec(dllimport) __stdcall ProcExport(const char *);
        void __declspec(dllimport) ProcExportCdecl(const char *);
    };
     
    int _tmain(int argc, _TCHAR* argv[])
    {
        cout << "IntExport : " << IntExport << endl; //affiche bien 321
     
        IntExport = 123;
     
        ProcExport( "Call ProcExport !" ); 
        // Msg -> ProcExport : Hello from Dll Call ProcExport ! 123
        ProcExportCdecl( "Call ProcExportCdecl !" ); //
        // Msg -> ProcExportCdecl : Hello from Dll Call ProcExportCdecl ! 123
        system("PAUSE");
        return 0;
    }
    Remarques :
    • Si le fichier def export ProcExport au lieu de ProcExport@4 => échec à l'éditions des liens sous VC++, à cause de la décoration de nom
    • si la Dll n'exporte pas ProcExport@4 alors que le .def (donc la lib) l'indique => échec au lancement du programme du fait du symbole non résolu
    • la Dll n'est même pas requise pour exécuter lib.exe, donc attention à écrire un .def correct

  12. #12
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Les noms dans la lib peuvent être entièrement manipulés (...). La décoration de nom permet justement de rajouter des informations en les codant dans le texte de la déclaration (...).
    Non ils peuvent juste être décorés ou non décorés, on n'a pas d'autre contrôle sur eux comme renommer une fonction f1 en f2 par exemple, comme on peut le faire avec les noms dans la DLL.

    Non pas besoin de l'underscore en fait. Le tutoriel du lien montre comment générer la lib.
    Si t'as bien lu mon message je parlais du nom que générerait Visual C++ pour une fonction stdcall, parce que c'est de quoi tu parlais, et il y a bien un underscore (Enfin, c'est ce que Microsoft dit. Je ne sais pas si esanu est d'accord ou non ni comment fonctionne la décoration avec Delphi, mais je sais au moins que Visual C++ préfixe les noms des fonctions d'un underscore.). Et de toute façon qu'il y ait underscore ou non, j'ai dit que ce n'est pas ce qui est important. Ce qui est important c'est d'avoir dans le .lib une "fonction" __imp__GetCurrentUserName@0 si la fonction est stdcall et __imp__GetCurrentUserName si la fonction est cdecl, peu importe la méthode utilisée pour les y placer (disons la tienne pour que tu sois content ). A ces fonctions ensuite de jumper vers la fonction de la DLL à qui elles correspondent mais le nom de cette fonction n'a absolument aucune importance. Elle peut même ne pas avoir de nom.

    Puisque t'as l'air d'aimer la lecture, un cadeau pour toi ! N'oublie pas de me donner des retours .

  13. #13
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Citation Envoyé par Melem Voir le message
    Si t'as bien lu mon message je parlais du nom que générerait Visual C++ pour une fonction stdcall, parce que c'est de quoi tu parlais, et il y a bien un underscore (Enfin, c'est ce que Microsoft dit. Je ne sais pas si esanu est d'accord ou non ni comment fonctionne la décoration avec Delphi, mais je sais au moins que Visual C++ préfixe les noms des fonctions d'un underscore.). Et de toute façon qu'il y ait underscore ou non, j'ai dit que ce n'est pas ce qui est important. Ce qui est important c'est d'avoir dans le .lib une "fonction" __imp__GetCurrentUserName@0 si la fonction est stdcall et __imp__GetCurrentUserName si la fonction est cdecl, peu importe la méthode utilisée pour les y placer (disons la tienne pour que tu sois content ). A ces fonctions ensuite de jumper vers la fonction de la DLL à qui elles correspondent mais le nom de cette fonction n'a absolument aucune importance. Elle peut même ne pas avoir de nom.
    Du côté de l'édition des liens en VC++, il faut en effet _GetCurrentUserName@0 (ou __imp__GetCurrentUserName@0 selon que l'on utilise __declspec(dllimport)).
    Mais les symboles que place Lib.exe contiennent automatiquement "__imp__" et "_".
    En Delphi, le symbole exporté dans la Dll est exactement celui que tu précises par "name" (ici par exemple GetCurrentUserName@0). Après génération par lib.exe (avec un .def contenant EXPORTS GetCurrentUserName@0), on a bien un ".lib" contenant _GetCurrentUserName@0 et __imp__GetCurrentUserName@0.

    Maintenant c'est vrai que Microsoft avec VC++ exporte dans les dll - en nommage "C" - les fonctions stdcall avec _<nom fonction>@<taille param>, mais les fonctions cdecl restent <nom fonction> (au lieu de _<nom fonction>) alors que le ".lib" généré avec contiendra bien fonction avec _ et __imp__ dans tous les cas.

    Enfin, je pense qu'il doit être possible de généré le ".lib" correct correspondant à une dll contenant des fonctions stdcall exportées sans le @<taille param> ; toutes les api Windows le sont et ont les .lib qui vont bien avec.


    A défaut de pouvoir faire de même, l'export de fonction en '@...' depuis Delphi me semblait la solution la moins intrusive coté code Pascal pour FDR2006.
    Citation Envoyé par Melem Voir le message
    Puisque t'as l'air d'aimer la lecture, un cadeau pour toi ! N'oublie pas de me donner des retours .
    Merci (attention, le lien contient .net au lieu de .com)

  14. #14
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Enfin, je pense qu'il doit être possible de généré le ".lib" correct correspondant à une dll contenant des fonctions stdcall exportées sans le @<taille param> ; toutes les api Windows le sont et ont les .lib qui vont bien avec.
    On en reparlera pour la possibilité ou non de générer le .lib correspondant à une dll de fonctions stdcall mais concernant les API, ça n'a rien à voir étant donné que chaque DLL et sa lib associée ont certainement été créés en même temps, lors de la compilation de la DLL.

    Pour revenir à la création de .lib sans les sources de la DLL, ça a mis du temps pour la trouver mais j'ai finalement trouvé ça : [KB] How To Create 32-bit Import Libraries Without .OBJs or Source

    L'article confirme bien que seules des imports pour des fonctions cdecl peuvent être créés à l'aide de LIB et d'un fichier .def.

    Je me suis cependant trompé en pensant que LIB ne pouvait pas créer des imports pour des fonctions non-cdecl cependant, c'est pas à l'aide d'un .def qu'on y arrive.

    (...) dans les dll, (...) les fonctions cdecl restent <nom fonction> (au lieu de _<nom fonction>)
    Ca c'est vrai. Mais c'est pas à partir du lien que t'as posté (qui revient au même que celui que j'ai posté avant) qu'on peut trouver cette information (ou bien est-ce que j'ai mal exploré ?). La page du lien que je viens juste de poster ci-dessus par contre l'explicite bien. Heureusement que ça ne change rien à tout ce qui a été établi jusqu'ici.

  15. #15
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Citation Envoyé par Melem Voir le message
    L'article confirme bien que seules des imports pour des fonctions cdecl peuvent être créés à l'aide de LIB et d'un fichier .def.
    ... des imports pour des fonctions non-cdecl cependant, c'est pas à l'aide d'un .def qu'on y arrive.
    Si le symbole a été préalablement exporté dans la Dll avec la signature adéquate (@<taille param>), là on peut générer le ".lib" correspondante (même sans la dll elle même) ; l'exemple de DllDelphi que j'ai donné plus haut est fonctionnel (avec un export d'une fonction cdecl, stdcall et une variable int).

  16. #16
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Citation Envoyé par gb_68
    Si le symbole a été préalablement exporté dans la Dll avec la signature adéquate (@<taille param>), là on peut générer le ".lib" correspondant
    Ce qui n'est pas documenté par MS donc je reste sceptique pour le moment. Je vais bien étudier le sujet et confirmer ou infirmer ce propos. En tous cas, je vais orienter mes recherches en faisant l'hypothèse que c'est vrai (puisque notre observation dit que c'est vrai).

  17. #17
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    J'ai bien fait de rester sceptique. Je vais m'expliquer en douceur :

    1.
    Citation Envoyé par Melem
    LIB suppose que les fonctions de la DLL (...), lorsqu'on les renseigne à l'aide d'un .fichier DEF (...), utilisent la convention d'appel CDECL
    C'est vrai, il n'y a pas d'exception, et c'est MS qui le dit. Cf la KB.

    2.
    Citation Envoyé par gb_68
    (...) dans les dll, (...) les fonctions CDECL restent <nom fonction> (au lieu de _<nom fonction>)
    C'est vrai aussi. Cf MSDN.

    Ces deux faits vont nous aider à établir quelque chose de très intéressant, à savoir que :

    3.
    Citation Envoyé par gb_68
    Si le symbole a été préalablement exporté dans la Dll avec la signature adéquate (@<taille param>), là on peut générer le ".lib" correspondant
    Est vrai MAIS, ça ne contredit pas 1. ! Au contraire, on peut le déduire de 1. !

    a. En effet, supposons que l'on ait une fonction int __cdecl cdecl1(int) qu'on a exporté dans une DLL sous le nom cdecl1 (avec EXPORTS cdecl1 par exemple). Si on crée un .lib pour cette DLL à l'aide de LIB et d'un fichier DEF, quel sera le nom du symbole placé dans le .lib ? En vertu de 1. et de 2., LIB va juste ajouter un underscore au nom de la fonction. C'est-à-dire que si on a mis cdecl1 dans le fichier DEF (puisque c'est bien le nom de la fonction dans la DLL), on aura _cdecl1 dans le .lib (et le même nom préfixé de __imp_), ce qui permettra d'utiliser implicitement cdecl1 en se liant avec le .lib avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    __declspec(dllimport) int __cdecl cdecl1(int);
    b. Pareil si on avait une fonction int __stdcall stdcall1(int) qu'on a exporté dans une DLL sous le nom stdcall1@4 (EXPORTS stdcall1@4=stdcall1). LIB, pensant que c'est une fonction CDECL (1.), va créer les entrées _stdcall1@4 (=> ajout de l'underscore) et __imp__stdcall1@4 dans le .lib. Le résultat est qu'on peut alors implicitement utiliser stdcall1 en se liant avec le .lib avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    __declspec(dllimport) int __stdcall stdcall1(int);
    c. Mais supposons maintenant que l'on ait une fonction int __cdecl cdecl2(int) qu'on exporte dans la DLL sous le nom cdecl2@4 (EXPORTS cdecl2@4=cdecl2). Toujours d'après 1., LIB va créer les entrées _cdecl2@4 et __imp__cdecl2@4 dans le .lib. Le résultat est qu'on peut alors implicitement utiliser cdecl2 en se liant avec le .lib avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    __declspec(dllimport) int __stdcall cdecl2(int);
    Mais bien entendu, il aurait été mieux d'appeler cdecl2 BeginBug dans ce cas. Pourtant l'édition des liens passe bien.

    En conclusion, on peut effectivement créer un .lib associé à une DLL de fonctions STDCALL avec LIB et un fichier DEF à condition que leurs noms soient de la forme ...@cb_parametres et ce parce que LIB pense que ce sont des fonctions CDECL .

  18. #18
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    85
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Argentine

    Informations forums :
    Inscription : Mars 2006
    Messages : 85
    Par défaut
    Salut GB_68 et Melem,

    J'ai voulu décharger la copie PDF du cadeau mais le link:

    Ne réponds pas. Merci pour tout votre temps,

    Joyeux Noël pour tout le monde dans ce beau site !

    Horacio

  19. #19
    Expert confirmé
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Par défaut
    Merci, c'est réglé, et joyeux noël à toi aussi.

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

Discussions similaires

  1. Réponses: 16
    Dernier message: 15/10/2009, 17h20
  2. Réponses: 3
    Dernier message: 19/10/2008, 20h14
  3. Argument d'appel de procédure à partir d'une fonction
    Par electrosat03 dans le forum VBA Access
    Réponses: 4
    Dernier message: 30/03/2008, 17h33
  4. [langage] appel d'un tableau dans une fonction
    Par donny dans le forum Langage
    Réponses: 11
    Dernier message: 13/11/2006, 16h17
  5. Comment appeler une fonction JavaScript depuis Delphi ?
    Par Alfred12 dans le forum Web & réseau
    Réponses: 4
    Dernier message: 17/06/2005, 18h15

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