A few weeks before February 14th, 2005, we emailed Anders Hejslberg to arrange an interview for Delphi's 10th Anniversary on February 14th, 2005. One of his replies was an email with some historic attachments. Here's the email:
Imagine that, 10 years. I still remember the Valentine's Day launch in San Francisco. It was an awesome moment. You don't get a standing ovation from several thousand people every day--boy, did that feel great.
Over the years several Microsoft folks that attended the launch have told me how they were constantly hearing oohs and aahs from people around them, and how they were completely floored when I forced a GP fault in the demo, but the app kept running. Good stuff!
I found the script and all the demo files I used in my launch talk. They are all in the attached .zip file. The Athena bust shown in the little "speed is" clip now sits in my home office.
Here is the text version of the demo script in HTML. You can also download the original Word Document from CodeCentral.
Code Sync / Two-Way Tool
Delphi is a "two-way" tool. When you do things visually, Delphi automatically keeps your code in sync. You can switch between code and visual editing at any time. Let's start a blank project.
Resize form to be above editor.
Delete private/public sections and comments.
First, let me explain that in Delphi, for every form there's an associated module, or unit, that contains the form's code. You can of course have code modules without forms. In a form's code module, there's a class declaration for the form. As you can see, forms you create are actually subclasses of a generic form. Now, as I drop components onto the form, they show up as instance variables in my class declaration.
Drop TButton, TButton, TEdit, TCheckBox.
And when I rename them, the code is kept in sync.
Rename to OkButton, CancelButton, FirstName, UpToDate.
If I double-click on a button, the system automatically adds a member function to my class.
Double-click on first button.
Add "Caption := 'Hello';"
When I scroll back, you can see header in the class declaration.
Scroll back to class declaration.
Now, what's really important is that Delphi isn't just a one-shot code generator with "__\\ don't delete me" kinds of comments. Instead, Delphi is actually constantly analyzing your code as you work with it, so that it knows where to make its surgical incisions. And any changes you make to the code, Delphi automatically recognizes. For example
Go to events page in object inspector.
Drop down event list for OnClick.
Here you see that the only available event handler for
OnClick is the
OkButtonClick method we just wrote. Now, if I go to the editor and add
procedure HelloWorld(Sender: TObject);
And drop the list down again, you'll see that Delphi picked up on it. But if I change the declaration to take an extra parameter
string; Sender: TObject);
it disappears because it is no longer compatible with the
But Delphi doesn't just understand your code. It can also convert objects to code. For example, let's select the OkButton, and cut it to the clipboard.
And now, let's paste it in the editor.
Scroll to end.
Here you see a textual representation of the button and its properties. Now let's change it around a bit.
Change "OkButton: TButton" to "OkButton: TRadioButton"
Font.Size = 14
Font.Color = clRed
Now let's move it back into the form
There's our button, which is now a radio button with a different font. And if we look at the code, we'll see that the class declaration has been updated to reflect the new type of the button.
Scroll to form class declaration.
All of this code sync is managed by a highly fault-tolerant background compiler that can analyze code even if it isn't syntactically correct. For example, if I add some garbage characters
Add '*&*&^*&^*&^' just above type declaration.
You'll see that I get a syntax error if I try to run.
But the background compiler can still analyze the code.
Drop new button.
Notice class declaration is updated.
Delete button again.
The two-way tool and code sync features of Delphi are important because.
- You're never playing the "where's the code" game that other tools suffer from, because all your code is in one module, not in little boxes.
- Your code doesn't have to be syntactically correct until you run the application. Other tools force you to correct your code before you can leave a code box.
- You can leave the Delphi environment and take your code with you, so to speak. For example, some programmers may have a favorite editor that they want to use when doing big editing sessions.
- Everything you do visually can be represented as text. For team development this is an invaluable feature because you can do source code merges even on your form files.
The next thing I wanted to talk about is exception handling. Even though this is not a visual feature, it is probably one of the most important aspects of Delphi, particularly when it comes to writing database and C/S apps.
For those of you who aren't familiar with exceptions, they are the structured and object-oriented equivalent of
ON ERROR GOTO found in some languages.
But exceptions go way further than that.
And Delphi was engineered from the ground up with exception handling built in.
Whenever something goes wrong in an app, an exception is raised. As a result of an exception, components automatically clean themselves up, and back out of whatever operation failed.
This all happens without any programmer intervention.
As a result, Delphi apps automatically have error detection and correction built-in, without you having to worry about it.
I'm sure you've heard something like this before: "Yeah, this app is great, but boy when something goes wrong you get GP faults all over the place".
Well not with Delphi.
In fact, speaking of GP faults, let me show you what happens in Delphi.
Drop button on form.
procedure TForm1.Button1Click(Sender: TObject);
P^ := 0;
And let's run.
As you can see, a GP fault occurred, and Delphi's default exception handler caught it and is showing an error dialog.
But notice that the app is still running.
Click button again.
Delphi has a bunch of built-in exception classes, and you can also create your own. And you can write exception handlers. For example:
Modify code to read:
procedure TForm1.Button1Click(Sender: TObject);
P^ := 0;
do Caption :=
'I say, that pointer is a little off!';
As in C++, Delphi exceptions are classes. Let me show you the hierarchy.
As you can see, the hierarchy is:
I could change my handler to handle
EFault, in which case I would also catch stack faults, etc.
Creating a New Component
Here I'll show how to create a new component.
Class name: TRunButton.
Ancestor type: TSpeedButton.
This automatically generates a module that implements the new class. The comments indicate where we can implement private, protected, public, and public fields. The call to the
Register procedure registers the component. First we'll add a new field,
FCommand, which holds the command we want executed when the button is pressed.
Private "FCommand: string;"
Next, we'll add a property.
Published "property Command: string read FCommand write FCommand;"
The property is in the published section so that run-time type information is automatically generated for it. In the property declaration we specify that to read it we'll get the value of the
FCommand field, and to write it we'll set the value of the
FCommand field. We could have used member functions here, to associate actions with reading and writing the property. Or we could have made the property read only or write only by only having a read or a write clause.
Next we'll override the button's Click method.
Public "procedure Click; override;" <--->
And we'll implement the method.
In implementation section add:
WinExec(StrPCopy(Buffer, FCommand), SW_SHOW);
We're calling the "WinExec" function in the Windows API. All Windows API functions are directly accessible in Delphi. You don't have to first come up with a declaration that specifies parameters, DLL names, and so on.
And that's it! We now have a functional component.
File|Save File As...
File name: "runbtns"
And let's install it into Delphi.
File name: "runbtns"
Now if we go to the Samples page, there's our new component. Notice that it inherited the icon of its ancestor class. Let's test it out.
Drop a TRunButton on the form.
Make it bigger.
Load a glyph (MSDOS.BMP).
Set Command property to "c:\command.com"
And now, when we click, here's a DOS box.
Now let me show you a little project I built using the TRunButton class.
This form has eight TRunButtons on it, which launch apps that I use a lot. Let me try and run it.
As you can see, it stays on top of all other windows. And we have little fly-by hints that explain the icons.
Launch various apps
Joke about EDLIN
Click on Delphi button to get back
Now the neat thing about this app is that there's no code in it.
Zoom editor window to show no code.
It was built entirely by tweaking properties and using the
TRunButton component we just put together. As you might imagine, this'll help us get a 32-bit version of Dashboard ready real soon!
Dynamic Component Creation / Delegation
One of the truly powerful features of Delphi is that everything you can do visually you can also do in code. This means that you can dynamically create forms and controls. For example, I have a little database browser app here:
Let's run it.
The browser allows me to pick tables in a database and view and modify them.
Close the app.
Zoom the editor.
As you can see, this app only has about five lines of code.
Now, let's say I want to dynamically create data entry form for the table I'm looking at. I obviously don't know what's in the table, so I'll have to create the entry form on the fly. So let me add some code to do it.
First, I'll declare a private instance variable called
In TTableForm class declaration add:
Next, I'll change the
TableNameClick method which gets executed when the user picks a new table.
Modify TTableForm.TableNameClick method:
procedure TTableForm.TableNameClick(Sender: TObject);
Table.TableName := TableName.Text;
EntryForm := TForm.Create(Self);
EntryForm.Font := Font;
EntryForm.Caption := Table.TableName;
for I := 0
to Table.FieldCount - 1
Parent := EntryForm;
Caption := Table.Fields[I].FieldName;
Alignment := taRightJustify;
SetBounds(0, I * 25 + 11, 80, 20);
Parent := EntryForm;
DataSource := TableForm.DataSource;
DataField := Table.Fields[I].FieldName;
SetBounds(88, I * 25 + 8, Table.Fields[I].DisplayWidth * 7, 20);
EntryForm.Height := Table.FieldCount * 25 + 40;
Now, let's run.
And when I pick a table, a dynamic entry form shows up.
Now, one thing you'll note is that the two forms are synchronized because the data aware controls refer to the same data source.
Scroll up and down.
Notice both forms changing.
Even if I insert a new record
Click on grid.
Press Ins key.
they both show the same data.
Type in grid.
Notice entry form shows the data.
Type in entry form.
Notice grid shows the data.
Delphi in Delphi