-1

I´m regularly importing files from a path into an application with Delphi 10.2. After each successful import I want to move the current file to a new path(SuccessPath). It´s possible to have file names that already exist in the SuccessPath. That´s why I first check if the file name exists. If thats the case I append an index to the filename (e.g. test.txt changes to test_2.txt).

In some cases RenameFile returns false and GetLastError returns Access denied. It´s probably an unclosed file handle. Do I need to close a handle after calling FileExists? How would I do so?

Here´s my example code:

procedure TDemo.Execute;
begin
  MoveFileWithRenameDuplicates('C:\temp\test.txt', 'C:\temp\new\');
end;

procedure TDemo.MoveFileWithRenameDuplicates(oldFile, newPath: string);
var
  lNewFile: string;
  i: integer;

begin
  lNewFile := newPath + TPath.GetFileName(oldFile);
  i := 0;
  repeat
    if FileExists(lNewFile) then
    begin
      i := i + 1;
      lNewFile := newPath + TPath.GetFileNameWithoutExtension(oldFile) + '_' + IntToStr(i) + TPath.GetExtension(oldFile);
    end;
  until not FileExists(lNewFile);
  WriteLn('lNewFile=' + lNewFile);

  if not RenameFile(oldFile, lNewFile) then
    WriteLn(SysErrorMessage(GetLastError));
end;
JonasK
  • 149
  • 2
  • 8
  • 1
    Sysinternals' Process Monitor or Process Explorer might help to see what is going on and what has the file locked etc. – Brian Mar 02 '21 at 16:04
  • Run your program under Delphi debugger and put a break point on the line with RenameFile which will fail. Then using Windows Explorer, try to rename the file and see it succeed. If it fails, maybe you'll get a better error message. – fpiette Mar 02 '21 at 16:22
  • 3
    "*Do I need to close a handle after calling FileExists?*" - no. However, I would avoid the `FileExists()` loop altogether. It is a race condition, after you check a file does not exist, someone else could create the file before you. I would put `RenameFile()` itself into a loop, so when it fails then change the filename and retry the rename, repeating until successful. – Remy Lebeau Mar 02 '21 at 18:29
  • `oldFile` might be opened by any other program (including the Explorer) or even by your own. In case only you open it, why not doing it without any sharing? (i.e. [`CreateFile()` with `dwShareMode` set to `0`](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea)) – AmigoJack Mar 02 '21 at 21:11

1 Answers1

0

I changed my loop like Remy suggested. I also realized that the appended index didn´t actually increment so I changed that aswell:

procedure TDemo.MoveFileWithRenameDuplicates(oldFile, newPath: string);
var
  lNewFile: string;
  i: integer;
 
begin
    lNewFile := newPath + TPath.GetFileName(oldFile);
    i := 0;
    while not RenameFile(oldFile, lNewFile) do
    begin
        if FileExists(lNewFile) then
        begin
          lNewFile := newPath
            + TRegEx.Match(TPath.GetFileNameWithoutExtension(oldFile), '/^(?''name''[^_]*)_\d*$', [roIgnoreCase]).Groups['name'].Value
            + '_' + IntToStr(i) + TPath.GetExtension(oldFile);
        end
    else
        begin
          WriteLn(SysErrorMessage(GetLastError));
          break;
        end;
    end;
end;

This seems to have fixed my problem.

JonasK
  • 149
  • 2
  • 8