2

Je suppose que mon problème est dû au concept de la façon dont UnicodeStrings est implémenté, mais je ne peux pas résoudre ce problème.Lazarus/Delphi: UnicodeString dans le type de données d'enregistrement auto-attribué provoque des violations d'accès

J'essaye de scanner une arborescence de répertoires sur le disque de façon récursive et de construire une arborescence qui devrait montrer tous les fichiers et sous-dossiers. De plus, je veux stocker des informations supplémentaires pour chaque nœud d'arbre. L'objet TTreeNode a uniquement une propriété "Data" (type Pointer) à cet effet, donc j'alloue la mémoire manuellement, stocke les informations et affecte le pointeur alloué à ma propriété de données. Tout semble fonctionner correctement à moins d'inclure l'utilisation d'un champ UnicodeString dans mon enregistrement de données.

Donc, voici ma définition d'enregistrement de données personnalisées:

type 
    TFileInformation = record 
    AbsoluteFileName: UnicodeString; 
    FileSize: Int64; 
    FileAttributes: LongInt; 
    CreationTime, ModificationTime: TDateTime; 
    end; 

Et voici mon code pour le répertoire recusion:

const NO_ERROR = 0; 

procedure ScanDirectory(Folder: UnicodeString; Node: TTreeNode); 

var 
    Details: Pointer; 
    NewNode: TTreeNode; 
    SearchAttributes: LongInt; 
    SearchMask: UnicodeString; 
    SearchRecord: TUnicodeSearchRec; 

begin 
    if (Folder <> '') and (Folder[Length(Folder)] <> DirectorySeparator) then begin 
    Folder += DirectorySeparator; 
    end; 
    SearchMask := Folder + '*'{$IFDEF WINDOWS} + '.*'{$ENDIF}; 
    SearchAttributes := faReadOnly or faHidden or faSysFile or faDirectory or faArchive or faSymLink; 

    if FindFirst(SearchMask, SearchAttributes, SearchRecord) = NO_ERROR then begin 
    repeat 
     if ((SearchRecord.Attr and faDirectory) <> faDirectory) or 
     ((SearchRecord.Name <> '.') and (SearchRecord.Name <> '..')) then begin 
     Details := MemAlloc(SizeOf(TFileInformation)); 
     //TFileInformation(Details^).AbsoluteFileName := Folder + SearchRecord.Name; 
     TFileInformation(Details^).FileAttributes := SearchRecord.Attr; 
     TFileInformation(Details^).FileSize := SearchRecord.Size; 
     TFileInformation(Details^).CreationTime := SearchRecord.Time; 
     //TFileInformation(Details^).ModificationTime := -1; 
     if Node = nil then begin 
      NewNode := self.trvOrigin.Items.AddNode(nil, nil, ansistring(SearchRecord.Name), Details, naAdd); 
     end else begin 
      NewNode := self.trvOrigin.Items.AddNode(nil, Node, ansistring(SearchRecord.Name), Details, naAddChild); 
     end; 

     if (SearchRecord.Attr and (faDirectory or faSymLink)) = faDirectory then begin 
      // only recurse folders which are NOT SymLink: 
      ScanDirectory(Folder + SearchRecord.Name, NewNode); 
     end; 
     end; 
    until FindNext(SearchRecord) <> NO_ERROR; 
    end; 
    FindClose(SearchRecord); 
end; 

Quand je décommenter la ligne contenant .AbsoluteFileName :=, je reçois une violation d'accès (SIGSEGV-Exception sous Unix). J'utilise actuellement Lazarus en mode objfpc sur Debian Linux, mais je suppose que c'est la même chose avec Delphi sous Windows. La valeur de la propriété Treeview.Data est stockée dans la variable "Details" dans mon exemple de code, self.trvOrigin est mon contrôle treeview.

+0

D'où vient la fonction 'MemAlloc'? Comme je me souviens, c'est la fonction spécifique de Windows. Peut le trouver uniquement dans l'unité 'Memory' de' FreeVision' (en plus de l'unité 'Windows') ... – Abelisto

+0

J'utilise l'unité de mémoire incluse dans Lazarus. –

+0

Ne serait-il pas préférable de simplement créer une classe dérivée à partir de TTreeNode qui contiendrait des champs supplémentaires dont vous avez besoin. De cette façon, vous n'aurez pas à vous soucier d'assigner et de libérer de la mémoire pour des informations supplémentaires par vous-même. Le seul scénario lors de l'utilisation de la propriété Data serait préférable lorsque plusieurs des nœuds d'arborescence contiendraient les mêmes informations supplémentaires ou si ces informations supplémentaires sont conservées séparément. – SilverWarior

Répondre

4

Lorsque vous attribuez l'enregistrement Details, la mémoire n'est pas définie.

AbsoluteFileName est un type géré et doit être correctement initialisé avant utilisation. Vous devez effacer la mémoire après l'attribution:

FillChar(Details^, SizeOf(TFileInformation), #0); 

Comme alternative, utiliser New(Details) en combinaison avec Dispose(Details). Ils vont correctement initialiser/finaliser l'enregistrement.

Remarque: Details doit être un pointeur typé, PFileInformation.

+0

Merci, cela a résolu le problème. Je pensais, j'initialiser la structure par une affectation de chaque domaine - mais vous avez raison. –

+0

sinon utilisez 'AllocMem' au lieu de' MemAlloc'; Delphi n'a pas de fonction 'MemAlloc'. – kludg

+0

Pourquoi ne pas utiliser la nouvelle fonction? Cela initialise correctement l'enregistrement, et devrait être utilisé de préférence, au lieu de GetMem, AllocMem, etc. Libérez le même enregistrement avec Dispose et assurez-vous que le pointeur est du bon type. –