Lazarus/Delphi: UnicodeString в самораспределенном типе данных записи вызывает нарушение прав доступа

Я предполагаю, что моя проблема вызвана концепцией реализации UnicodeStrings, но я не могу решить эту проблему.

Я пытаюсь рекурсивно сканировать дерево каталогов на диске и создавать древовидное представление, которое должно отображать все файлы и подпапки. Кроме того, я хочу хранить дополнительную информацию для каждого узла дерева. Объект TTreeNode имеет для этой цели только свойство «Данные» (тип Pointer), поэтому я выделяю память вручную, сохраняю информацию и назначаю выделенный указатель своему свойству данных. Кажется, все работает нормально, если я не включу использование поля UnicodeString в свою запись данных.

Итак, вот мое определение пользовательской записи данных:

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

И вот мой код для отказа от каталога:

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;

Когда я раскомментирую строку, содержащую .AbsoluteFileName :=, я получаю нарушение прав доступа (исключение SIGSEGV в Unix). В настоящее время я использую Lazarus в режиме objfpc в Debian Linux, но я думаю, что то же самое и с Delphi в Windows. Значение свойства Treeview.Data хранится в переменной «Подробности» в моем примере кода, self.trvOrigin — это мой элемент управления древовидной структурой.


person Community    schedule 02.07.2016    source источник
comment
Откуда функция MemAlloc? Насколько я помню, это специфическая функция Windows. Нашел только в Memory отряде FreeVision (помимо Windows отряда)...   -  person Abelisto    schedule 02.07.2016
comment
Я использую блок памяти, включенный в Lazarus.   -  person    schedule 02.07.2016
comment
Не лучше ли просто сделать производный класс от TTreeNode, который будет содержать дополнительные поля, которые вам нужны. Таким образом, вам не придется беспокоиться о выделении и освобождении памяти для дополнительной информации самостоятельно. Единственный сценарий, когда использование свойства Data было бы лучше, — это когда несколько узлов дерева будут содержать одинаковую дополнительную информацию или если эта дополнительная информация поддерживается отдельно.   -  person SilverWarior    schedule 02.07.2016
comment
Да, вывод классов проще и лучше. Спасибо за эту подсказку. Но сначала я хотел решить этот конкретный вопрос.   -  person    schedule 03.07.2016


Ответы (1)


Когда вы выделяете запись Details, память не определяется.

AbsoluteFileName является управляемым типом и должен быть правильно инициализирован перед использованием. Вам нужно очистить память после выделения:

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

В качестве альтернативы используйте New(Details) в сочетании с Dispose(Details). Они будут корректно инициализировать/финализировать запись.

Примечание: Details должен быть типизированным указателем, PFileInformation.

person LU RD    schedule 02.07.2016
comment
Спасибо, это решило проблему. Я думал, я инициализирую структуру назначением каждого поля - но вы правы. - person ; 02.07.2016
comment
иначе используйте AllocMem вместо MemAlloc; В Delphi нет функции MemAlloc. - person kludg; 02.07.2016
comment
Почему бы не использовать новую функцию? Это правильно инициализирует запись и должно использоваться предпочтительно вместо GetMem, AllocMem и т. д. Освободите ту же запись с помощью Dispose и убедитесь, что указатель имеет правильный тип. - person Rudy Velthuis; 02.07.2016
comment
Да, @Rudy - определение типа PFileInformation = ^TFileInformation, var Подробности: PFileInformation и использование New() кажется лучшим решением, особенно для удобочитаемости. - person ; 02.07.2016