home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue52 / System / UFileSys.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1999-10-29  |  17.7 KB  |  550 lines

  1. unit UFileSys;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  7.   StdCtrls, Grids, ExtCtrls;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     DriveList: TComboBox;
  12.     Label1: TLabel;
  13.     VolSize: TLabel;
  14.     VolName: TLabel;
  15.     FSystem: TLabel;
  16.     SerNum: TLabel;
  17.     DrvType: TLabel;
  18.     FreeSp: TLabel;
  19.     Bevel1: TBevel;
  20.     FolderList: TComboBox;
  21.     Label2: TLabel;
  22.     Label16: TLabel;
  23.     CurFolder: TEdit;
  24.     GroupBox1: TGroupBox;
  25.     cbHidden: TCheckBox;
  26.     cbSystem: TCheckBox;
  27.     cbReadOnly: TCheckBox;
  28.     cbArchive: TCheckBox;
  29.     FileList: TListBox;
  30.     FileCount: TLabel;
  31.     TotFileSize: TLabel;
  32.     Button1: TButton;
  33.     procedure FormCreate(Sender: TObject);
  34.     procedure FormDestroy(Sender: TObject);
  35.     procedure DriveListChange(Sender: TObject);
  36.     procedure FolderListChange(Sender: TObject);
  37.     procedure CurFolderKeyPress(Sender: TObject; var Key: Char);
  38.     procedure cbReadOnlyClick(Sender: TObject);
  39.     procedure Button1Click(Sender: TObject);
  40.   private
  41.     { Private declarations }
  42.     DirSize: TLargeInteger;
  43.     function FormatBigBytes (const Msg: String; Value: TLargeInteger): String;
  44.     procedure UpdateFolderList (const FolderName: String);
  45.     procedure SumProc (const Name: String; const Info: TSearchRec; var Continue: Boolean);
  46.   public
  47.     { Public declarations }
  48.   end;
  49.  
  50. var
  51.   Form1: TForm1;
  52.  
  53. implementation
  54.  
  55. {$R *.DFM}
  56.  
  57. type
  58.     EFileSystem = class (Exception);
  59.  
  60.     TDriveType  = ( fsUnknown, fsNoRoot, fsRemovable, fsFixed, fsRemote, fsCDROM, fsRAMDisk );
  61.     TDriveTypes = set of TDriveType;
  62.  
  63.     TFileType   = ( ftReadOnly, ftHidden, ftSystem, ftArchive );
  64.     TFileTypes  = set of TFileType;
  65.  
  66.     TWalkProc = procedure (const Name: String; const Info: TSearchRec; var Continue: Boolean) of Object;
  67.  
  68.     TFileSystem = class (TComponent)
  69.     private
  70.         fDriveLetter: Char;
  71.         fSerialNumber: DWord;
  72.         fDriveType: TDriveType;
  73.         fDriveTypes: TDriveTypes;
  74.         fFileTypes: TFileTypes;
  75.         fFolders: TStringList;
  76.         fFiles: TStringList;
  77.         fFolderName: String;
  78.         fFileSystem, fDrives, fVolumeName: String;
  79.         fTotalSize, fTotalFileSize, fAvailableSpace, fFreeSpace: TLargeInteger;
  80.         procedure FileWalker (const Folder: String; Proc: TWalkProc; var Continue: Boolean);
  81.         procedure FolderWalker (const Folder: String; Proc: TWalkProc; var Continue: Boolean);
  82.         procedure InitDrivesList;
  83.         procedure RefreshFolderAndFileList;
  84.         function GetDriveCount: Integer;
  85.         function GetIsReady: Boolean;
  86.         function MatchingFile (Rec: TSearchRec): Boolean;
  87.         procedure SetDriveLetter (Value: Char);
  88.         function GetDriveChar (Index: Integer): Char;
  89.         function GetUsedSpace: TLargeInteger;
  90.         function GetSerialNumber: String;
  91.         procedure SetFolderName (Value: String);
  92.         procedure SetVolumeName (Value: String);
  93.         procedure SetDriveTypes (Value: TDriveTypes);
  94.         procedure SetFileTypes (Value: TFileTypes);
  95.     public
  96.         constructor Create (AOwner: TComponent); override;
  97.         destructor Destroy; override;
  98.         procedure Refresh;
  99.         procedure TreeWalkFiles (Proc: TWalkProc);
  100.         procedure TreeWalkFolders (Proc: TWalkProc);
  101.         class function DirectoryExists (const DirName: String): Boolean;
  102.         property Drives [Index: Integer]: Char read GetDriveChar;
  103.         property Folders: TStringList read fFolders;
  104.         property Files: TStringList read fFiles;
  105.     published
  106.         // File-specific stuff
  107.         property TotalFileSize: TLargeInteger read fTotalFileSize;
  108.         property FileTypes: TFileTypes read fFileTypes write SetFileTypes default [ftArchive];
  109.         // Folder-specific stuff
  110.         property FolderName: String read fFolderName write SetFolderName;
  111.         // Drive-specific stuff....
  112.         property DriveLetter: Char read fDriveLetter write SetDriveLetter;
  113.         property DriveType: TDriveType read fDriveType;
  114.         property IsReady: Boolean read GetIsReady;
  115.         property VolumeName: String read fVolumeName write SetVolumeName;
  116.         property FileSystem: String read fFileSystem;
  117.         property SerialNumber: String read GetSerialNumber;
  118.         property SerialNum: DWord read fSerialNumber;
  119.         property TotalSize: TLargeInteger read fTotalSize;
  120.         property FreeSpace: TLargeInteger read fFreeSpace;
  121.         property UsedSpace: TLargeInteger read GetUsedSpace;
  122.         property AvailableSpace: TLargeInteger read fAvailableSpace;
  123.         property DriveCount: Integer read GetDriveCount;
  124.         property DriveTypes: TDriveTypes read fDriveTypes write SetDriveTypes default [fsFixed];
  125.     end;
  126.  
  127. { TFileSystem }
  128.  
  129. constructor TFileSystem.Create (AOwner: TComponent);
  130. begin
  131.     Inherited Create (AOwner);
  132.     fFolders := TStringList.Create;
  133.     fFiles := TStringList.Create;
  134.     fFileTypes := [ftArchive];
  135.     fFolders.Sorted := True;
  136.     fFiles.Sorted := True;
  137.     SetDriveTypes ([fsFixed]);
  138. end;
  139.  
  140. destructor TFileSystem.Destroy;
  141. begin
  142.     fFolders.Free;
  143.     fFiles.Free;
  144.     Inherited Destroy;
  145. end;
  146.  
  147. function TFileSystem.GetDriveCount: Integer;
  148. begin
  149.     Result := Length (fDrives);
  150. end;
  151.  
  152. function TFileSystem.GetDriveChar (Index: Integer): Char;
  153. begin
  154.     Result := #0;
  155.     if (Index >= 0) and (Index < Length (fDrives)) then Result := fDrives [Index + 1];
  156. end;
  157.  
  158. procedure TFileSystem.InitDrivesList;
  159. var
  160.     p: PChar;
  161.     Buff: array [0..255] of Char;
  162. begin
  163.     fDrives := '';
  164.     GetLogicalDriveStrings (sizeof (Buff), Buff);
  165.     p := Buff;
  166.     while p^ <> #0 do begin
  167.         if TDriveType (GetDriveType (p)) in fDriveTypes then begin
  168.             fDrives := fDrives + UpperCase (p^);
  169.             // If this is the first, make it the current drive by default.
  170.             if Length (fDrives) = 1 then SetDriveLetter (p^);
  171.         end;
  172.  
  173.         Inc (p, 4);
  174.     end;
  175. end;
  176.  
  177. function TFileSystem.GetUsedSpace: TLargeInteger;
  178. begin
  179.     Result := fTotalSize - fFreeSpace;
  180. end;
  181.  
  182. function TFileSystem.GetSerialNumber: String;
  183. begin
  184.     // Precision specifier in the format string ensures that leading zeroes
  185.     // actrually get printed instead of being silently discarded....
  186.     Result := Format ('%.4x-%.4x', [HiWord (fSerialNumber), LoWord (fSerialNumber)]);
  187. end;
  188.  
  189. procedure TFileSystem.SetDriveLetter (Value: Char);
  190. begin
  191.     Value := UpCase (Value);
  192.     if (Value <> fDriveLetter) and (Pos (Value, fDrives) > 0) then begin
  193.         fDriveLetter := Value;
  194.         fDriveType := TDriveType (GetDriveType (PChar (Value + ':\')));
  195.         Refresh;
  196.     end;
  197. end;
  198.  
  199. function TFileSystem.GetIsReady: Boolean;
  200. var
  201.     errMode, FindErr: Integer;
  202.     SearchRec: TSearchRec;
  203. begin
  204.     Result := fDriveType in [fsFixed, fsRemote, fsRAMDisk];
  205.     if not Result then begin
  206.         errMode := SetErrorMode (sem_FailCriticalErrors);
  207.         try
  208.             FindErr := FindFirst (fDriveLetter + ':\', faAnyFile, SearchRec);
  209.             try
  210.                 Result := FindErr = 2;
  211.             finally
  212.                 FindClose (SearchRec);
  213.             end;
  214.         finally
  215.             SetErrorMode (errMode);
  216.         end;
  217.     end;
  218. end;
  219.  
  220. procedure TFileSystem.SetDriveTypes (Value: TDriveTypes);
  221. begin
  222.     if Value <> fDriveTypes then begin
  223.         fDriveTypes := Value;
  224.         InitDrivesList;
  225.     end;
  226. end;
  227.  
  228. procedure TFileSystem.Refresh;
  229. var
  230.     Junk: DWord;
  231.     szVolumeName, szFileSystem: array [0..255] of char;
  232. begin
  233.     // Initialise drive-information properties
  234.     if not GetIsReady then raise EFileSystem.Create ('Drive not ready');
  235.     GetDiskFreeSpaceEx (PChar (fDriveLetter + ':\'), fAvailableSpace, fTotalSize, @fFreeSpace);
  236.     GetVolumeInformation (PChar (fDriveLetter + ':\'), szVolumeName, sizeof (szVolumeName),
  237.                           @fSerialNumber, Junk, Junk, szFileSystem, sizeof (szFileSystem));
  238.     fVolumeName := szVolumeName;
  239.     fFileSystem := szFileSystem;
  240.     SetFolderName ('');
  241. end;
  242.  
  243. procedure TFileSystem.SetVolumeName (Value: String);
  244. begin
  245.     if GetIsReady and (fVolumeName <> Value) then begin
  246.         if Length (Value) > 11 then Value := Copy (Value, 1, 11);
  247.         SetVolumeLabel (PChar (fDriveLetter + ':\'), PChar (Value));
  248.         Refresh; // Ensure fVolumeName reflects reality.....
  249.     end;
  250. end;
  251.  
  252. procedure TFileSystem.SetFolderName (Value: String);
  253. var
  254.     Idx: Integer;
  255. begin
  256.     // Do the trivial stuff first....
  257.     if Value = '.' then Exit;
  258.     if Value = '' then Value := fDriveLetter + ':\';
  259.  
  260.     // Handle a request to go up one level
  261.     if Value = '..' then begin
  262.        if Length (fFolderName) = 3 then Exit; // Already at root
  263.        Idx := Length (fFolderName) - 1;
  264.        while fFolderName [Idx] <> '\' do Dec (Idx);
  265.        Value := Copy (fFolderName, 1, Idx);
  266.     end;
  267.  
  268.     // Handle a relative path (no leading drive letter or backslash)
  269.     if (Value [1] <> '\') and (Value [2] <> ':') then Value := fFolderName + Value;
  270.  
  271.     // Handle an absolute path (no leading drive letter)
  272.     if Value [1] = '\' then Value := fDriveLetter + ':' + Value;
  273.  
  274.     // Handle a path -- with drive letter
  275.     if Value [2] = ':' then begin
  276.         Value [1] := UpCase (Value [1]);
  277.         if Value [1] <> fDriveLetter then Exit;
  278.         if Value [3] <> '\' then Value := fFolderName + Copy (Value, 3, MaxInt);
  279.     end;
  280.  
  281.     // At this point, Value should be in the form X:\YYYYYY
  282.     // Now, we need to check that the wanted path exists
  283.     if not DirectoryExists (Value) then Exit;
  284.  
  285.     // Finally, set the new folder name and refresh folder list
  286.     if Value [Length (Value)] <> '\' then Value := Value + '\';
  287.     if AnsiLowerCaseFileName (Value) <> AnsiLowerCaseFileName (fFolderName) then begin
  288.         fFolderName := Value;
  289.         RefreshFolderAndFileList;
  290.     end;
  291. end;
  292.  
  293. procedure TFileSystem.SetFileTypes (Value: TFileTypes);
  294. begin
  295.     if Value <> fFileTypes then begin
  296.         fFileTypes := Value;
  297.         RefreshFolderAndFileList;
  298.     end;
  299. end;
  300.  
  301. class function TFileSystem.DirectoryExists (const DirName: String): Boolean;
  302. var
  303.     OldDir: String;
  304. begin
  305.     OldDir := GetCurrentDir;
  306.     try
  307.         Result := SetCurrentDir (DirName);
  308.     finally
  309.         SetCurrentDir (OldDir);
  310.     end;
  311. end;
  312.  
  313. function TFileSystem.MatchingFile (Rec: TSearchRec): Boolean;
  314. begin
  315.     Result := True;
  316.     // Read-only file ?
  317.     if ((Rec.Attr and faReadOnly) <> 0) and (ftReadOnly in fFileTypes) then Exit;
  318.     // Hidden-file ?
  319.     if ((Rec.Attr and faHidden) <> 0) and (ftHidden in fFileTypes) then Exit;
  320.     // System-file ?
  321.     if ((Rec.Attr and faSysFile) <> 0) and (ftSystem in fFileTypes) then Exit;
  322.     // Archive file ?
  323.     if ((Rec.Attr and faArchive) <> 0) and (ftArchive in fFileTypes) then Exit;
  324.     Result := Rec.Attr = 0;
  325. end;
  326.  
  327. procedure TFileSystem.RefreshFolderAndFileList;
  328. var
  329.     Err: Integer;
  330.     Rec: TSearchRec;
  331. begin
  332.     fFolders.Clear;  fFiles.Clear;  fTotalFileSize := 0;
  333.     Err := FindFirst (fFolderName + '*.*', faAnyFile, Rec);
  334.     try
  335.        while Err = 0 do begin
  336.            if (Rec.Attr and faDirectory) <> 0 then begin
  337.                // Ignore the accursed '.' and '..' names
  338.                if Rec.Name [1] <> '.' then fFolders.Add (Rec.Name);
  339.            end else if (Rec.Attr and faVolumeID) = 0 then
  340.                // Not a directory, not a volumeID - must be a file!
  341.                if MatchingFile (Rec) then begin
  342.                    fFiles.Add (Rec.Name);
  343.                    fTotalFileSize := fTotalFileSize + Rec.Size;
  344.                end;
  345.  
  346.            Err := FindNext (Rec);
  347.        end;
  348.     finally
  349.         FindClose (Rec);
  350.     end;
  351. end;
  352.  
  353. procedure TFileSystem.TreeWalkFiles (Proc: TWalkProc);
  354. var
  355.     Continue: Boolean;
  356. begin
  357.     Screen.Cursor := crHourGlass;
  358.     try
  359.         Continue := True;
  360.         if Assigned (Proc) then FileWalker (fFolderName, Proc, Continue);
  361.     finally
  362.         Screen.Cursor := crDefault;
  363.     end;
  364. end;
  365.  
  366. procedure TFileSystem.TreeWalkFolders (Proc: TWalkProc);
  367. var
  368.     Continue: Boolean;
  369. begin
  370.     Screen.Cursor := crHourGlass;
  371.     try
  372.         Continue := True;
  373.         if Assigned (Proc) then FolderWalker (fFolderName, Proc, Continue);
  374.     finally
  375.         Screen.Cursor := crDefault;
  376.     end;
  377. end;
  378.  
  379. procedure TFileSystem.FileWalker (const Folder: String; Proc: TWalkProc; var Continue: Boolean);
  380. var
  381.     Err: Integer;
  382.     Rec: TSearchRec;
  383. begin
  384.     Err := FindFirst (Folder + '*.*', faAnyFile, Rec);
  385.     try
  386.        while (Err = 0) and Continue do begin
  387.            if (Rec.Attr and faDirectory) <> 0 then begin
  388.                // Ignore the accursed '.' and '..' names
  389.                if Rec.Name [1] <> '.' then FileWalker (Folder + Rec.Name + '\', Proc, Continue);
  390.            end else if (Rec.Attr and faVolumeID) = 0 then
  391.                // Not a directory, not a volumeID - must be a file!
  392.                if MatchingFile (Rec) then begin
  393.                    Proc (Folder + Rec.Name, Rec, Continue);
  394.                end;
  395.  
  396.            Err := FindNext (Rec);
  397.        end;
  398.     finally
  399.         FindClose (Rec);
  400.     end;
  401. end;
  402.  
  403. procedure TFileSystem.FolderWalker (const Folder: String; Proc: TWalkProc; var Continue: Boolean);
  404. var
  405.     Err: Integer;
  406.     Rec: TSearchRec;
  407. begin
  408.     Err := FindFirst (Folder + '*.*', faAnyFile, Rec);
  409.     try
  410.        while (Err = 0) and Continue do begin
  411.            if (Rec.Attr and faDirectory) <> 0 then begin
  412.                // Ignore the accursed '.' and '..' names
  413.                if Rec.Name [1] <> '.' then FileWalker (Folder + Rec.Name + '\', Proc, Continue);
  414.                Proc (Folder + Rec.Name + '\', Rec, Continue);
  415.            end;
  416.            Err := FindNext (Rec);
  417.        end;
  418.     finally
  419.         FindClose (Rec);
  420.     end;
  421. end;
  422.  
  423. //--------- End of TFileSystem component ---------------------------------
  424.  
  425. var
  426.     FileSys: TFileSystem;
  427.  
  428. procedure TForm1.FormCreate (Sender: TObject);
  429. var
  430.     Idx: Integer;
  431. begin
  432.     FileSys := TFileSystem.Create (Self);
  433.     with FileSys do begin
  434.         DriveTypes := [ fsFixed, fsRemote, fsCDROM ];
  435.         for Idx := 0 to DriveCount - 1 do DriveList.Items.Add (Drives [Idx] + ':');
  436.         DriveList.ItemIndex := 0;
  437.         DriveListChange (Self);
  438.         // Set File attribute checkboxes according to current 'FileTypes'
  439.         cbReadOnly.Checked := ftReadOnly in FileTypes;
  440.         cbHidden.Checked   := ftHidden in FileTypes;
  441.         cbSystem.Checked   := ftSystem in FileTypes;
  442.         cbArchive.Checked  := ftArchive in FileTypes;
  443.     end;
  444. end;
  445.  
  446. procedure TForm1.FormDestroy(Sender: TObject);
  447. begin
  448.     FileSys.Free;
  449. end;
  450.  
  451. function TForm1.FormatBigBytes (const Msg: String; Value: TLargeInteger): String;
  452. var
  453.     Dbl: Double;
  454. begin
  455.      Dbl := Value;
  456.      Result := Format (Msg + ' %n', [Dbl]);
  457.      Result := Copy (Result, 1, Length (Result) - 3) + ' bytes';
  458. end;
  459.  
  460. procedure TForm1.DriveListChange(Sender: TObject);
  461. var
  462.     S: String;
  463.  
  464.     function StrDriveType (Typ: TDriveType): String;
  465.     begin
  466.         case Typ of
  467.           fsRemovable: Result := 'Removable';
  468.           fsFixed:     Result := 'Fixed    ';
  469.           fsRemote:    Result := 'Remote   ';
  470.           fsCDROM:     Result := 'CD-ROM   ';
  471.           fsRAMDisk:   Result := 'RAM-Disk ';
  472.           else         Result := '-unknown-';
  473.         end;
  474.     end;
  475.  
  476. begin
  477.     with FileSys do begin
  478.         // First, point TFileSystem object at the new drive
  479.         DriveLetter := DriveList.Text [1];
  480.         // Now display the various drive properties
  481.         VolSize.Caption := FormatBigBytes ('Total size of this drive is:', TotalSize);
  482.         S := VolumeName;  if S = '' then S := '[None]';
  483.         VolName.Caption := Format ('Volume label of this drive is: %s', [S]);
  484.         FSystem.Caption := Format ('File system of this drive is: %s', [FileSystem]);
  485.         SerNum.Caption := Format ('Serial number of this drive is: %s', [SerialNumber]);
  486.         DrvType.Caption := Format ('Type of this drive is: %s', [StrDriveType (DriveType)]);
  487.         FreeSp.Caption := FormatBigBytes ('Free space on this drive is:', FreeSpace);
  488.         UpdateFolderList ('');
  489.     end;
  490. end;
  491.  
  492. procedure TForm1.UpdateFolderList (const FolderName: String);
  493. begin
  494.     if FolderName <> '' then FileSys.FolderName := FolderName;
  495.     CurFolder.Text := FileSys.FolderName;
  496.     FolderList.Items.Assign (FileSys.Folders);
  497.     FolderList.ItemIndex := 0;
  498.     FileList.Items.Assign (FileSys.Files);
  499.     FileList.ItemIndex := 0;
  500.     FileCount.Caption := Format ('File count = %d', [FileList.Items.Count]);
  501.     TotFileSize.Caption := FormatBigBytes ('Total size of files is:', FileSys.TotalFileSize);
  502. end;
  503.  
  504. procedure TForm1.FolderListChange(Sender: TObject);
  505. begin
  506.     UpdateFolderList (FolderList.Text);
  507. end;
  508.  
  509. procedure TForm1.CurFolderKeyPress(Sender: TObject; var Key: Char);
  510. begin
  511.     if Key = #13 then UpdateFolderList (CurFolder.Text);
  512. end;
  513.  
  514.   //  TFileType   = ( ftReadOnly, ftHidden, ftSystem, ftArchive );
  515.   //                     1         2          4          8
  516.  
  517. procedure TForm1.cbReadOnlyClick(Sender: TObject);
  518. var
  519.    ft: TFileType;
  520. begin
  521.     with Sender as TCheckBox do begin
  522.         ft := TFileType (Tag);
  523.         if Checked then FileSys.FileTypes := FileSys.FileTypes + [ft]
  524.         else FileSys.FileTypes := FileSys.FileTypes - [ft];
  525.         FileList.Items.Assign (FileSys.Files);
  526.         FileList.ItemIndex := 0;
  527.         FileCount.Caption := Format ('File count = %d', [FileList.Items.Count]);
  528.         TotFileSize.Caption := FormatBigBytes ('Total size of files is:', FileSys.TotalFileSize);
  529.     end;
  530. end;
  531.  
  532. procedure TForm1.SumProc (const Name: String; const Info: TSearchRec; var Continue: Boolean);
  533. begin
  534.     DirSize := DirSize + Info.Size;
  535. end;
  536.  
  537. procedure TForm1.Button1Click(Sender: TObject);
  538. begin
  539.     DirSize := 0;
  540.     FileSys.TreeWalkFiles (SumProc);
  541.     ShowMessage (FormatBigBytes ('Total size of this directory is:', DirSize));
  542. end;
  543.  
  544. end.
  545.  
  546.  
  547.  
  548.  
  549.  
  550.