Discussion:
Cannot delete Dirs after using FindFirst/FindNext. on WinXP.
(too old to reply)
Stephen Jackson
2003-07-22 10:36:09 UTC
Permalink
Having trouble deleting directories from a program that finds them
using the TSearchRec/FindFirst/FindNext method. The path is deletable
before the search and also after the program has closed (using an
hardcoded <DeleteFile> or the FileExplorer), but not after a search
whilst the program is still running. It looks like the directory is
locked somehow but the directory is not opened? The files are deleted
using <DeleteFile> (Dirs by <RemoveDir>). Note all directories fail
when being deleted. Curiously the program works fine on Win95/98. I
am working on XP with FAT32. Anybody else had this?

Here is the search routine, which finds files and stores in a
TStringList:
==========================================================================

procedure GetFileNamesInDir(var FileNames: TStringList;
const Dir, FileMask: string; const Attribute: Integer;
const Recursive: Boolean; const PathType: TmyPathType;
const ClearList: Boolean = True); overload; {2+}{11~}{16~}
{=smj=============================================================}
// Creates a list of FileNames from a Dir.
// Dir: Start point.
// FileMask: Accepts wildcards i.e. 'A*.*'.
// Attribute: faAnyFile, faDirectory etc. (see Delphi's FindFirst
function).
// Recursive: Does it look within Sub Dir's.
// PathType: Determines the amount FileName path is returned.
// ptNone: Just the FileName, no Path.
// ptPartial: Path + Filename, not including the Start Dir path.
// ptFull: Full qualified Path + Filename.
var
RootDirLen: Integer;

procedure FindFilesIn(const Dir: string);
{=smj===================================}
var
RelativeDir: string;

procedure SaveFileName(const FileName: string);
{=smj=========================================}
begin
case PathType of
ptNone: FileNames.Add(FileName);
ptPartial: FileNames.Add(PathConcat(RelativeDir, FileName));
ptFull: FileNames.Add(PathConcat(Dir, FileName));
end;
end;

var
Status: Integer;
SearchRec: TSearchRec;

function Valid(const Attribute: Integer): Boolean;
{=smj============================================}
begin
Result :=
(SearchRec.Name <> '.') and
(SearchRec.Name <> '..') and
((Attribute = faAnyFile) or
(SearchRec.Attr and Attribute > 0));
end;

begin
if PathType = ptPartial then
// Relative Path required, create. (More efficient this way).
RelativeDir := Copy(Dir, RootDirLen + 1, Length(Dir));

Status := FindFirst(PathConcat(Dir, [FileMask]), Attribute,
SearchRec);
try
repeat
while Status = 0 do begin
if Valid(Attribute) then {4+}
SaveFileName(SearchRec.Name);
Status := FindNext(SearchRec);
end;
if Recursive then begin
// Check Sub-Dirs. {11+}
Status := FindFirst(
PathConcat(Dir, ['*.*']), faDirectory, SearchRec);
while Status = 0 do begin
if Valid(faDirectory) then
FindFilesIn(PathConcat(Dir, [SearchRec.Name]));
Status := FindNext(SearchRec);
end;
end;
until Status <> 0;
finally
SysUtils.FindClose(SearchRec);
end;
end;

begin
if ClearList then {16~}
FileNames.Clear;

// Relative path pre-calculation.
if PathType = ptPartial then
if AnsiLastChar(Dir)^ = '\' then
RootDirLen := Length(Dir)
else
RootDirLen := Length(Dir) + 1;

FindFilesIn(Dir);
end;

Delete rountine:
================

procedure TfrmMain.bitDeleteClick(Sender: TObject);
{=smj=============================================}
var
deleted: Boolean;
i: Integer;
Pfn: string;
begin
for i := lsvHitlist.Items.Count - 1 downto 0 do begin
Pfn := lsvHitList.Items[i].Caption;
if FileGetAttr(Pfn) = faDirectory then
deleted := RemoveDir(Pfn)
else
deleted := DeleteFile(Pfn);
if deleted then
lsvHitlist.Items[i].Delete
else
lsvHitlist.Items[i].StateIndex := 1;
end;
UpdateUI();
end;
RedBaron
2003-07-22 11:26:32 UTC
Permalink
I came Across the same problem some time ago.
I solved it by setting the curent directory to the parent directory of the
directory which should be deleted using the setCurrentDir function.
--
________________________________
NJoy the Silence
RedBaron
________________________________
Post by Stephen Jackson
Having trouble deleting directories from a program that finds them
using the TSearchRec/FindFirst/FindNext method. The path is deletable
before the search and also after the program has closed (using an
hardcoded <DeleteFile> or the FileExplorer), but not after a search
whilst the program is still running. It looks like the directory is
locked somehow but the directory is not opened? The files are deleted
using <DeleteFile> (Dirs by <RemoveDir>). Note all directories fail
when being deleted. Curiously the program works fine on Win95/98. I
am working on XP with FAT32. Anybody else had this?
Here is the search routine, which finds files and stores in a
==========================================================================
procedure GetFileNamesInDir(var FileNames: TStringList;
const Dir, FileMask: string; const Attribute: Integer;
const Recursive: Boolean; const PathType: TmyPathType;
const ClearList: Boolean = True); overload; {2+}{11~}{16~}
{=smj=============================================================}
// Creates a list of FileNames from a Dir.
// Dir: Start point.
// FileMask: Accepts wildcards i.e. 'A*.*'.
// Attribute: faAnyFile, faDirectory etc. (see Delphi's FindFirst
function).
// Recursive: Does it look within Sub Dir's.
// PathType: Determines the amount FileName path is returned.
// ptNone: Just the FileName, no Path.
// ptPartial: Path + Filename, not including the Start Dir path.
// ptFull: Full qualified Path + Filename.
var
RootDirLen: Integer;
procedure FindFilesIn(const Dir: string);
{=smj===================================}
var
RelativeDir: string;
procedure SaveFileName(const FileName: string);
{=smj=========================================}
begin
case PathType of
ptNone: FileNames.Add(FileName);
ptPartial: FileNames.Add(PathConcat(RelativeDir, FileName));
ptFull: FileNames.Add(PathConcat(Dir, FileName));
end;
end;
var
Status: Integer;
SearchRec: TSearchRec;
function Valid(const Attribute: Integer): Boolean;
{=smj============================================}
begin
Result :=
(SearchRec.Name <> '.') and
(SearchRec.Name <> '..') and
((Attribute = faAnyFile) or
(SearchRec.Attr and Attribute > 0));
end;
begin
if PathType = ptPartial then
// Relative Path required, create. (More efficient this way).
RelativeDir := Copy(Dir, RootDirLen + 1, Length(Dir));
Status := FindFirst(PathConcat(Dir, [FileMask]), Attribute,
SearchRec);
try
repeat
while Status = 0 do begin
if Valid(Attribute) then {4+}
SaveFileName(SearchRec.Name);
Status := FindNext(SearchRec);
end;
if Recursive then begin
// Check Sub-Dirs. {11+}
Status := FindFirst(
PathConcat(Dir, ['*.*']), faDirectory, SearchRec);
while Status = 0 do begin
if Valid(faDirectory) then
FindFilesIn(PathConcat(Dir, [SearchRec.Name]));
Status := FindNext(SearchRec);
end;
end;
until Status <> 0;
finally
SysUtils.FindClose(SearchRec);
end;
end;
begin
if ClearList then {16~}
FileNames.Clear;
// Relative path pre-calculation.
if PathType = ptPartial then
if AnsiLastChar(Dir)^ = '\' then
RootDirLen := Length(Dir)
else
RootDirLen := Length(Dir) + 1;
FindFilesIn(Dir);
end;
================
procedure TfrmMain.bitDeleteClick(Sender: TObject);
{=smj=============================================}
var
deleted: Boolean;
i: Integer;
Pfn: string;
begin
for i := lsvHitlist.Items.Count - 1 downto 0 do begin
Pfn := lsvHitList.Items[i].Caption;
if FileGetAttr(Pfn) = faDirectory then
deleted := RemoveDir(Pfn)
else
deleted := DeleteFile(Pfn);
if deleted then
lsvHitlist.Items[i].Delete
else
lsvHitlist.Items[i].StateIndex := 1;
end;
UpdateUI();
end;
Stephen Jackson
2003-07-25 12:46:58 UTC
Permalink
Unfortunately setting the CurrentDir to the parent of the Dir and then
trying RemoveDir is not any more successful. :( Thanks anyway for
trying, anything else you know of? :)

procedure TfrmMain.bitDeleteClick(Sender: TObject);
{=smj=============================================}
var
deleted: Boolean;
i: Integer;
Pfn: string;
begin
for i := lsvHitlist.Items.Count - 1 downto 0 do begin
Pfn := lsvHitList.Items[i].Caption; // <-- Path to Dir.
if FileGetAttr(Pfn) = faDirectory then begin
SetCurrentDir(ExtractFilePath(Pfn)); // <-- Added as suggested.
deleted := RemoveDir(Pfn); // <-- Always False!
end else
deleted := DeleteFile(Pfn);
if deleted then
lsvHitlist.Items[i].Delete
else
lsvHitlist.Items[i].StateIndex := 1;
end;
UpdateUI();
end;
RedBaron
2003-07-25 13:39:30 UTC
Permalink
Like Quivis said, try to set the current directory to 'c:\temp'. this should
work.

My suggestion was to set the current dir to the parent dir.
eg.:
dir to be deleted: 'C:\Program Files\dir_to_be_deleted'
dir to set to current: 'C:\Program Files'

I would also suggest to use the ExtractFileDir function after you have
determined if it is a directory. Then rip of the 'dir_to_be_deleted' part to
get the parent directory. (String manipulation.)

I usually don't use the temp dir because I like working with relative
directories so i can use changedirs with only the directory names and not
the full paths.

Hope this helps...
--
________________________________
NJoy the Silence
RedBaron
________________________________
8>< /snip
-> for i := lsvHitlist.Items.Count - 1 downto 0 do begin
-> Pfn := lsvHitList.Items[i].Caption; // <-- Path to Dir.
-> if FileGetAttr(Pfn) = faDirectory then begin
-> SetCurrentDir(ExtractFilePath(Pfn)); // <-- Added as suggested.
-> deleted := RemoveDir(Pfn); // <-- Always False!
-> end else
-> deleted := DeleteFile(Pfn);
As I understood the suggestion, you should set CurrentDir to one step
above this level. It seems you add the same level you're trying to
delete, i.e. CurrentDir points to the same dir you're trying to remove.
You could probably set current dir to c:\temp (or something). Since you
provide the full path to the DeleteFile() routine, it ought to work
even if current dir is c:\temp.
If that works, as I see it you can do it in either of two ways: 1) peel
off the extra path info from the end of your desired path (string
manipulation), or 2) set a temp variable before hand.
HTH
Q.
Maarten Wiltink
2003-07-25 13:52:48 UTC
Permalink
Post by RedBaron
Like Quivis said, try to set the current directory to 'c:\temp'. this should
work.
It should not. There is no such directory on _my_ computer.
Post by RedBaron
set
TEMP=C:\Tmp

Groetjes,
Maarten Wiltink
Quivis
2003-07-25 23:24:56 UTC
Permalink
In article <3f213631$0$49109$***@news.xs4all.nl>,
***@kittensandcats.net says...

8>< /snip
-> It should not. There is no such directory on _my_ computer.

Go easy on the beer, will you...


Q.
J French
2003-07-25 13:36:58 UTC
Permalink
Have you traced this code ?

if FileGetAttr(Pfn) = faDirectory then
deleted := RemoveDir(Pfn)
else
deleted := DeleteFile(Pfn);

I would test for :-

FileGetAttr(Pfn) And faDirectory = faDirectory

It is possible that your App or another App is doing something in one
of the directories
- have you tested a _really_ simple cut down version ?

Also ... how about a ShowMessage on each File/Dir and its attributes
before you decide to delete

- Win95/98 (incorrectly) does NOT set the UnArchived bit on new
files...
Post by Stephen Jackson
Having trouble deleting directories from a program that finds them
using the TSearchRec/FindFirst/FindNext method. The path is deletable
before the search and also after the program has closed (using an
hardcoded <DeleteFile> or the FileExplorer), but not after a search
whilst the program is still running. It looks like the directory is
locked somehow but the directory is not opened? The files are deleted
using <DeleteFile> (Dirs by <RemoveDir>). Note all directories fail
when being deleted. Curiously the program works fine on Win95/98. I
am working on XP with FAT32. Anybody else had this?
Here is the search routine, which finds files and stores in a
==========================================================================
procedure GetFileNamesInDir(var FileNames: TStringList;
const Dir, FileMask: string; const Attribute: Integer;
const Recursive: Boolean; const PathType: TmyPathType;
const ClearList: Boolean = True); overload; {2+}{11~}{16~}
{=smj=============================================================}
// Creates a list of FileNames from a Dir.
// Dir: Start point.
// FileMask: Accepts wildcards i.e. 'A*.*'.
// Attribute: faAnyFile, faDirectory etc. (see Delphi's FindFirst
function).
// Recursive: Does it look within Sub Dir's.
// PathType: Determines the amount FileName path is returned.
// ptNone: Just the FileName, no Path.
// ptPartial: Path + Filename, not including the Start Dir path.
// ptFull: Full qualified Path + Filename.
var
RootDirLen: Integer;
procedure FindFilesIn(const Dir: string);
{=smj===================================}
var
RelativeDir: string;
procedure SaveFileName(const FileName: string);
{=smj=========================================}
begin
case PathType of
ptNone: FileNames.Add(FileName);
ptPartial: FileNames.Add(PathConcat(RelativeDir, FileName));
ptFull: FileNames.Add(PathConcat(Dir, FileName));
end;
end;
var
Status: Integer;
SearchRec: TSearchRec;
function Valid(const Attribute: Integer): Boolean;
{=smj============================================}
begin
Result :=
(SearchRec.Name <> '.') and
(SearchRec.Name <> '..') and
((Attribute = faAnyFile) or
(SearchRec.Attr and Attribute > 0));
end;
begin
if PathType = ptPartial then
// Relative Path required, create. (More efficient this way).
RelativeDir := Copy(Dir, RootDirLen + 1, Length(Dir));
Status := FindFirst(PathConcat(Dir, [FileMask]), Attribute,
SearchRec);
try
repeat
while Status = 0 do begin
if Valid(Attribute) then {4+}
SaveFileName(SearchRec.Name);
Status := FindNext(SearchRec);
end;
if Recursive then begin
// Check Sub-Dirs. {11+}
Status := FindFirst(
PathConcat(Dir, ['*.*']), faDirectory, SearchRec);
while Status = 0 do begin
if Valid(faDirectory) then
FindFilesIn(PathConcat(Dir, [SearchRec.Name]));
Status := FindNext(SearchRec);
end;
end;
until Status <> 0;
finally
SysUtils.FindClose(SearchRec);
end;
end;
begin
if ClearList then {16~}
FileNames.Clear;
// Relative path pre-calculation.
if PathType = ptPartial then
if AnsiLastChar(Dir)^ = '\' then
RootDirLen := Length(Dir)
else
RootDirLen := Length(Dir) + 1;
FindFilesIn(Dir);
end;
================
procedure TfrmMain.bitDeleteClick(Sender: TObject);
{=smj=============================================}
var
deleted: Boolean;
i: Integer;
Pfn: string;
begin
for i := lsvHitlist.Items.Count - 1 downto 0 do begin
Pfn := lsvHitList.Items[i].Caption;
if FileGetAttr(Pfn) = faDirectory then
deleted := RemoveDir(Pfn)
else
deleted := DeleteFile(Pfn);
if deleted then
lsvHitlist.Items[i].Delete
else
lsvHitlist.Items[i].StateIndex := 1;
end;
UpdateUI();
end;
Helge Pretzlaff
2003-07-25 17:56:45 UTC
Permalink
Post by Stephen Jackson
Having trouble deleting directories from a program that finds them
using the TSearchRec/FindFirst/FindNext method. The path is deletable
before the search and also after the program has closed (using an
hardcoded <DeleteFile> or the FileExplorer), but not after a search
whilst the program is still running. It looks like the directory is
locked somehow but the directory is not opened? The files are deleted
using <DeleteFile> (Dirs by <RemoveDir>). Note all directories fail
when being deleted. Curiously the program works fine on Win95/98. I
am working on XP with FAT32. Anybody else had this?
Here is the search routine, which finds files and stores in a
(snip)

For each successful FindFirst() there should be a corresponding
FindClose(). You have two FindFirst() in your code and only one
FindClose(). I think the error is in the following part:

...
if Recursive then begin
// Check Sub-Dirs. {11+}
Status := FindFirst(
PathConcat(Dir, ['*.*']), faDirectory, SearchRec);
...

You are passing the same SearchRec to FindFirst() that was previously
used for scanning files but it was not closed by FindClose(). You are
losing resources here (one search handle) and the directory is locked
under NT/W2K/XP until your program ends.

HTH
Helge
Jeremy Collins
2003-07-28 15:56:00 UTC
Permalink
Everyone is welcome to a copy of the program if they want it. It's a
small application that hunts stale paradox lock files and allows you
to delete with one button click. Please email me for a copy of the
exe (zipped).
What logic do you use to determine "stale" (just curious)

--
jc
J French
2003-07-28 16:30:14 UTC
Permalink
On Mon, 28 Jul 2003 16:56:00 +0100, Jeremy Collins
Post by Jeremy Collins
Everyone is welcome to a copy of the program if they want it. It's a
small application that hunts stale paradox lock files and allows you
to delete with one button click. Please email me for a copy of the
exe (zipped).
What logic do you use to determine "stale" (just curious)
Probably Last Accessed Date ... (in its various forms)
Stephen Jackson
2003-07-30 09:32:53 UTC
Permalink
I use the term "Stale" as the lock files (which are hunted) are no
longer any good and are preventing Paradox files from being re-opened,
i.e. bin them and allow some new one to be created (just like "stale"
bread, chuck it and get some fresh).

Hope this explains my logic.

Steve. ;)
Stephen Jackson
2003-07-30 09:43:20 UTC
Permalink
Oops,

Just read your question again, in the right context this time.

"Stale" is determined simply by if the Lockfiles are deleteable. If
their "in use" then they are locked and <DeleteFile> will simply not
delete them, but even if non-stale locks are deleted Paradox
automatically re-creates them when it needs to. So this simple method
works, even when it deletes too many locks.

Background:
===========
When a Paradox Session is closed the lock file get deleted, in some
case when a application crashes the lock files are not deleted, any
other session will not be able to open the same files whilst the now
"stale" lock files exist. Usually restart the PC works, but not
always (especially when there in the User's temp area on XP).

Hope this answers the question (right this time),
Steve. ;)
Jeremy Collins
2003-07-30 10:27:46 UTC
Permalink
Post by Stephen Jackson
Oops,
Just read your question again, in the right context this time.
"Stale" is determined simply by if the Lockfiles are deleteable. If
their "in use" then they are locked and <DeleteFile> will simply not
delete them, but even if non-stale locks are deleted Paradox
automatically re-creates them when it needs to. So this simple method
works, even when it deletes too many locks.
That's what I suspected (similar to deleting old Access LDB files,
if they're not in use, delete 'em).

--
jc

Loading...