To access files and file information, you can
For example, the FilesList example uses the FindFirst , FindNext , and FindClose combination to retrieve from within a folder a list of files that match a filter, with the same code you could use on Kylix and Linux (an example of the output appears in Figure 3.5).
Figure 3.5: An example of the output of the FilesList application
The following code adds the filenames to a list box called lbFiles :
procedure TForm1.AddFilesToList(Filter, Folder: string; Recurse: Boolean); var sr: TSearchRec; begin if FindFirst (Folder + Filter, faAnyFile, sr) = 0 then repeat lbFiles.Items.Add (Folder + sr.
Name); until FindNext(sr) <> 0; FindClose(sr);
parameter is set, the
procedure gets a list of subfolders by examining the local files again, and then calls itself for each of the
procedure GetSubDirs (Folder: string; sList: TStringList); var sr: TSearchRec; begin if FindFirst (Folder + '*.*' , faDirectory, sr) = 0 then try repeat if (sr.Attr and faDirectory) = faDirectory then sList.Add (sr.Name); until FindNext(sr) <> 0; finally FindClose(sr); end; end;
Finally, the program uses an interesting technique to ask the
if SelectDirectory ('Choose Folder', '', CurrentDir) then ...
Figure 3.6: The dialog box of the SelectDirectory procedure, displayed by the FilesList application
As mentioned earlier, a key element of the System unit is the definition of the
class, which is the
mother of all Delphi classes
. Every class in the system inherits from the
class, either directly (if you specify
as the base class), implicitly (if you
(when you specify another class as the
of any class type in the system, according to the type compatibility rules covered in Chapter 2 in the section "Inheritance and Type Compatibility."
from TObject . The typical drawback of such an approach is that to work on the object, you need to know its data type. In fact, when you have a variable or a parameter of the TObject type, you can apply to it only the
You can also use another approach. For any object, you can call the methods defined in the
class. For example, the
method returns a string with the
Text := Button1.ClassName; Text := TButton.ClassName;
On some occasions you need to use the name of a class, but it can also be useful to retrieve a class reference to the class itself or to its base class. The class reference allows you to
Another method that might be useful is
, which returns the run-time
In Listing 3.1, you can find the complete definition of the TObject class, extracted from the System unit. In addition to the methods I've already mentioned, notice InheritsFrom , which provides a test that's similar to the is operator but that can also be applied to classes and class references (the first argument of is must be an object).
Listing 3.1: The definition of the TObject class (in the System RTL unit)
type TObject = class constructor Create; procedure Free; class function InitInstance(Instance: Pointer): TObject; procedure CleanupInstance; function ClassType: TClass; class function ClassName: ShortString; class function ClassNameIs( const Name: string): Boolean; class function ClassParent: TClass; class function ClassInfo: Pointer; class function InstanceSize: Longint; class function InheritsFrom(AClass: TClass): Boolean; class function MethodAddress( const Name: ShortString): Pointer; class function MethodName(Address: Pointer): ShortString; function FieldAddress( const Name: ShortString): Pointer; function GetInterface( const IID: TGUID; out Obj): Boolean; class function GetInterfaceEntry( const IID: TGUID): PInterfaceEntry; class function GetInterfaceTable: PInterfaceTable; function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; virtual; procedure AfterConstruction; virtual; procedure BeforeDestruction; virtual; procedure Dispatch( var Message); virtual; procedure DefaultHandler( var Message); virtual; class function NewInstance: TObject; virtual; procedure FreeInstance; virtual; destructor Destroy; virtual; end;
method returns a pointer to the internal run-time type information (RTTI) of the class, introduced in the
These methods of TObject are available for objects of every class, because TObject is the common ancestor class of every class. Here is how you can use these methods to access class information:
procedure TSenderForm.ShowSender(Sender: TObject); begin Memo1.Lines.Add ( 'Class Name: ' + Sender.ClassName); if Sender.ClassParent <> nil then Memo1.Lines.Add ( 'Parent Class: ' + Sender.ClassParent.ClassName); Memo1.Lines.Add ( 'Instance Size: ' + IntToStr (Sender.InstanceSize)); end;
The code checks to see whether the ClassParent is nil , in case you are using an instance of the TObject type, which has no base type.
method is part of the IfSender example. The method is connected with the
event of several controls: three
Figure 3.7: The output of the IfSender example
You can use other methods to perform tests. For example, you can check whether the Sender object is of a specific type with the following code:
if Sender.ClassType = TButton then ...
You can also check whether the Sender parameter corresponds to a given object, with this test:
if Sender = Button1 then ...
Instead of checking for a particular class or object, you'll
if Sender.InheritsFrom (TButton) then ... if Sender is TButton then ...
I've extended the IfSender example to show a complete list of base classes of a given object or class. Once you have a class reference you can add all of its base classes to the ListParent list box with the following code:
with ListParent.Items do begin Clear; while MyClass.ClassParent <> nil do begin MyClass := MyClass.ClassParent; Add (MyClass.ClassName); end; end;
You'll notice that I use a class reference at the heart of the while loop, which tests for the absence of a parent class (so that the current class is TObject ). Alternatively, I could have written the while statement in either of the following ways:
while not MyClass.ClassNameIs ( 'TObject' ) do ... while MyClass <> TObject do ...
The code in the with statement referring to the ListParent list box is part of the ClassInfo example, which displays the list of parent classes and some other information about a few components of the VCL (basically those on the Standard page of the Component Palette). These components are manually added to a dynamic array holding classes and declared as
private ClassArray: array of TClass;
When the program starts, the array is used to show all the class
Figure 3.8: The output of the ClassInfo example
As a further extension of this example, you can create a tree with all the base classes of the various components in a hierarchy. To do that, I've created the VclHierarchy wizard, discussed in Appendix A ("Extra Delphi Tools by the Author").