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

Langage Delphi Discussion :

Dans quel dossier placer une DLL utilisée par EXTRENAL (early binding) ?


Sujet :

Langage Delphi

  1. #1
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    309
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 309
    Points : 172
    Points
    172
    Billets dans le blog
    1
    Par défaut Dans quel dossier placer une DLL utilisée par EXTRENAL (early binding) ?
    Bonjour,

    Je développe une DLL à l'ai de Delphi 6 Personal Edition sous Windows (W10).

    Je veux accéder à une autre DLL en "early binding", et c'est SQLite3.dll.

    Dans quel dossier dois-je placer SQLite3.dll pour qu'au chargement de ma DLL, le loader trouve SQLite3.dll, quelque soit l'endroit où est placé mon programme exécutable ? Ke voudrais éviter d'avoir autant de copies de SQLite3.dll que de dossiers de programmes application.

    Si je place SQLite3.dll dans le dossier dans lequel se trouve mon exécutable, pas de problème -lors du chargement de ma DLL en Delphi, le loader trouve SQLite3.dll, remplit la table des fonctions accédées avec la directive EXTERNAL et tout fonctionne. Mais si j'ai un autre programme application dans un autre dossier, ça ne marche plus.

    J'ai essaye d'enregistrer SQLite3.dll mais elle n'a pas le point d'entrée requis pour cette fonction. J'ai essaye de le placer dans "Mes documents", même dans "Windows\System32\", rien n'y fait.

    Petit extrai du wrapper pour SQLite3:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function sqlite3_prepare(sqlite3:pointer;sql:pchar;nBytes:integer;var stmt:pointer;var ztail:pchar):integer; cdecl; external 'sqlite3.dll';
    Toutes les fonctions sont déclarées de la même manière.

    Question: est-ce qu'il y a un dossier où je peux placer SQLite3.dll pour qu'elle soit trouvée par mes programmes application quelque soit leur localisation (sur le même disque C:\) ?

  2. #2
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    les DLL sont à recherchées
    1) dans le répertoire de l'application
    2) dans la chemin du PATH

    attention, une DLL 32Bits ne doit pas être placée dans C:\Windows\System32 sur un Windows 64bits mais dans C:\Windows\SysWOW64

  3. #3
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    309
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 309
    Points : 172
    Points
    172
    Billets dans le blog
    1
    Par défaut
    Merci pour ta réponse !

    attention, une DLL 32Bits ne doit pas être placée dans C:\Windows\System32 sur un Windows 64bits mais dans C:\Windows\SysWOW64
    Oups... en effet ! J'avais complaitement zappé cela ! De toutes façons, j'ai essayé cela en désepoir de cause, mais je sais bien que ce n'est pas une solution sérieuse de placer ma DLL dans ces dossiers-là.

    les DLL sont à recherchées
    1) dans le répertoire de l'application
    2) dans la chemin du PATH
    Dossier de l'application: oui, ça marche. Mais cela implique de dupliquer la DLL autant de fois qu'il y a de dossiers d'application. Infaisable.
    Dans le PATH: j'ai essayé. J'ai créé un dossier directement sous C:\ et placé ma DLL dedans. Puis, j'ai ajouté ce dossier au PATH des variables d'environnement de mon compte, j'ai fermé ma session, je l'ai réouverte et vérifié que le PATH contient bien mon dossier. Puis j'ai refait mes tests, mais la DLL n'est pas trouvée.

    Alors, j'ai complètement changé mon fusil d'épaule et je suis passé au "late binding", en chargeant la DLL par LoasLibrary et en créant un wrapper dynamique autour des fonctions qui m'intéressent. Dans une fonction d'initialisation, je vérifie la présence du dossier C;\SQLite367 existe et je le crée s'il est absent. Ensuite, je vérifie pa présence de SQLite367.dll dans ce dossier et je l'extrais de ma grande DLL dans laquelle je l'ai embarquée en tant que ressource, si elle est absente.
    Ensuite, je gais LoadLibrary de cette DLL, Finalement, je charge les adresses des fonctions qui m'intéressent par GetProcAddress.

    Voici ce que ça donne:
    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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
     
    ...
     
    type
      T_sqlite3_open_v2 = function (filename: pchar; var sqlite3: pointer;flags:integer;zVfs:pointer): integer; cdecl;
      T_sqlite3_close = function (sqlite3: pointer): integer; cdecl;
      T_sqlite3_libversion = function:pchar; cdecl;
      T_sqlite3_libversion_number = function:integer; cdecl;
      T_sqlite3_prepare_v2 = function (sqlite3:pointer;sql:pchar;nBytes:integer;var stmt:pointer;var ztail:pchar):integer; cdecl;
      T_sqlite3_step = function (sqlite3_stmt: pointer): integer; cdecl;
      T_sqlite3_finalize = function (sqlite3_stmt: pointer): integer; cdecl;
      T_sqlite3_column_text = function (sqlite3_stmt: pointer; col: integer): pchar; cdecl;
      T_sqlite3_errmsg= function (sqlite3:pointer): pchar; cdecl;
     
    const
      SQLite3Path = 'C:\SQLite367';
      SQLite367dll = 'SQLite367.dll';
    var
      hSQLite3: THandle;
      user_sqlite3_open_v2:            T_sqlite3_open_v2;
      user_sqlite3_close:              T_sqlite3_close;
      user_sqlite3_libversion:         T_sqlite3_libversion;
      user_sqlite3_libversion_number:  T_sqlite3_libversion_number;
      user_sqlite3_prepare_v2:         T_sqlite3_prepare_v2;
      user_sqlite3_step:               T_sqlite3_step;
      user_sqlite3_finalize:           T_sqlite3_finalize;
      user_sqlite3_column_text:        T_sqlite3_column_text;
      user_sqlite3_errmsg:             T_sqlite3_errmsg;
     
    ...
     
    Implementation
     
    // load library and connect to the required SQLite367 functions
    procedure Load_sqlite3_interface;
    begin
      hSQLite3 := LoadLibrary(SQLite3Path+'\'+SQLite367dll);
      @user_sqlite3_open_v2 := GetProcAddress(hSQLite3, pchar('sqlite3_open_v2'));
      @user_sqlite3_close := GetProcAddress(hSQLite3, pchar('sqlite3_close'));
      @user_sqlite3_libversion := GetProcAddress(hSQLite3, pchar('sqlite3_libversion'));
      @user_sqlite3_libversion_number := GetProcAddress(hSQLite3, pchar('sqlite3_libversion_number'));
      @user_sqlite3_prepare_v2 := GetProcAddress(hSQLite3, pchar('sqlite3_prepare_v2'));
      @user_sqlite3_step := GetProcAddress(hSQLite3,pchar('sqlite3_step'));
      @user_sqlite3_finalize := GetProcAddress(hSQLite3,pchar('sqlite3_finalize'));
      @user_sqlite3_column_text := GetProcAddress(hSQLite3,pchar('sqlite3_column_text'));
      @user_sqlite3_errmsg := GetProcAddress(hSQLite3,pchar('sqlite3_errmsg'));
    end;
     
    // unload library
    procedure Unload_sqlite3_interface;
    begin
      FreeLibrary(hSQLite3);
    end;
     
    // interface functions replacind the wrapper within sqlite3.dll
    function sqlite3_open_v2(filename: pchar; var sqlite3: pointer;flags:integer;zVfs:pointer): integer;
    begin
      result := user_sqlite3_open_v2(filename,sqlite3,flags,zVfs);
    end;
    function  sqlite3_close(sqlite3: pointer): integer; cdecl;
    begin
      result := user_sqlite3_close(sqlite3);
    end;
    function sqlite3_libversion:pchar; cdecl;
    begin
      result := user_sqlite3_libversion;
    end;
    function sqlite3_libversion_number:integer;
    begin
      result := user_sqlite3_libversion_number;
    end;
    function sqlite3_prepare_v2(sqlite3:pointer;sql:pchar;nBytes:integer;var stmt:pointer;var ztail:pchar):integer; cdecl;
    begin
      result := user_sqlite3_prepare_v2(sqlite3,sql,nBytes,stmt,ztail);
    end;
    function sqlite3_step(sqlite3_stmt: pointer): integer; cdecl;
    begin
      result := user_sqlite3_step(sqlite3_stmt);
    end;
    function  sqlite3_finalize(sqlite3_stmt: pointer): integer; cdecl;
    begin
      result := user_sqlite3_finalize(sqlite3_stmt);
    end;
    function  sqlite3_column_text(sqlite3_stmt: pointer; col: integer): pchar; cdecl;
    begin
      result := user_sqlite3_column_text(sqlite3_stmt, col);
    end;
    function  sqlite3_errmsg(sqlite3:pointer): pchar; cdecl;
    begin
      result := user_sqlite3_errmsg(sqlite3);
    end;
     
    {
      ************ fonctions générales *************
    }
     
    // initialiser l'environnement SQLite3: extraire la DLL si nécessaire
    function InitializeSQLite3: integer; stdcall; export;
    var
      s: string;
    begin             
      result := -1;
      try
        // créer le dossier C:\SQLite367 si nécessaire
    //if not directoryexists(SQLite3Path) then showmessage('création de '+SQLite3Path);
        if not directoryexists(SQLite3Path) then CreateDir(SQLite3Path);
        s := SQLite3Path+'\'+SQLite367dll;
    //if not FileExists(s) then showmessage('extact dans '+s);
        if not FileExists(s) then ExtractResToFile('DATAFILE','SQLITE367',s);  // récupérer le fichier SQLite3.dll
        Load_sqlite3_interface;
        result := 0;
      except
      end;
    end;
    exports InitializeSQLite3;
     
    // purger l'environnement SQLite: supprimer SQLite367.dll
    function ResetSQLite3: integer; stdcall; export;
    var
      s: string;
    begin
      result := -1;
      try
      Unload_sqlite3_interface;
      result := 0;
      except
      end;
    end;
    exports ResetSQLite3;
     
    ...
    Et tout fonctionne parfaitement !

    En construisant mon wrapper dynamique de cette façon, je oeux utiliser les noms d'origine des fonctions de SQlite3.dll, ce qui simplifiait le passage de "early binding" à "late binding", commme je n'avais rien à modifier dans mon code, sauf ajouter le wrapper ci-dessus.

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    C:\ c'est un endroit spécial pour Windows, je ne suis pas certain que ce soit un choix judicieux

    sinon il existe une autre technique qui marche bien mais uniquement sous Windows, c'est avec "delayed", car non seulement le chargement de la DLL se fait à la demande, mais en plus il est possible d'intercepter son chargement.

    j'utilise cette technique sur l'application Vidal dont l'emplacement est défini de façon indirecte

    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
     
    // ici VIDAL est un nom bidon qui ne correspondant pas au nom réel de la DLL
    function VidalAPI_init(var API: HVidalAPI; pid: TVidalProductID): TVidalAPIError; stdcall external 'VIDAL' delayed;
     
    var
    // le Handle de DLL une fois chargée
      VidalDLL: THandle;
    // ancien Hook de chargement
      LoadHook: TDelayedLoadHook;
     
    // nouveau Hook
    function VidalHook(dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall;
    begin
    // si Delphi veux charger la DLL "VIDAL"
      if (dliNotify = dliNotePreLoadLibrary) and (pdli.szDll = 'VIDAL') then
      begin
        if Load = VIDALAPI_OK then // si le chargement de la DLL se passe bien (à adapter à ton cas)
        begin
          Result := Pointer(VidalDLL); // on donne son Handle
          Exit;
        end;
      end;
      if @LoadHook <> nil then // s'il existe un ancien Hook
        Result := LoadHook(dliNotify, pdli) // l'appeler
      else
        Result := nil; // sinon retourner un pointer vide
    end;
     
    initialization
      LoadHook := SetDliNotifyHook2(VidalHook);
    finalization
      SetDliNotifyHook2(LoadHook);
    end.
    dans ton cas, tu remplace "VIDAL" par "SQLite3.dll" et le "if Load = VIDALAPI_OK then" par ton code d'installation et chargement de la DLL

    ça évite de faire des GetProcAdress partout

    tu peux même inverser la logique du Hook

    1) appeler l'ancien Hook
    2) si Result = nil alors utiliser ton code car la DLL n'a pas été trouvée

  5. #5
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    309
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 309
    Points : 172
    Points
    172
    Billets dans le blog
    1
    Par défaut
    je sais que C:\ est "particulier". Mais ce n'est pas protégé et réservé aux administrateurs. Nombre de logiels tiers s'installent directemetn sous C:\, comme Lazarus, Windev, Pyton etc. Je pense que tant que je ne touche pas à un dossier système, je ne risque pas grand-chose.

    il existe une autre technique qui marche bien mais uniquement sous Windows, c'est avec "delayed", car non seulement le chargement de la DLL se fait à la demande, mais en plus il est possible d'intercepter son chargement.
    Oui, j'ai trouvé cette directive sur internet. Malheureusement, elle n'est pas disponible en Delphi 6 Personal Edition - refusé à la compilation comme mot-clé inconnu. C'est pour cette raison que je me suis rabattu sur la technique traditionnelle.

    Ceci dit, j'ai une fonction d'initialisation de l'environnement SQLite qui est appelée une fois, tout au début des programmes utilisant cette base de données. Dans cette routine, je charge la DLL et recherche les adresses des fonctions que j'utilise. Ensuite, ce sont les mêmes appels que pour la version "early binding" et il n'y a plus de pénalisation à l'exécution. De plus, les noms des fonctions et leur format d'appel est exactement le même que pour le wrapper "early binding".

    Je sais, Delpi 6 Personal Eidition est vieux et dépassé. Mes pour mes besoins, c'est suffisant. J'ai essaye d'utiliser la Community Edition d'Embarcadero. C'est beaucoup, mais vraiment beaucoup pls lourd et ne digère pas les sources de D6 PE sans modification. Idem pour Lazarus que j'ai installé également. Pour info, voici l'importance de mon projet de DLL:
    Nom : aa1.png
Affichages : 89
Taille : 6,0 Ko
    Il a environ 1300 fonctions exportées.

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

Discussions similaires

  1. Réponses: 35
    Dernier message: 09/12/2017, 03h08
  2. TTimer dans une dll utilisée par une application .net
    Par Pascale38 dans le forum C++Builder
    Réponses: 16
    Dernier message: 12/07/2017, 17h18
  3. Réponses: 4
    Dernier message: 02/12/2011, 15h52
  4. Réponses: 3
    Dernier message: 03/09/2008, 16h09
  5. Utilisation d'une dll native par une toolbar managée
    Par didierll dans le forum C++/CLI
    Réponses: 1
    Dernier message: 10/07/2007, 08h56

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