Solved

How to replicate dos command "DIR /S"

Posted on 1999-01-29
17
153 Views
Last Modified: 2011-09-20
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?
0
Comment
Question by:utomg
  • 7
  • 5
  • 2
  • +3
17 Comments
 
LVL 5

Expert Comment

by:heathprovost
ID: 1363834
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
0
 

Author Comment

by:utomg
ID: 1363835
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.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 1363836
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
0
Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

 

Author Comment

by:utomg
ID: 1363837
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.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363838
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.
0
 
LVL 15

Expert Comment

by:simonet
ID: 1363839
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.
0
 
LVL 5

Expert Comment

by:heathprovost
ID: 1363840
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
0
 
LVL 5

Expert Comment

by:heathprovost
ID: 1363841
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
0
 
LVL 5

Expert Comment

by:heathprovost
ID: 1363842
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
0
 
LVL 5

Expert Comment

by:heathprovost
ID: 1363843
Took 9 seconds to do my whole harddrive- 32,767 files.  Cant get much faster than that.
0
 
LVL 4

Expert Comment

by:itamar
ID: 1363844
Hi heath,

what do you mean by "use a DrawGrid for display and then hook it to the StringList" ?

Thanks,
Itamar


0
 
LVL 5

Expert Comment

by:heathprovost
ID: 1363845
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


0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 1363846
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


0
 

Author Comment

by:utomg
ID: 1363847
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.
0
 
LVL 5

Accepted Solution

by:
heathprovost earned 50 total points
ID: 1363848
I am answering this question since you stated in you comment that you wanted to give me the points.  I will repost my last code here to that anyone looking through the PAQs will see it.

This is a VERY rough example of how to display a very large stringlist quickly.

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

0
 

Author Comment

by:utomg
ID: 1363849
Thanks to all for the answers, Heath's was the best one for my purposes.  The others worked fine.
0
 

Author Comment

by:utomg
ID: 1363850
Thanks to all for the answers, Heath's was the best one for my purposes.  The others worked fine.
0

Featured Post

PRTG Network Monitor: Intuitive Network Monitoring

Network Monitoring is essential to ensure that computer systems and network devices are running. Use PRTG to monitor LANs, servers, websites, applications and devices, bandwidth, virtual environments, remote systems, IoT, and many more. PRTG is easy to set up & use.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…

821 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question