Link to home
Start Free TrialLog in
Avatar of utomg
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?
Avatar of heathprovost
heathprovost
Flag of United States of America image

Here is one way to do it

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+faSysFile+faReadOnly, FSearchRec);
      try
            while (FindResult = 0) do
            begin
                  AList.Add(APath+FSearchRec.NAme);
                  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);
                  FindResult := FindNext(DSearchRec);
            end;
      finally
            FindCLose(FSearchRec);
      end;
end;


Heath
Avatar of utomg
utomg

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.
Avatar of kretzschmar
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,NIL,Pchar('Command.Com'),PChar('/C dir c:\ /s /b > c:\cdir.txt'),nil,SW_Hide);
  richedit1.Lines.LoadFromFile('C:\cdir.txt');
end;

meikl
Avatar of utomg

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.
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.
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(ADateTime);
  FindResult := FindFirst(APath+AFile, faAnyFile+faHidden+faSysFile+faReadOnly, FSearchRec);
  try
  while (FindResult = 0) do
  begin
    if (fSearchRec.Time < DosDate) then
    begin
              BDateTime := FileDateToDateTime(fSearchRec.Time);
      Form1.RichEdit1.Lines.Add(APath+FSearchRec.NAme+#9+#9+DateTimeToStr(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;

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
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


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(ADateTime);
  FindResult := FindFirst(APath+AFile, faAnyFile+faHidden+faSysFile+faReadOnly, FSearchRec);
  try
  while (FindResult = 0) do
  begin
    if (fSearchRec.Time < DosDate) then
    begin
              BDateTime := FileDateToDateTime(fSearchRec.Time);
      MyStringList.Add(APath+FSearchRec.Name+#9+DateTimeToStr(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.Count);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyStringList := TStringList.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MyStringList.Free;
end;

procedure TForm1.DrawGrid1DrawCell(Sender: TObject; Col, Row: Integer;
  Rect: TRect; State: TGridDrawState);
begin
  if MyStringList.Count > 0 then
  DrawGrid1.Canvas.TextOut(Rect.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


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


Avatar of utomg

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.
ASKER CERTIFIED SOLUTION
Avatar of heathprovost
heathprovost
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of utomg

ASKER

Thanks to all for the answers, Heath's was the best one for my purposes.  The others worked fine.
Avatar of utomg

ASKER

Thanks to all for the answers, Heath's was the best one for my purposes.  The others worked fine.