utomg
asked on
How to replicate dos command "DIR /S"
I need a complete listing of a network hard disk, including drive/path/filename. Long file names are on the disk. I want to put this in tstrings, or richedit and then I'll be able to get information about the files, but I can't find an easy API call to auto-recurse the directory tree. Do I have to write my own recursive procedure, or am I missing something easy?
ASKER
Thanks, Heathprovost --
My recursive proc looks a lot like yours! I was hoping, however, to
avoid that proc and find an existing function call for this information.
I guess there is none.
My recursive proc looks a lot like yours! I was hoping, however, to
avoid that proc and find an existing function call for this information.
I guess there is none.
hi utomg,
shellapi-unit must be added in the uses clause
the old dos can do it
procedure TForm1.Button1Click(Sender : TObject);
begin
Shellexecute(self.handle,N IL,Pchar(' Command.Co m'),PChar( '/C dir c:\ /s /b > c:\cdir.txt'),nil,SW_Hide) ;
richedit1.Lines.LoadFromFi le('C:\cdi r.txt');
end;
meikl
shellapi-unit must be added in the uses clause
the old dos can do it
procedure TForm1.Button1Click(Sender
begin
Shellexecute(self.handle,N
richedit1.Lines.LoadFromFi
end;
meikl
ASKER
Don't want to offend 'cuz I really appreciate the response, but I'm concerned about
using the dos shell command, and I'm really interested in creating the list as the
directories are parsed --- your method would need a temp "txt" file created that
will be far to large to deal with when perusing network drives. Also, will that have
the long file names?
I'm really trying to be able to have a complete directory listing created from
a network drive, but only the files that were created on or before a given
date. If I can find an easy "search" proc, I'll be able to test the condition
at each file name and then decide whether or not to add the filename to the list.
I guess I wasn't clear enough with my question. Please let me know
if you have any other ideas.
using the dos shell command, and I'm really interested in creating the list as the
directories are parsed --- your method would need a temp "txt" file created that
will be far to large to deal with when perusing network drives. Also, will that have
the long file names?
I'm really trying to be able to have a complete directory listing created from
a network drive, but only the files that were created on or before a given
date. If I can find an easy "search" proc, I'll be able to test the condition
at each file name and then decide whether or not to add the filename to the list.
I guess I wasn't clear enough with my question. Please let me know
if you have any other ideas.
You could use IShellFolder interface to enumerate all the files. But that would even be much more complicated. So you'll have to use something like Heath's code.
Regards, Madshi.
Regards, Madshi.
Download TASFindFile from http://www.bhnet.com.br/~simonet (AS Power Tools for Delphi 3 or AS OmniTools for Delphi 4). YOu ca select the root drive (C:\ for instance) and pass *.* as the search parameter. For each file found, an event will be triggered, giving you the file name + lots of file info. Then you can do whatever you want with the file.
How about this, probably what you already have but ill post anyway
procedure FillList(APath, AFile, ADate: String);
var
FSearchRec, DSearchRec: TSearchRec;
FindResult: integer;
ADateTime, BDateTime: TDateTime;
DosDate: Integer;
begin
ADateTime := StrToDate(ADate);
DosDate := DateTimeToFileDate(ADateTi me);
FindResult := FindFirst(APath+AFile, faAnyFile+faHidden+faSysFi le+faReadO nly, FSearchRec);
try
while (FindResult = 0) do
begin
if (fSearchRec.Time < DosDate) then
begin
BDateTime := FileDateToDateTime(fSearch Rec.Time);
Form1.RichEdit1.Lines.Add( APath+FSea rchRec.NAm e+#9+#9+Da teTimeToSt r(BDateTim e));
end;
FindResult := FindNext(FSearchRec);
end;
FindResult := FindFirst(APath+'*.*', faDirectory, DSearchRec);
while (FindResult = 0) do
begin
if ((DSearchRec.Attr and faDirectory) = faDirectory) and (pos('.', DSearchRec.Name) = 0) then
FillList(APath+DSearchRec. Name+'\', AFile, ADate);
FindResult := FindNext(DSearchRec);
end;
finally
FindCLose(FSearchRec);
end;
end;
Heath
procedure FillList(APath, AFile, ADate: String);
var
FSearchRec, DSearchRec: TSearchRec;
FindResult: integer;
ADateTime, BDateTime: TDateTime;
DosDate: Integer;
begin
ADateTime := StrToDate(ADate);
DosDate := DateTimeToFileDate(ADateTi
FindResult := FindFirst(APath+AFile, faAnyFile+faHidden+faSysFi
try
while (FindResult = 0) do
begin
if (fSearchRec.Time < DosDate) then
begin
BDateTime := FileDateToDateTime(fSearch
Form1.RichEdit1.Lines.Add(
end;
FindResult := FindNext(FSearchRec);
end;
FindResult := FindFirst(APath+'*.*', faDirectory, DSearchRec);
while (FindResult = 0) do
begin
if ((DSearchRec.Attr and faDirectory) = faDirectory) and (pos('.', DSearchRec.Name) = 0) then
FillList(APath+DSearchRec.
FindResult := FindNext(DSearchRec);
end;
finally
FindCLose(FSearchRec);
end;
end;
Heath
BTW - I am afraid using recursion is the best way you are going to find to do this. Is speed the problem? Are you loading into a RichEdit or a ListBox? I promise you that the recursive routine takes a FRACTION (like 1/1000th) of the time to run with a bare StringList. The problem comes is when you load the damn control with the data. That is what takes time, not the routine. A ListBox is a bit faster, but it isnt much better than a RichEdit Box. The best possible thing to do (if you really want it fast), is to use a DrawGrid for display and then hook it to the StringList you generate. Dont you a stringGrid as it has the same problem as the Other controls. A DrawGrid with your own onDraw event will be MUCH faster if that is your concern
I just tested my theory, the runtime for printing all files in the windows directory went from about 12 seconds to less then 1 on my computer with a DrawGrid. Let me know if you need code examples.
Heath
Heath
Took 9 seconds to do my whole harddrive- 32,767 files. Cant get much faster than that.
Hi heath,
what do you mean by "use a DrawGrid for display and then hook it to the StringList" ?
Thanks,
Itamar
what do you mean by "use a DrawGrid for display and then hook it to the StringList" ?
Thanks,
Itamar
Like this: (This is VERY rough but works for testing purposes)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, Grids;
type
TForm1 = class(TForm)
Button1: TButton;
DrawGrid1: TDrawGrid;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure DrawGrid1DrawCell(Sender: TObject; Col, Row: Integer;
Rect: TRect; State: TGridDrawState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
MyStringList: TStringList;
implementation
{$R *.DFM}
procedure FillList(APath, AFile, ADate: String);
var
FSearchRec, DSearchRec: TSearchRec;
FindResult: integer;
ADateTime, BDateTime: TDateTime;
DosDate: Integer;
begin
ADateTime := StrToDate(ADate);
DosDate := DateTimeToFileDate(ADateTi me);
FindResult := FindFirst(APath+AFile, faAnyFile+faHidden+faSysFi le+faReadO nly, FSearchRec);
try
while (FindResult = 0) do
begin
if (fSearchRec.Time < DosDate) then
begin
BDateTime := FileDateToDateTime(fSearch Rec.Time);
MyStringList.Add(APath+FSe archRec.Na me+#9+Date TimeToStr( BDateTime) );
end;
FindResult := FindNext(FSearchRec);
end;
FindResult := FindFirst(APath+'*.*', faDirectory, DSearchRec);
while (FindResult = 0) do
begin
if ((DSearchRec.Attr and faDirectory) = faDirectory) and (pos('.', DSearchRec.Name) = 0) then
FillList(APath+DSearchRec. Name+'\', AFile, ADate);
FindResult := FindNext(DSearchRec);
end;
finally
FindCLose(FSearchRec);
end;
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
MyStringList.Clear;
FillList('C:\', '*.*', '1/1/99');
DrawGrid1.RowCount := MYStringList.Count;
DrawGrid1.Refresh;
Label1.CAption := IntToStr(MyStringList.Coun t);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyStringList := TStringList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
MyStringList.Free;
end;
procedure TForm1.DrawGrid1DrawCell(S ender: TObject; Col, Row: Integer;
Rect: TRect; State: TGridDrawState);
begin
if MyStringList.Count > 0 then
DrawGrid1.Canvas.TextOut(R ect.Left, Rect.Top, MyStringList[Row]);
end;
end.
Just put a DrawGrid, Label, and button on the form. Set the column count of the drawgrid to 1 and to fixed col/row count to 0. Paste code over form code and run it.
Heath
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, Grids;
type
TForm1 = class(TForm)
Button1: TButton;
DrawGrid1: TDrawGrid;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure DrawGrid1DrawCell(Sender: TObject; Col, Row: Integer;
Rect: TRect; State: TGridDrawState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
MyStringList: TStringList;
implementation
{$R *.DFM}
procedure FillList(APath, AFile, ADate: String);
var
FSearchRec, DSearchRec: TSearchRec;
FindResult: integer;
ADateTime, BDateTime: TDateTime;
DosDate: Integer;
begin
ADateTime := StrToDate(ADate);
DosDate := DateTimeToFileDate(ADateTi
FindResult := FindFirst(APath+AFile, faAnyFile+faHidden+faSysFi
try
while (FindResult = 0) do
begin
if (fSearchRec.Time < DosDate) then
begin
BDateTime := FileDateToDateTime(fSearch
MyStringList.Add(APath+FSe
end;
FindResult := FindNext(FSearchRec);
end;
FindResult := FindFirst(APath+'*.*', faDirectory, DSearchRec);
while (FindResult = 0) do
begin
if ((DSearchRec.Attr and faDirectory) = faDirectory) and (pos('.', DSearchRec.Name) = 0) then
FillList(APath+DSearchRec.
FindResult := FindNext(DSearchRec);
end;
finally
FindCLose(FSearchRec);
end;
end;
procedure TForm1.Button1Click(Sender
begin
MyStringList.Clear;
FillList('C:\', '*.*', '1/1/99');
DrawGrid1.RowCount := MYStringList.Count;
DrawGrid1.Refresh;
Label1.CAption := IntToStr(MyStringList.Coun
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyStringList := TStringList.Create;
end;
procedure TForm1.FormDestroy(Sender:
begin
MyStringList.Free;
end;
procedure TForm1.DrawGrid1DrawCell(S
Rect: TRect; State: TGridDrawState);
begin
if MyStringList.Count > 0 then
DrawGrid1.Canvas.TextOut(R
end;
end.
Just put a DrawGrid, Label, and button on the form. Set the column count of the drawgrid to 1 and to fixed col/row count to 0. Paste code over form code and run it.
Heath
Hi utomg,
by reading the comments above, i will say, that your question was not clear enough, the date option was set on a later comment, and the temp.file can be deleted after reading in the richeditControl or redirected to a other path and you have all options of the dos-dir command available (by me long-filenames are included).
Well, now i know more about your question and heath has a good workaround done.
meikl
by reading the comments above, i will say, that your question was not clear enough, the date option was set on a later comment, and the temp.file can be deleted after reading in the richeditControl or redirected to a other path and you have all options of the dos-dir command available (by me long-filenames are included).
Well, now i know more about your question and heath has a good workaround done.
meikl
ASKER
once again I'm afraid I'm going to offend, but I was looking for an easy way to code the answer. turns out that the speed of the recursive procedure was acceptable, but like
an idiot I was posting each file my form --- it took 3 minutes to search c:\windows\*.*.
Just sending each answer to a tstringlist like heathprovost said works just fine. Since
I'm using Heath's answer, is there a way he can get the points?
Thanks to all that responded --- interesting thread to follow. I'm going to use the recursive procedure but will look at the drawcanvas route also.
an idiot I was posting each file my form --- it took 3 minutes to search c:\windows\*.*.
Just sending each answer to a tstringlist like heathprovost said works just fine. Since
I'm using Heath's answer, is there a way he can get the points?
Thanks to all that responded --- interesting thread to follow. I'm going to use the recursive procedure but will look at the drawcanvas route also.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks to all for the answers, Heath's was the best one for my purposes. The others worked fine.
ASKER
Thanks to all for the answers, Heath's was the best one for my purposes. The others worked fine.
procedure FillList(APath, AFile: String);
//APath is starting directory WITH trailing backslash, AFile is file mask *.*, *.EXE, etc.
//AList is a stringlist, but can be anything you want (listbox, etc.)
var
FSearchRec, DSearchRec: TSearchRec;
FindResult: integer;
begin
FindResult := FindFirst(APath+AFile, faAnyFile+faHidden+faSysFi
try
while (FindResult = 0) do
begin
AList.Add(APath+FSearchRec
FindResult := FindNext(FSearchRec);
end;
FindResult := FindFirst(APath+'*.*', faDirectory, DSearchRec);
while (FindResult = 0) do
begin
if ((DSearchRec.Attr and faDirectory) = faDirectory) and (pos('.', DSearchRec.Name) = 0) then
FillList(APath+DSearchRec.
FindResult := FindNext(DSearchRec);
end;
finally
FindCLose(FSearchRec);
end;
end;
Heath