Recent

Author Topic: move folders to another drive  (Read 2335 times)

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #15 on: November 22, 2022, 04:16:17 am »
where can i put showmessage('successful operation')  :-\
I simplified it for you so you can tell "success" or "failed".
When you want more of the "failed" messages, stick to old code and add some "else" wherever you need it.
Code: Pascal  [Select][+][-]
  1. function MoveFileW(lpExistingFileName, lpNewFileName: PWideChar): BOOL; stdcall; external 'Kernel32.dll' name 'MoveFileW';
  2.  
  3. type
  4.   TSHFileOpStructW = record
  5.     Wnd: HWND;
  6.     wFunc: UINT;
  7.     pFrom: LPCWSTR;
  8.     pTo: LPCWSTR;
  9.     fFlags: FILEOP_FLAGS;
  10.     fAnyOperationsAborted: BOOL;
  11.     hNameMappings: Pointer;
  12.     lpszProgressTitle: LPCWSTR;
  13.   end;
  14.  
  15. const
  16.   FO_MOVE = $0001;
  17.   FOF_NOCONFIRMMKDIR = $0200;
  18.  
  19. function SHFileOperationW(const lpFileOp: TSHFileOpStructW): Integer; stdcall; external 'shell32.dll' name 'SHFileOperationW';
  20.  
  21. function MoveDir(const Src, Dst: WideString): Boolean;
  22. var
  23.   FOS: TSHFileOpStructW;
  24. begin
  25.   ZeroMemory(@FOS, SizeOf(FOS));
  26.   FOS.Wnd := Application.Handle;
  27.   FOS.wFunc := FO_MOVE;
  28.   FOS.fFlags := FOF_NOCONFIRMMKDIR;
  29.   FOS.pFrom := PWideChar(IncludeTrailingPathDelimiter(Src) + '*.*'#0);
  30.   FOS.pTo := PWideChar(Dst + #0);
  31.   Result := (SHFileOperationW(FOS) = 0);
  32. end;
  33.  
  34. // this method wants either a full source and destination path
  35. // or a full source and destination path with "filename.ext"
  36. // it return true on success or false on failure
  37. function MoveData(const Src, Dst: WideString): Boolean;
  38. begin
  39.   Result := False;
  40.   if FileExists(Src) then
  41.     begin
  42.       Result := ForceDirectories(ExtractFilePath(Dst));
  43.       if Result then
  44.         Result := MoveFileW(PWideChar(Src), PWideChar(Dst));
  45.     end
  46.   else // file did not exists, try folder operation
  47.   if DirectoryExists(Src) then
  48.     begin
  49.       Result := MoveDir(Src, Dst);
  50.       if Result then
  51.         Result := RemoveDir(Src);
  52.     end;
  53. end;
  54.  
  55. procedure TForm1.Button1Click(Sender: TObject);
  56. var
  57.   Src, Dst: WideString;
  58. begin
  59.   Src := 'C:\Test';
  60.   Dst := 'D:\Test';
  61.   if MoveData(Src, Dst) then
  62.     ShowMessage('All Good.')
  63.     else
  64.     ShowMessage('Not successful.');
  65. end;
Thank you very much friend
You are welcome  O:-)
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: move folders to another drive
« Reply #16 on: November 22, 2022, 05:55:12 am »
Hello friend, thank you very much, very kind, I have copied the code as it is and I get this error:

Code: Pascal  [Select][+][-]
  1. function MoveDir(const Src, Dst: WideString): Boolean;
  2. var
  3.   FOS: TSHFileOpStructW;
  4. begin
  5.   ZeroMemory(@FOS, SizeOf(FOS));
  6.   FOS.Wnd := Application.Handle;
  7.   FOS.wFunc := FO_MOVE;
  8.   FOS.fFlags := FOF_NOCONFIRMMKDIR;
  9.   FOS.pFrom := PWideChar(IncludeTrailingPathDelimiter(Src) + '*.*'#0);
  10.   FOS.pTo := PWideChar(Dst + #0);
  11.   Result := (SHFileOperationW(FOS) = 0);  // here mark error in "FOS"
  12. end;  

in the message window it shows:
Code: Pascal  [Select][+][-]
  1. unit1.pas(66,34) Error: Incompatible type for arg no. 1: Got "TSHFileOpStructW", expected "_SHFILEOPSTRUCTA"

 :( :(

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #17 on: November 22, 2022, 06:03:24 am »
I have copied the code as it is and I get this error
You need to copy and paste everything from my last again. The older is not compatible, I changed source.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #18 on: November 22, 2022, 03:09:51 pm »
If you still got errors, in attachment is a unit, just include it in your uses like:
Code: Pascal  [Select][+][-]
  1. uses ....FileHelper...
and call it like i showed on last post:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   Src, Dst: WideString;
  4. begin
  5.   Src := 'C:\Test';
  6.   Dst := 'D:\Test';
  7.   if FileHelper.MoveData(Src, Dst) then // i added unit name infront of method to ensure that my method is called.
  8.     ShowMessage('All Good.')
  9.     else
  10.     ShowMessage('Not successful.');
  11. end;
I hope now all works fluffy for you.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: move folders to another drive
« Reply #19 on: November 22, 2022, 03:55:24 pm »
Thank you very much friend, now everything works perfect.  :D

Excuse me if I abuse your courtesy, I have a question, if one day I have files or folders with long names, will this code work ??
Thanks for your time.

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #20 on: November 22, 2022, 04:01:28 pm »
My method is limited to a max name length of 32767 (wide)-chars. Is that long enough?
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: move folders to another drive
« Reply #21 on: November 22, 2022, 04:03:50 pm »
wow thank you very much friend.  :)

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #22 on: November 22, 2022, 04:15:39 pm »
Sorry, you need to update the filehelper.pas file to enable to long path setting.
Change this
Code: Pascal  [Select][+][-]
  1.           Result := MoveFileW(PWideChar(Src), PWideChar(Dst));
into that
Code: Pascal  [Select][+][-]
  1.           Result := MoveFileW(PWideChar('\\?\' + Src), PWideChar('\\?\' + Dst));
and now you got 32k for files.


edit:
Sorry, for folders this does not apply, I never tested anything beyond MAX_PATH length.
As far as I understand the WinAPI it is not possible with my method to use folders longer than MAX_PATH.
« Last Edit: November 22, 2022, 04:36:52 pm by KodeZwerg »
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: move folders to another drive
« Reply #23 on: November 22, 2022, 05:03:57 pm »
I understand my friend, don't worry, it's not urgent, rather I have a question, the code you gave me works perfect for folders, in the case of files, if it already exists, it doesn't overwrite it, I've read that I should use copyfile and then deletefile to be able to overwrite it, I'll do some tests and tell you

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #24 on: November 22, 2022, 05:33:43 pm »
I understand my friend, don't worry, it's not urgent, rather I have a question, the code you gave me works perfect for folders, in the case of files, if it already exists, it doesn't overwrite it, I've read that I should use copyfile and then deletefile to be able to overwrite it, I'll do some tests and tell you
Re-Uploaded, fixed small issue.
Now it will overwrite per default even hidden, readonly etc.. files.

Call it like this to overwrite files
Code: Pascal  [Select][+][-]
  1.   if FileHelper.MoveData(Src, Dst) then

Call it like this to skip moving existing files
Code: Pascal  [Select][+][-]
  1.   if FileHelper.MoveData(Src, Dst, False) then

I hope now all is okay.

Re-ReUploaded.... forgot to tweak the deletefile operation.
« Last Edit: November 22, 2022, 05:37:34 pm by KodeZwerg »
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: move folders to another drive
« Reply #25 on: November 22, 2022, 05:43:11 pm »
Thank you very much friend, one question, what would be better to overwrite?
first delete destination file (if it exists) and then move source file.
or
first copy file overwriting old file, and then delete source file
 :-\

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #26 on: November 22, 2022, 06:00:34 pm »
Thank you very much friend, one question, what would be better to overwrite?
first delete destination file (if it exists) and then move source file.
or
first copy file overwriting old file, and then delete source file
 :-\
I do not really understand the question.
If you want to overwrite just call my method without using 3rd parameter and it does do all automagical for you.

When you plan to create your own way of doing with overwrite ability than this would be the correct order:
1. check if destination exists
2. if 1. is true than set the file attribute to be able to delete
3. if 1. and 2. is true than delete the file
4. if 2. and 3. is true or 1. was false than copy the file
5. if 4. is true than do the same 2. and 3. for the source file
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: move folders to another drive
« Reply #27 on: November 22, 2022, 11:08:36 pm »
ok friend, thank you very much for helping me  :)

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: move folders to another drive
« Reply #28 on: December 01, 2022, 01:46:41 am »
Hello brothers, I just made a modification to the code to be able to copy files and folders, that's how I'm using it at the moment and it works fine  :)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, FileHelper;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Button2: TButton;
  17.     procedure Button1Click(Sender: TObject);
  18.     procedure Button2Click(Sender: TObject);
  19.     procedure FormCreate(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.   Src, Dst: WideString;
  29.  
  30.   const
  31.     FO_MOVE   = $0001;
  32.     FO_COPY   = $0002;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38. { TForm1 }
  39.  
  40. procedure TForm1.Button1Click(Sender: TObject); // FOR MOVE
  41. begin
  42.   if FileHelper.MoveData(Src, Dst, FO_MOVE) then // i added unit name infront of method to ensure that my method is called.
  43.      ShowMessage('All Good.')
  44.      else
  45.      ShowMessage('Not successful.');
  46.  
  47. end;
  48.  
  49. procedure TForm1.Button2Click(Sender: TObject); // FOR COPY
  50. begin
  51.   if FileHelper.MoveData(Src, Dst, FO_COPY) then // i added unit name infront of method to ensure that my method is called.
  52.      ShowMessage('All Good.')
  53.      else
  54.      ShowMessage('Not successful.');
  55. end;
  56.  
  57. procedure TForm1.FormCreate(Sender: TObject);
  58. begin
  59.   Src := 'D:\test\casa\jaja.txt';
  60.   Dst := 'C:\test\trabajo\jaja.txt';
  61.   {
  62.   Src := 'D:\test\casa\';
  63.   Dst := 'C:\test\trabajo\casa\';
  64.   }
  65. end;
  66.  
  67. end.

also modify the "filehelper.pas" unit of the friend KodeZwerg

Code: Pascal  [Select][+][-]
  1. unit FileHelper;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, SysUtils;
  9.  
  10. function MoveData(const Src, Dst: WideString; Func: Integer; FileOverwrite: Boolean = True): Boolean;
  11.  
  12. implementation
  13.  
  14.   const
  15.     {FO_MOVE   = $0001;
  16.     FO_COPY   = $0002;
  17.     FO_DELETE = $0003;
  18.     FO_RENAME = $0004;}
  19.     FOF_NOCONFIRMMKDIR = $0200;
  20.     FOF_NOCONFIRMATION = $0010;    { Don't prompt the user. }
  21.     FOF_SIMPLEPROGRESS = $0100;    { means don't show names of files }
  22.     FOF_SILENT         = $0004;    { don't create progress/report }
  23.  
  24.   type
  25.     TSHFileOpStructW = record
  26.       Wnd: HWND;
  27.       wFunc: UINT;
  28.       pFrom: LPCWSTR;
  29.       pTo: LPCWSTR;
  30.       fFlags: FILEOP_FLAGS;
  31.       fAnyOperationsAborted: BOOL;
  32.       hNameMappings: Pointer;
  33.       lpszProgressTitle: LPCWSTR;
  34.     end;
  35.  
  36.   function MoveFileW(lpExistingFileName, lpNewFileName: PWideChar): BOOL; stdcall; external 'Kernel32.dll' name 'MoveFileW';
  37.   function CopyFileW(lpExistingFileName, lpNewFileName: PWideChar; bFailIfExists: BOOL): BOOL; stdcall; external 'Kernel32.dll' name 'CopyFileW';
  38.  
  39.   {
  40.   function CopyFileW(lpExistingFileName:LPCWSTR; lpNewFileName:LPCWSTR; bFailIfExists:WINBOOL):WINBOOL; external 'kernel32' name 'CopyFileW';
  41.   function MoveFileW(lpExistingFileName:LPCWSTR; lpNewFileName:LPCWSTR):WINBOOL; external 'kernel32' name 'MoveFileW';
  42.   }
  43.  
  44.   function SHFileOperationW(const lpFileOp: TSHFileOpStructW): Integer; stdcall; external 'shell32.dll' name 'SHFileOperationW';
  45.  
  46.   function MoveDir(const Src, Dst: WideString; Func: Integer): Boolean;
  47.   var
  48.     FOS: TSHFileOpStructW;
  49.   begin
  50.     ZeroMemory(@FOS, SizeOf(FOS));
  51.     FOS.wFunc := Func; // FO_MOVE , FO_COPY;
  52.     FOS.fFlags := FOF_NOCONFIRMMKDIR or FOF_SIMPLEPROGRESS or FOF_NOCONFIRMATION or FOF_SILENT;
  53.     FOS.pFrom := PWideChar(IncludeTrailingPathDelimiter(Src) + '*.*'#0);
  54.     FOS.pTo := PWideChar(Dst + #0);
  55.     Result := (SHFileOperationW(FOS) = 0);
  56.   end;
  57.  
  58.   // this method wants either a full source and destination path
  59.   // or a full source and destination path with "filename.ext"
  60.   // it return true on success or false on failure
  61.   function MoveData(const Src, Dst: WideString; Func: Integer; FileOverwrite: Boolean = True): Boolean;
  62.   begin
  63.     Result := False;
  64.     if FileExists(Src) then
  65.       begin
  66.         Result := ForceDirectories(ExtractFilePath(Dst));
  67.         if Result then
  68.           begin
  69.             if (FileOverwrite and FileExists(Dst)) then
  70.               Result := ((SetFileAttributesW(PWideChar('\\?\' + Dst), faNormal)) and DeleteFileW(PWideChar('\\?\' + Dst)));
  71.             if Result then
  72.                begin
  73.                     if Func = $0001 then Result := MoveFileW(PWideChar('\\?\' + Src), PWideChar('\\?\' + Dst));  // move
  74.                     if Func = $0002 then Result := CopyFileW(PWideChar('\\?\' + Src), PWideChar('\\?\' + Dst), false);  // copy
  75.                end;
  76.  
  77.           end;
  78.       end
  79.     else
  80.     if DirectoryExists(Src) then
  81.       begin
  82.         Result := MoveDir(Src, Dst, Func);
  83.         if Result then
  84.           begin
  85.             if Func = $0001 then Result := RemoveDir(Src); // only remove empty folder when using FO_MOVE
  86.             //Result := RemoveDir(Src);
  87.           end;
  88.       end;
  89.   end;
  90.  
  91. end.

At the moment it works fine, but if I'm doing something wrong please correct me  :)

PD: I attached the project.

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2006
  • Fifty shades of code.
    • Delphi & FreePascal
Re: move folders to another drive
« Reply #29 on: December 01, 2022, 09:29:12 am »
At the moment it works fine, but if I'm doing something wrong please correct me  :)
Just a minor thing I would do different, when you having your "Func" like FO_COPY, use it also in code and not the binary part.

I would write this
Code: Pascal  [Select][+][-]
  1.             if Result then
  2.                begin
  3.                     if Func = $0001 then Result := MoveFileW(PWideChar('\\?\' + Src), PWideChar('\\?\' + Dst));  // move
  4.                     if Func = $0002 then Result := CopyFileW(PWideChar('\\?\' + Src), PWideChar('\\?\' + Dst), false);  // copy
  5.                end;
In a way like that
Code: Pascal  [Select][+][-]
  1.             if Result then
  2.                case Func of
  3.                  FO_MOVE: Result := MoveFileW(PWideChar('\\?\' + Src), PWideChar('\\?\' + Dst));  // move
  4.                  FO_COPY: Result := CopyFileW(PWideChar('\\?\' + Src), PWideChar('\\?\' + Dst), false);  // copy
  5.                  else
  6.                    Result := False;
  7.                end;
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

 

TinyPortal © 2005-2018