Delphi Tutorial
Table of Contents
Basic Delphi
Introduction
For this tutorial I assume that you are familiar with Pascal. If you are not, read one of the many Turbo Pascal tutorials first (search for "pascal tutorial" in Yahoo). It also helps to know the concepts of object oriented programing (OOP), but I'll briefly explain this.
Please excuse all the typos and other faults. English is not my native language.
Delphi
Delphi is a so called RAD-Tool (RAD stands for Rapid Application Development). It allows you to visually design (by using drag-and-drop) dialog and even whole applications. In the toolbar of the main window you can find a lot of predifined conponents to use on your projects. Simple double-click on the icons to paste them into the active form. On the left hand side of your screen is the object inspector. You can set properties for your components. There is no need to write source code for this! A good way to learn Delphi is to play around with the components.
A first example
- Choose File - New Application
This will create a empty form to which you can add all the components you want.- You can now add components from the component palette by double-clicking on them. To change their size select them, click on one of the corners and move the mouse.
As a first step add a button (sixth from the left in the "standard" palette). It'll be in the middle of the form. Move it any place you want. The button will have "button1" written on it which is not very usefull for your program. To change the text on the button (it is called caption), go to the object inspector and change the "Caption" field.- Now the user could click on the button in your program, but nothing would happen. To assign any actions to the button double-click on it. Delphi brings up the source code window. The cursor is already in the correct position.
- You can put any pascal code in the procedure. For this example simply type "close;". This closes the form and terminates the application.
- Your project is now ready to be compiled. Choose File - Save As to save it. I recommend you use different directories for all your projects because they tend to have many files (at least 5).
- To compile and execute the project click on the "play" button in the toolbar.
- You will see a window that can be resized and moved around the normal "windows" way. You can close it by either clicking your button, clicking on the "close"-button in the top right corner, or selecting close from the window menu (top left corner).
The IDE
When you start Delphi for the first time, you see a couple of windows. You will also notice that there is no parent window which contains all other (child) windows. Instead there is a main window (on top of the screen) that has a menu bar and the icons. It also offers a component palette (which will be discussed later). On the left side of the screen you can see the object inspector. It allows you to change the properties of the current component.
The components
You can find the components you will need most on the first three tabs in the component palette. I am not going to discuss all of them here, refer to the help file if you have specific questions (or mail me if you can't figure something out). Here is an overview over the most important components:
- MainMenu
Lets you add a standard menu bar to your form. To change the contents double-click on the icon on the form.- Label
Puts text on your form. The character in the caption with a "&" in front of it will be underlined. You can link another component such as TEdit to it by using the FocusControl Property. When the user pressen Alt+CHARACTER that component will be selected. Modify the appearence of the text with the "font" property.- Edit
Allows the user to input text. He can also use cut & paste to the Windows clipboard.- Memo
Multi-line Edit.- Button
Powerful properties are "default" and "cancel" (make button default- or cancel-button).- Checkbox and Radiobutton
Allow the user to switch them on and off. From a group of radiobuttons only one can be selected.- Listbox and Combobox
Display lists. User can select an entry by clicking on it. You can allow multiple selections, make them sorted, etc.- Image
Probably easiest way to add a bitmap to your form. Use the "picture" property.- Tab- and Pagecontrol
Let you put more components on a form than would normally fit by providing several layers. The user can switch between the layers by using the tabs on top of the control. You can use the page-editor to add pages at design time. This control is often used for option dialogs.To add a component to your form simply double-click on it. You can move it arround with the mouse and customize its properties in the object inspector. To start your program just press the "play" button in the main window.
Object Pascal
Object Pascal is the programming language you use in Delphi. It is mostly similar to Turbo Pascal, but Borland has added some features to it. I will deal with these later. Object Pascal is obviously an object oriented language. For those who don't know what this is I'll give a brief summary in the next section. Those who are familiar with OOP can skip it.
Object Oriented Programming (OOP)
The idea of OOP is to put both the data and the program in a single container. This container is called object. What you would noramlly declare like this:
var MyByte: Byte; Name: String; procedure DoSomething; function Whatever: Byte;Can be summed up to a single object. You specify an oject by using the "object"-directive:
type PMyObject = ^TObject; TMyObject = object MyByte: Byte; Name: String; procedure DoSomething; function Whatever: byte; end;Note that this does not declare the object you will later use. It merily provides a type (think of it as a "template"). You can use this template to create objects from it. To do this use:
var MyObject: PMyObject; begin MyObject:= TMyObject.Create; //... MyObject.Free; end."MyObject" is what you can work with. You also need to tell Delphi to create the object (create is called constructor). This will reserve memory form it. When you are done using it, you should free this memory using MyObject.Free; (free is destructor).
What makes objects powerful is that they can inherit variables and methods (constructors, destructors, functions and procedures) from other objects. If you need an object that is just the same as MyObject but has an additional FirstName: String variable, you can use inheritance to achieve this:
type TMySecondObject = object(TMyObject) FirstName: String; end;There is no need to reprogram all stuff that you already specified in TMyObject.
The object concept is taken from the Turbo Pascal days. The components in Delphi are all classes. Classes are very similar to objects. The main difference is in the declaration. Classes are always Pointers, you do not need to declare this any more. PMyObject as a class would look like this:
type TMyClass = class MyByte: Byte; Name: String; procedure DoSomething; function Whatever: byte; end;Inheritance works the same way as with objects. There are different sections in a class: private, protected, public, and published. Example:
type TMyClass = class private Password: String; procedure ModifyPassword; protected Username: String; public Phonenumber: String; end;
The password should not be visible from outside the object (this means only methods of the object can access it). However, a private member is not as private as you might think. Inside the unit in which the class is declared, it is accessible as well. (This is similar to the friend directive in C++.)
The protected directive somewhat relaxes the restrictions of private. Protected members are visible to all decendantes of the class.
Public members are literally public - they are accessible from anywhere (if the class is visible).
Published members behave the same as public ones. The difference is that for them runtime type information is generated. This means that outside applications can get information about the members (for details please refer to Delphi's online help system since this is not exactly something you need when getting started with Delphi).
Details on OOP
An object can inherit methods from its ancestors - very nice. But what do you do if you want to change the behavior of an object? Lets say you are writing a program with some graphical stuff and you have these objects:
TThing = Object // ... procedure draw; end; TBox = Object(TThing) // ... procedure draw; end; TCircle = Object(TThing) // ... procedure draw; end;TBox and TCircle are successors of TThing because this eases up drawing your graphics. You can just call AThing.Draw without knowing if AThing is really a TBox or a TCircle. However, you would never call the draw method of TThing - what should TThing draw?
Since TThing.Draw is not supposed to ever be called, you should declare it as an abstract method. When an abstract method is called the program exits with an exception. This makes it easy to find senseless method calls. The functionality of the Draw method is only implemented in the successor objects. They know what they look like and hence can draw something on the screen.
How does Delphi know which Draw method to call if you write AThing.Draw (and AThing is a TBox)? The answer is that you have to declare methods that you want to overwrite as virtual. Simply use the keyword virtual behind the method's declaration. To tell Delphi to overwrite a method in a successor object use the keyword override. Our objects now look like this:
TThing = Object // ... procedure draw; virtual; abstract; end; TBox = Object(TThing) // ... procedure draw; override; end; TCircle = Object(TThing) // ... procedure draw; override; end;How does Delphi find the correct method at runtime (when typecasting an object)? The answer lies in the VMT (Virtual Method Table). As you can guess from the name this is a table with all virtual methods of the object. There is a copy of the VMT created each time you inherit an object (this means that the single instances do not have there own copies of the VMT - would hardly make sense). When you call a virtual method it will be looked up in the VMT and the correct implementation will automatically be run (in case you overrode a virtual method from an ancestor object).
Dynamic methods are similar to the virtual ones. The difference is that the DMT (Dynamic Method Table) is completely not copied every time you instanciate the object. There are links back to precessor objects. When a dynamic method is called it is first looked up in the DMT of the object itself. If it is not found the DMT of the ancestors are searched. The saves the extra memory you need for copying the whole VMT every time you inherit. Using dynamic methods only makes sense in some special cases (objects with a lot of dynamic/vitual methods (>100) and about as many inheritance steps). In general you should stick to virtual ones.
Constants and Variables
Constants and Varibales are declared the same way as in Turbo Pascal:
const MyConstant = 100; MyOtherConst: real = 1.0; var MyVar: integer; MySet: set of byte;Note that (same as in Turbo Pascal) the variables are not initialized. You can however declare local typed constants (which you cannot do in standard Pascal):
procedure DoSomething; const MyLocalConstant: real= 1.0; begin //... end;Ordinal types in Object Pascal:
Name Range Memory ShortInt -128..127 1 Byte Byte 0..255 1 Byte Integer -32768..32768 4 Byte Word 0..65535 2 Byte Cardinal 0..65535 4 Byte Longint -2147483648..2147483647 4 Byte Currency +/-922337203685477.5807 4 Byte Floating-point types in Object Pascal:
Name Range Precision Memory Real 2.9*10^-39..1.7*10^38 11-12 digits 6 Byte Single 1.5*10^-45..3.4*10^38 7-8 digits 4 Byte Double 1.5*10^-324..1.7*10^308 15-16 digits 8 Byte Extended 5*10^-4932..1.1*10^4932 19-20 digits 10 Byte Comp -9.2*10^18..9.2*10^18 19-20 digits 8 Byte Strings:
In 32bit Delphi (2.0 and 3.0) you also have long string instead of the old pascal string that were limited to 255 characters. The "Huge-String" compiler option is on by default. You can turn it off on the Compiler-tab in the project options.
Many Windows functions use the PChar type. It is a pointer to a string that does not have a length byte at the beginning. To indicate the end of the string the #0-byte is used. To convert a Pascal string to a PChar use PChar(MyString).
Typecasting
Language elements
Most parts of Object Pascal are identical with normal Pascal. Please refer to a pascal documentation for this.
Exceptions
Exceptions are used to deal with special occurences such as errors. When you run your program in debug mode (that is from within Delphi), a dialog pops up every time an exception occurs. This is not the only way your program can react to exceptions.
To convert a string into an integer you can use the StrToInt(MyString: String) function. If you pass it 'xaslk' as MyString you will get an exception informing you that 'xaslk' is not an integer-value. It would be pretty annoying if your program crashed every time, the user passes an invalid value. To catch the exception yourself use:
procedure Button1Click(Sender: TObject); begin try strtotint('xaslk'); except showmessage('You did not pass a valid integer value. Please try again.'); exit; finally //... end; end;Everything behind except is only executed if an exception occurs. All the statements behind finally are always executed. You should free resources here etc.
Events
Windows programs are event-oriented. This means that the program waits for the user to do something and then reacts. If the user presses a button, the corresponding event is issued. Most VCL-components have at least one event property. Its name is usually made up of "On" plus a describtion of the event. Thus the event that occurs when the user clicks a button is called OnClick. It is a property of the TButton class.
You can control the events of the components in you form on the event-tab of the object-inspector. Select a button and look at its events. You will find that there are surprisingly many. OnClick is by far most important. Please refer to the Delphi help-file for a describtion of the events.
To assign an action to an event, simply double-click on the empty field to the right of the event's name. Delphi will open a procedure for you with the correct parameters and assign it to the event. The name of the procedure is made up of the component's name and the event (without the "on"). For Button1 and OnClick this would be Button1Click.
To assign a procedure to an event manually simply use:
Component.OnEvent:= MyEventprocedure;Where Component.OnEvent is an event-property (Button1.OnClick for example) and MyEventprocedure is the procedure to be assigned to the event. Be aware that the procedure needs to have to correct arguments, in this case it would look like this:
procedure MyEventprocedure(Sender: TObject);For other events this might be a lot different. The easiest way to find out what arguments an event-procedure needs is to double-click on the event and look at the template that Delphi makes for you.
A bigger example project
This a step-by-step guide to create a simple text editor in Delphi. In this example you will get to know some more components.
First, make a new application by choosing File - New Application. You will see a blank form and an almost empty source code window. As a first step create the main menu of the editor. To do this double-click on the MainMenu component form the "standard" tab. Now you have an blank, useless menu. To add any contents to it double-click on its icon. Delphi will open the menu editor. All you will see is an blue box in the top left corner. This is the first menu entry. To give it a name, go to the object inspector and change the "caption" property to "&File". Please note that the "&" is no typo but underline the character after it. This character can be used as a hotkey in your program. Click in the menu editor outside the blue box. The entry will be renamed to "File". Now you have an empty menu under it. Rename the first entry to "&Exit". Double-click on Exit to open the code that is executed when the user clicks on the menu-entry. Put "Close;" here. Press F9 to compile your program.
Now you can add more entries to the menu by using the above procedure. Name them "&New", "&Open", "&Save", "Save &As", and "&Print". You can put seperators between them by using "-" as caption.
Add a RichEdit component (Win32 palette) to your form. RichEdit is a component that allows the user to input multi-line text. He can also change attributes (such as bold, italic...) but we will not use this. The advantage of RichEdit over Memo is that RichEdit automatically provides scrollbars. Set the "Align" property of the RichEdit to alClient. This will ensure that it is always as big as the form. Next select the lines property. You will see a button with three dots. Click it to go to the line editor. Delete "RichEdit1".
To allow the user to select a file to open and a filename to save add a OpenDialog and a SaveDialog (Dialogs palette) to your form. Opening a file to the RichEdit is fairly easy. Go to the menu editor (double-click on the menu icon on your form), then double-click on open. To open a file into the RichEdit add the following code:
procedure TForm1.Open1Click(Sender: TObject); begin if opendialog1.execute then RichEdit1.Lines.LoadFromFile(Opendialog1.filename); end;Execute the program and try to load a file. If it is longer than one screen you will see that you don't get the scrollbars you expected. Exit the program, select the RichEdit, and look at the object inspector. You will find a property ScrollBars. It is set to ssNone by default. Set it to ssBoth. Start the program again. Now the RichEdit has always scrollbars which is not quite what we want. To hide the scrollbars if they are not needed set HideScrollBars to true.
As a next step we will add a filename and changed variable to the form. Filename is (obviously) there to contain the filename of the file that is currently edited. Changed is set to true if the user changed the file. Add the open procedure to:
procedure TForm1.Open1Click(Sender: TObject); begin if opendialog1.execute then begin RichEdit1.Lines.LoadFromFile(Opendialog1.filename); filename:= OpenDialog1.filename; changed:= false; end; end;And the TForm1 class to:
TForm1 = class(TForm) {components left out} procedure Exit1Click(Sender: TObject); procedure Open1Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } filename: String; changed: boolean; end;To trigger any changes to the RichEdit component you can use its OnChange event. Double-click on it to edit its source. Since the variable changed is declared in the same object as this procedure we can simply put:
procedure TForm1.RichEdit1Change(Sender: TObject); begin changed:= true; end;This will set changed to true every time the user changes anything. Next we will implement the save-feature. Go to the menu editor to edit the source associated with the "&Save" entry in the menu. The lines property of TRichEdit is a TStrings. This class has a lot of capabilities, including loading and saveing to and from files (look at the help file form more information).
procedure TForm1.Save1Click(Sender: TObject); begin if (filename<>'') and (changed= true) then begin RichEdit1.Lines.SaveToFile(Filename); changed:= false; filename := ''; end; end;Now the user can save the changes he made to the file. He still cannot save the file by a different name. Change the source:
procedure TForm1.SaveAs1Click(Sender: TObject); begin if SaveDialog1.Execute then begin RichEdit1.Lines.SaveToFile(SaveDialog1.Filename); filename:= SaveDialog1.Filename); changed:= false; end; end;You will probably notice that there are no file-types in the SaveDialog (there are none in the OpenDialog eiter). To change this, select the dialog and edit the filter property.
Implementing the new-function is faily easy. All you have to do is ask the user if he wants to save (only when the file was changed) and then to clear the RichEdit.
procedure TForm1.New1Click(Sender: TObject); begin if changed= true then begin if MessageDlg('Do you want to save the current file?', mtWarning,[mbYes, mbNo],0)= idYes then Save1Click(self); RichEdit1.Clear; end else RichEdit1.clear; end;Next is the printing procedure. Printing is very easy - thanks to RichEdit. Just call the TRichEdit.Print method. You can even pass a caption for the print job. We will use the filename for this. Printing prints to the standard printer.
procedure TForm1.Print1Click(Sender: TObject); begin Richedit1.print(filename); end;![]()
Lets provide the standard editing functions next. Make a new top-level menu called "&Edit" with submenus "Cu&t", "&Copy", "&Paste" and "C&lear". All these functions are very easy to implement because TRichEdit provides all of them. All you have to do is connect the RichEdit functions with your menu:
procedure TForm1.Cut1Click(Sender: TObject); begin RichEdit1.CutToClipboard; end; procedure TForm1.Copy1Click(Sender: TObject); begin RichEdit1.CopyToClipboard; end; procedure TForm1.Paste1Click(Sender: TObject); begin RichEdit1.PasteFromClipboard; end; procedure TForm1.Clear1Click(Sender: TObject); begin RichEdit1.Clear; end;Now we are about as good as the standard Windows notepad. Didn't take very long, huh?
Let's add a couple of additional features to the editor. First a "&Options" menu with a "&Font" submenu. Luckily we don't need to write the dialog to choose the font. Delphi/Windows provides it for us. The component is located in the "dialogs" palette. The source to use it looks like this:
procedure TForm1.Font1Click(Sender: TObject); begin if FontDialog1.Execute then RichEdit1.Font:= FontDialog1.Font; end;Improving the example
In most Windows programs the most important functions can not only be reached through the menu but also via hotkeys and a toolbar. The hotkeys are very easy. Just set the "shortcut" property of a menu entry to anything you want. To get a toolbar in your form you need to add a ToolBar component from the Win32 palette. By right-clicking on it you can add new switches. The images on the switches are provided by a component called ImageList. You can find it in the Win32 palette as well. Add one ImageList to your form. To add images to the ImageList double-click on it and choose add. First you need to create a couple of images (for the buttons: new, open, save, print, font). Use the picture editor (tools menu in Delphi). Choose New - Bitmap. Make it 20x20 in size. Save the bitmaps in the same directory as your project. Add all the bitmaps to the ImageList. Set the "Images" property of the toolbar to your ImageList (ImageList1). Set the ImageIndex of each toolbutton to the correct value.
To Connect the toolbutton to the functions you already wrote, simply double-click on the toolbuttons. Call your functions from there. For the open-button this looks like this:
procedure TForm1.ToolButton2Click(Sender: TObject); begin Open1Click(self); end;Self is a pointer to the object of which self is a property. Think of it as a loop: it point back to itself (TForm1 in the example). This might sound weird at first, but turns out to be very useful for cases like this one.
In real applications menu entries that do not make sense at the time are grayed out. We should do this with Save. This is easy to achieve. Every time the file is changed, it would make sense to save it. So we have to enable Save after changed:= true and to disable it after changed:= false. Just put in a line saying
Save1.enabled:= false;after every changed:= false. (Save1.enabled:= true after changed:= true) and set enabled to false in the object inspector.
Currently the save as-function simply overwrites existing files. To add a dialog box that asks the user whether he wants to overwrite an existing file, you first have to check if a file with that name already exists. Use FileExists to do this:
procedure TForm1.SaveAs1Click(Sender: TObject); begin if SaveDialog1.Execute then if FileExists(SaveDialog1.Filename) then begin if MessageDlg('Do you want to overwrite the existing file?', mtWarning,[mbYes, mbNo],0)= idYes then begin RichEdit1.Lines.SaveToFile(SaveDialog1.Filename); filename:= SaveDialog1.Filename; changed:= false; end; end else begin RichEdit1.Lines.SaveToFile(SaveDialog1.Filename); filename:= SaveDialog1.Filename; changed:= false; end; end;First, the SaveDialog is shown. If the user inputs a filename and clicks yes, the program checks if a file with that name already exists. If so the user is asked whether he wants to overwrite the file. MessageDlg is passed the text in the dialog box, a "kind" of box (mtWarning in this case), and a combination of buttons to show. It returns the ID of the button that was clicked (idYes).
There is no much use in the possibility to change a few options in the program if the user has to do it all over when he starts the program again. What you obviously need here is a way to save the options. Bill Gates wants you to save them to the Windows registry. If you don't want this you are supposed to at least put your config file in the c:\windows directory. Now, please go to Explorer, change to the c:\windows directory and try to name the applications that created all the files that are in it (I have about 400 even so I try to clean it occasionally). My point is: Do NOT write anything to the registry (look at it with regedit.exe, its huge as well) or any directory that does not belong to your program unless you have a special reason for doing so. What harm does it do if your program saves its configuration to its own directory? If the user wants to delete the program he can simply delete the directory and be sure that everything is gone and that there are no side-effects.
Delphi provides a class that helps you to deal with configuration files (called Ini-files in Windows). The class is called TIniFile and encapsulates all the functionality you usually need to save options to a file. It provides reading and writing of string, integer, booleans and also organized your ini-file in sections.
When your program exits you want to save the options. The TForm.OnClose event occurs whenever a form is closed. trigger it and put in the following code:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var AIniFile: TIniFile; begin AIniFile:= TIniFile.Create(IniDir+'\'+'editor.ini'); with AIniFile do begin WriteString('Config', 'FontName', RichEdit1.Font.Name); WriteInteger('Config', 'FontSize', RichEdit1.Font.Size); WriteInteger('Config', 'Left', Left); WriteInteger('Config', 'Top', Top); WriteInteger('Config', 'Width', width); WriteInteger('Config', 'Height', height); end; AIniFile.Free; end;You also need to add a IniDir: String variable to the TForm1 class. It is used to save the directory that was current directory when the program was started (the user could change this). As you see from the source, the AIniFile needs to be initialized first using TIniFile.create(PATHNAME);. Then the options are written to the file. Finally AIniFile is released again.
Reading the options is somewhat more difficult because you cannot just trigger the OnCreate event and then read and set the options. When TForm1.OnCreate occurs, RichEdit is not created yet. You need to delay the process. Add a timer component from the "system" palette to your form. Set its interval property to 5 (it is measured in milliseconds). The component only has one event: OnTime (occurs when the interval is over). Put the following source in its procedure:
procedure TForm1.Timer1Timer(Sender: TObject); var AIniFile: TIniFile; begin AIniFile:= TIniFile.Create(getcurrentdir+'\'+'editor.ini'); IniDir:= getcurrentDir; with AIniFile do begin RichEdit1.Font.Name:= ReadString('Config', 'FontName', 'Courier New'); RichEdit1.Font.Size:= ReadInteger('Config', 'FontSize', 10); left:= ReadInteger('Config', 'Left', 0); top:= ReadInteger('Config', 'Top', 0); width:= ReadInteger('Config', 'width', 0); if width= 0 then width:= screen.width - 50; height:= ReadInteger('Config', 'Height', 0); if height= 0 then height:= screen.height - 50; end; AIniFile.Free; Timer1.Interval:= 0; Timer1.Free; Timer1:= Nil; end;First the AIniFile is initialized. The file is stored in the current directory (determined with GetCurrentDir) and called 'editor.ini'. Then the current directory is saved in the IniDir variable. The options are read. As a last step the inteval of the timer is set to 0 so no more OnTimer events will occur. Since we don't need the timer any more we can free it. It helps a lot to set objects you freed to Nil. If you want to find out later if the Object is initialized you can simply check for MyObject<>Nil.
Help files
A real application usually includes a help file as well. If you want to create one for your program please refer to other documentation. It would be off-topic to discuss this here.
Digging deeper
Threads
Threads are parts of a program that can be executed "simultaniously". The are assigned running time by the operating system. Usually a Delphi project has just one thread (it is created without you knowing about it). This is the main thread. When it is terminated the whole program quits. Threads are useful if you want to perform time consuming tasks in the background, while the user can go on using your program. This was somewhat hard to achieve in 16-bit Windows. 32-bit Windows now provides threads which free the programmer of the co-ordiantion overhead. I will not create a sample project for threads but just discuss the one that comes with Delphi. You can find it in the Delphi 3\Demos\Thread directory. Load it a run it to see what it does.
Now take a look at the source, especially the SortThds unit. In the type block you will find class called TSortThread. It inherits from TThread which is a class provided with Delphi. It encapsulates the Windows Thread object, so you don't need to make Windows function calls.
TSortThread = class(TThread) private FBox: TPaintBox; FSortArray: PSortArray; FSize: Integer; FA, FB, FI, FJ: Integer; procedure DoVisualSwap; protected procedure Execute; override; procedure VisualSwap(A, B, I, J: Integer); procedure Sort(var A: array of Integer); virtual; abstract; public constructor Create(Box: TPaintBox; var SortArray: array of Integer); end;The most important method is the execute procedure. It is called when the thread starts and performs the calculations (or calls the functions that perform the calculations). Further down are more thread classed which define the type of sorting to be done. These are the ones that are used by the program. The purpose of TSortThread is to provide common functionality to all the different sorting-threads.
If you have more than one thread running (this is always the case when you create a thread yourself), there is danger that both threads want to access data simultaniously. This would result in a collision with unpredictable results. To avoid this the TThread class a a method called synchronize. It synchronizes the activities of the threads. It is passed a procedure as a parameter that is to be synchronized. In the example program VisualSwap has all the critical procedures in it. Thus the threads call
Synchronize(DoVisualSwap);to synchronize. The parameters for VisualSwap are contained in the object because you cannot pass Synchronize a procedure with parameters.
Now take a look at the other unit: ThSort. The StartBtnClick procedure initializes the Threads:
procedure TThreadSortForm.StartBtnClick(Sender: TObject); begin RandomizeArrays; ThreadsRunning := 3; with TBubbleSort.Create(BubbleSortBox, BubbleSortArray) do OnTerminate := ThreadDone; with TSelectionSort.Create(SelectionSortBox, SelectionSortArray) do OnTerminate := ThreadDone; with TQuickSort.Create(QuickSortBox, QuickSortArray) do OnTerminate := ThreadDone; StartBtn.Enabled := False; end;You do not have to do it the way it is done here. You could also use:
//... var MyThread: TBubbleSort; //... begin MyThread:= TBubbleSort.Create(BubbleSortBox, BubbleSortArray); MyThread.OnTerminate:= ThreadDone; //...This is only useful if you need the pointer to the thread later on. You need this pointer to change the priority of the Thread in the course of the program. Each thread has a priority property that you can set to six values (taken from the help file):
TThreadPriority = (tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical);You can change it whenever you like.
Using DLLs
Note: This is just a very basic introduction. If you need more details please refer to the Dephi help system. There is a good series of articles on it.
A DLL is - like an executable - a compiled program module. However, it cannot be run by itself but rather is a collection of functions that are called by outside programs. The abbriviation DLL stands for Dynamic Link Library. It is not compiled into the EXE-file at compile-time but linked into it at run-time.
Because DLLs are independend of the programming language you use (well, almost) there are a couple of things you should be aware of:
- To call a DLL function the calling program has to know the name of the function. This is a problem with some C++ compilers who add type information to the function's name.
- The types of the parameters have to be identical (or at least compatible) in both languages. If you want to call a DLL function that uses the C type int as a Parameter, you have to make it an integer in your Pascal program.
- The calling program and the DLL have to use the same calling conventions. This means that the parameters have to be passed in the same order.
Importing functions - a simple example
There is a Windows DLL called USER32.DLL which includes the function MessageBox. To use this function in your program you have to tell the compiler to import it from the DLL:
function MessageBox(HWnd: Integer; Text, Caption: PChar; Flags: Integer): Integer; stdcall; external 'user32.dll' name 'MessageBoxA';Open the file Win32.hlp in your Delphi\Help director and take a look at the description of the function. You will see how the parameters are matched by Delphi types. The word "stdcall" after the normal declaration of the function causes the compiler to use C calling conventions. "external" tells the compiler not to look for the function in a unit but rather the specified outside file (a DLL in this case). After external you pass the name of the DLL. Please note that it has to be in the current, \Windows, or \Windows\System directory. If the name you use in Pacal is different form the name of the function in the DLL you have to specify the real name of the function. Please be aware that C is case sensitive and that your function name in Pascal has to be in the correct case if you don't use the name directive.
Writing DLLs in Delphi
Writing your own DLLs in Delphi is very easy. There are only a few things you have to consider, which will probably become clear in this example:
library Test; uses MyUnit; exports MyFunction name 'MyFunction'; begin end.Of course this only works if MyFunction is declared in MyUnit.
Books on Delphi
A very good German book about Delphi is "Delphi 3" by Elmar Warken (ISBN 3-8273-1238-8).
Algorithms
You can find information on specific algorithms on this site as well. Look in the "texts and articles" section on the Delphi main page.
In addition, a good book on algorithms is "Algorithms" by Robert Sedgewick. I don't know the ISBN number for the English version
Please e-mail me with any comments!
© 17.5.98 Sebastian Boßung
Home