Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

delphi - TStreamWriter locks file for Read

In our project, we are using a StreamWriter to write into a log file.

While writing, I wanted to search inside the file for some specific lines. E.g. during a unit test. But somehow I can't read from the file, because it is blocked by a process. I don't understand why it is blocked, because I think I opened the FileStream without any blocking.

I extracted everything from to project into this little example. What do I have to change to not block the file for reading, while writing to it?

program Playground;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes;

var
  FileStream: TFileStream;
  FileStreamWriter : TStreamWriter;
  strList: TStringList;
  fileName: String;

begin
  try
    strList := TStringList.Create;
    fileName := 'TestFile.txt';

    FileStream := TFileStream.Create(fileName, fmCreate or fmShareDenyNone);
    FileStreamWriter := TStreamWriter.Create(FileStream, TEncoding.Unicode);
    FileStreamWriter.WriteLine('12345');
    strList.LoadFromFile(fileName); // Crashes because a proccess blocks the file

    strList.Free;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Readln;
end.
question from:https://stackoverflow.com/questions/65843958/tstreamwriter-locks-file-for-read

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As Peter Wolf stated in his comment the cause for your troubles doesn't lie in your code but instead in StringList.LoadFromFile code.

You see when string list is trying to load the contents of a file it is opening it in a way that would prevent other applications from reading its contents.

Here is how StringList.LoadFromFile code looks like:

procedure TStrings.LoadFromFile(const FileName: string);
var
  Stream: TStream;
begin
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(Stream);
  finally
    Stream.Free;
  end;
end;

So in order to avoid this problem you should create yourself another File Stream and than use that file stream to read contents of your file into StringList.

program Playground;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes;

var
  FileStream: TFileStream;
  StrListFS: TFileStream;
  FileStreamWriter : TStreamWriter;
  strList: TStringList;
  fileName: String;

begin
  try
    strList := TStringList.Create;
    fileName := 'D:TestFile.txt';

    FileStream := TFileStream.Create(fileName, fmCreate or fmShareDenyNone);
    FileStreamWriter := TStreamWriter.Create(FileStream, TEncoding.Unicode);
    FileStreamWriter.WriteLine('12345');
    //strList.LoadFromFile(fileName); // Crashes because a proccess blocks the file

    //Create a new file stream that we will use for reading the file contents into
    //our string list.
    StrListFS := TFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);
    //Load contets from a file into a stream by using our newly created FileStream
    strList.LoadFromStream(StrListFS);
    //Free the file stream when we are done loading the data.
    StrListFS.Free;

    strList.Free;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Readln;
end.

You could theoretically use same file stream that is used by your stream writer to read the contents of that file into string list but do mind that doing so would change your stream position so you would have to save your previous position before and restore it after reading the contents of your file into your string list.

This way would be able to do this even if you would be opening your stream writer File Stream in locked mode which would prevent opening file by multiple processes/handles.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...