6
votes

I'm trying to open a text file for reading in a Delphi 7 app, but am getting I/O error 32 (sharing violation) because another application already has the file open. I've tried setting FileMode to "fmOpenRead or fmShareDenyNone" but now realise this doesn't apply to text files anyway.

Is there a way of reading text files that are open by another application?

var
  f: TextFile;
begin
  FileMode := fmOpenRead or fmShareDenyNone;   // FileMode IS NOT APPLICABLE TO TEXT FILES!!
  AssignFile(f, FileName);
  Reset(f);
5
Why are you so keen on text files? Why not use the stream classes which allow for proper file access and sharing modes? - mghie
because I want to read a single line at a time, and TFileStream doesn't have methods for that. I suppose I could read a buffer full and split on CR/LF. - Simes
You can use TStreamReader to read lines from a TFileStream. It has a ReadLine() method, and does the buffering internally for you. - Remy Lebeau
@RemyLebeau - I tried using TStreamReader and it seems to give the access violation when the file is open by another application. - lkessler
@lkessler that is not an access violation (accessing an invalid memory address), that is a sharing violation (opening the file for rights that are not allowed by another open handle to the same file). Those are two different things. You can still use TStreamReader, just open the file with a TFileStream first with sufficient sharing rights (fmOpenRead or fmShareDenyNone is usually fine, unless the other handle has exclusive access to the file), and then pass the TFileStream to TStreamReader for reading. - Remy Lebeau

5 Answers

13
votes

Use the LoadFromStream method of TStringList, rather than LoadFromFile. You get to control the locking that way:

var
    slFile: TStrings;
    stream: TStream;
begin
   slFile := TStringList.Create;
   try
      stream := TFileStream.Create(filename, fmOpenRead or fmShareDenyNone);
      try 
         slFile.LoadFromStream(stream);
      finally
         stream.Free;
      end;

      //Use the stringlist
   finally
      slFile.Free;
   end;
end;

That example is using the stream to load into a TStringList. If you only want to read pieces, you can do that. Just read from the stream.

3
votes

It depends on how that other process opened the file... If it opened the file exclusively you are not going to succeed at all.

And TextFile is old hat, I think it will open in exclusive mode to be compatible with Old style DOS. You should use TFileStream or similar.

TStringList may also work, again depending on what the other process is doing. But if the file is being written (like a .log file) the fmShareDenyWrite won't work.

1
votes

This will solve your problem instantly. Load the file using a TStringList. Just call:

...
var sl: TStringList;
begin
  sl := TStringList.create();
  try
    sl.loadFromFile(Filename);
    ...do your stuff here...
  finally
    freeAndNil(sl);
  end;
end;

I found that dealing with text files, it's best to use the TStringList. Otherwise I'd go for TFileStream and there you can specify your opening mode.

1
votes

If I remember correctly, there is also a Textfilemode Variable that applies to text files only.

1
votes

Maybe like this:

  vFileList := TStringList.Create;
  try
    vFileStream := TFileStream.Create('myfile.txt', fmOpenRead or fmShareDenyNone);
    try
      vFileList.LoadFromStream(vFileStream);
    finally
      vFileStream.Free;
    end;
    // Use vFileList
  finally
    vFileList.Free;
  end;