Solved

How to replicate dos command "DIR /S"

Posted on 1999-01-29
17
157 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

Suggested Solutions

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…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…
In a recent question (https://www.experts-exchange.com/questions/29004105/Run-AutoHotkey-script-directly-from-Notepad.html) here at Experts Exchange, a member asked how to run an AutoHotkey script (.AHK) directly from Notepad++ (aka NPP). This video…

738 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