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 :

Problème pour récupérer valeur d'un pointer non typé stocké dans un record


Sujet :

Langage Delphi

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    51
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2003
    Messages : 51
    Points : 60
    Points
    60
    Par défaut Problème pour récupérer valeur d'un pointer non typé stocké dans un record
    Bonjour,

    J'ai un souci avec un pointer.

    J'ai un record avec une valeur de type "pointer". Cette valeur peut être amenée à stocker des valeurs de type integer, string, clientdataset (d'où le choix du type pointer)

    Lorsque, je stocke un clientdataset, je ne peux récupérer ce dernier.
    Voici une petite maquette du problème :

    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
     
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, DB, DBClient;
     
    type
      Tparam = Packed record
        aPointer : Pointer;
        aTypeInt : integer;
      end;
     
      TForm1 = class(TForm)
        CdsBase: TClientDataSet;
        CdsBaseName: TStringField;
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
        FParam : TParam;
      public
        { Public declarations }
        function Valorise(Cds : TClientDataset) : TParam;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      FParam := Valorise(CdsBase);
    end;
     
    function TForm1.Valorise(Cds: TClientDataset): TParam;
    begin
      Result.aPointer := @Cds;
      Result.aTypeInt := 1;
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      CdsTest : TClientDataSet;
    begin
      CdsTest :=  TClientDataSet(FParam.aPointer^);
      ShowMessage(CdsTest.ClassName);
    end;
     
    end.
    Lorsque j'exécute Button2Click, le CdsTest est notifié "Inaccessible Value"
    J'ai fait l'essai sans passer par le record, et je peux bien récupérer mon clientdataset.

    Le fait de passer par un packed record modifie-t-il la gestion des pointeurs ?

    Merci d'avance.

    PS : Delphi 7 / Windows 7

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    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 : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    Tu ne sais donc pas qu'un objet c'est une référence osons le dire c'est un pointeur !
    Retire @ et ^
    Result.aPointer := Cds;.
    CdsTest := TClientDataSet(FParam.aPointer);.

    Sinon, fait attention à la durée de vie des variables !
    Pour un integer, veille à allouer un pointeur et de copier la valeur car l'adresse d'un paramètre ...
    Idem pour le string, utilise un PString, ou à la limite, utilise un PChar dans lequel tu copies la chaine !

    Pour des membres d'objets, cela peut fonctionner, pour des variables locales, copie oblige !
    Je me demande pour un integer, si tu ne devrais pas mettre var pour la version integer de Valorise, sinon tu vas récupérer le pointeur sur le paramètre qui est une copie de la valeur d'origine dont la durée de vie sera faible !
    Pour le string, c'est subtil, je ne sais pas trop si ça pointe sur la même référence entre le paramètre et la variable qui a été passé comme paramètre, comme un string c'est un pointeur aussi ...

    Pense aussi à cette construction !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    type
      Tparam = Packed record
        case aTypeInt: integer of
          1: (Cds: TClientDataSet); // Copie de la référence
          2: (S: string); // Copie de la chaine avec toute la gestion tordue du compteur de référence
          3: (I: Integer); // Copie de la valeur
      end;
    En plus, pour le moment, Delphi 7 c'est que Win32, tout ça fait 4 octets
    Cds, S et I occupe le même espace mémoire ! Tu ne peux en avoir qu'un seul "cohérent" sur les trois, cela devrait te convenir !

    Pourquoi packed ? ça c'est utilise lors de manipulation manuelle de la mémoire, tu veux forcer l'alignement sur un octet ?
    Packed pour des int ou double, lu directement d'un fichier binaire, je comprends, pour un objet, cela me semble n'avoir aucun intérêt !

    NativeInt pourrait remplacer Integer en XE2, si tu veux que l'espace mémoire soit le meme, donc 4 ou 8 octets !

    Pense sinon que tout cela existe déjà ! le Variant qui peut stocker tout ça !
    Mais aussi le TVarData qui est la version brute du Variant

  3. #3
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

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

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 173
    Points
    4 173
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Pour le string, c'est subtil, je ne sais pas trop si ça pointe sur la même référence entre le paramètre et la variable qui a été passé comme paramètre, comme un string c'est un pointeur aussi ...
    C'est foireux... Et ça foirera différemment selon la façon dont le code est écrit. Le paramètre string sera géré correctement par le compilateur. Si c'est un string sans modificateur, le paramètre sera considéré comme une variable de plus qui référence la même chaîne. Le compteur de référence sera incrémenté au passage du paramètre et décrémenté à la destruction du paramètre.
    Ce qui sera foireux, c'est le devenir de la chaîne dans aPointer :

    Cas 1 :
    La méthode Valorise est quelque chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     string(Result.aPointer) := s; // avec s : string
    Dans ce cas, l'affectation devient une affectation de chaîne classique : Le compteur de référence de la chaîne va être incrémenté.
    Mais la où sa va coincé, c'est que le compilateur ne saura plus ensuite que aPointer désigne une chaîne. Lorsque le record sera finalisé, le compteur de référence de s ne sera pas décrémenté et ça donnera un memory leak.
    Autrement dit, il faudra finaliser le record manuellement avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      string(result.aPointer) := '';
    Cas 2 :
    La méthode Valorise est quelque chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      result.aPointer := @s[1];
    Cette fois ci, aPointer pointe sur le premier caractère de la chaîne (autrement dit, ça devient un PChar).
    Le problème qui va se poser alors c'est qu'on ne maitrise pas le cycle de vie de s. La chaîne risque d'être détruite alors que aPointer la référence toujours...

  4. #4
    Membre expérimenté Avatar de guillemouze
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    876
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2004
    Messages : 876
    Points : 1 448
    Points
    1 448
    Par défaut
    Citation Envoyé par Franck SORIANO Voir le message
    Cas 2 :
    La méthode Valorise est quelque chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      result.aPointer := @s[1];
    Cette fois ci, aPointer pointe sur le premier caractère de la chaîne (autrement dit, ça devient un PChar).
    Le problème qui va se poser alors c'est qu'on ne maitrise pas le cycle de vie de s. La chaîne risque d'être détruite alors que aPointer la référence toujours...
    Question annexe : Il est possible de convertir une string en PChar de cette maniere ?! Il n'y a pas forcement de byte 0 a la fin de la string. Si je ne m'abuse, l'octet immediatement apres le dernier caractere d'une chaine est libre d'etre alloué a quelqu'un d'autre, c'est le debut de la chaine qui gere la longueur (s[0]). Par contre je ne sais pas comment les chaine de plus de 256 caracteres sont gérées en interne, je suppose que s[0] est un id (ou pointeur) sur une structure qui maintient le compteur de reference et la longueur de la chaine.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    51
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2003
    Messages : 51
    Points : 60
    Points
    60
    Par défaut
    Pour Shail :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Tu ne sais donc pas qu'un objet c'est une référence osons le dire c'est un pointeur !
    Honnêtement, non je ne le savais pas
    C'est l'avantage de ce forum, je suis moins bête après qu'avant

    Plus sérieusement, je vais reprendre dès demain matin vos différentes explications et tente de les mettre en oeuvre. La suite au prochain épisode


    Merci beaucoup aux différents contributeurs

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    51
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2003
    Messages : 51
    Points : 60
    Points
    60
    Par défaut
    Bonjour,

    J'ai modifié mon code en fonction de vs différentes contributions. Y a pas à ch..., cela fonctionne beaucoup mieux !


    Voici le résultat :
    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
     
    unit FTest;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, DB, DBClient, ExtCtrls;
     
    type
      TParam = Packed record
        aValue   : Pointer;
        aTypeInt : Integer;
      end;
     
      TFrmTest = class(TForm)
        CdsBase: TClientDataSet;
        CdsBaseName: TStringField;
        BtSaveRecord: TButton;
        BtShowValue: TButton;
        RgTest: TRadioGroup;  // ItemIndex --> 0 : TclientDataset / 1 : string / 2 : integer
        procedure BtSaveRecordClick(Sender: TObject);
        procedure BtShowValueClick(Sender: TObject);
      private
        { Private declarations }
        FParam   : TParam;
        FPointer : pointer;
        FChaine  : string;
        FEntier  : integer;
      public
        { Public declarations }
        function Valorise(aType : Integer; Valparam : Pointer): TParam;
      end;
     
    var
      FrmTest: TFrmTest;
     
    implementation
     
    {$R *.dfm}
     
    procedure TFrmTest.BtSaveRecordClick(Sender: TObject);
    begin
      FChaine := 'Test pointeur';
      FEntier := 42;
     
      case RgTest.ItemIndex of
        0 : FParam := Valorise(1, CdsBase);
        1 : FParam := Valorise(2, Addr(FChaine));
        2 : FParam := Valorise(3, Addr(FEntier));
      end;
    end;
     
    function TFrmTest.Valorise(aType : Integer; Valparam : Pointer): TParam;
    begin
      Result.aValue   := ValParam;
      Result.aTypeint := aType;
    end;
     
    procedure TFrmTest.BtShowValueClick(Sender: TObject);
    var
      CdsTest : TClientDataSet;
    begin
      case FParam.aTypeInt of
        1 : begin
              CdsTest := FParam.aValue;
              ShowMessage(CdsTest.Fields[0].FieldName);
              FParam.aValue := nil;
            end;
        2 : begin
              ShowMessage(string(FParam.aValue^));
              string(FParam.aValue^) := '';
            end;
        3 : begin
              ShowMessage(IntToStr(Integer(FParam.aValue^)));
              FParam.aValue := nil;
            end;
      end;
    end;
     
    end.
    J'ai trouvé sinon ce tuto, n'étant pas du tout habitué à manipuler les pointeurs cela ne me fera certainement pas de mal.

    Merci pour vos réponse

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    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 : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    Citation Envoyé par guillemouze Voir le message
    Question annexe : Il est possible de convertir une string en PChar de cette maniere ?! Il n'y a pas forcement de byte 0 a la fin de la string.
    Si systématiquement, Borland a bien fait son travail, il ajoute un 0 supplémentaire, et même deux pour la gestion des caractères multi-Octets

    Citation Envoyé par guillemouze Voir le message
    Si je ne m'abuse, l'octet immediatement apres le dernier caractere d'une chaine est libre d'etre alloué a quelqu'un d'autre
    Si ShortString à la fin de l'espace mémoire, oui
    Si AnsiString, UnicodeString, #0 ou #0#0 en plus sont fournis pour compatibilité avec les API Windows !

    Citation Envoyé par guillemouze Voir le message
    c'est le debut de la chaine qui gere la longueur (s[0]). Par contre je ne sais pas comment les chaine de plus de 256 caracteres sont gérées en interne, je suppose que s[0] est un id (ou pointeur) sur une structure qui maintient le compteur de reference et la longueur de la chaine.
    Ben, c'est presque ça !
    Pour une AnsiString, cela pointe sur le 1er caractère de la chaine, mais le véritable pointeur qui a été alloué en -8 ou -12 par rapport à cette référence, selon la version du compilateur !
    Lorsque tu libère une chaine, tu ne libère pas le pointeur du string, mais sa valeur -8 ou -12, tu as le code dans Sytem.pas, très interessant à lire, cherche PStrRec

    @Wriggles, lit bien nos réponses, pour le moment, tu réinventes le TVarData !
    Nos réponses sont longues mais bourrées de mots clés et d'astuces que tu ne prends pas en compte !
    Et fait gaffe à tes String tout de même !
    Relit la réponse de Franck SORIANO, il confirme mes doutes, que c'est proche de la roulette russe ton histoire !
    Cela fonctionne pour un cas, essaye ça

    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
    procedure TFrmTest.BtSaveRecordClick(Sender: TObject);
    begin
      TmpChaine := 'Test pointeur';
      TmpEntier := 42;
     
      case RgTest.ItemIndex of
        0 : FParam := Valorise(1, CdsBase);
        1 : FParam := Valorise(2, Addr(TmpChaine));
        2 : FParam := Valorise(3, Addr(TmpEntier));
      end;
     
      TmpChaine := 'Réponse à la Question de l''Univers ...';
      TmpEntier := -1;
      ShowMessage(Format('%s %d', [TmpChaine, TmpEntier ]));
    end;
    Précision F c'est pour indiquer un membre de classe, ne nomme pas des variables locales de cette façon !
    A étant réservé pour Argument

    Là, tu mélanges des Membres et des Variables Locales avec le même nom !
    Fait attention, les variables locales cachent les membres !


    l'utilisation de Partie variable des enregistrements, serait plus simple et pour les string, cela sera une affectation normale !

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

Discussions similaires

  1. Problème pour récupérer valeur du $_GET
    Par maxime612001 dans le forum Langage
    Réponses: 8
    Dernier message: 27/05/2011, 17h47
  2. problème pour récupérer valeur sélectionnée de combobox
    Par batou22003 dans le forum Développement Windows
    Réponses: 2
    Dernier message: 03/02/2011, 11h30
  3. Problème pour récupèrer valeurs checkbox
    Par Thewil dans le forum Struts 2
    Réponses: 0
    Dernier message: 15/12/2009, 12h03
  4. Problème pour récupérer valeurs d'un ComboBox
    Par david71 dans le forum Windows Forms
    Réponses: 5
    Dernier message: 07/08/2007, 11h10
  5. Réponses: 4
    Dernier message: 13/12/2006, 00h08

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