Reputation: 3238
I have written a routine in Delphi Tokyo which takes multiple files (such as CSV) and merges them together, giving the user the option to ignore the first line on all files except the first one (as CSV files often have header lines/column name lines, when merging the files, I only want one copy of the header). The issue I am having is that even though I am only reading the various input files, if the file is open in another process, (specifically Excel), my app gives an error: "Cannot open file . The process cannot access the file because it is being used by another process."
I am using TStreamReader. How do I tell TStreamReader that it should open the file in read-only...and continue even if the file is open elsewhere?
Code below:
procedure glib_MergeTextFiles(const InFileNames: array of string; const OutFileName: string;
HasHeader: Boolean = True;
KeepHeader: Boolean = True);
var
I: Integer;
InStream: TStreamReader;
OutStream: TStreamWriter;
Line: string;
IsFirstLine: Boolean;
begin
// Create our output stream
OutStream := TStreamWriter.Create(OutFileName, False, TEncoding.UTF8);
try
for I := 0 to high(InFileNames) do
begin
InStream := TStreamReader.Create(InFileNames[I], TEncoding.UTF8);
IsFirstLine := True;
try
while not InStream.EndOfStream do
begin
Line := InStream.ReadLine;
if IsFirstLine then { First Line }
begin
if HasHeader = False then
begin
OutStream.WriteLine(Line);
end
else
begin
// Is First Line, Has Header
if I = 0 then {is first file}
OutStream.WriteLine(Line);
end;
end
else
begin
OutStream.WriteLine(Line);
end;
IsFirstLine := False;
end;
finally
InStream.Free;
end;
end;
finally
OutStream.Free;
end;
end;
Upvotes: 2
Views: 2934
Reputation: 54832
The problem is with the sharing mode. By default, the stream reader creates a file stream for reading only, but specifies no sharing mode, so it opens the file for exclusive access. However, to open a file for reading when it is already opened elsewhere, the file must have been previously opened to share reading access using the FILE_SHARE_READ
flag:
FILE_SHARE_READ
0x00000001Enables subsequent open operations on a file or device to request read access.
Otherwise, other processes cannot open the file or device if they request read access.
If this flag is not specified, but the file or device has been opened for read access, the function fails.
You can pass your own file stream to the stream reader, opened with the mode you like:
var
I: Integer;
FileStream: TFileStream;
InStream: TStreamReader;
..
begin
...
FileStream := TFileStream.Create(InFileNames[I], fmOpenRead or fmShareDenyNone);
try
InStream := TStreamReader.Create(FileStream, TEncoding.UTF8);
try
..
Again, this requires Excel doing the same while opening the file, but with my simple test it looks like it does.
Upvotes: 7