Solved

How to replicate dos command "DIR /S"

Posted on 1999-01-29
17
151 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
 

Author Comment

by:utomg
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 5

Expert Comment

by:heathprovost
Comment Utility
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
Comment Utility
Took 9 seconds to do my whole harddrive- 32,767 files.  Cant get much faster than that.
0
 
LVL 4

Expert Comment

by:itamar
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Thanks to all for the answers, Heath's was the best one for my purposes.  The others worked fine.
0
 

Author Comment

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

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Firewall issues 6 84
Communication Between RC4 Delphi <-> PHP 3 85
Strange behavior when a form is closed 6 42
Magic Software info 18 100
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
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…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
This video discusses moving either the default database or any database to a new volume.

762 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

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now