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 :

DLL String Pourquoi ça marche sans BORLNDMM.DLL


Sujet :

Langage Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 087
    Par défaut DLL String Pourquoi ça marche sans BORLNDMM.DLL
    Voici le code (fichier joints), je l'ai testé depuis la machine d'où je poste ce sujet, il n'y jamais eu delphi, il n'y a nulle par non plus BORLNDMM.DLL, et hop mystère contre toute indication suivante, cela fonction SANS soucis :

    Remarque importante concernant la gestion de mémoire de DLL : ShareMem doit
    être la première unité de la clause USES de votre bibliothèque ET de votre projet
    (sélectionnez Projet-Voir source) si votre DLL exporte des procédures ou des
    fonctions qui passent des chaînes en tant que paramètres ou résultats de fonction.
    Cela s'applique à toutes les chaînes passées de et vers votre DLL --même celles
    qui sont imbriquées dans des enregistrements et classes. ShareMem est l'unité
    d'interface pour le gestionnaire de mémoire partagée BORLNDMM.DLL, qui doit
    être déployé avec vos DLL. Pour éviter d'utiliser BORLNDMM.DLL, passez les
    informations de chaînes avec des paramètres PChar ou ShortString
    .
    Ayant nombre de sujet sur ce problème, et n'y ayant jamais été confronté malgré quelques DLL déjà déployé (souvent je fais du PChar par reflexe DLL), mais quand j'ai des formes et des string normal, je m'interroge ...

    Voici, le code d'appel, accompagnant les fichiers ... je pense que le passage en out/var/const des string doit tout changer, puisque cela revient à faire un passage de "PString = ^String;" et non une copie de string avec un passage de paramètre normal (qui rappelons fait une copie de la variable, et pour un objet, je crois que cela fait une copie du pointeur d'instance qui pointe sur l'objet mais cela ne duplique pas l'objet biensur, la duplication n'est valable que pour ordinales, string, array ...), je vais le tester sur la machine neutre dès que j'ai modifié le source sur mon poste de travail habituel (demain ...)

    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
    procedure TFrmTestDivers.WriteStringList(const FileName: string; const Strings: TStrings);
    var
      HandleDLL: HMODULE;
      _WriteStringList: procedure(const FileName: string; const Strings: TStrings); stdcall;
    begin
      HandleDLL := LoadLibrary('DLLTest.dll');
     
      if HandleDLL > 0 then
      begin
        try
          @_WriteStringList := GetProcAddress(HandleDLL, 'WriteStringList');
          if Assigned(@_WriteStringList) then
            _WriteStringList(FileName, Strings);
        finally
          FreeLibrary(HandleDLL);
        end;
      end;
    end;
     
    procedure TFrmTestDivers.BtnWriteStringListInFileClick(Sender: TObject);
    begin
      WriteStringList(EdDLLOutFileName.Text, MemoDLL.Lines);
    end;
     
    procedure TFrmTestDivers.GetDLLForm(out Form: TForm);
    var
      HandleDLL: HMODULE;
      _GetDLLForm: procedure(out Form: TForm); stdcall;
    begin
      HandleDLL := LoadLibrary('DLLTest.dll');
     
      if HandleDLL > 0 then
      begin
        @_GetDLLForm := GetProcAddress(HandleDLL, 'GetDLLForm');
        if Assigned(@_GetDLLForm) then
          _GetDLLForm(Form)
        else
          Form := nil;
        if Assigned(Form) then
        begin
          Form.OnClose := DLLFormClose;
          Form.Tag := HandleDLL;
          Form.Caption := Form.Caption + ' - ' + Application.ExeName;
        end;
      end;
    end;
     
    procedure TFrmTestDivers.FreeDLLForm(const Form: TForm);
    var
      _FreeDLLForm: procedure(const Form: TForm); stdcall;
    begin
      if Form.Tag > 0 then
      begin
        @_FreeDLLForm := GetProcAddress(Form.Tag, 'FreeDLLForm');
        if Assigned(@_FreeDLLForm) then
        begin
          _FreeDLLForm(Form);
          FreeLibrary(Form.Tag);
        end;
      end;
    end;
     
    procedure TFrmTestDivers.SetDLLFormMemo(const Form: TForm; const Lines: string);
    var
      _SetDLLFormMemo: procedure(const Form: TForm; const Lines: string); stdcall;
    begin
      if Form.Tag > 0 then
      begin
        @_SetDLLFormMemo := GetProcAddress(Form.Tag, 'SetDLLFormMemo');
        if Assigned(@_SetDLLFormMemo) then
          _SetDLLFormMemo(Form, Lines);
      end;
    end;
     
    procedure TFrmTestDivers.DLLFormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := caHide;
    end;
     
    var
      Form: TForm;
     
    procedure TFrmTestDivers.BtnGetDLLFormClick(Sender: TObject);
    begin
      GetDLLForm(Form);
      Form.Show();
    end;
     
    procedure TFrmTestDivers.BtnFreeDLLFormClick(Sender: TObject);
    begin
      FreeDLLForm(Form);
    end;
     
    procedure TFrmTestDivers.BtnSetMemoDLLFormClick(Sender: TObject);
    begin
      SetDLLFormMemo(Form, MemoDLL.Lines.Text);
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

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

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

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    Bon, j'ai reconstitué ton unité de test...ouf

    1) le OnClose fonctionne car il ne fait aucunement référence à Form, c'est juste une méthode avec un paramètre qui est modifié...mais si tu t'amusais à modifier une propriété de Form...badaboum !

    2) le FreeDLLForm plante chez moi sur la ligne FreeLibrary(Form.Tag)...voir ce que je viens de dire en 1

    3) SetDLLFormMemo, ici ça peut paraitre plus surprenant, mais en fait tout est ok. Form est une fiche créer par la DLL qui a tout à fait le droit de la manipuler comme bon lui semble...la chaine que tu envoie en paramètre n'est jamais modifiée par la DLL, là encore aucun soucis...mais essaie de modifier la chaine en retour et badaboum

    4) WriteStringList, c'est encore plus tordu, mais ça fonctionne toujours le DLL se content d'invoquer une méthode de la classe qui manipule une chaine en lecture seule...c'est on ne peux plus casse gueule, mais là encore ça fonctionne car la DLL et l'EXE ne se marchent pas sur les pieds

    en résumé, l'objet de ShareMem est de partager le même gestionnaire de mémoire entre la DLL et l'EXE, sans cela, un objet alloué dans l'un ne peux pas être MODIFIE par l'autre sous peine d'une exception.

    mais il est tout à fait acceptable de retourner une instance de classe de l'un vers l'autre...c'est ce que je fais régulièrement en déclarant différemment la même fonction côté DLL et côté EXE

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // côté DLL :
    function GetObject:TMonObject; 
    begin
     Result:=TMonObject.Create;
    end;
     
    //côté EXE:
    function GetObject:THandle; external "madll.dll";
    et ce "Handle" est alors utilisé comme suis

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    //côté EXE:
    function DoSomething(TheObject:THandle):HResult; external "madll.dll";
     
    // côté DLL :
    function DoSomething(TheObject:TMonObject):HResult; 
    begin
     Result:=TheObject.DoSomething();
    end;
    Et la fonction indispensable dans ce cas de figure :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    // côté EXE
    procedure FreeObject(Handle:THandle); external "madll.dll"
     
    // côté DLL
     
    procedure FreeObject(TheObject:TMonObject);
    begin
     TheObject.Free;
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  3. #3
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 087
    Par défaut
    Citation Envoyé par tothpaul
    1) le OnClose fonctionne car il ne fait aucunement référence à Form, c'est juste une méthode avec un paramètre qui est modifié...mais si tu t'amusais à modifier une propriété de Form...badaboum !
    Aucun problème directement depuis l'Exe

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure TFrmTestDivers.BtnChangeTitleDirectClick(Sender: TObject);
    begin
      Form.Caption := EdDLLOutFileName.Text;
    end;
    Citation Envoyé par tothpaul
    2) le FreeDLLForm plante chez moi sur la ligne FreeLibrary(Form.Tag)...voir ce que je viens de dire en 1
    Idem aucun soucis, il me permet de stocker la DLL propriétaire de la Form, je n'aurais pas pu faire fonctionner mon programme (je suis en D6 Ent, les deux machines de test son en XP, l'une avec Delphi, l'autre fraichement formatée)
    Citation Envoyé par tothpaul
    3) SetDLLFormMemo, ici ça peut paraitre plus surprenant, mais en fait tout est ok. Form est une fiche créer par la DLL qui a tout à fait le droit de la manipuler comme bon lui semble...la chaine que tu envoie en paramètre n'est jamais modifiée par la DLL, là encore aucun soucis...mais essaie de modifier la chaine en retour et badaboum
    la chaine de SetDLLFormMemo est une Const, donc impossible de modifier la valeur, il est évident que dans le cas d'une modification, il faut utiliser un PChar alloué par l'Exe avec une longueur fixe, comme pour toute API, souvent le problème est de transmettre une chaine que l'on ne modifie pas et j'ai vu souvent ceci comme un problème, ... la phase de modification étant encore plus délicate, mais simple passage de paramètre en const (simule un pointeur), c'est une astuce à savoir ... même si pour le principe je préfère le PChar
    Citation Envoyé par tothpaul
    4) WriteStringList, c'est encore plus tordu, mais ça fonctionne toujours le DLL se content d'invoquer une méthode de la classe qui manipule une chaine en lecture seule...c'est on ne peux plus casse gueule, mais là encore ça fonctionne car la DLL et l'EXE ne se marchent pas sur les pieds
    Effectivement, tout est en param const (donc pointeur sur variable), ...
    Citation Envoyé par tothpaul
    en résumé, l'objet de ShareMem est de partager le même gestionnaire de mémoire entre la DLL et l'EXE, sans cela, un objet alloué dans l'un ne peux pas être MODIFIE par l'autre sous peine d'une exception.
    Pour quoi donc la Form est modifiable de mon côté alors ? C'est ce que je ne comprend pas, et c'est testé sur deux machines différentes, donc tu échange des objets mais tu ne les manipules que d'un seul coté ?
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

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

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

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    Citation Envoyé par ShaiLeTroll
    Pour quoi donc la Form est modifiable de mon côté alors ? C'est ce que je ne comprend pas, et c'est testé sur deux machines différentes, donc tu échange des objets mais tu ne les manipules que d'un seul coté ?
    tout dépend de ce que tu fais, il faut bien voir que quand l'executation touche à la fiche de la DLL c'est le code de l'executation qui est exécuté, pas celui de la DLL...tant qu'il sagit d'accéder à un offset dans l'objet pour lire un Handle par exemple et soumettre à Windows ce paramètre y'a pas de soucis

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    procedure TControl.SetTextBuf(Buffer: PChar);
    begin
      Perform(WM_SETTEXT, 0, Longint(Buffer));
      Perform(CM_TEXTCHANGED, 0, 0);
    end;
    mais si tu commences à vouloir modifier les données internet de l'objet Delphi, c'est là que le gestionnaire de mémoire entre en jeu et que tu fais tout planter.

    Au hasard, l'ajoute d'un composant sur la fiche devrait te provoquer l'erreur puisqu'il modifie les listes internes Controls et Components.


    A noter aussi que FastMM utilise une autre technique pour partager le gestionnaire de mémoire, qui ne nécessite pas ShareMem
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  5. #5
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 087
    Par défaut
    J'ai regardé FastShareMem et FastMM (en D5, car il permettait d'augmenter la performance avec un grand nombre de thread, et c'était pour remplacer le Memory Manager qu'avait fait le créateur du produit sur lequel nous devions travailler)

    Ensuite, Il n'est pas possible de créer un Bouton, puis que la Classe TButton de l'Exe et TButton de la DLL ne sont pas compatibles (surement un truc RTTI qui différe) , c'est pour cela qu'il faut faire un paquet d'execution (souvent rencontré dès le départ avec le TFont) ...

    simplement comme ceci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure TFrmTestDivers.BtnAddButtonToFormDirectClick(Sender: TObject);
    begin
      TButton.Create(Form).Parent := Form; 
    end;
    Mais on peut faire ceci dans la DLL

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure NewButton(out Button: TButton); stdcall;
    begin
      Button := TButton.Create(nil);
    end;
    et dans l'Exe

    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
    procedure TFrmTestDivers.BtnAddButtonToFormClick(Sender: TObject);
    var
      Button: TButton;
      _NewButton: procedure(out Button: TButton); stdcall;
    begin
      if Form.Tag > 0 then
      begin
        @_NewButton := GetProcAddress(Form.Tag, 'NewButton');
        if Assigned(@_NewButton) then
        begin
          try
            _NewButton(Button);
            Form.InsertComponent(Button);
            Button.Parent := Form;
          except
            on E: Exception do ShowMessage(E.Message);
          end;
        end;
      end;
    end;
    Cela fonctionne parfaitement ...

    ou encore, plus vicieux, juste en récupérant la bonne classe (celle de la DLL différente de l'Exe)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure GetButtonClass(out AClass: TWinControlClass); stdcall;
    begin
      AClass := TButton;
    end;
    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
     
    procedure TFrmTestDivers.BtnAddButtonToFormByClassClick(Sender: TObject);
    var
      AClass: TWinControlClass;
      _GetButtonClass: procedure(out AClass: TWinControlClass); stdcall;
    begin
      if Form.Tag > 0 then
      begin
        @_GetButtonClass := GetProcAddress(Form.Tag, 'GetButtonClass');
        if Assigned(@_GetButtonClass) then
        begin
          try
            _GetButtonClass(AClass);
            AClass.Create(Form).Parent := Form;
          except
            on E: Exception do ShowMessage(E.Message);
          end;
        end;
      end;
    end;
    Sinon, pourquoi Tag et Caption fonctionne chez moi et pas chez toi, c'est ce qui me pose le plus de problème ... la différence de comportement entre une machine et une autre ... mais sinon, si je fais ma DLL pour le projet prévu au bureau, il n'y a aura pas de manipulation de la Form en direct, voir même, l'application n'aura pas connaissance de la Form en tant qu'Objet mais comme toi, juste un Handle ... et si il y a du passage de chaine, ça sera du PChar
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

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

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

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    Citation Envoyé par ShaiLeTroll
    Sinon, pourquoi Tag et Caption fonctionne chez moi et pas chez toi, c'est ce qui me pose le plus de problème ...
    Caption j'ai pas testé, mais ça fait appel à Windows, ce n'est pas une propriété de l'objet Delphi

    quand au Tag, si mon souvenir est bon, tu fais référence à une propriété d'un objet que tu viens de libérer...vu que Tag est juste un offset vers une adresse qui vient d'être libérée...ça peut fonctionner, mais c'est vraiment pas propre
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

Discussions similaires

  1. Réponses: 5
    Dernier message: 04/01/2008, 09h55
  2. Débutant Borlndmm.dll requises
    Par ducelier dans le forum C++Builder
    Réponses: 14
    Dernier message: 12/10/2006, 16h26
  3. DLL borlndmm.dll et cc3270mt.dll requises
    Par Nebelmann dans le forum C++Builder
    Réponses: 13
    Dernier message: 26/06/2006, 13h47
  4. DLL BORLNDMM.DLL
    Par masseur dans le forum C++Builder
    Réponses: 1
    Dernier message: 10/05/2005, 12h57
  5. [Execution] qtintf70.dll Mais pourquoi?
    Par Pedro dans le forum EDI
    Réponses: 4
    Dernier message: 03/06/2004, 13h23

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