FsRemoveDir - delete DIR without delete files and dir inside
Moderators: Hacker, petermad, Stefan2, white
FsRemoveDir - delete DIR without delete files and dir inside
Hello.
My first post. I'm from Poland. Sometimes I write programs for themselves. Sorry for my English.
Now I write plug WFX to support a specific website file hosting. I write plug in Delphi 7. They begin to experience problems with which I do not know what to do.
My question.
Is it possible to delete a directory (FsRemoveDir) without deleting files and directories inside?
Website file hosting needs only a one single command to delete a folder and all of its contents. But TotalCommander enter the directory and deletes every file and directory separately, which is unnecessary in this case.
I tried to add to FsRemoveDir variable inform the other functions, but the function FsRemoveDir is executed after deleting files (FsDeleteFile).
Is there any other solution to this problem?
My first post. I'm from Poland. Sometimes I write programs for themselves. Sorry for my English.
Now I write plug WFX to support a specific website file hosting. I write plug in Delphi 7. They begin to experience problems with which I do not know what to do.
My question.
Is it possible to delete a directory (FsRemoveDir) without deleting files and directories inside?
Website file hosting needs only a one single command to delete a folder and all of its contents. But TotalCommander enter the directory and deletes every file and directory separately, which is unnecessary in this case.
I tried to add to FsRemoveDir variable inform the other functions, but the function FsRemoveDir is executed after deleting files (FsDeleteFile).
Is there any other solution to this problem?
- ghisler(Author)
- Site Admin
- Posts: 50421
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
Yes there is: You need to implement the function FsStatusInfo.
When you get the FS_STATUS_START, FS_STATUS_OP_DELETE notification, set a flag DoNotReadSubdirs:=TRUE;
Then when TC calls FsFindFirst, call SetLastError(ERROR_NO_MORE_FILES) and return INVALID_HANDLE_VALUE. Total Commander assumes that the directory is empty, and deletes it directly.
Don't forget to set DoNotReadSubdirs:=FALSE; when receiving FsStatusInfo with parameters FS_STATUS_END, FS_STATUS_OP_DELETE !
Btw, I use this trick when the user wants to delete a server in some plugins, so he doesn't delete all the files on that server too...
When you get the FS_STATUS_START, FS_STATUS_OP_DELETE notification, set a flag DoNotReadSubdirs:=TRUE;
Then when TC calls FsFindFirst, call SetLastError(ERROR_NO_MORE_FILES) and return INVALID_HANDLE_VALUE. Total Commander assumes that the directory is empty, and deletes it directly.
Don't forget to set DoNotReadSubdirs:=FALSE; when receiving FsStatusInfo with parameters FS_STATUS_END, FS_STATUS_OP_DELETE !
Btw, I use this trick when the user wants to delete a server in some plugins, so he doesn't delete all the files on that server too...
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com
Ah, that's exactly what I needed! Indeed it works. Thanks!
Regards
Dalai
Regards
Dalai
#101164 Personal licence
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
Thank you. I confirm that works.
In FsFindFirst, only instead Result: = INVALID_HANDLE_VALUE, I just set Result: = 0. Otherwise do not want to perform the functions FsRemoveDir.
For interested
:
For sure I'll have more questions, but this is a new post. Problem solved, post to close.
In FsFindFirst, only instead Result: = INVALID_HANDLE_VALUE, I just set Result: = 0. Otherwise do not want to perform the functions FsRemoveDir.
For interested

Code: Select all
procedure FsStatusInfo(RemoteDir : PChar; InfoStartEnd, InfoOperation : Integer); stdcall;
begin
if (InfoStartEnd = FS_STATUS_END) and (InfoOperation = FS_STATUS_OP_GET_MULTI) then AbortCopy := False;
// from www.ghisler.ch/board/viewtopic.php?t=41977
if (InfoStartEnd = FS_STATUS_START) and (InfoOperation = FS_STATUS_OP_DELETE) then DoNotReadSubdirs := True;
if (InfoStartEnd = FS_STATUS_END) and (InfoOperation = FS_STATUS_OP_DELETE) then DoNotReadSubdirs := False;
end;
Of course, FsRemoveDir will be called even if FsFindFirst returns INVALID_HANDLE_VALUE. I'm currently using it exactly like this. Maybe there's some other code interfering with it. And you have to make sure that FsFindFirst sets ERROR_NO_MORE_FILES, as Ghisler pointed out:jaco777 wrote:Otherwise do not want to perform the functions FsRemoveDir.
Code: Select all
function FsFindFirst [...]
[..]
if DontReadSubdirs then begin
SetLastError(ERROR_NO_MORE_FILES);
Exit;
end;
Just a note: You can do this with less code:For interested:
Code: Select all
[...]
if (InfoOperation = FS_STATUS_OP_DELETE) then begin
DontReadSubdirs:= InfoStartEnd = FS_STATUS_START;
end;
[...]
Dalai
#101164 Personal licence
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
To Dali: You're right, just so I wrote a function FsFindFirst. I am a novice programmer. I wrote the code is easier for me to understand. Thank you for the tips.
In my case, my code works:
In my case, my code works:
Code: Select all
function FsFindFirst(Path :PChar; var FindData : tWIN32FINDDATA) : THandle; stdcall;
var
ofn, Path2 : string;
begin
If DoNotReadSubdirs = False Then
begin
// code for other operations
end
else
begin
// code for delete DIR
SetLastError(ERROR_NO_MORE_FILES);
Result := 0;
end;
end;
Code: Select all
procedure FsStatusInfo(RemoteDir : PChar; InfoStartEnd, InfoOperation : Integer); stdcall;
begin
if (InfoStartEnd = FS_STATUS_END) and (InfoOperation = FS_STATUS_OP_GET_MULTI) then AbortCopy := False;
// from www.ghisler.ch/board/viewtopic.php?t=41977
if (InfoStartEnd = FS_STATUS_START) and (InfoOperation = FS_STATUS_OP_DELETE) then DoNotReadSubdirs := True;
if (InfoStartEnd = FS_STATUS_END) and (InfoOperation = FS_STATUS_OP_DELETE) then DoNotReadSubdirs := False;
end;
Yes, of course. It's what everbody does. OK, some people can write code they don't understand laterjaco777 wrote:I wrote the code is easier for me to understand.

To explain the little code I posted: This
Code: Select all
DontReadSubdirs:= InfoStartEnd = FS_STATUS_START;
Code: Select all
DontReadSubdirs:= (InfoStartEnd = FS_STATUS_START);
Do NOT compare Boolean variables with True or False! Don't start to make that a habit or your code will sometimes fail. You may ask why. Well, True is equal to 1 if it's a Boolean type but -1 if it's a Bool, LongBool or WordBool type! So, code like thisIn my case, my code works:Code: Select all
function FsFindFirst(Path :PChar; var FindData : tWIN32FINDDATA) : THandle; stdcall; var ofn, Path2 : string; begin If DoNotReadSubdirs = False Then begin // code for other operations end else begin // code for delete DIR SetLastError(ERROR_NO_MORE_FILES); Result := 0; end; end;
Code: Select all
var foo: BOOL;
[...]
if foo = true then
Code: Select all
if someboolvalue then ...
Code: Select all
if NOT someotherboolvalue then ...
Regards
Dalai
#101164 Personal licence
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
Variables in Delphi is a challenge for me:-) - too many. I more programing in AutoIt, and there is easier.
That is, although I declared global variables:
and in procedure FsStatusInfo is
it is better to write as Your example.
I changed my code and of course working properly. Once again, thank you for Your tips.
I have another question. In the function FsFindFirst, does the statement Result: = 0 is correct (compatible), what better way?
That is, although I declared global variables:
Code: Select all
AbortCopy, DoNotReadSubdirs : Boolean;
Code: Select all
...
DoNotReadSubdirs := False;
...
Code: Select all
function FsFindFirst(Path :PChar; var FindData : tWIN32FINDDATA) : THandle; stdcall;
var
ofn, Path2 : string;
begin
If NOT DoNotReadSubdirs Then
begin
// code for other operations
end
else
begin
// code for delete DIR
SetLastError(ERROR_NO_MORE_FILES);
Result := 0;
end;
end;
I have another question. In the function FsFindFirst, does the statement Result: = 0 is correct (compatible), what better way?
Typeless variables have their pros, but they also have some cons... I code in Delphi, AutoIt, Bash, PowerShell and some others and I admit that I prefer typesafe languages, although I don't complain about the typeless variables in AutoItjaco777 wrote:Variables in Delphi is a challenge for me:-) - too many. I more programing in AutoIt, and there is easier.

Well, it's in the TC plugin SDK. You should return INVALID_HANDLE_VALUE if an error occurs, and a value of your choice if not. So, you should not return 0 in this case because there's an error (ERROR_NO_MORE_FILES), although you "created" that error yourself. I don't know if it makes a difference for TC (I guess not), but it's better to conform to the documentation/API/SDK/whatever.I have another question. In the function FsFindFirst, does the statement Result: = 0 is correct (compatible), what better way?
I use this approach:
Code: Select all
function FsFindFirst(Path: PChar; var FindData: TWin32FindData): THandle; stdcall;
begin
Result:= INVALID_HANDLE_VALUE;
if DontReadSubdirs then begin
SetLastError(ERROR_NO_MORE_FILES);
Exit;
end;
// Some more code here
try
// Some more code here
except
on E: Exception do begin
// Handle exception here
Exit;
end;
end;
Result:= 1;
end;
Regards
Dalai
#101164 Personal licence
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64
Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
Another good tipsSo, Result is only set to 1 if all is well. In all other cases, the function will be exited and return INVALID_HANDLE_VALUE.

That's right, the function will always return a value.
I think that the topic has already been fully explained.
Added on 06.04.2015, after many tests:
With this code:
Code: Select all
function FsFindFirst(Path :PChar; var FindData : tWIN32FINDDATA) : THandle; stdcall;
var
ofn, Path2 : string;
begin
If DoNotReadSubdirs = False Then
begin
// code for other operations
end
else
begin
// code for delete DIR
SetLastError(ERROR_NO_MORE_FILES);
Result := 0;
end;
end;
When instead Result: = 0 is Result: = INVALID_HANDLE_VALUE, as suggested Ghisler, good deletes directories (with files inside), and does not call function FsDeleteFile.