【问题标题】:Lazarus/Delphi: UnicodeString in self-allocated record data type causes access violationsLazarus/Delphi:自分配记录数据类型中的 UnicodeString 导致访问冲突
【发布时间】:2016-07-02 11:51:19
【问题描述】:

我想,我的问题是由如何实现 UnicodeStrings 的概念引起的,但我无法解决这个问题。

我正在尝试以递归方式扫描磁盘上的目录树并构建一个应该显示所有文件和子文件夹的树视图。另外,我想为每个树节点存储附加信息。 TTreeNode 对象为此目的只有一个“数据”属性(类型指针),所以我手动分配内存,存储信息并将分配的指针分配给我的数据属性。除非我在数据记录中包含使用 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 := 的行时,我收到访问冲突(Unix 中的 SIGSEGV 异常)。 我目前在 Debian Linux 上以objfpc 模式使用 Lazarus,但我想这与 Windows 上的 Delphi 相同。 Treeview.Data 属性值存储在我的示例代码中的“详细信息”变量中,self.trvOrigin 是我的树视图控件。

【问题讨论】:

  • MemAlloc 函数来自哪里?我记得它是 Windows 特定的功能。只能在FreeVisionMemory单元中找到(除了Windows单元)...
  • 我使用 Lazarus 中包含的内存单元。
  • 从 TTreeNode 创建一个包含您需要的附加字段的派生类不是更好吗?这样您就不必担心自己为附加信息分配和释放内存。使用 Data 属性会更好的唯一情况是,当多个树节点包含相同的附加信息或这些附加信息单独维护时。
  • 是的,类派生更简单更好。感谢您的提示。但起初我想解决这个具体问题。

标签: dynamic-memory-allocation lazarus unicode-string delphi


【解决方案1】:

分配Details 记录时,内存未定义。

AbsoluteFileName 是托管类型,使用前必须正确初始化。 分配后需要清空内存:

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

也可以将New(Details)Dispose(Details) 结合使用。 他们将正确地初始化/完成记录。

注意:Details 必须是类型指针,PFileInformation

【讨论】:

  • 谢谢你,这解决了这个问题。我在想,我通过分配每个字段来初始化结构 - 但你是对的。
  • 否则使用AllocMem 而不是MemAlloc; Delphi 没有MemAlloc 函数。
  • 为什么不使用新功能呢?这会正确初始化记录,最好使用它,而不是 GetMem、AllocMem 等。使用 Dispose 释放相同的记录并确保指针的类型正确。
  • 是的,@Rudy - 定义类型 PFileInformation = ^TFileInformation, var Details: PFileInformation 并使用 New() 似乎是最好的解决方案,尤其是在可读性方面。
猜你喜欢
  • 2011-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 2017-01-06
  • 2010-11-05
相关资源
最近更新 更多