Search code examples
delphidelphi-10-seattle

How copy a folder by recursion without include source folder label inside dest folder?


I use the code below to copy a folder by recursion. Works fine, but exists a problem even least to me, because source folder label also is being included inside dest folder after copy, and i not want it. In this moment what happens is this:

SRC Folder:

C:\MyTest
  -firstfolder
  -secondfolder
  -- secondfolderFile

DEST Folder (after copy):

C:\NewTest
  -MyTest
  -firstfolder
  -secondfolder
  -- secondfolderFile

And then i need that in dest folder only stays:

C:\NewTest
  -firstfolder
  -secondfolder
  -- secondfolderFile

How make this using the following code?

program testCopyRecursion;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Windows,
  ShellAPI,
  SysUtils;

function CopyFolder(const SrcFolder, DestFolder: String; iFileOp: Integer;
  OverWrite: Boolean; ShowDialog: Boolean): Boolean;
var
  MyFOStruct: TSHFileOpStruct;
  Src, Dest: String;
  ResultVal: Integer;
begin
  Result := False;

  Src := SrcFolder;
  Dest := DestFolder;

  if not DirectoryExists(Dest) then
    ForceDirectories(Dest);

  if (Src = '') or ((iFileOp <> FO_DELETE) and (Dest = '')) or
    (CompareText(Src, Dest) = 0) then
    Exit;

  if Src[Length(Src)] = '\' then
    SetLength(Src, Length(Src) - 1);
  Src := Src + #0#0;

  if (Dest <> '') and (Dest[Length(Dest)] = '\') then
    SetLength(Dest, Length(Dest) - 1);
  Dest := Dest + #0#0;

  FillChar(MyFOStruct, SizeOf(MyFOStruct), 0);

  with MyFOStruct do
  begin
    Wnd := 0;

    wFunc := iFileOp;
    pFrom := @Src[1];
    pTo := @Dest[1];

    fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMMKDIR;

    if not OverWrite then
      fFlags := fFlags or FOF_RENAMEONCOLLISION;
    if not ShowDialog then
      fFlags := fFlags or FOF_SILENT;
  end;

  try
    MyFOStruct.fAnyOperationsAborted := False;
    MyFOStruct.hNameMappings := nil;
    ResultVal := ShFileOperation(MyFOStruct);
    Result := (ResultVal = 0);
  finally
  end;
end;

begin
  try
    CopyFolder('C:\MyTest', 'C:\NewTest', FO_COPY, True, False);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.

Solution

  • You are copying the C:\MyTest folder itself into the C:\NewTest folder, rather than just copying what is inside of C:\MyTest. Try setting the source path to 'C:\MyTest\*' instead to copy only what is inside of C:\MyTest.

    And FYI, you don't need the call to ForceDirectories(), as SHFileOperation() creates the destination folder if it does not already exist. The documentation even says so:

    Copy and Move operations can specify destination directories that do not exist. In those cases, the system attempts to create them and normally displays a dialog box to ask the user if they want to create the new directory. To suppress this dialog box and have the directories created silently, set the FOF_NOCONFIRMMKDIR flag in fFlags.

    Try something more like this:

    program testCopyRecursion;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      Windows, ShellAPI, SysUtils;
    
    function CopyFolder(const SrcFolder, DestFolder: String;
      OverWrite: Boolean; ShowDialog: Boolean): Boolean;
    var
      MyFOStruct: TSHFileOpStruct;
      Src, Dest: String;
    begin
      Result := False;
    
      if (SrcFolder = '') or (DestFolder = '') or
         (CompareText(SrcFolder, DestFolder) = 0) then
        Exit;
    
      Src := IncludeTrailingPathDelimiter(SrcFolder) + '*'#0;
      Dest := ExcludeTrailingPathDelimiter(DestFolder) + #0;
    
      FillChar(MyFOStruct, SizeOf(MyFOStruct), 0);
    
      with MyFOStruct do
      begin
        wFunc := FO_COPY;
        pFrom := PChar(Src);
        pTo := PChar(Dest);
        fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMMKDIR;
        if not OverWrite then
          fFlags := fFlags or FOF_RENAMEONCOLLISION;
        if not ShowDialog then
          fFlags := fFlags or FOF_SILENT;
      end;
    
      Result := (SHFileOperation(MyFOStruct) = 0);
    end;
    
    begin
      try
        CopyFolder('C:\MyTest', 'C:\NewTest', True, False);
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
      Readln;
    end.