Chapter 4 for Delphi Unleashed 1.0: 16 Bit

By: Charles Calvert

Abstract: To fully understand and take advantage of Delphi, you need to develop a familiarity with the ObjectPascal language.

Chapter 4 for Delphi Unleashed 1.0: 16 Bit

Copyright (c) 1995 by Charlie Calvert

Sample Code

The Structure of a Program

Overview

To fully understand and take advantage of Delphi, you need to develop a familiarity with the ObjectPascal language. The visual portion of Delphi is extremely powerful, but the real key to your success as a programmer depends on how well you understand coding techniques.

To help acquaint you with Delphi's language, this chapter discusses four major topics:

  • Creating and using procedures.
  • Creating and using procedures with a visual tool.
  • Identifying the parts and names of the code in a typical Delphi program.
  • Recognizing and using the basic components of an object-oriented language.
  • Using multiple forms within a single program.
  • Allocating and deallocating memory for forms at runtime.
  • Using the WINCRT unit to emulate simple DOS programs.

As we discuss these topics, I'll be showing you four very simple example programs. As a side benefit, the code you'll be seeing demonstrates techniques for using several important Windows controls. For some readers, certain portions of this chapter will be old hat, but many portions contain information unique to the Delphi environment.

A Fundamental Building Block: Procedures

In cases like this, it's probably best to start at the beginning, which means taking a close look at procedures. For now, you can think of a procedure as a block of code designed to accomplish a single task. For instance, if you want to write your name to the screen, prompt the user for a number, or read some values from a file, you probably want to encapsulate that process within a single procedure or group of procedures.

Procedures serve the same function in programming that paragraphs do in writing. They break up long bodies of code into manageable chunks that can be easily grasped. Just as paragraphs are broken up into sentences, so are procedures broken up into statements. For now, you can think of a statement as any block of code that contains a single semicolon. The semicolon always appears at the end of a statement, just as a period ends a sentence. Furthermore, most statements are only one line long. Statements are discussed in greater depth during the first part of Chapter 8, "Branching and Integers."

Enough theory. Let's take a look at an actual procedure:

procedure Button1OnClick;
begin
  Edit1.Text := 'No matter, never mind!';
end;

This procedure, called Button1OnClick, displays an appealing, but nevertheless extreme, philosophical position in an edit control, as shown in Figure 4.1.



Figure 4.1. A simple program that displays a single line of text in an edit control.

Most of the time, the basic framework of a procedure is created automatically by the compiler. It is only necessary for you to fill in a few lines of code to form the body of the procedure. Nonetheless, it is vitally important that you understand the basic structure of a procedure. If you don't understand the fundamental elements of Delphi's language, you will never be able to use it correctly.

The procedure depicted previously has two parts. The first is the header:

procedure Button1OnClick;
The second is the body:
begin
  Edit1.Text := 'No matter, never mind.';
end;

In turn, you can see that the header itself is broken up into three parts. The first and last parts are pure syntax. They consist of the word procedure and the trailing semicolon that rounds off the line of code. In between, you find the procedure's name, which should be as descriptive as possible. In this case, the name Button1OnClick tells you that this procedure is executed when someone clicks the mouse on button number one.

    It's important to give sensible names to the procedures, types, and variables in your programs. Every procedure you create should do only one thing, and the name you give it should describe that one thing as clearly as possible. Many good names for procedures contain a verb and a noun. For instance, GetFirstName, LoadFile, OpenFile, PostError, and ExitProgram are all good names for procedures.

The body of a procedure is always encapsulated within a begin..end pair. This syntax forms the bookends around a code block. From a purely technical point of view, these two symbols exist primarily to tell the compiler where a block of code begins and ends. As a result, it is very important that every begin statement be matched up with an end statement. If these symbols are not paired off in a logical fashion, the compiler races off searching for the next pair, and the certain result is an error of one kind or another. For instance, you don't want to write code that looks like this:

begin
begin
  Edit1.Text := 'No matter, never mind.';
end;

Or, like this:

begin
  Edit1.Text := 'No matter, never mind.';
end;
end;

It may seem unlikely that you would ever make mistakes like these, but if your procedures grow long, it is easy to make these errors.

The only line of code from the Button1OnClick procedure that hasn't been discussed is the simple statement that uses the assignment operator := to assign a string to the edit control:

Edit1.Text := 'No matter, never mind.';

For now, all you need to know is that this line of code causes four words to be shown in an edit control. These four words are set off by single quotes, and together they form a syntactical element called a string. You will hear more about strings and the assignment operator, :=, in the next few chapters.

Take a few moments to review the key points discussed in this section of the chapter:

  • A procedure is made up of two parts. The first part is called the header, and the second part is called the body.
  • A header begins with the word procedure, followed by the name of the procedure, and finally the whole line is rounded off with a semicolon.
  • The body of a procedure is always encapsulated within a begin..end pair. Furthermore, the final end statement has a semicolon after it. Between the begin..end pair, you usually find a series of statements. As a rule, statements end with a semicolon.

The last few paragraphs have plunged unremittingly into the depths of various syntactical issues. It's territory that this book will visit again in many different guises. But for now, you can breath a sigh of relief, for the next section temporarily abandons technical issues in favor of some simple visual programming techniques.

    Object Pascal is not a case-sensitive language. In other words, the compiler does not care whether you write EDIT1.TEXT or Edit1.Text. For that matter, it won't bat an eyelash if you write eDiT1.tEXt. The compiler just doesn't care about capitalization. People, however, do care. As a result, Borland has established a set form for capitalization, and I try to follow it with only one or two minor variations. You need not conform to this style if it doesn't please you, but it certainly won't do you any harm to follow suit.

Creating Procedures with Visual Tools

Earlier, you saw that the syntactical framework of a procedure can be created with the help of visual tools. It's now time to create CONTROL1, a program that will help you learn more about the specific techniques involved in creating this framework.

To create CONTROL1, use the mouse to place a TEdit component on a form. When you're done, your form should look like the one shown in Figure 4.2.



Figure 4.2. The form for the CONTROL1 program.

Now go to the Object Inspector, select the Events Page, and double-click the OnDblClick entry, as shown in Figure 4.3. In the blink of an eye, a code window is displayed, and the basic framework of a procedure is pasted inside it. The result looks something like the window shown in Figure 4.4.



Figure 4.3. To create a procedure, simply double-click the space to the right of the words OnDblClick.

When creating a procedure as previously described, you can either tell Delphi to create a default name, or you can type in your own procedure name. For now, you'll probably want to have Delphi create the default name for you. To do this, double-click the property editor to the right of the title in the Events Page. If you prefer to use a different name, just type in the name you want and press Enter.



Figure 4.4. After you double-click an edit control, a procedure is automatically pasted into an Editor window.

Now edit the body of the procedure so it looks like this:

procedure TForm1.Edit1DblClick(Sender: TObject);
begin
  Edit1.Text := 'You clicked on the edit control';
end;

Take a moment to save the program. You should call the project CONTROL1, and the main source file MAIN. Now, run the program. Notice that when you double-click the edit control, the text inside it changes, as shown in Figure 4.5.



Figure 4.5. The edit control in program CONTROL1 changes when you double-click it.

Listings 4.1 and Listing 4.2 show the complete code for the CONTROL1 program. (If you're having trouble creating this program, you can use the copy of the code that's on your program disk.) An explanation of this code is provided in the next section of this chapter.

Listing 4.1. The CONTROL1 program shows you how to create and use a Delphi procedure.

program Control1;

uses
  Forms,
  Main in 'MAIN.PAS' {Form1};

begin
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Listing 4.2. The unit that contains the main body of code for the CONTROL1 program.

unit Main;

{ Program copyright (c) 1994 by Charles Calvert }
{ Project Name: CONTROL1 }

interface

uses
  WinTypes, WinProcs,
  Classes, Graphics, Controls,
  Printers, Menus, Forms, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    procedure Edit1DblClick(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Edit1DblClick(Sender: TObject);
begin
  Edit1.Text := 'You clicked on the edit control';
end;

end.

You can see in the listings that there are two main source files used in the CONTROL1 program. The first file is called CONTROL1.DPR, and the second file is called MAIN.PAS. An in-depth explanation of both modules will be presented in the next section. However, I'll say a few words about them here, so that you can start thinking about some of the fundamental concepts they use.

When your program is loaded into memory, the two lines of code in CONTROL1.DPR are called first. These lines of code are generated automatically by the compiler, and are surrounded by a begin..end pair:

begin
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

This code's job is to first allocate memory for the main form:

Application.CreateForm(TForm1, Form1);

Then, it gets the form up on the screen and responds to events that occur inside it. It does the latter task with a single command:

Application.Run;

This instruction tells the predeclared object called Application to begin processing the events that occur during the run of a program. For now, you don't need to know what class Application is all about, or what specific role it plays in your program. All you need to know is that the Application object has the intelligence needed to handle the program flow at runtime.

As a rule, the project files Delphi builds for you remain almost identical for all programs in this book. Project files usually do little more than launch the main form and quickly fade into the background. As a result, I generally won't show you the project file for a program.

You've just learned a little about the structure of a Pascal program. You also learned how to create a procedure using visual programming tools. In particular, you learned how to create a procedure that responds to a double-click a particular control. The steps involved were simple:

  • Place a control on a form.
  • Select its DblClick method from the Events Page on the Object Inspector. You can either type in the name of the procedure you want to create and press Enter, or double-click the proper entry to have Delphi create a default name. In either case, the result is a procedure listed in the Editor window.

When you've created the procedure, you can fill in its body with code of your own design. The next section, which describes MAIN.PAS, continues to unravel some of the mysteries of visual programming.

The Main Parts of the CONTROL1 Program

Even though there's a lot of code in the CONTROL1 program, it's important to remember that Delphi generated most of it automatically. The programmer had to type in only one line:

Edit1.Text := 'You clicked on the edit control';

Despite the ease with which it was created, CONTROL1 still has a fairly complicated underlying structure. You'll be creating many programs that have the same basic composition, so it's important for you to understand how it's put together. That's why I'm going discuss the main parts of the source code before I move on to discuss the procedure you just created.

You saw in the last section that CONTROL1 is divided into two parts. The first part is a project file called CONTROL1.DPR, and the second part is a unit called MAIN.PAS.

Units never stand alone. They can't be executed by themselves, but must first be linked into an executable. In this case, unit MAIN happens to be part of a larger program called CONTROL1. CONTROL1.DPR depends on unit MAIN. It's not complete without it. You can tell which units a module depends on by checking its uses clause (the clause beginning with the word uses). Once again, here is the complete source code for CONTROL1.DPR:

program Control1;                       
                                             
     uses                                    
       Forms,                                
       Main in 'MAIN.PAS' {Form1};           
                                             
     begin                                   
       Application.CreateForm(TForm1, Form1);
       Application.Run;                      
     end.

CONTROL1.DPR lists unit MAIN in its uses clause. As a result, CONTROL1.DPR is said to depend on MAIN.PAS. Notice that the word Form1 appears in a comment (indicated by curly braces) after the word Main. This is a reminder from the compiler to the programmer to help you remember that Form1 is associated with MAIN.PAS. In other words, the code for the visual component called Form1 is kept inside MAIN.PAS. The use of the word in as part of a uses clause makes it clear to the compiler which module contains the unit called MAIN. Furthermore, it states that Main is part of the project called Control1, while Forms is merely listed in its uses clause. Units that are part of a project are listed in the Project Manager, which as you recall, is accessible from the top of the View menu.

When you create a project file, you must always put the word program at the top of it (or allow the compiler to place it there for you):

program Control1;

Likewise, when you create a unit, you must always place the word unit at the top (or allow the compiler to place it there for you):

unit Main;

There is no functional difference between a unit that Delphi generates automatically and a unit you create in a text editor: Both units function in exactly the same way. Code that's generated by the compiler doesn't have anything special or magical about it; it's just created differently (and more quickly and conveniently) than code you create in an editor.

Notice that MAIN.PAS is divided into three sections:

  • Title
  • Interface
  • Implementation

If you stripped out all the other code and left only these main sections, the module would look like this:

unit Main;

interface

implementation

end.

This code, which represents a unit in its simplest possible form, compiles without error. It won't, however, do anything useful. To make a unit functional, you need to add more code. What you see above is just the skeleton[md]the bare bones[md]of a unit.

Here is one way to think about the difference between the interface and the implementation. An interface is the visible portion of a unit. It is the unit's face, as in interface. When any other part of the program looks at a unit, all it sees is this visible portion, the unit's face. Everything else is hidden, internal to the unit, part of the implementation. These hidden parts tend to contain the actual "brains" of a unit. That is, the implementation is where you write code that performs actions. Thus, the external interface, like the human face, is visible to others, the internal implementation, like the human brain, is hidden from others.

Therefore, a unit is divided into two sections. One is the outword appearance, or face; the other is the internal engine, or brain. The first section is called the interface, and the second the implementation. The interface is public, and can contain only declarations; the implementation is private, and can contain both declarations and code.

It is also possible to add a section beginning with the word initialization, which appears right before the word end in the previous example. Initialization sections can be used to initialize global variables to a particular value. In Turbo Pascal, all initialization sections were marked off by a simple begin..end pair, but that syntax has now been changed to more explicitly mark the significance of the section. As a rule, initialization sections should be left empty in programs written by beginning or intermediate programmers. The problem is not that initialization sections are complex, but that they represent an unnecessary temptation. In particular, you might be tempted to declare a large group of global variables and initialize them all at the bottom of your units. This is a dangerous practice that can lead to poor program design. As a rule, most variables should be declared as parts of an object, and global variables should only be declared in rare circumstances when there is no other possible solution to a complex problem.

Objects and the Interface of MAIN.PAS

The interface section of a Delphi program is used to declare any public sections of code that appear in a unit. In this particular case, you can see that the code for MAIN.PAS has a uses clause, a type section, and a variable section:

unit Main;

{ Program copyright (c) 1994 by Charles Calvert }
{ Project Name: CONTROL1 }

interface

uses
  WinTypes, WinProcs,
  Classes, Graphics, Controls,
  Printers, Menus, Forms, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    procedure Edit1DblClick(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

There are various other syntactical elements that can appear in an interface, but there is no need to be exhaustive at this early stage. In fact, the only part of the interface that should concern you right now is the TForm1 object definition:

TForm1 = class(TForm)
    Edit1: TEdit;
    procedure Edit1DblClick(Sender: TObject);
  end;

    A detailed description of classes and object-oriented programming is covered in Chapter 27, "Objects and Inheritance." For now, however, I'm just going to say a few introductory words about the subject.

The code shown here declares the existence and structure of an object called TForm1. The keyword used in Delphi to designate an object declaration is class. For instance, the following line of code states that TForm1 is declared to be a class that is descended from another class called TForm:

TForm1 = class(TForm)

Programmers use classes in part because they need to encapsulate a set of related data and procedures under a single aegis. Objects serve much the same purpose as a chapter in a book. Related code can be encapsulated under a single object, the same way several related topics can be encapsulated inside a single chapter in a book.

In particular, this object definition states that class TForm1 has one piece of data called Edit1 and one procedure called Edit1DblClick. Edit1 references the class that controls the physical edit control, which you see on the screen when you run the program. When you need to query the edit control or change the state of the edit control, you use the Edit1 variable. For instance, the following line of code places a string inside the edit control called Edit1:

Edit1.Text := 'You clicked on the edit control';

A procedure that is part of an object is sometimes referred to as a method. All methods are either procedures or functions, but not all procedures or functions are methods. It is completely acceptable to refer to a routine that is part of an object by the names procedure or function. However, a procedure that is not part of an object should never be called a method. You should note that I sometimes call the same block of code either a procedure or a method. For all practical purposes, right now you can think of the two words as synonymous, as long as you recall that methods are always parts of objects.

Take a moment to consider one more quick definition before moving on. A variable such as Edit1, which appears as part of an object, is sometimes called a field of an object. The same syntax is used when referring to the fields of a record. So, a variable that is part of a record is called a field of the record, and a variable that is part of a class definition is called a field of that object.

Frankly, the details of how to define a class can become quite complex; for now, it is best if you start out with a few very general ideas. As time goes on, you can dig deeper and try to come to an understanding of all the major aspects of object-oriented code. To get there, however, you first need to learn the basic facts about objects, and learn to at least recognize one when you see one. The brief explanation presented here should be enough to get you at least that far.

In this section, you learned about the interface of a Delphi program. The interface contains declarations, but no code. You will never see the body of a procedure in an interface section. The most important declaration in most Delphi interface sections is the declaration for the object representing the main form.

The Implementation of MAIN.PAS

After the interface section of a unit, you find the keyword implementation. The implementation portion of a unit is where procedures or methods are actually defined. The header of a procedure or method may appear in a unit's interface, but its body always appears in the implementation, along with a second version of its header.

Anything that appears in a unit's implementation that is not referenced in the interface is private to that unit. This means that a procedure or method listed in an implementation cannot be called from another unit unless its header is listed in that unit's interface.

All of the syntactical elements listed in the interface section can also be listed in the implementation. For instance, classes, types, and variables can all be declared inside a program's implementation. The only difference, of course, is that these declarations are now local to the particular unit in question. This means they cannot be called or referenced from another unit.

The key point for you to grasp right now is that the implementation is the place where procedures and methods are defined or implemented. Specifically, code fragments (such as the following one) always appear in the implementation section of a unit. They can never appear in the interface.

procedure TForm1.Edit1DblClick(Sender: TObject);
begin
  Edit1.Text := 'You clicked on the edit control';
end;

Notice that the procedure listed here contains a header and a body. If you look carefully, you see that the procedure's name is qualified by the word TForm1. This means that Edit1DblClick is a method of class TForm1. The portions of an object that do something are called methods. For instance, the Edit1DblClick method displays a string in an edit control.

I'm sure you have noticed that the header for the Edit1DblClick method has a set of parentheses at the end with the words Sender and TObject inside them:

procedure TForm1.Edit1DblClick(Sender: TObject); 

This additional piece of syntax is called a parameter.

The parameter called Sender references the control that was used to call the method. If you double-click the edit control, causing the Edit1DblClick method to be called, a copy of the Edit1 object is passed to Edit1DblClick in the parameter called Sender.

Parameters take a little while to understand, so I address them separately in the next section. In this case, there is nothing in the parameter called Sender that is of interest to the CONTROL1 program, so the parameter is simply ignored.

The body of the Edit1DblClick method looks like this:

begin
  Edit1.Text := 'You clicked on the edit control';
end;

As you learned earlier, the begin..end pair surrounding the body of the code is merely a syntactical convention used to tell the compiler where the body of your code begins and ends.

For now, all you need to know about Edit1 is that it is of type TEdit. TEdit is a class used to manipulate an edit control. The actual definition of the TEdit class is found in a unit called CONTROLS.PAS.

In the last few sections, you have learned about the basic parts of a Delphi program. In particular, you learned the following:

  • Every Delphi program has at least two main parts. The first is a project file such as CONTROL1.DPR, and the second is one or more units such as the module MAIN.PAS shown earlier.
  • Units are divided into three main sections. The first is the title, the second is the interface, and the third is the implementation.
  • The interface section of a unit is where you declare any public portions of your code. For instance, the class called Form1 is defined in MAIN.PAS, but referenced twice in PROJECT1.DPR. If Form1 was not listed in the interface of MAIN.PAS, there would be no way for PROJECT1 to even know that Form1 existed. As a result, unit MAIN and Form1 could not be linked into the program.
  • The implementation section of MAIN.PAS is where the body of the procedures used in MAIN.PAS are defined. You can declare a procedure or object in an interface, but you define it in the implementation. Everything you see in the interface is merely preliminary. The actual body of the code appears in the implementation.
  • After the title, interface, and implementation, the fourth optional section of a unit is called the initialization section. The initialization section gets called first, just as you must type WIN before you can start Windows. The short program called INITDATA, found on disk with the code for this chapter, shows how to use an initialization section.
  • A class can be used to encapsulate a set of procedures and data that are logically bound together. For instance, the visual element called Form1, its edit control, and the Edit1DblClick method are all part of one large logical entity. It makes sense to think of them as parts of a single object, just as a steering wheel, engine, and tires may be part of a single car. One of the purposes of classes is that they help to create order out of diversity. Objects are discussed in greater depth in Part 3, and in many other parts of this book.

I hope the material presented in this section has helped you grasp the major blocks of code and the major syntactical elements that are found in every Delphi program. I am aware that this material can be difficult, but it forms the basis for Delphi programs. If you are having trouble with this material, you can rely for a while on the fact that almost all of the code explained here is generated automatically by the compiler. All I have done in this section is explain why the code exists and what functions it performs. If you have understood the broad concepts that I am referencing, you should get along fine for now.

Passing Parameters

Procedures are passed parameters to supply them with information that they can process. Suppose you have a procedure that, given the current date, calculates the number of days left in the year. In the design phase, you might decide to pass a parameter (containing the current date) to this procedure. It could then process the date that you passed to it, and print the result to the screen.

The following program, called PARAMS, is designed to demonstrate how to use parameters. As a side benefit, it also shows you how to do the following:

  • Create your own procedures.
  • Add procedures to an object so they become methods of that object.
  • Call one procedure from within another procedure.

The PARAMS program enables you to type a string in an edit control. After you click a button, this string is copied to six other edit controls, so that one string is repeated seven times, as shown in Figure 4.6.



Figure 4.6. Killing seven birds with one stone is the goal of the PARAMS program.

To get started, create a form like the one shown in Figure 4.7. It should contain seven edit controls, labeled Edit1 through Edit7. At the bottom of the form, place a button called Button1. Using the Object Inspector, change the caption of Button1 so that it says Call Procedure WriteAll. Figure 4.8 shows how the Object Inspector should look after you change Button1's caption.



Figure 4.7. The form for the PARAMS program contains seven edit controls and a button.



Figure 4.8. The Object Inspector after Button1's Caption property has been changed.

After designing the form for the PARAMS program, glance at the program's main unit. Class TForm1 should now look like this:

TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    Edit6: TEdit;
    Edit7: TEdit;
    Button1: TButton;
  end;

Notice that it contains eight fields, one for each of the visual components you created. Remember that you can use these fields to query or manipulate the controls at runtime.

The next step is to add a method that will be called when the user clicks the form's button. To do this, either select the OnClick method from the Object Inspector's Events Page, or double-click the button itself. When you're done, the following code should be generated:

procedure TForm1.Button1Click(Sender: TObject);
begin

end;

The currently selected control appears at the top of the Object Inspector in a drop-down combobox. In this program, the Object Inspector is used only to modify Button1. Therefore, you should use the combobox at the top of the Object Inspector to be sure that Button1 is selected, and not the main form or one of the edit controls.

The PARAMS program serves no useful function other than to show you how to write a procedure and pass a parameter to it. In particular, the program responds to a click Button1 by grabbing the current text from Edit7 and passing it to a procedure called WriteAll:

procedure TForm1.Button1Click(Sender: TObject);
begin
  WriteAll(Edit7.Text);
end;

I'll explain the WriteAll procedure soon; for now, you need to be sure that you understand what's happening inside Button1Click. The key point to grasp here is that Edit7 is of type TEdit, and all TEdit components have a property called Text. The Text property is part of the TEdit class; it resides in the TEdit object and provides access to the text that the edit control displays onscreen. On startup, the text in Edit7 is the string Edit7. During the course of the program, you can change this string by clicking on the edit control with the mouse and typing in a new string.

The text to be displayed in the six uppermost edit controls is passed to procedure WriteAll as a parameter. To pass a parameter to a procedure, you just write the name of the procedure and enclose the parameter to be passed in parentheses.

  WriteAll(Edit7.Text); 

Again, let's suppose you have a procedure that calculates the number of days left in a year. If the name of that procedure is CalcDaysLeft, and the current date is stored in a variable called CurrentDate, you can call the procedure by writing:

CalcDaysLeft(CurrentDate); 

The actual header for the procedure might look something like this, with TDate being a user-defined type:

procedure CalcDays(CurrentDate: TDate); 

To return to the case at hand, the goal of the WriteAll procedure is to copy the text in Edit7 to all six of the other edit controls. Here's what the procedure looks like:

procedure TForm1.WriteAll(Edit7String: String);
begin
  Edit1.Text := Edit7String;
  Edit2.Text := Edit7String;
  Edit3.Text := Edit7String;
  Edit4.Text := Edit7String;
  Edit5.Text := Edit7String;
  Edit6.Text := Edit7String;
end;

There's no way to automatically generate code for this kind of procedure; you must design and write the code yourself. To get started, create the procedure's header:

procedure TForm1.WriteAll(Edit7String: String); 

Although they were mentioned earlier, it won't hurt to take a second, closer look at a procedure header. It consists of five parts:

  • The first part is the word procedure, and the fifth part is a semicolon. Both of these elements have purely syntactical purposes: They inform the compiler that a procedure is being defined, and that its header ends at a particular point.
  • The second part of the header is the word TForm1, which qualifies the procedure name so the compiler knows that this is a method of the TForm1 class.
  • The third part of the header is the procedure's name; I've chosen to call it WriteAll. If you want to, you can change this name to virtually any combination of letters. You can't, however, start an identifier with a number. For instance, 1WriteAll and 2WriteAll aren't valid identifiers.
  • The fourth part of the header is the parameter. Parameters are declared inside parentheses, and consist of two parts. The first part is the name of the parameter, and the second part is its type. These two parts are separated by a colon. If you pass more than one parameter to a procedure, each parameter should be separated by a semicolon, like so:
  • procedure Example(Param1: string; Param2: string); 

    By this time, you've had plenty of opportunities to learn what a string is, but this is the first time you've worked explicitly with a string type. A variable declared to be of type string can hold any string that is shorter than 256 characters. If you're confused about variables and types, you should refer to an introductory book on Pascal programming. For now, all you need to know is that the parameter called Edit7String can be used to hold whatever text was in the control called Edit7.

    After you've created the header for the WriteAll procedure, you need to copy it into the TForm1 class declaration:

    TForm1 = class(TForm)
        Edit1: TEdit;
        Edit2: TEdit;
        Edit3: TEdit;
        Edit4: TEdit;
        Edit5: TEdit;
        Edit6: TEdit;
        Edit7: TEdit;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        procedure WriteAll(Edit7String: String);
      end;

    The relevant method is the last one listed, right before the word end. As you can see, the entire WriteAll header has been copied verbatim into the class declaration, with the exception of the class name itself. There's no need to include the class name qualifier because everything in the TForm1 class declaration is automatically a member of the TForm1 class.

    The actual body of the WriteAll procedure is trivial:

    begin
      Edit1.Text := Edit7String;
      Edit2.Text := Edit7String;
      Edit3.Text := Edit7String;
      Edit4.Text := Edit7String;
      Edit5.Text := Edit7String;
      Edit6.Text := Edit7String;
    end;

    This code assigns the contents of the parameter called Edit7String to each of the remaining edit controls on the form. Other than issues of convenience and flexibility, there's no significant difference between modifying the Text property of an edit control at runtime and modifying the same property during design time using the Object Inspector.

    The following listings have the full code for the PARAMS program. I even included the project file, though I won't usually be doing that during these chapters. The code looks a bit long when typed out like this, but remember that you need to type in only 10 lines of this program. The rest is generated automatically by Delphi. If you have trouble creating this program, you can find the full source code on the disk.

      This book was written while Delphi was in beta. As a result, it is possible that the following code might differ in some minor way from the final syntax used in the shipping product. If you are having trouble with this code, check my web site for updates:

    Listing 4.3. The project file for the PARAMS program does nothing more than launch the main form.
    program Params;
    
    uses
      Forms,
      Main in 'MAIN.PAS' {Form1};
    
    begin
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.
    Listing 4.4. The source for the PARAMS program shows how to use edit controls and how to pass parameters.
    unit Main;
    
    { Program copyright (c) 1994 by Charles Calvert }
    { Project Name: PARAMS }
    
    interface
    
    uses
      WinTypes, WinProcs, Classes,
      Graphics, Controls,
      Printers, Forms, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Edit1: TEdit;
        Edit2: TEdit;
        Edit3: TEdit;
        Edit4: TEdit;
        Edit5: TEdit;
        Edit6: TEdit;
        Edit7: TEdit;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        procedure WriteAll(Edit7String: String);
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    
    procedure TForm1.WriteAll(Edit7String: String);
    begin
      Edit1.Text := Edit7String;
      Edit2.Text := Edit7String;
      Edit3.Text := Edit7String;
      Edit4.Text := Edit7String;
      Edit5.Text := Edit7String;
      Edit6.Text := Edit7String;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      WriteAll(Edit7.Text);
    end;
    
    end.

    When experimenting with the PARAMS program, you might try changing the names of the WriteAll procedure and Edit7String parameter. These are two elements of the program that you have almost complete control over. Other words such as procedure, string, begin, and end are reserved words, which means they can't be used for any purpose other than the ones officially designated by the language. For this reason, you can't name a procedure or variable string, end, begin, and so on. For example, the following declarations won't compile:

    procedure Begin;
    procedure Class;
    program String;

    For a complete list of reserved words, see Delphi's on-line help. Don't bother trying to memorize all the reserved words in the language. The compiler always tells you if you try to use one incorrectly, usually by popping up a message saying Identifier expected. Identifiers are the names assigned to variables, procedures, types, and nearly all other syntactical elements in a program. For instance, Form1, Control1, Main, WriteAll, and Button1Click are all identifiers. When you get a message such as Identifier expected, the compiler (in its own cryptic way) is usually trying to tell you that it expected an identifier, but instead found a reserved word.

    In this section, you learned how to pass a parameter to a procedure and how to create your own procedures. I know that technical terms and new concepts are flying fast and thick in this chapter. However, what's really important is that you grasp the broad concepts, rather than memorize all the specifics. If you can complete each of the following tasks, you're doing fine:

    • Create a procedure using visual techniques.
    • Add code to your procedure. The code can perform any useful function, such as inserting text in an edit control.
    • Recognize the keywords implementation and interface as being specific to units.
    • Recognize the declaration of an object when you see it.
    • Recognize the difference between a method and a field of an object.
    • Recognize a parameter to a procedure and realize that it's a way to pass information to the procedure.

    It's icing on the cake if you can write your own procedures or create your own units at this point. This chapter is meant to help you understand the basics about procedures, classes, and units. Readers with more experience may be picking up more detailed information, but it's not essential that you do so right now.

    Delphi is an easy language to use. If you reach the stage at which you know it very well, you'll be able to create very powerful professional-looking programs. A less thorough knowledge still lets you get serious work done with Delphi. For instance, neither a scientist who uses Delphi to help track data, nor a home user who wants help managing financial records, needs a detailed understanding of the language.

    More Procedures, More Controls

    Now that you have the basics down, it's time to put your knowledge to work by using some of the major controls on the Standard Component Palette, which is shown in Figure 4.9 and described in detail in Chapter 3.



    Figure 4.9. The Standard Component palette contains a wide array of powerful controls.

    The program you're about to create is an extension of the CONTROL1 program you created at the beginning of this chapter. If you like, you can copy the code from that program into a new directory called CONTROL2. However, there's no real need to do this. You can simply continue working with the CONTROL1 program, adding more code to it where appropriate.

    Starting with the code found in the CONTROL1 program, you can now proceed to build a more complex program that reveals a lot about the controls used in Delphi programs.

    First, add a static control to your form and name it Lselection. Set its caption to Selection, as shown in Figure 4.10.



    Figure 4.10. The Control1 main form after a static control has been added.

    Now select the OnDblClk method from the Events Page of the Object Inspector, and double-click it. Another procedure is created automatically, and a few small changes are made to the Form1 object at the top of your code. The next step is to modify the LSelectionDblClick procedure so that it looks like this:

    procedure TForm1.LSelectionDblClick(Sender: TObject);
    begin
      Edit1.Text := 'You double-clicked on a label';
    end;

    Run the program and double-click the label. You'll see that the edit control changes to display the text you specified in the LSelectionDblClick method. Now close the program to return to design mode.

    The previous code shows the pattern that's used throughout this program. Specifically, you'll add a control to the form, and add code so a click or double-click that control places descriptive text in the edit control.

    While you're still in design mode, add a large group box beneath the edit control and label. Then, add two radio buttons and two check boxes to the group box, as shown in Figure 4.11. Finally, add a single button at the bottom of the form. Stretch it out so that it covers nearly the entire width of the form. Name the button BClose, and give it the word Close as a caption. More specifically, the Name property in the Object Inspector should be BClose, and the Caption property should be Close. (If you assign the name Close to the button, you'll get an error.)

    You should add the group box to the form before you add the radio buttons, check boxes, or other controls that you want to place on the group box. If you move these controls from the surface of the form onto a group box, you may run into trouble later (namely, the control will still think the form is its parent, and it won't move with the group box when the group box is moved). To avoid this problem, it's better to first place the group box on the form, and then place the radio buttons and other controls on top of the group box. You can then drag the entire collection of controls by moving the group box. However, if you forget to place a TGroupBox onto a form before adding the controls that belong on top of it, you can rectify the problem by highlighting the controls, choosing Edit | Cut from the main menu, and pasting the controls onto the surface of the group box. (Note that everything said in this paragraph about group boxes is also true for panels. The two components behave in the same way, even though they are slightly different in appearance.)



    Figure 4.11. The main form for the Control2 application, as it appears after it has been populated with a variety of controls.

    The final step in this process is to create an OnClick method for each of the new controls you've created, except the group box. Each of the methods should contain explanatory text, following the general pattern shown in this RadioButton1Click procedure:

    procedure TForm1.RadioButton1Click(Sender: TObject);
    begin
      Edit1.Text := 'Click on RadioButton1';
    end;

    When you're done, the code you've created should look like the program shown in Listing . This time, I omitted the project file because it is almost identical to the project file from the other applications shown in this chapter. If you need further direction, review the source code found on the disk.

    Listing 4.5. The CONTROL2 program gives names to some of the major components used in Windows programs.
    unit Main;
    
    { Program copyright (c) 1994 by Charles Calvert }
    { Project Name: CONTROL2 }
    
    interface
    
    uses
      WinTypes, WinProcs, Classes,
      Graphics, Controls, Printers,
      Menus, Forms, StdCtrls;
    
    type
      TForm1 = class(TForm)
        LSelection: TLabel;
        Edit1: TEdit;
        BClose: TButton;
        GroupBox1: TGroupBox;
        CheckBox1: TCheckBox;
        RadioButton1: TRadioButton;
        CheckBox2: TCheckBox;
        RadioButton2: TRadioButton;
        procedure Edit1DblClick(Sender: TObject);
        procedure LSelectionDblClick(Sender: TObject);
        procedure CheckBox1Click(Sender: TObject);
        procedure CheckBox2Click(Sender: TObject);
        procedure RadioButton2Click(Sender: TObject);
        procedure RadioButton1Click(Sender: TObject);
        procedure BCloseClick(Sender: TObject);
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    
    procedure TForm1.Edit1DblClick(Sender: TObject);
    begin
      Edit1.Text := 'Double click on edit control';
    end;
    
    procedure TForm1.LSelectionDblClick(Sender: TObject);
    begin
      Edit1.Text := 'Double click on label';
    end;
    
    procedure TForm1.CheckBox1Click(Sender: TObject);
    begin
      Edit1.Text := 'Click on Checkbox1';
    end;
    
    procedure TForm1.RadioButton1Click(Sender: TObject);
    begin
      Edit1.Text := 'Click on RadioButton1';
    end;
    
    procedure TForm1.BCloseClick(Sender: TObject);
    begin
      Edit1.Text := 'Click on Button';
    end;
    
    procedure TForm1.CheckBox2Click(Sender: TObject);
    begin
      Edit1.Text := 'Click on CheckBox2';
    end;
    
    procedure TForm1.RadioButton2Click(Sender: TObject);
    begin
      Edit1.Text := 'Click on Radiobutton2';
    end;
    
    end.

    This program serves two purposes:

    • It shows how to create procedures, and how to fill out the bodies of these procedures so that they perform a useful action. In this case, the only purpose of each of these procedures is simply to write a line of text in the form's edit control. This is a simple task, but it serves to clearly illustrate what a procedure is, and what its syntax looks like in a code window.
    • It illustrates the names and appearance of several important controls. As a programmer, it's important for you to understand the rudimentary facts about edit controls, labels, radio buttons, check boxes, buttons, and group boxes. If these controls are unfamiliar to you, you should refer to an introductory book on the Windows environment.

    Working with Forms

    Most Delphi projects consist of a DPR file and one or more units. Most, if not all, units in a project contain a form. At runtime, the creation of a form involves two steps:

    • Delphi registers the objects in the form with the system. This step occurs automatically, without any intervention on your part.
    • Memory is allocated for the form via a call to CreateForm.

    The following is the default syntax for a simple Delphi DPR file:

    program Project1;
    
    uses
      Forms,
      Unit1 in 'UNIT1.PAS' {Form1};
    
    {$R *.RES}
    
    begin
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.

    The compiler inserts the call to CreateForm. If you omit the call, Application.Run returns immediately, and the program is over at almost the same moment it began.

    The main block of more complex programs might look something like this:

    begin
      Application.CreateForm(TForm1, Form1);
      Application.CreateForm(TForm2, Form2);
      Application.CreateForm(TForm3, Form3);
      Application.CreateForm(TForm4, Form4);
      Application.Run;
    end.

    In this code fragment, the last three calls to CreateForm allocate memory for forms that aren't visible when the program is first launched. You can make them visible by writing

    Form2.Visible := True; 

    or

    Form2.Show; 

    In most cases, the calls to show a form come in response to a click a button or menu item. For example, if you want a click a button to launch a second form, you should create an OnClick method for the button by double-clicking it while in design mode. Then, fill in the resulting procedure so that it looks like this:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2.Show;
    end;

    After you fill in the procedure, the second form becomes visible. You can switch to and from this second form with a click of the mouse. When you want to shut down the second form, you can just call the Close method:

    procedure TForm2.Button1Click(Sender: TObject);
    begin
      Close;
    end;

    This call causes the second form to hide itself, but not to deallocate its memory. There is no real distinction between calling Close on the second form, and writing the following code:

    procedure TForm2.Button1Click(Sender: TObject);
    begin
      Visible := False;
    end;

    However, if you call Close on an application's main form, it causes the entire application to shut down and all of the memory associated with it to be deallocated.

    The following extremely simple program demonstrates how to work with an application containing two forms. Listing 0-1 contains the first form from the program, and Listing 0-2 contains the second form.

      The code for the TWOFORM program is in the CHAP4 directory on this book's CD-ROM.

    The TWOFORM program is very straightforward. It consists of two forms, each with one button. The button on the first form is called bLaunch, and the one on the second form is called bClose. Notice that you can't name the button on the second form Close, because that would conflict with the Close method.

    The only other portion of the program worth mentioning is the second uses clause in the MAIN form:

    uses ModTwo;

    This statement references the program's second module, which is called MODTWO.PAS. The reference is necessary because, otherwise, the first form is unaware of the second form's existence. You need to use this syntax to explicitly clue the form in on the design of the program.

    In this section, you've taken a look at one of the most commonly used programming techniques in the Delphi environment. In the next section, you can see how to create something that looks and feels like a modal dialog box, but isn't really a modal dialog box.

    Creating Modal Dialogs

    When a modal dialog box appears, it's the only part of an application that can get user input. None of the other buttons, menus, or windows respond until you close the modal dialog box. The File Open dialog box, accessible from the File menu of most Windows applications, is an example of this type of dialog box.

    Delphi enables you to create traditional Windows dialog boxes using Resource Workshop or command-line tools such as BRC.EXE or RC.EXE. In fact, if you do a little exploring with the File Manager, you see that Delphi generates a small RES file to accompany most of the applications you build. However, resources are not the standard means for Delphi programmers to create a dialog box-like object.

    I use the phrase dialog box-like object because standard Delphi dialog boxes are not "real" Windows dialog boxes. There are no resource files for these dialog boxes; instead, they're saved as DFM files. If you're not familiar with the terminology used here, there's no need for concern. The main point is simply that Windows has a set of relatively complex tools and commands that are normally used to create a dialog. Delphi takes a different approach. It provides you with a simple means of creating something that looks and feels like a standard Windows dialog, but isn't nearly so difficult to make.

    To create a dialog box in Delphi, just create a standard form. If you want it to appear in a modal state, make the following call, in which Form2 is the name of the form you want to show, and Return is an integer value:

    Return := Form2.ShowModal; 

    To close a modal dialog box, set the TForm property called ModalResult equal to one of the following values:

    • mrOk
    • mrCancel
    • mrAbort
    • mrRetry
    • mrIgnore
    • mrYes
    • mrNo

    To close a modal dialog box in response to a button click, write code that looks like this:

    procedure TForm2.Button1Click(Sender: TObject);
    begin
      ModalResult := mrOk;
    end;

    The value you assign to ModalResult is what's returned from ShowModal. This lets you write code such as the following:

    Return := Form2.ShowModal;
    if Return = mrOk then
      MessageBox(Handle, 'Ok', 'Hi', mb_Ok);

    The following program is called FORMAL1. You'll probably notice that the source code for the FORMAL1 program is very similar to that of the TWOFORM program in structure, even though the details are a bit different. FORMAL1 features two forms, shown in Listings 0-2 and 0-4. The first form in the program launches the second form as a modal dialog. The second form, which is depicted in Figure 0-1, contains Cancel and OK buttons. The code associated with the Cancel buttons sets ModalResult equal to mrCancel, and the code for the OK button sets ModalResult equal to mrOk.

      The code for the FORMAL1 program is in the CHAP4 directory on this book's CD-ROM.

    Figure 4.12 shows the FORMAL1 program's form.



    Figure 4.12. The modal form from the FORMAL1 program.

    The FORMAL1 program is interesting primarily because it shows how to return a value from a modal form, and how to handle that value when you receive it. In particular, the following method shows how to close a modal form while simultaneously signaling the parent form that the user wants to cancel any activity that might have occurred in the form:

    procedure TForm2.Button2Click(Sender: TObject);
    begin
      ModalResult := mrCancel;
    end;

    There's no reason that you can't return any integer from a form via the ModalResult property. You don't have to return one of the mr constants shown previously; you can return a literal number or some constant that you define for your own purposes:

    ModalResult := 35;

    This means that there are about 32,000 possible return values, which should provide all the freedom you need to create a wide variety of forms, all returning a multitude of possible integer values.

    Freeing Forms at Runtime

    In large programs, and particularly in large database programs, you usually don't want to create all the forms in your program at the time of initial startup. There are often times when you would prefer not to find code that looks like the following in your program:

    begin
      Application.CreateForm(TForm1, Form1);
      Application.CreateForm(TForm2, Form2);
      Application.CreateForm(TForm3, Form3);
      Application.CreateForm(TForm4, Form4);
      Application.CreateForm(TForm5, Form5);
      Application.CreateForm(TForm6, Form6);
      Application.CreateForm(TForm7, Form7);
      Application.Run;
    end.

    The problem with this code is that it allocates a large chunk of memory for the program's forms at startup. As I hinted earlier, programmers want to be able to dynamically allocate and dispose memory for a form at runtime. Your code should look like this:

    begin
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.

    Next, you want to have the option of allocating resources for the other forms only when you need them. In most programs, this ability won't be missed, but for cases in which secondary forms allocate large chunks of memory, it becomes a necessity. That's why I'll show you a way to write large programs with many forms that still contain simple startup code like the two-line example shown previously.

    As it happens, you're free to move CreateForm calls out of the main project file and into other units of your program. In particular, you can write code that looks like this:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2 := TForm2.Create(Application);
      Form2.ShowModal;
      Form2.Free;
    end;

    This code allocates the memory for a form, displays it, and then disposes it by calling Form2.Free. Notice that you don't call CreateForm in this situation, but instead call TForm2.Create, and assign the Application object as the form's owner. This syntax is preferred unless you're working in the main block of a DPR file.

    The previous code guarantees that the memory for Form2 is only used while it's being displayed. As soon as the user closes the form, the memory associated with the form is disposed by a simple call to Free. This technique helps you create large programs that leave a relatively small memory footprint.

    You should be careful, however, when writing code that looks like this:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2 := TForm2.Create(Application);
      Form2.Show;
    end;

    The code shown here allocates memory for a form and displays it as a modeless dialog box. As a result, the user can switch back to Form1 and launch a second copy of Form2 without ever disposing of the first copy. Unless you're absolutely sure you know what you're doing, this scenario can quickly lead to a memory leak. The simplest solution, of course, is to call ShowModal rather than Show, and to then call Free as soon as ShowModal returns.

    If you're careful, there are ways to create logic like that shown previously in the second code fragment; but, you must be sure that you know what you're doing and why you're doing it before you attempt this kind of maneuver. Specifically, you can respond to the user's request to close the second form by creating an OnClose event handler:

    procedure TWinForm.FormClose(Sender: TObject;
                                 var Action: TCloseAction);
    begin
      Action := caFree;
    end;

    By setting Action to caFree, you ensure that the runtime library code in TForm.Close never references any of its variables, but simply calls Free. It is important to make sure that none of the form's variables are referenced, because they'll be invalid after the call to Free. If you have the runtime library, you can look in the Forms unit to see the actual source code for TForm.Close.

    TCloseAction is an enumerated type with the following declaration:

    TCloseAction = (caNone, caHide, caFree); 

    If you set Action to caHide instead of caFree, the default action occurs: The form becomes invisible, but isn't destroyed. Setting Action to caNone ensures that nothing happens inside of TForm.Close.

    Because the technique shown here can be so useful, I've included a second version of the FORMAL program (see Listings 0-3 and 0-4). This version demonstrates how you can write code that opens multiple forms of the same type in a modeless state. The source code will also be available on CompuServe.

      The code for the FORMAL2 program is in the CHAP4 directory on this book's CD-ROM.

    When looking at the code to the MODFORM.PAS unit, you need to understand two separate, but related, code fragments. If the user clicks on Button1, the system executes the Close command in the Button1Click method. Calling Close eventually generates an OnClose event, which is handled by the FormClose method. As stated earlier, FormClose can set Action to one of three recommended paths:

    • caHide: The form is hidden.
    • caFree: The form is destroyed.
    • caNone: Nothing should occur.

    The FORMAL2 program also uses a TTimer object and the MemAvail call to track memory usage in the program. Specifically, the current amount of available memory is displayed every tenth of a second in a label located on the first form. Timers are discussed in the next section.

    Before closing this section, it's worth reminding you that when you give a form or component an owner, that owner is responsible for disposing the form. As a result, you can create six or seven forms and know that they will be deallocated when their owner is freed. In particular, at shut down, Delphi iterates through the Components array that belongs to each component on the screen, and it calls free on each of those components.

    Using the WinCRT Unit

    Old hands at Turbo Pascal programming are bound to be a bit surprised by some of the material that's been presented in this chapter. Visual Programming with Delphi is very different from Object Pascal as we used to know it.

    These experienced souls might be glad to hear that it's possible to create a program using Delphi that is a good deal like the Pascal used by DOS programmers. I've intentionally avoided describing that technique because I want to be sure that you understand that Visual programming is every bit as simple as DOS programming. In fact, it might be a bit simpler.

    I know, however, that some programmers may want to port apps over directly from DOS with a minimum of fuss. In certain very limited circumstances, this is possible due to the good graces of the WINCRT unit, which also shipped with earlier versions of Turbo and Borland Pascal for Windows. (Users of the Borland C/C++ compilers will recognize the WINCRT unit as being akin to EASYWIN programs.)

    There are two ways to create a WINCRT program. The simplest is to use the Project Expert found in the Gallery. Specifically, you choose CRT Application from the Gallery. However, you won't learn much about the structure of these programs if you automate the task of creating them. As a result, I will explain the way to create a WINCRT program on your own, so that you can see the relationship between these programs and standard Delphi programs.

    To create a WINCRT program by hand, first start out just as you would in a normal program. Go to the File menu and select New. Now, click once on the Project Source menu choice from the View menu.

    When you look at the project file that has been created by the compiler, it should look like this:

    program Project1;
    
    uses
      Forms,
      Unit1 in 'UNIT1.PAS' {Form1};
    
    {$R *.RES}
    
    begin
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.

    To convert this program into a WINCRT program, change the uses clause and the main body of the program:

    program Project1;
    
    uses
      WinCRT;
    
    begin
      WriteLn('Look ma, no forms!');
    end.
    

    Before saving the project, remove UNIT1.PAS from the project by opening the Project Manager, highlighting the words Unit1, and then clicking on the minus (-) symbol. Now save the project under a name of your own choosing. In this particular case, you might want to choose the name EASYWIN.

      The WINCRT.PAS file may not be on your source path, so the compiler may not be able to find it. To resolve this problem, choose Options | Project | Directories/Conditionals and type in the following line in the Search Path section:

      c:delphisourcertlwin;c:delphisourcertl70 

      Of course, the drive designations should match the location of your version of Delphi.

    When you save your program, you see that its title automatically switches to EasyWin:

    program EasyWin; 

    I point this out to remind you that you are still working in a Visual Programming environment in which certain portions of your code can be changed automatically by the compiler. But, other than this one small factor, you are totally on your own. By removing the reference to FORMS and UNIT1, and replacing them with the WINCRT, you have severed your main ties to the world of Visual Programming.

    If you run the EasyWin program, it creates a Window like the one shown in Figure 4.13.



    Figure 4.13. The EASYWIN program displays a single string in an otherwise barren window.

    Old hands at Pascal understand that this string appeared through the good graces of the WriteLn procedure, which is still supported in Delphi under certain limited circumstances. In this case, you can use WriteLn as long as you include the WINCRT unit:

      WriteLn('Look ma, no forms!'); 

    If you omit the reference to the WINCRT unit from your program, you get a runtime error 105. (See Figure 4.14.) All but the most careful programmers see this error sooner or later, and it will save you time if you remember this very common way of generating a 105 error.



    Figure 4.14. The bitter fruits of running the EASYWIN program without listing WINCRT in its uses clause.

      The EASYWIN program displays the word Inactive in its title. This specifies that the program has finished its run, but that the main window of the app is being held open so that the user can view the results of the run. This type of message would make no sense in a true event-oriented Windows program, but it does make sense inside the limited scope of a WINCRT application.

    For now, I'm not going to go into any more depth describing the WINCRT unit. I simply wanted to let you know that it existed, and that it will be referenced again in Appendix B, and in many other places throughout this book. You should understand, however, that WINCRT programs do not support the full range of functions and capabilities offered by either Delphi or a standard DOS program. Even when used in conjunction with the WINDOS unit, WINCRT is still very limited in scope.

    I strongly recommend that you adopt Delphi in its entirety, and use WINCRT only for very simple projects. My personal preference is to use WINCRT primarily as a convenient hacker's tool. It's helpful when you need to experiment with small fragments of code in order to grasp some new ideas, or to straighten out some complex algorithms. However, you can usually slap together a real Delphi program much faster than you can build a WINCRT program. So, perhaps, WINCRT is on its way to becoming entirely obsolete.

    ***Begin Note***

    The one group of people who have historically been most enamored of the WINCRT unit are students who are working with a teacher who uses a DOS- or UNIX-based Pascal compiler. Many of these people use WINCRT to imitate the types of programs their teachers have created using Turbo Pascal 6 or 7 for DOS. If you find yourself in this position, you should probably try to pick up a DOS compiler to use while you are in the course. My experience is that it's best to use the same compiler as your fellow students, because it helps to level the playing field. In this case, you will almost always be at a disadvantage if you're using a different tool from your teacher. However, students can get by using Delphi if they are willing to do some extra work. Also, if you get a chance, you might show Delphi to your teacher, so that he or she can see that there is something new, and better, in the world.

    ***End Note***

    Summary

    This chapter covers a great deal of material that is essential to Delphi programming. Specifically, you learned the following:

    • The major parts of a program
    • The way project files and unit files interact
    • How to create and use procedures
    • How to pass parameters to a procedure
    • How to recognize many of the important controls such as edit controls, labels, buttons, radio buttons, and group boxes

    Despite the wide range of material covered in this chapter, most of the information was not particularly difficult. You've seen a lot of technical terms in this chapter, but the actual programming skills you've been asked to master have been fairly simple. For example, it's a breeze to slap a few controls onto a form and create a few methods that make them hop through some hoops.

    Visual programming is fun. Just relax and play around with the compiler for awhile, taking the time to create a few methods and change the text in an edit control or two. Delphi can be a very simple tool to use. At the same time, it gives you enormous benefits in terms of programming capabilities. The more fully you understand the information in this chapter, the more fully you'll be able to exploit the capabilities of this programming environment.

    Delphi Programming Unleashed by Charlie Calvert 04-

    Server Response from: ETNASC04