Sunteți pe pagina 1din 36

December 2002, Volume 8 Number 12

Delphi 7
Web Services

UDDI
Browser,
SOAP
Attachments,
Custom
Headers,
and More
Cover Art By: Arthur A. Dugoni Jr.

ON THE COVER
5

First Look

22

Delphi 101

Delphi 7 Web Services Bill Todd

From Class to Component: Part I Rick Spence

Showing by doing, Bill Todd introduces the expanded Web Service features of
Delphi 7 including its UDDI browser, and SOAP attachment and custom SOAP
headers capabilities by sharing demonstration client and server applications.

Rick Spence makes it easy to start writing components with this two-part
series. This month he explains how to create a component based on an
existing class, general rules for component writing, tips, pitfalls, and more.

FEATURES

REVIEWS

10

27

On the Net

Fill HTML Forms Automatically Corbin Dunn

Borland R&D Engineer Corbin Dunn shows how you can easily write a program
that submits a URL to search engines, ads to classified sites, shareware to
shareware sites, or anything else that involves filling out a form on a Web site.

13

31
34

Greater Delphi

17

Sound+Vision

Genetic Algorithms Fernando Vicaria

Borland QA Engineer Fernando Vicaria introduces genetic algorithms in the


context of software engineering, then shares a sample Delphi application that
solves the classic traveling salesperson problem. Be sure to download the
impressive demo app!

WPTools Version 4

Product Review by Alan C. Moore, Ph.D.

The Tomes of Delphi: Win32 Core API,


Windows 2000 Edition
Book Review by Mike Riley

Statistically Enable Your Delphi Apps Fred Edberg

Exploring the design, database, and statistical issues, Fred Edberg shares his
BVStatClientDataSet component, which provides Delphi developers with an easy
way to incorporate basic statistical functions into their applications.

AQtime 2.0

Product Review by Robert Leahey

DEPARTMENTS
2

Symposium by Jerry Coffey

Delphi Tools

35

File | New by Alan C. Moore, Ph.D.

Symposium
DelphiZineNOW
Your subscription to Delphi Informant just got more valuable, more timely,
more varied, and more fun. Thats because weve launched an e-newsletter companion to our print magazine. Its named DelphiZineNOW, and
if youre a subscriber to DI and we have your e-mail address youve
already received the premiere issue.
DelphiZineNOW will be sent free to all subscribers including trial subscribers. It will be monthly to begin with, and I plan to produce it twicea-month as soon as I can. In each issue Ill point readers to links of interest
on www.DelphiZine.com. Much of the content will appear in the print
magazine; however, some material will never appear in DI. Everything, of
course, will be published at www.DelphiZine.com.
Its free, but dont miss out. Just because youre a subscriber doesnt
mean that we necessarily have your correct e-mail address or any email address for that matter. To check, just go to www.DelphiZine.com
and select Change of Address from the menu on the left-hand side of
the page. That way youll get DelphiZineNOW delivered to your mail
box every month.
So what can you expect from DelphiZineNOW? For one thing, its a great
way to find out about new content on the DelphiZine Web site. And, via
DelphiZineNOW, youll receive links to material before anyone else. Theres
a lot going on in the Delphi/Borland universe, now and in the near future,
so its a particularly important time to stay current.
Take Delphi 7 Studio for instance. It was first discussed in the July 2002
issue of DI in my editorial in which I shared the startling amount of
information made available at the annual Borland conference. Delphi 7
Studio got the full-length article treatment by Cary Jensen in the October
issue. DelphiZineNOW readers would have had that material available to
them up to a month earlier.
Now Delphi for .NET (not its real name) is just around the corner.
Weve already had in-depth articles about this exciting new departure
for Delphi and Borland outstanding among them was Jon Shemitz
Borlands .NET Plans which appeared in the August 2002 DI. Our
coverage has continued ever since, and weve got some great .NETrelated material coming up, from an in-depth series that introduces the
.NET Framework to Delphi developers (by Alexei Fedorov and Natalia
Elmanova), to an example-laden exploration of the Delphi for .NET
Compiler Preview (by Jani Jrvinen).
Which brings up an interesting aside. Alexei and Natalia (who are Russian), and Jani (a Finn) are excellent examples of just how international
the Delphi developer community has become. In fact, when we mailed
the December 2002 Delphi Informant, 49% were sent to non-U.S. subscribers. If youre an international subscriber, DelphiZineNOW is even
more valuable. Depending on the vagaries of the worlds various postal
services, you could be getting your content a full two months earlier, or
even sooner.
And although you can always get in touch with me Ive long contended that Im all-too-easy to contact the e-newsletter gives you yet
another opportunity (excuse?) to send an e-mail my way. Im always
keenly interested to hear what you have to say, positive and/or negative,
about our humble publications. I dont hear from you often enough, so
please let me know what you think.
Thanks for reading.

Jerry Coffey, Editor-in-Chief


jcoffey@DelphiZine.com
2 December 2002 Delphi Informant Magazine

Delphi
T O O L S

New Products
and Solutions

Book Picks
Windows XP Under the Hood
Brian Knittel
QUE

ObjectVenture Ships ObjectAssembler 2.0.4


ObjectVenture announced
ObjectAssembler 2.0.4, the
latest edition of its Java
2 Platform, Enterprise
Edition (J2EE) development
environment that supports
the pattern-based design,
development, assembly, and
deployment of enterprise
applications. The company
also announced their free
Web-based software pattern
tutorials (available at
www.objectventure.com/
tutorials) and animated
product demonstrations
(available at
www.objectventure.com/
demos.html).
The ObjectAssembler 2.0.4
release is intended to strengthen
the support for Sun ONE
Studio/NetBeans management
of project source files and
add a number of features to
increase support for automated
patterns in the ObjectAssembler
integrated design and

development environment. It
will support connector roles
and add markup components
to use when mapping pattern
strategies; expand and
document the Source Code
Markup Language (SCML)
capabilities; extend support for
Struts 1.1; add Customer Tag,
XML Page, and HTML Page

components; provide pattern


and component viewlets; and
include a full online Help
system.
ObjectVenture, Inc.
Price: Professional Edition, US$499;
Enterprise Edition, US$1,999
Contact: info@objectventure.com
Web Site: www.objectventure.com

TurboPower Ships Memory Sleuth 3


ISBN: 0-7897-2733-1
Cover Price: US$39.99
(736 pages)
www.quepublishing.com

SAMS Teach Yourself Crystal


Reports 9 in 24 Hours
Joe Estes, et al.
SAMS

TurboPower announced
Memory Sleuth 3, a major new
upgrade to its debugging tool
for professional Windows
programmers. Memory
Sleuth 3 works by watching
a Windows application while
it runs. It then reports any
problems it finds during
the applications execution.
Unlike some other debugging
products, Memory Sleuth
requires no changes to a
programs source code; a
Memory Sleuth user simply

compiles their project with


the compilers debugging
information turned on. When
Memory Sleuth finds an error,
it shows the programmer
exactly where in their source
code the error starts.
New features in Memory
Sleuth 3 include API function
failure detection, optional
suppression of dependent
leaks, RTTI memory type
detection, memory overwrite
detection, and a project-based
interface. Memory Sleuth 3 is

fully compatible with programs


compiled with Delphi 2
(and later), C++Builder (any
version), Visual C++ 6, and
Visual Basic 6. It requires no
source code changes to work.

SDL_Image.h, SMPEG.h, SDL_


sound, and the SFont library.
The latest release
for Windows can be
downloaded from http:
//codecentral.borland.com/
codecentral/ccweb.exe/
listing?id=18581, and
for Linux from http:
//codecentral.borland.com/
codecentral/ccweb.exe/
listing?id=18333.
Improvements made in JEDISDL v0.5 include FreePascal

support, an updated
SDL_Mixer with an Effects API,
and demo directories split into
2D- and 3D-related subdirectories.
The JEDI-SDL project is also
available on Source Forge
(http://sf.net/projects/jedi-sdl/).

TurboPower Software Company


Price: US$199, with upgrades from
version 2 at US$79 and upgrades from
other versions at US$139. Registered
customers of other TurboPower products
can order Memory Sleuth 3 for US$159.
Contact: (800) 333-4160
Web Site: www.turbopower.com

JEDI-SDL v0.5 Released


ISBN: 0-672-32090-8
Cover Price: US$24.99
(528 pages)
www.samspublishing.com

3 December 2002 Delphi Informant Magazine

Version 0.5 of JEDI-SDL, the


Object Pascal headers for Simple
DirectMedia Layer (SDL), has
been announced. JEDI-SDL
allows you to access all the
functions within the SDL libraries
under both Windows and Linux,
so you can write cross-platform
games or multimedia applications.
The headers have been converted,
comments and all, to a Delphi
unit called SDL.pas. Other
completed conversions are
SDL_Mixer.h, SDL_Net.h,

Project JEDI
Price: Free
Contact: Dominique@
SavageSoftware.com.au
Web Site: www.delphi-jedi.org/
Jedi:TEAM_SDL_HOME

Delphi
T O O L S

New Products
and Solutions

Book Picks
.NET Development for
Java Programmers
Paul Gibbons
Apress

ISBN: 1-59059-038-4
Cover Price: US$49.95
(432 pages)
www.apress.com

Hacking Exposed
Web Applications
Joel Scambray and Mike Shema
McGraw-Hill Osborne

ISBN: 0-07-222438-X
Cover Price: US$49.99
(386 pages)
www.osborne.com

4 December 2002 Delphi Informant Magazine

New Version of InstallShield Professional Available


InstallShield announced
InstallShield Professional 7.
Professional 7 offers software
developers an installation
solution for the creation of nonWindows Installer (MSI)-based
software installations. Professional 7 has over 40 new features
and enhancements, including
source code control integration,
automated update capabilities,
and Microsoft .NET support.
New update capabilities enable
software developers to manage
the entire update lifecycle from
within a single tool. Professional 7 offers a Media Wizard
that creates software updates
using advanced differencing
technology. The Wizard creates a
difference file that contains only
the changes between versions,
reducing the size of each update.
The file differences are then
distributed as part of an update
setup created in Professional
7, which includes an updateaware script allowing developers
to fully customize the update
setup experience.
Professional 7 also offers
direct integration with the

InstallShield Update Service, a


subscription-based Web service
that delivers immediate updates
through a software update platform. Using a complimentary
six-month trial, developers can
deliver updates and send messages to customers via a built-in
update service.
Professional 7 offers fully
integrated source code control
support, enabling developers to
manage installation projects the
same way they manage other
code and ensure proper versioning control during development.

Using a new project format


specifically designed to support
source code control activities,
Professional 7 facilitates the
usage of virtually all the leading
source code control solutions.
It supports Microsoft Visual
SourceSafe, Merant PVCS, and
other solutions that support
the Microsoft Common Source
Code Control Interface.

Delphi developers an affordable


way in which to fully leverage
Delphi and build highly evolved
and flexible Windows and Web
applications.
Products included are
ASTA IO, DBISAM, Direct
Oracle Access, DXSock,
Enterprise Architect,
ExpressQuantumGrid Suite,
ExpressWeb Framework,
IB Objects, MSADODAC,

MySQLDAC, RemObjects
SDK, ReportBuilder, and
SourceConneXion+. The
retail price of all of these
components is over US$4,000.
Project Dionysus offers all
these tools for US$1,795.

to select different content for


different versions of their Help
system. This is ideal for a Help
author who needs to create two
builds of their Help system for
one application. In addition,
the Context-Sensitive Help
Toolkit assists in linking the
Help system to any Delphi,
C, C++, Visual Basic, Java, or
Web-based application.
Other significant features in
RoboHelp X3 include a new

way to auto-create hyperlinks


by dragging and dropping,
a Windows XP-compatible
interface, and an add-on
WebHelp Merge Module that
allows users to easily generate
and merge multiple browserbased Help systems.

InstallShield Software Corp.


Price: US$1,199
Contact: (800) 374-4353
Web Site: www.installshield.com

Introducing Project Dionysus


Project Dionysus is the
combined effort of 11 leading
vendors of component solutions
for Delphi. Each component of
Dionysus is the flagship product
from one of those vendors, and
each represents a productivity
boost in your Delphi
development process. Project
Dionysus is a collection of 13
individual component libraries
carefully selected to offer

Project Dionysus
Price: US$1,795
Contact: support@projectdionysus.com
Web Site: www.projectdionysus.com

eHelp Delivers RoboHelp X3


eHelp announced RoboHelp
X3, which creates multiple
Help systems in various online
formats, plus print-quality
documentation. This product
provides flexibility to software developers and technical
writers with conditional text,
airplane Help, single-sourcing, customizable skins, Help
system merging, new usability
features, and much more.
Conditional text allows users

eHelp Corp.
Price: See Web site for pricing.
Contact: robohelpinfo@ehelp.com
Web Site: www.ehelp.com

First Look
Web Services / UDDI / SOAP / Delphi 7

By Bill Todd

Delphi 7 Web Services


UDDI Browser, SOAP Attachments, Custom Headers,
and More

elphi 6 provided pioneering support for Web Service clients and servers. Delphi 7
expands those capabilities with important new features, starting with a UDDI browser.
UDDI is an acronym for Universal Description, Discovery, and Integration. UDDI provides a
standard way to register Web Services in any of the public registries available on the Internet.
The UDDI browser is integrated into the WSDL
Import Wizard in the Object Repository. To use
it, just select File | New | Other to open the Object
Repository, then click the Web Services tab.
Double-click the WSDL Importer icon to display the
WSDL Import Wizard shown in Figure 1. Then
click the Search UDDI button to open the UDDI
Browser shown in Figure 2.
Drop down the Registry combo box to choose one of
the pre-defined public registries. To use a registry that
isnt listed, just type its name into the combo box. This
lets you use a private UDDI registry on your local
network, or any other registry with which you want to
work. Since UDDI registries are organized hierarchically by business name and then service, you need to
enter the name of the business you want to search for
in the Business Name edit box. This is a starts with

search. In Figure 2, I entered the letter B, clicked


the Find button, and got a list of all companies whose
name starts with B. You can change the search to
require an exact match and/or make it case sensitive
with the check boxes in the Search For group box.
When you click the Find button, the results of your
search appear in the tree view in the upper-right
corner. Each node is a business; expand the node
for a business to see the services provided by that
business. Expand the service to see the TModel for
the service. A TModel is a generic data structure
used by UDDI to describe a particular service
type. When you select a TModel, the Import WSDL
button is enabled. Click it to return to the WSDL
Importer and finish importing the service.

Using Attachments
Delphi 7 adds support for SOAP attachments via
the TSOAPAttachment object, a descendant of
TRemotable. The best way to understand SOAP
attachments is by using them in an application. Begin
by creating a SOAP server application following the
steps in my article Building Web Services: Part I
in the July 2002 Delphi Informant Magazine (also
available online at http://www.delphizine.com/
features/2002/07/di200207bt_f/di200207bt_f.asp).
Name the server AttachmentDemo. This server will
be able to send and receive both binary and text files
as SOAP attachments. Figure 3 shows the invokable
interface declaration from the AttachmentDemoIntf
unit, with four methods added.

Figure 1: The WSDL Import Wizard.


5 December 2002 Delphi Informant Magazine

The GetImageFile function takes the file name


as a parameter and returns a TSOAPAttachment
object. A call to this method sends the file named
in the FileName parameter to the SOAP client as
an attachment. The SendImageFile method lets the
client send a file to the server. The file name and

First Look
the TSOAPAttachment object
are passed as parameters. The
declarations for the GetTextFile
and SendTextFile methods
are identical, although their
implementations are different.
Figure 4 shows the
implementation of the
GetImageFile method. All
you have to do is create the
TSOAPAttachment object and
call its SetSourceFile method
to specify the name of the
file that contains the data
to be sent as an attachment.
Calling SetSourceFile causes the
TSOAPAttachment object to
load the file and send it to the
client. The TSOAPAttachment
object is freed automatically
after the client has received the
attachment.

Figure 2: The UDDI Browser.

{ Invokable interfaces must derive from IInvokable. }


IAttachmentDemo = interface(IInvokable)
['{350EACE8-BD0A-4AC6-B8CC-05B7B658A8D9}']
{ The methods of Invokable interface must not use the
default calling convention; stdcall is recommended. }
function GetImageFile(FileName: string): TSOAPAttachment;
stdcall;
procedure SendImageFile(FileName: string;
var AttachedFile: TSOAPAttachment); stdcall;
function GetTextFile(FileName: string): TSOAPAttachment;
stdcall;
procedure SendTextFile(FileName: string;
var AttachedFile: TSOAPAttachment); stdcall;
end;

Figure 3: Declaring the servers invokable interface.

function TAttachmentDemo.GetImageFile(FileName: string):


TSOAPAttachment;
// Return a binary file to the client.
begin
Result := TSOAPAttachment.Create;
Result.SetSourceFile(FileName);
end;

Figure 4: The GetImageFile method.

procedure TAttachmentDemo.SendImageFile(FileName: string;


var AttachedFile: TSOAPAttachment);
// Save a binary file sent by the client.
begin
AttachedFile.SaveToFile(FileName);
end;

Figure 5: The SendImageFile method.


6 December 2002 Delphi Informant Magazine

The implementation of the


SendImageFile method is shown
in Figure 5, and consists of
a single line of code. Calling
the TSOAPAttachments SaveToFile method saves the attachment in
the file specified in the FileName parameter. When the server receives
an attachment, it writes it to a temporary file. You can set the name
and location of the temporary file using the TSOAPAttachment
CacheFile property. By default, the temporary file is deleted when
the TSOAPAttachment object is freed. To save the cache file, set the
CacheFilePersist property to True before freeing the TSOAPAttachment.
Note that Delphi SOAP attachments are marshaled as MIME
multipart forms. Some Web Services, most notably .NET, use
DIME multipart forms for attachments. TSOAPAttachment is not
compatible with services that use DIME.
Another important limitation of TSOAPAttachment is that no
encoding is performed on the attachment data. The sample server
described here will work to send data across your local network or
any other 8-bit network, but not across the 7-bit Internet. If you
want to send a binary file as an attachment, you must encode the data
before sending it and decode it on the other end. There are a number
of freeware Base64 encoding components available on the Internet
function TAttachmentDemo.GetTextFile(FileName: string):
TSOAPAttachment;
var
Fs: TFileStream;
begin
Fs := TFileStream.Create(FileName, fmOpenRead);
Result := TSOAPAttachment.Create;
with Result do begin
SetSourceStream(Fs, soOwned);
// Setting SourceStream sets ContentType to
// Application/binary.
ContentType := 'Text/simple';
end;
end;

Figure 6: The GetTextFile method.

First Look
that will handle the encoding and decoding
for you. You can use TSOAPAttachments
Encoding property to tell the client how the
attachment is encoded. Valid values are:

BASE64
QUOTED-PRINTABLE
8BIT
7BIT
BINARY
x -EncodingName

TSOAPAttachment can also get its data


from a stream, as shown in the code for the
GetTextFile method in Figure 6. In this code, a
TFileStream object is created to open the text
file to be sent to the client. After creating the
TSOAPAttachment object, GetTextFile calls the
SetSourceStream method of TSOAPAttachment,
passing the FileStream as the first parameter,
and the constant soOwned as the second. The
soOwned parameter tells TSOAPAttachment that
it should free the FileStream when its no longer
Figure 7: The attachment client main form.
needed. The soOwned parameter is also assigned
to the TSOAPAttachments Ownership property, and you can change this
procedure TMainForm.GetFileBtnClick(Sender:
property at any time if you need to change the ownership of the stream.
Calling SetSourceStream sets the TSOAPAttachments ContentType
property to Application/binary. In this case, Text/simple is a more
appropriate content type. TSOAPAttachment doesnt use the ContentType
property. Its provided as a standard way to pass information about the
attachment content between the client and server. The SendTextFile
method is identical to the SendImageFile method shown in Figure 5.
Figure 7 shows the main form of the client application. Create the
basic client application as described in Building Web Services:
Part I. Figure 8 contains the OnClick event handler for the
Get File button in the Send & Receive Binary File group box. This
method starts by declaring a variable of type TSOAPAttachment
to receive the attachment returned by the server. The next
line calls the servers GetImageFile method and assigns the
TSOAPAttachment returned by GetImageFile to the Attachment
variable. A call to the TSOAPAttachment.SaveToFile method saves
the file on the client. The Headers property of TSOAPAttachment
contains all the SOAP headers in the attachment. Assigning the
Headers property to the Memos Lines property allows you to see
the attachments headers.
Sending a file is even easier than receiving one. Figure 9 shows the
code from the binary Send File buttons OnClick event handler.
This method creates an instance of TSOAPAttachment and calls its
SetSourceFile method to set the name of the file. Then it calls the
servers SendImageFile method and passes the file name and the
TSOAPAttachment instance as parameters.
The code for sending and receiving text files shows some slightly
different techniques. Figure 10 shows the OnClick event handler
for the Get File button. This method declares a TSOAPAttachment
instance variable, and then calls the GetTextFile method on the
server and assigns the returned TSOAPAttachment to that variable.
Next, the method creates a StringStream and saves the attachment to
the stream. Finally, it clears the Memo components Lines property
and adds the StringStreams DataString property to the Lines
property, so the contents of the text file appear in the Memo.
7 December 2002 Delphi Informant Magazine

TObject);
var
Attachment: TSOAPAttachment;
begin
try
// Get the file.
Attachment := (RIO as IAttachmentDemo).GetImageFile(
FileNameEdit.Text);
// Save the file.
Attachment.SaveToFile(FileNameEdit.Text);
// Show the attachment headers in the memo.
Memo.Lines.Clear;
Memo.Lines := Attachment.Headers;
// Show the file name and size.
ShowMessage('File ' + FileNameEdit.Text +
' successfully saved. ' + 'File size = ' +
Attachment.Headers.Values['Content-Length'] +
' bytes.');
finally
Attachment.Free;
end;
end;

Figure 8: The binary Get File buttons OnClick event handler.


procedure TMainForm.SendFileBtnClick(Sender: TObject);
var
Attachment: TSOAPAttachment;
begin
Attachment := TSOAPAttachment.Create;
try
Attachment.SetSourceFile(FileNameEdit.Text);
(RIO as IAttachmentDemo).SendImageFile(
FileNameEdit.Text, Attachment);
ShowMessage('File ' + FileNameEdit.Text +
' sent successfully.');
finally
Attachment.Free;
end;
end;

Figure 9: The binary Send File buttons OnClick event handler.

Figure 11 shows the Send File buttons OnClick event handler. This
method creates a TSOAPAttachment instance and assigns the Memo

First Look
procedure TMainForm.GetTextBtnClick(Sender: TObject);
var
Attachment: TSOAPAttachment;
StrStream: TStringStream;
begin
try
Attachment := (RIO as IAttachmentDemo).GetTextFile(
TextFileNameEdit.Text);
StrStream := TStringStream.Create('');
try
Attachment.SaveToStream(StrStream);
Memo.Lines.Clear;
Memo.Lines.Add(StrStream.DataString);
finally
StrStream.Free;
end;
finally
Attachment.Free;
end;
end;

type
TSecurityHeader = class(TSOAPHeader)
private
FSecurityCode: Integer;
published
property SecurityCode: Integer
read FSecurityCode write FSecurityCode;
end;

Figure 12: The TSecurityHeader class declaration.


initialization
{ Invokable interfaces must be registered. }
InvRegistry.RegisterInterface(TypeInfo(IHeaderDemo));
InvRegistry.RegisterHeaderClass(TypeInfo(IHeaderDemo),
TSecurityHeader);

Figure 13: Registering the custom header class.

Figure 10: The text Get File buttons OnClick event handler.
procedure TMainForm.SendTextBtnClick(Sender: TObject);
var
Attachment: TSOAPAttachment;
begin
Attachment := TSOAPAttachment.Create;
try
Attachment.SourceString := Memo.Text;
(RIO as IAttachmentDemo).SendTextFile(
TextFileNameEdit.Text, Attachment);
ShowMessage('File ' + TextFileNameEdit.Text +
' sent successfully.');
finally
Attachment.Free;
end;
end;

Figure 11: The text Send File buttons OnClick event handler.

components Text property to the TSOAPAttachments SourceString


property. Next, a call to the servers SendTextFile method sends
the attachment as its second parameter. Note that when you send
ASCII text you do not have to encode it to ensure safe transmission
across the Internet (ASCII is a 7-bit character set).

Sending Custom Headers


SOAP data packets include a set of header nodes. With Delphi 7, you
can add your own custom headers to the messages sent between your
SOAP client and server. SOAP headers are useful for passing information
thats not specific to one method call. Examples might include security
or state information thats required by all of the server methods you call.
Headers are implemented using the TSOAPHeader class that descends
from TRemotable. To add a custom header to an existing SOAP server,
begin by adding the declaration for your TSOAPHeader descendant
to the servers interface unit. Figure 12 shows the declaration that was
added to the type section of the sample application. TSecurityHeader
has a single integer property named SecurityCode. However, as with
any remotable object, you can add as many published properties as you
wish. Your custom header class must also be registered in the invokable
registry just like any other TRemotable descendant. Figure 13 shows
the initialization section of the servers interface unit, with the call to
InvRegistry.RegisterHeaderClass added.
The sample SOAP server includes a single method (shown in
Figure 14). This method receives a string parameter and returns
8 December 2002 Delphi Informant Magazine

function THeaderDemo.ReturnHeader(Msg: string): string;


var
Headers: ISOAPHeaders;
ClientHeader: TSecurityHeader;
ServerHeader: TSecurityHeader;
begin
Result := Msg;
// Get the header sent by the client and return it.
Headers := Self as ISOAPHeaders;
Headers.Get(TSecurityHeader, TSOAPHeader(ClientHeader));
if Assigned(ClientHeader) then
begin
Result :=
Result + IntToStr(ClientHeader.SecurityCode);
ClientHeader.Free;
end
else
Result := Result + ' (ClientHeader nil)';
// Send a new header back to the client.
ServerHeader := TSecurityHeader.Create;
ServerHeader.SecurityCode := 99999;
Headers.OwnsSentHeaders := True;
Headers.Send(ServerHeader);
end;

Figure 14: The ReturnHeader method.

that string along with the value of the SecurityCode property of the
TSecurityHeader object shown in Figure 12. The first line simply
assigns the Msg parameter to the Result string.
Access to the message headers is provided by the ISOAPHeaders
interface. ISOAPHeaders is implemented by TInvokableClass. The class
created by the SOAP server application wizard, THeaderDemo in this
example, descends from TInvokableClass. This means you can get a reference to ISOAPHeaders by casting Self as ISOAPHeaders. In Figure 14, the
ISOAPHeaders interface reference is assigned to the Headers variable.
The next step is to get a reference to the header in which youre
interested. Two variables, ClientHeader and ServerHeader, are used to
hold references to TSecurityHeader objects. To get a reference to the
header received from the client as part of the call to the ReturnHeader
method, the ISOAPHeaders Get method is called with two parameters.
The first is the class name of the header to which you want a reference,
and the second is the variable to which the reference will be assigned.
If the header isnt found, the call to Get will set ClientHeader to nil.
The if statement ensures that ClientHeader has been assigned a value
before using it to get the value of the SecurityCode property. The value of

First Look

Figure 15: The clients main form.

SecurityCode is concatenated onto the Result string thats returned by this


function, and the header object is freed.
This method also sends a custom header back to the client in the
message that carries the return value. The first step in sending the return
message is to create an instance of TSecurityHeader and assign a value to
its SecurityCode property. The next step is to set the OwnsSentHeaders
property of the ISOAPHeaders interface to True, so the TSecurityHeader
object will be freed automatically after it has been sent. Finally, a call to
the ISOAPHeaders.Send method sends the custom header.
Headers have two built-in properties that you may want to use. The first
is MustUnderstand. If this property is set to True, the application that
receives the header must be able to receive and process it. If it cannot,
it must not process any part of the message. The second property is
Actor. If a header has MustUnderstand set to True, and if the message
that contains the header will be forwarded by the recipient to another
application for processing, you must assign the URI of the application
that will process the message to the Actor property. This tells the
application that initially receives the message that it can forward the
message, even though it contains a MustUnderstand header, because the
application identified by Actor is responsible for processing the header.
Now lets look at custom headers from the client side. Figure 15 shows
the client applications main form, and Figure 16 shows the code
from the Call buttons OnClick event handler. This method declares
three variables: S of type string, Headers of type ISOAPHeaders, and
ClientHeader of type TSecurityHeader. The first block of code sends
the custom header to the server when the client calls the servers
ReturnHeader method.
The first step is to get a reference to the servers ISOAPHeader
interface by casting the THTTPRIO object as ISOAPHeaders. The
next line creates an instance of TSecurityHeader and the following
line assigns the value 11111 to its SecurityCode property. Next,
the ISOAPHeaders.OwnsSendHeaders property is set to True, so the
TSecurityHeader instance will be freed automatically when it is sent.
Finally, a call to the ISOAPHeaders.Send method sends the header.

9 December 2002 Delphi Informant Magazine

procedure TForm1.CallBtnClick(Sender: TObject);


var
S: string;
Headers: ISOAPHeaders;
ClientHeader: TSecurityHeader;
begin
// Send the header.
Headers := RIO as ISOAPHeaders;
ClientHeader := TSecurityHeader.Create;
ClientHeader.SecurityCode := 11111;
Headers.OwnsSentHeaders := True;
Headers.Send(ClientHeader);
// Call the ReturnHeader method of the server.
S := (RIO as IHeaderDemo).ReturnHeader(
'Header from client: ');
// Display the returned value.
Memo.Lines.Clear;
Memo.Lines.Add(S);
// Get the header returned by the server.
Headers.Get(TSecurityHeader, TSOAPHeader(ClientHeader));
if Assigned(ClientHeader) then
Memo.Lines.Add('Header from server: ' +
IntToStr(ClientHeader.SecurityCode))
else
Memo.Lines.Add('Header from server: nil');
end;

Figure 16: The Call buttons OnClick event handler.

The next line in the OnClick event handler calls the servers ReturnHeader
method and passes the string "Header from client: " as its parameter.
The remaining code clears the Memo control and displays the header
returned by the server. The call to ISOAPHeaders.Get receives a reference
to the TSecurityHeader returned by the server and assigns it to the
ClientHeader variable. If a TSecurityHeader was returned, its SecurityCode
property is converted to a string and displayed in the Memo. As you can
see, custom headers provide a convenient way to pass any number of
values back and forth between the client and server.

Conclusion
In addition to the major new features described here, there are
many other new features to help you implement SOAP services in
Delphi 7. These include new events on several components, the
ability to implement your own routines to handle the conversion
of remotable objects to and from their SOAP representation, and
ERemotableException exceptions. See Whats new in Delphi 7 in
the online help for details.
The sample server and client projects referenced in this article are
available on the Delphi Informant Magazine Complete Works CD
located in INFORM\2002\DEC\DI200212BT.

Bill Todd is president of The Database Group, Inc., a database consulting and
development firm based near Phoenix. He is co-author of four database programming books, author of more than 90 articles, a contributing editor to Delphi Informant Magazine, and a member of Team B, which provides technical support on the
Borland Internet newsgroups. Bill is a nationally known trainer and is a frequent
speaker at Borland Developer Conferences in the United States and Europe. He has
taught Delphi programming classes across the country and overseas. Readers may
reach him at bill@dbginc.com.

On the Net
HTML Forms / Indy / Delphi

By Corbin Dunn

Fill HTML Forms Automatically


Indy Makes it Easy

ow many times have you visited a Web site and had to fill out the same
information again and again? Probably more times than you wanted. Luckily, as
a programmer, you can automate the process of submitting forms on the Web.
You can easily write a program that submits a
URL to search engines, ads to classified sites,
shareware to shareware sites, or anything else
that involves filling out a form on a Web site.
In this article, youll see how to submit a simple
guest-book entry on my Web site at http://
www.cruzio.com/~seaweb/corbin/guestbook.html.

Analyze the HTML


Browse to my guest-book URL and view the
source of the page. The form on this page is a
simple example, as Figure 1 shows. The first
thing to notice is the forms METHOD attribute,
which determines how the Web browser will send
the information to the server. In this example, its
set to POST, which means a browser will send a
header followed by the complete POST data. The
other method is GET, which means the data will
be sent in the headers request URL. If a form
doesnt have a method, it defaults to GET.
You typically see GET requests encoded in
the URL, such as this search for corbin on
Google: http://www.google.com/search?q=corbin.
Everything from the last forward slash to the
<FORM METHOD=POST ACTION=
"http://www.cruzio.com/~seaweb/corbin/guestbook.cgi">
Your Name: <INPUT TYPE=TEXT NAME=realname SIZE=30><BR>
E-Mail: <INPUT TYPE=TEXT NAME=username SIZE=40><BR>
URL: <INPUT TYPE=TEXT NAME=url SIZE=50><BR>
City: <INPUT TYPE=TEXT NAME=city SIZE=15>,
State: <INPUT TYPE=TEXT NAME=state SIZE=2>
Country: <INPUT TYPE=TEXT value=USA NAME=country SIZE=15>
<P>Comments:<BR>
<TEXTAREA NAME=comments COLS=60 ROWS=4></TEXTAREA><P>
<INPUT TYPE=SUBMIT> * <INPUT TYPE=RESET>
</FORM>

Figure 1: The complete source for the guest-book HTML form.


10 December 2002 Delphi Informant Magazine

question mark is the CGI program that will be


executing your request. After the question mark,
you will see URL-encoded name=value pairs
separated by ampersands. A CGI program on the
server will decode the name=value pairs and use
them as parameters to generate the HTML result.
When simulating a request to a server, you will
have to generate the encoded parameters from
what you see in the HTML form.

Creating the User Interface


Its simple to create an application that submits
guest-book forms; start Delphi and select File |
New | Application. Drop down some edit boxes,
labels, and a button to create a user interface
similar to that shown in Figure 2.
Now, you could do plain socket communication to
generate a full HTTP request, send it to the server,
receive the response, and decode it. Or, you could
simply use the Indy TIdHTTP component to do the
dirty work. Delphi developers are used to simplicity,
so you should choose the latter and drop a TIdHTTP
component down from the Indy Clients tab.
While youre at it, drop a TIdAntiFreeze
component down from the Indy Misc tab. All
Indy components use blocking sockets, which
means that any network operation will block
until the request has been completed. This has
the nasty side effect of freezing your application
until the operation is complete. The TIdAntiFreeze
component will help prevent the application from
freezing. It may still freeze for short moments,
however, while certain operations occur. To
eliminate this, the socket operations could be
placed in a TThreads Execute method, but Ill
leave that for another discussion.

On the Net
procedure TForm1.FormCreate(Sender: TObject);
begin
SetLength(FTempFileName, MAX_PATH + 1);
GetTempFileName(PChar(ExtractFilePath(
Application.ExeName)), 'crb', 0,
PChar(FTempFileName));
SetLength(FTempFileName, StrLen(PChar(FTempFileName)));
{ Delete the temp file that Windows just created... }
DeleteFile(FTempFileName);
{ ...and make it an HTML temp file. }
FTempFileName := FTempFileName + '.html';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if FileExists(FTempFileName) then
DeleteFile(FTempFileName)
end;

Figure 3: Code for creating and destroying a temporary file.

Figure 2: The form-submitting application.

Rename the TIdHTTP component to httpClient. Then add


these declarations to the private section of the TForm1 unit:
FTempFileName: string;
FResponseStream: TStream;

To easily show the resulting HTML that will be returned from a


request, you need to create a temporary HTML file in which to
store the data. Double-click on the form and add the code you
see in Figure 3 to create a temporary file. While you are at it, add
some clean-up code in the forms OnDestroy event (also shown in
Figure 3).

Using Indy for the Dirty Work


Now, use the HTML source from Figure 1 to generate the data
in a form submittal. Each INPUT or TEXTAREA tags NAME
attribute seen in the HTML will become a name=value pair in
a string that you send to the server. For example, for the first
INPUT tag, you see the NAME attribute value of realname.
For the second, you see username, and so on. To concatenate all
the inputs into one string, an HTTP request separates each item
with an ampersand. This would allow you to generate a single
string, such as realname=Corbin&username=corbin@cruzio.com,
that you would send to the server.
One thing I havent mentioned yet is how to handle special
characters in the request string, such as ampersands, spaces, and
equal signs. Again, Indy comes to the rescue with the URLEncode
function found in the IdGlobal unit. Add IdGlobal and ShellApi to
your uses list, then double-click on the forms button. Add the code
you see in Listing One to the buttons OnClick event handler.
Notice that PostStr is a single URL-encoded string with all the data.
The actual post to the server is made with the call to httpClient.Post.
The URL used in the POST method is set to the value of the forms
ACTION attribute, shown in Figure 1. If you were to do a GET
instead of a POST, it would look something like this:
httpClient.Get('http://YourURL' +
'?' + PostStr, ResponseStream);

11 December 2002 Delphi Informant Magazine

procedure TForm1.httpClientRedirect(Sender: TObject;


var dest: string; var NumRedirect: Integer;
var Handled: Boolean);
begin
Handled := True;
FResponseStream.Position := 0;
httpClient.Get(dest, FResponseStream);
end;

Figure 4: Handling a redirect from the server.

Note the question mark that separates the URL from the encoded string.
Once the post is done, the code saves the HTML response
into a string for easy searching. By looking for some keywords
on the resulting HTML page, you can determine if the post
was successful or not. In this case, if you find the title of my
page, Corbins Guestbook and Treehouse Comments Page, it
was successful. Otherwise, this simple example will show the
resulting HTML page using ShellExecute.
Quite often, a CGI program will redirect the user to a new page,
instead of sending a response back directly. With the TIdHTTP
component, you can handle this easily by following the redirect in
the OnRedirect event. Add the code shown in Figure 4 to do this.

Thats It!
Now, you have the power to submit any HTML form by writing
some simple code. Feel free to download the associated code
for this article. It contains the complete source and a few more
tidbits of information. From search engines to your favorite
informational sites, youve probably already thought of a few
time-saving ways to apply this technique...
The example project referenced in this article is available on
the Delphi Informant Magazine Complete Works CD located in
INFORM\2002\DEC\DI200212CD.

Corbin Dunn is a research and development software engineer at Borland


Software Corporation. He is working on the IDE, but he has dabbled in many
areas, including WebSnap, the VCL, and CLX. In his spare time, you can find
him riding his motorcycle or rock climbing at the local gym. Corbin can be
reached via e-mail at cdunn@borland.com.

On the Net
Begin Listing One
Generating the Request; Reading the Response

if MessageDlg(
'Thanks for submitting your entry!' +
#13#10 +
'Would you like to see resulting page?',

procedure TForm1.btnSubmitClick(Sender: TObject);

mtInformation,[mbYes,mbNo],0) = mrYes then

var

ShellExecute(Handle, 'open',

PostStream: TStringStream;

PChar(FTempFileName), nil, nil,

PostStr: string;

SW_NORMAL);

ResponseStream: TMemoryStream;

end

ResponseStr: string;

else

StartPos, EndPos: Integer;

begin

begin

{ From observing the resulting HTML, one can

PostStr := 'realname=' + URLEncode(edtName.Text) + '&' +

see that the title tag contains the error

'username=' + URLEncode(edtEmail.Text) + '&' +

message: <title>No Comments</title>. Copy

'url=' + URLEncode(edtUrl.Text) + '&' +

this to the user and display it. }

'city=' + URLEncode(edtCity.Text) + '&' +

StartPos := Pos('<title>', ResponseStr);

'state=' + URLEncode(edtState.Text) + '&' +

EndPos := Pos('</title>', ResponseStr);

'country=' + URLEncode(edtCountry.Text) + '&' +

if (StartPos > 0) and (EndPos > 0) then

'comments=' + URLEncode(memComments.Lines.Text);

begin

PostStream := TStringStream.Create(PostStr);

{ Length of <title>. }

try

StartPos := StartPos + 7;

ResponseStream := TMemoryStream.Create;

ResponseStr := Copy(ResponseStr,

try

StartPos, EndPos - StartPos);

{ Save response stream we're using for redirects. }

MessageDlg(

FResponseStream := ResponseStream;

'The following error happened:' +

httpClient.Post(

#13#10 + ResponseStr,mtError,[mbOK],0);

'http://www.cruzio.com/~seaweb/corbin/guestbook.cgi',

end

PostStream, ResponseStream);

else { Just show the user the HTML. }

{ Copy response stream to string so we can

ShellExecute(Handle, 'open',

search it. }

PChar(FTempFileName), nil, nil,

SetLength(ResponseStr, ResponseStream.Size);

SW_NORMAL);

ResponseStream.Position := 0;

end;

ResponseStream.ReadBuffer(

end

ResponseStr[1], ResponseStream.Size);

else

{ Save the stream to a temp file so we can show it

{ Error! Just show any resulting HTML to user. }

in the Web browser. }

ShellExecute(Handle, 'open', PChar(FTempFileName),

ResponseStream.SaveToFile(FTempFileName);
if httpClient.ResponseCode = 200 then
begin
{ Successful HTTP request. See if post worked; if
it took us back to main Guestbook page, then it
worked. Otherwise, something else happened.
Generally, you can look for success keywords
such as "Thank You" }
if Pos('Corbin's Guestbook and Treehouse ' +
'Comments Page', ResponseStr) > 0 then
begin

12 December 2002 Delphi Informant Magazine

nil, nil, SW_NORMAL);


finally
ResponseStream.Free;
end;
finally
PostStream.Free;
end;
end;

End Listing One

Greater Delphi
Statistical Analysis / MIDAS / Database / Delphi 6

By Fred Edberg

Statistically Enable
Your Delphi Apps
Linear Regression, Clustering, Variance, and More

raditionally, database applications provide extensive capabilities for adding, editing, storing, and reporting data. Few, however, have extended such functionality
to make it easy to understand or interpret that data. In this article, Ill demonstrate
methods to incorporate basic but useful statistical functionality into data-based applications. Often, you can add powerful functions with just a few lines of code.

A variety of statistical techniques exists. They range


from the basic univariate statistics taught in introductory statistics courses, to advanced multivariate techniques used in state-of-the-art research, simulations,
and biomedical applications. Ill focus on a basic one:
simple linear regression. This technique undoubtedly
will be familiar to many readers.
The BVStatClientDataSet component provided with
this article gives Delphi developers an easy way to
incorporate basic statistical functions within an application. (The component and a suite of demonstration
projects are available for download; see end of article
for details.) This article explores the design issues that
come into play to give a data-aware component such
functionality. After that, Ill give you a short refresher
of the regression procedure. Finally, Ill describe some
of the most important properties and methods available in the component, and demonstrate their use.
In addition to BVStatClientDataSet, there are
components that offer multivariate functionality.
These other TClientDataSet descendants include
multivariate capabilities, such as linear regression and
supporting functions, a simple clustering algorithm
(minimum distance-to-means), the calculation of
statistical distances, and one-way analysis of variance
(ANOVA). In the interest of brevity, however, Ill
postpone coverage of these techniques to a later time.

Basic Design Considerations


TClientDataSet is an excellent candidate for the
base class of a data-aware statistical component. The
TClientDataSet and TProvider architecture offers
tremendous power and flexibility by caching its data
13 December 2002 Delphi Informant Magazine

in memory. It also provides outstanding performance


characteristics. Since Delphi 6, nearly every Delphi
developer has the option of using TClientDataSet,
because its available in both the Professional and
Enterprise editions.
TClientDataSet allows the retrieval of records from
the database to be logically separated from the
invocation of the statistical methods (functions).
Accordingly, a query can be executed to retrieve a
set of records from which subsets of records can be
derived and summarized through client-side filtering.
Another desirable feature is the relative neutrality with
respect to the database type used. By avoiding the use
of TQuery or TSQLQuery, etc. as the ancestor class,
the component should work equally well with all
types of databases, including those using dbExpress,
dbGo, and MyBase (xml files), and those relying on
the Borland Database Engine.
Ease of deployment is another advantage.
Deploying the MIDAS.dll support library is
much simpler than fully installing the Borland
Database Engine on each client machine.
In fact, when careful attention is given to
application design, developers can eliminate the
need to deploy even the supporting MIDAS.dll
(see Borland Delphi 6 Developers Guide for
more information on this). Now, the necessary
code units can be compiled and linked directly
into the executable. Finally, routines to load
and save data in XML format readily facilitate
the exchange of data. In short, TClientDataSet
offers several qualities that make it an excellent
base class from which to build the component.

Greater Delphi
public
QueryStatRec: TBVRegStatRec;
X: TDataArray;
Y: TDataArray;
Residuals: TDataArray;
PredictedY: TDataArray;
CIUpper, CILower: TDataArray;
PIUpper, PILower: TDataArray;
function CalcRegression: TRegStatRec;
function BVRegressionMemo: string;
...
property N: Integer read fN write fN;
property DF: Integer read fDF write fDF;
property R: Extended read fR write fR;
property XMean: Extended read fXMean write fXMean;
property YMean: Extended read fYMean write fYMean;
property Beta0: Extended read fBeta0 write fBeta0;
property Beta1: Extended read fBeta1 write fBeta1;
property SSXX: Extended read fSSXX write fSSXX;
property SSYY: Extended read fSSYY write fSSYY;
property SSXY: Extended read fSSXY write fSSXY;
property SumX: Extended read fSumX write fSumX;
property SumY: Extended read fSumY write fSumY;
property SumXY: Extended read fSumXY write fSumXY;
property SumX2: Extended read fSumX2 write fSumX2;
property SumY2: Extended read fSumY2 write fSumY2;
property XVariance: Extended
read fXVariance write fXVariance;
property YVariance: Extended
read fYVariance write fYVariance;
property XStdDev: Extended read fXStdDev write fXStdDev;
property YStdDev: Extended read fYStdDev write fYStdDev;
property XMin: Extended read fXMin write fXMin;
property XMax: Extended read fXMax write fXMax;
property YMin: Extended read fYMin write fYMin;
property YMax: Extended read fYMax write fYMax;
property T: Extended read fT write fT;
property RegressionPrepared: Boolean
read fRegressionPrepared write fRegressionPrepared;
...
published
property SignificanceLevel: Integer
read FSignificanceLevel write FSignificanceLevel;
property XField: string read fXField write fXField;
property YField: string read fYField write fYField;
...
end;

Figure 1: Properties, functions, and variable arrays of


BVStatClientDataSet (partial listing).

Note that Borlands MIDAS run-time license may be required,


depending upon the structure of the application when deployed.
Now that you have a basic understanding of the design of the component, its time for a refresher of the simple linear-regression technique.

Statistical Analyses
Before going into more specifics of the technique, let me identify
some important assumptions. First, the data should consist of
samples taken randomly from a larger population of observations (in the examples here, observations are synonymous with
records in a data set). Second, the data values (field values, to
be technically precise) must be of numerical type, e.g. of type
Float or Integer and must represent interval-scale data. Another
important assumption is that the data are independent no data
point should rely upon, or be tied to, other data points in a serial
or other manner. Also, the data shall exhibit a roughly normalshaped distribution. A graphical scatterplot display of the X and
Y data points is often useful to assess the degree to which the data
conforms to the normality assumption.
14 December 2002 Delphi Informant Magazine

Figure 2: Dialog box produced in Example 1, showing the


correlation coefficient r, which results from an invocation of the
CalcRegression method using the fields BETA and CUR_PRICE as
X and Y variables respectively.

The bivariate simple linear regression technique uses a single


independent, or X variable, with a single dependent Y variable.
The goal is to measure the degree of association between Y and X.
An important summary statistic of the procedure is the correlation
coefficient. This example uses Pearsons correlation coefficient, a
summary statistic that quantifies the strength of the association
between the two variables using basic parametric assumptions.
Of course, the usual cautionary statement applies: The resulting
correlation coefficient describes only the degree of association
between the variables, and does not describe nor imply causation.
Many of you will recall the resulting value r is an index value ranging from -1 to 1. Large negative values suggest a strong negative
relationship between the values of the X and Y variables, whereas
large positive values indicate a strong positive relationship. An r
roughly equal to 0 suggests little or no relationship between the
variables. More precisely, the computed regression equation is said
to be of little utility in modeling the linear association between the
values of the X and Y variables.
This formula is advantageous to program, as well. In order to accumulate the necessary sums and sums of squares, one full scan of the
data set is necessary. This is done through simple forward navigation
one record at a time, accumulating the necessary sums of the field
values within the loop. Once these sums and sums of squares are
accumulated, the component only needs to calculate the final summary statistics. The component does this, sets the summary properties, and populates the arrays containing predicted Y values, residuals,
etc. for each input record. Figure 1 lists these properties and arrays.
This same logic works equally well in cases in which the data set is
filtered. Only matching records in the filtered data set are used to
derive the summary statistics. The internal logic of the component
does not alter a filter on a data set (if one exists). In fact, the code
within the component works quite transparently overall.
Typically, the developer would first execute the CalcRegression
method (the main method that performs a host of calculations),
and then examine the values of the desired properties. Youll see
how this can be done in Example 1 below. Other properties, such
as Beta0 (the Y intercept), Beta1 (slope), and the variance of X, are
calculated and exposed in properties, as well. Now that you have
an understanding of the basics, you can apply that knowledge to
building a few examples.

Greater Delphi
Example 1
The first example is a very simple one to find the correlation coefficient
r, using a simple function call and a ShowMessage dialog box. This
example will introduce you to the most basic steps to use the component. You will find the degree to which two fields are associated the
BETA and CUR_PRICE fields from the data set master.xml.
Here are the steps:
Create a new application (File | New | Application).
Add a Button component to the main form, then double-click
it to create an OnClick event handler.
Add a BVStatClientDataSet component to the form, set its
FileName property to master.xml, and set the Active property to
True.
Select the BVStatClientDataSet, and enter BETA for the XField
property, and CUR_PRICE for the YField property.
Insert the following code into the Button1.OnClick event
handler:

procedure TForm1.BVStatClientDataSet1CalcFields(
DataSet: TDataSet);
begin
with DataSet as TBVStatClientDataSet do begin
FieldByName('PredictedY').AsFloat :=
PredictedY[RecNo-1];
FieldByName('Residual').AsFloat :=
Residuals[RecNo-1];
end;
end;

Figure 3: This OnCalcFields event handler sets the values of the


added fields PredictedY and Residuals.

with BVStatClientDataSet1 do begin


// Or, set Xfield & Yfield properties at run-time:
// Xfield:= 'Beta'; Yfield:='Cur_Price';
CalcRegression;
ShowMessage(Format(
'Correlation coefficient: r = %7.2f ',[r]))
end;

Run the example. Your dialog box should appear as shown in


Figure 2. Voil! With just a few lines of code, youve calculated
the correlation, r, between the values of the BETA field and the
values of the CUR_PRICE field. The result suggests there is little
or no relationship between values in the BETA field and those in
the CUR_PRICE field.

Figure 4: Example 2 at run time, showing the values of the input


X and Y fields and the residuals and predicted values generated
for each record in the data set (after a click of Button1).

Example 2
In the second example, you will create a new application project
and add DBGrid, DataSource, and BVStatClientDataSet
components to the main form to display the contents of the source
data set. To make things more interesting, youll add columns to
the data set and a DBGrid to contain the residuals and predicted
values, using the same X and Y input fields. That way, you expose
to your user other important information in the same data grid.
Here are the steps: First, create a new application. Second, add
these components to the main form: BVStatClientDataSet
(named BVStatClientDataSet1), DataSource (DataSource1),
DBGrid (DBGrid1), DBNavigator (DBNavigator1), and Button
(Button1).
Third, assign the component properties. Set the DataSource1.DataSet
property to BVStatClientDataSet1. Set the DataSource property of
both DBGrid1 and DBNavigator1 to DataSource1. Set the FileName
property of BVStatClientDataSet1 to master.xml (youll use an XML
data set for this example). Specify the fields to use in the regression
procedure. Set the XField property to BETA and set YField to CUR_
PRICE. (You must type these field names into the corresponding box
in the Object Inspector). Double-click Button1 to create an OnClick
event handler for it. Next, create calculated fields to expose both the
predicted values of BETA and the associated residuals (the predicted
value of Beta minus the actual value of BETA) for each record.
Double-click BVStatClientDataSet1 to view its Fields editor.
Then, right-click and select Add Field. Add both the BETA field
and CUR_PRICE fields. Next, create two new calculated fields.
15 December 2002 Delphi Informant Magazine

Figure 5: Example 2 at run time, augmented with confidence and


prediction intervals on the predicted value of Y (CUR_PRICE) for a
selected record in the data grid.

Right-click and select New Field to display the New Field dialog
box. In the dialog box, enter PredictedY for Name, then select
Float for Type and Calculated for Field Type. In the same way, create
a second field and enter Residual for Name, then select Float for
Type and Calculated for Field Type.
Now, add an OnCalcFields event handler to the data set and insert a
few lines of code, as shown in Figure 3. This code will populate the
new fields with the corresponding array data, calculated internally by
the component using this code.
Fourth, you can add the main method, CalcRegression, into the desired
event handler to compute the summary statistics. In this case, you

Greater Delphi
will use the Button1Click event handler youve
already defined. To ensure that your view of the
data is consistent, add a reference to the First
method to force you back to the top of the data
set. Then run the application to view the results.
Figure 4 shows the example at run time.
Finally, you can add a few additional lines
of code into the example to expose other
interesting capabilities. Add a doubleclick handler for the data grid. Within
the handler, you will find and display the
confidence and prediction intervals on the
currently selected records predicted Y value.
You will display these values using a few
Label components and format functions.
Do these steps: Select DBGrid1. Set its
options to allow SelectedRows, and define
a DblClick event handler. Add three Label
components onto the main form in the
lower, left corner. Insert the following code
into the DBGrid1.DblClick event handler:

Figure 6: This example demonstrates several of the concepts outlined in this article. Here, I
have provided options for the user to select among significance levels and to filter the data set.
The residuals and confidence intervals are displayed graphically using a TeeChart component.

with BVStatClientDataSet1 do begin


Label1.Caption:= Format(
'Rec. Number: %3d, Y = %7.3f, Predicted Y = %7.3f',
[RecNo-1, Y[RecNo-1], PredictedY[RecNo-1]]);
Label2.Caption:= Format('CI (%7.3f, %7.3f)',
[CIUpper[RecNo-1], CILower[RecNo-1]]);
Label3.Caption:= Format('PI (%7.3f, %7.3f)',
[PIUpper[RecNo-1], PILower[RecNo-1]]);
end;

Now, run the application. Your result should look similar to the form
shown in Figure 5.
This additional code simply shows the predicted value of Y for the
selected record, and displays the upper and lower bounding intervals
(prediction and confidence) on that predicted Y value. The current
SignificanceLevel property value is used in computing these intervals.
Three fixed significance levels are available: .90, .95, and .99. Greater
values produce wider intervals, and smaller levels produce narrower
intervals. Note that the prediction and confidence intervals are
defined by a minimum and maximum value, and calculated results
are stored in separate but corresponding arrays.
Finally, let me offer a demonstration that might suggest other interesting
ways to use the component. Figure 6 illustrates the concepts outlined
previously, with additional functionality, as well. In this example, I
show the basic regression concepts combined with a graphical display
of the regression line and confidence or prediction intervals. Figure 6
also uses a simple TeeChart to provide a non-tabular view of the data.
Here, the input X and Y data points are displayed as a series of points in
a TeeChart. Both confidence and prediction intervals were added as line
series, as well. (Predicted Y values are displayed as a dark green line, for
reference.) The display conveys the overall distribution of the data points
and the fit of your regression line to the data points. Such displays often
can be easier to interpret than a table of numbers.
Here, Ive included several other summary statistical properties to display. I also have allowed the user to choose a significance level. I have
provided the user the ability to compare a subset of records, using a
filter applied to the data set.
16 December 2002 Delphi Informant Magazine

Conclusion
There is tremendous potential for improvement. Displaying the
statistical summary fields in the Object Inspector would be an
improvement. Adding fields to the data set to contain the summary
statistics as the residuals, the predicted values, confidence intervals,
and prediction intervals generated from the regression summary
would eliminate the need to do this manually. The logic used to set
the statistical summary properties could be enhanced through the
use of more complete get and set methods.
From here, the next step would be to address the more complex statistical techniques, such as multivariate regression, analysis of variance, and
clustering. But that, as they say, will have to wait until another day.
I used the following sources as references when writing this article:
Applied Regression Analysis and Other Multivariable Methods, Second
Edition by Kleinbaum, Kupper, and Muller (PWS-Kent Publishing
Company, Boston, MA, 1988).
Delphi 6 Developers Guide (Borland Software Corporation, Scotts
Valley, CA, 2001)
The components and projects referenced in this article are available
on the Delphi Informant Magazine Complete Works CD located in
INFORM\2002\DEC\DI200212FE.

Fred Edberg is a Delphi developer living and working in the Portland area. He is a
former Windows developer with a regional health-maintenance organization, where
he maintained and enhanced a client-server practice-management application that
processed accounts receivable, billing, claims, etc. Earlier, he worked for the US Army as
a geographic information systems (GIS) specialist. He developed a GIS for planning and
managing natural resources at a domestic military installation. Fred earned a bachelors
degree and a masters degree in the geosciences, specializing in digital mapping
technologies. He completed his thesis research with the development and comparison
of statistical classification algorithms for satellite image data. He is interested in data
analysis, statistics, and image processing, especially using Delphi. Fred plays Spanish
and South American tunes on the classical guitar, when time allows.

Sound+Vision
Genetic Algorithms / Shortest Path / Delphi 5-7

By Fernando Vicaria

Genetic Algorithms
Or, What Does Natural Selection Have to Do with a
Traveling Salesperson?

enetic algorithms have been around for the past three decades, but theyre only now
finding a place in the mainstream of application development. In the past few years
in fact, the use of genetic algorithms in software engineering has increased dramatically.
In this article, well go through the most important points of the theory behind genetic
algorithms and find out how we can apply that theory to real-life problems.
For the finale, well test everything weve learned
by building a Delphi project to solve the wellknown problem of the traveling salesperson, using
a genetic algorithm. The idea is to determine the
shortest path for a salesperson visiting multiple
cities, without the salesperson passing through the
same city more than once.
Genetic algorithms are search algorithms based on
the mechanics of natural selection and genetics.
They combine a survival-of-the-fittest approach to
string structures, with a structured-yet-randomized information exchange. Genetic algorithms
are stochastic by nature. They supply sub-optimal
solutions without any boundaries on their quality,
and do not necessarily provide polynomial running
times. Empirically, however, genetic algorithms
often provide a successful trade-off between optimality and speed.
Genetic algorithms are part of evolutionary
computing, which is a rapidly increasing area of
artificial intelligence. In the last decade, genetic
algorithms have been applied to a variety of computational problems in place of more classic algorithms. The main goal behind the development
of genetic algorithms is robustness that is, the
balance between efficiency and efficacy necessary
for survival in many different environments.

Imitation of Life
The idea of a genetic algorithm is to imitate life
and the way it evolves toward the form thats
most suitable for a specific set of conditions, e.g.
17 December 2002 Delphi Informant Magazine

climate. All living organisms contain a unique set


of chromosomes that serve as a blueprint for the
whole organism. A chromosome consists of genes,
or individual blocks of DNA. Each gene encodes a
trait, such as the color of an organisms eyes.
Nature uses natural selection and the genetic
operators, mutation and recombination, to eliminate individuals in a population who are less fit
and to ensure survival of the fittest with reproduction. The genes from the parents are propagated to
the next generation through their offspring. The
offspring can also undergo some sort of mutation
that, if beneficial, will improve its survival chances
compared with the rest of the population. The fitness of an organism is measured by how successful
the organism is in its life.

When and Where to Use Genetic Algorithms


Sometimes we dont know all the rules in order
to represent a problem exactly. In fact, sometimes
a complete mathematical representation simply
doesnt exist. When this happens, we use what we
know is true about the problem and try to solve it
with some sort of trial-and-error mechanism. We
get closer to the solution, but we never really know
if it is the best solution, or just a very good one.
The region in which all possible solutions are found
is called a search space. Looking for a solution is like
looking for some extreme (a minimum or maximum)
in the search space. As I mentioned, the problem is
that the search can be very complex and not fully
represented mathematically. We dont know where

Sound+Vision
to look for the exact solution, but many methods are available to help
us find a suitable one, i.e. not necessarily the best solution. Solutions
include hill climbing, simulated annealing, and genetic algorithms.
The main advantage of genetic algorithms when compared with
other search algorithms is that genetic algorithms arent closely tied to
the problems they solve, and therefore can be easily adapted to solve
a different problem with little or no modification. This could reduce
or even eliminate costs in redesigning systems. Genetic algorithms
are ideal for situations in which higher levels of adaptation and
robustness are the main areas of concern. Genetic algorithms are
theoretically and empirically proven to provide both adaptability and
robustness in complex search spaces.
Once they learn how to encode the solutions of a given problem into
chromosomes, nearly everyone can benefit from genetic algorithms
and compare their relative performance (fitness). An effective geneticalgorithm representation and meaningful fitness evaluation are the
keys to success in genetic-algorithm applications.
As a general rule, genetic algorithms are useful and efficient when:
The search space is large, complex, or poorly understood.
Domain knowledge is scarce, or expert knowledge is difficult to
encode to narrow the search space.
No mathematical analysis is available.
Traditional search methods fail.
The advantages of using genetic algorithms in such situations are that:
They require no knowledge of the search space.
Discontinuities in the search space have little effect on the overall
optimization.
They resist becoming trapped in local optima.
They provide an excellent (and easy-to-tune) balance between
speed and efficiency for large-scale optimization problems.

The Structure of a Genetic Algorithm


A basic genetic algorithm will follow the cycle of life in which a
population evolves through the generations. After each generation,
some organisms will die, some new ones will be born, and some
will mutate. Who will die and who will get a chance to reproduce is
based on a predefined quality criterion, referred to as fitness, or fitness
function.
To form a new population (the next generation), individuals are
selected according to their fitness. Heres the outline for a typical
genetic-algorithm program:
1) Start. Generate random population of n organisms (suitable solutions for the problem).
2) Rank. Evaluate the fitness f(x) of each organism x in the population.
3) Evolve. Create a new population by repeating the following
three steps until the new population is complete. Each complete
evolutionary loop is called a generation.
a) Kill. Eliminate the weaker organisms based on their fitness,
i.e. survival of the fittest.
b) Reproduce. The healthier and stronger organisms will have
a better chance of reproducing and passing on their genes to
the next generation. To avoid the offspring being an exact
copy of the parents, well perform crossover (defined below).
c) Mutate. Mutate the new offspring with a mutation probability
for the population. In nature, this is usually a very low number.
4) Test. If the end condition is satisfied, stop and return the best
solution in the current population.
5) Loop. Go to step 2.
18 December 2002 Delphi Informant Magazine

This represents only the idea behind evolutionary and genetic algorithms; there are no hard and fast rules. You are free to modify it to
fit the details of a particular problem you want to solve. For example,
your test condition can be based on a local best, or on a predetermined number of generations to which to evolve.
The representation or encoding of the variables being optimized
has a large impact on search performance because the optimization
is performed on this representation of the variables. Most of the
developmental work of the genetic-algorithm theory was performed
using a binary-encoded genetic algorithm historically, the most
widely used representation. In a binary encoding, each chromosome
is vector comprised of zeroes and ones, with each bit representing a
gene. Well see how that works soon.
Before we start building our solution to the traveling-salesperson
problem, we need to go over some important genetic-algorithm
concepts. Ive mentioned some of them already:
Organism. One of the elements in the population. Each organism or individual is a complete solution to a problem. Each
individual is represented by a finite string of symbols known as a
genome, which encodes the solution.
Natural selection. The process we use to eliminate the weaker
organisms in a population. Natural selection is responsible for
killing a specific percentage of the weaker individuals. The process used for choosing these individuals can vary.
Elitism. For our purposes, elitism is the process in which we
use the fitness criteria to determine which organisms should die
and which ones should generate offspring. Elitism is also used to
decide if a particular mutation is beneficial. Elitism can increase
performance of genetic algorithms very rapidly because the best
solutions are never discarded.
Crossover. A genetically inspired operator applied to a population (solution set). Crossover is performed with a probability
of pi (the crossover probability or crossover rate) between two
selected individuals (parents) by exchanging parts of their
genomes (encodings) to form new individuals (offspring). This
operator tends to enable the evolutionary process to move toward
promising regions of the search space.
Mutation. As with crossover, mutation is also an operator used
during the generation of the offspring. The mutation operator is
introduced to prevent premature convergence to local optima by randomly sampling new points in the search space. Mutation is carried
out by flipping bits at random, with some (small) probability of pj.

A Simple Example
Because encoding the chromosomes is usually the trickiest part of
building a successful model for a genetic algorithm, well start with a
very simple example. The problem were going to solve, using derivatives, is probably something you can remember from your calculus
classes in high school. We need to find the maximum value of the
following function:
y = -x2 + 8x + 15
To simplify this example, well assume that the maximum is between
0 and 25 (the actual maximum is x = 4), and that the maximum is an
integer value.
For simplicitys sake, were also using a quadratic function that contains only one local maximum, which is also the absolute maximum
of the function. Local maximums and minimums are easy to find by
looking at the places where the derivative of the function is 0, that

Sound+Vision
is y=0. For functions of
higher order, a local maximum or minimum isnt
necessarily the absolute.

Chromosome

Decoded Value

f(x)

Fitness

Number of
Selections

00001

00101

00010

27

0.35

22

00111

22

0.34

10110

22

-293

0.008

01011

11

-18

0.3

10110

So, with this knowledge


Figure 1: Representing a chromosome
in hand, now we must
in a population.
choose an encoding
scheme for the chromosome. In binary code, we can represent integer values from 0 to 31
with a five-bit string. Figure 1 shows one way of representing the
chromosome of an organism in a population.
Now, we need to decide on a fitness function, which will give us
the relative fitness values of each organism. The simplest method
to employ here is to use the decoded x value to calculate the y
coordinate, and use the y coordinate as the fitness rating. Then, the
fitness value for organism i (as a percentage), will be the y value at i,
divided by the sum of all the y values for every chromosome:
Fitness Value = fi / f
For example, say we have a function y = x2, and were trying to
find the maximum value of the function between 0 and 31. Then,
the chromosomes would have the relative fitness as indicated in
Figure 2. In reality, because the value of the function we want
to minimize can take on negative values, the fitness function is
slightly more complex than that just shown. In essence, however,
the two remain equivalent.
Chromosome

Chromosome

f(x)

Relative Fitness

00101

25

0.04

01101

13

169

0.25

10110

22

484

0.71

Figure 2: Relative fitness of solutions.

Now we need to create a random population


of chromosomes, and apply the outline presented in Figure 2 to them so we can evolve
the population to the next generation. Lets
start with the population shown in Figure 3.

Population
00010
00111
10110
01011

Now, we perform selection. The fitness value


Figure 3: A random
of each organism is calculated and will be
population.
proportional to each organisms chances of
reproducing. We can obtain the values using some sort of weighted
roulette wheel. This is a method for choosing members from the
population of chromosomes in a way that is proportional to their fitness. It doesnt guarantee that the fittest member goes through to the
next generation, merely that it has a very good chance of doing so.
Figure 4 shows the final result. With these selections, our mating pool
now looks like that shown in Figure 5.
Finally, the crossover probabilities need to be calculated (two crossovers need to be performed to create a new population of two). We
should perform splicing twice on two sets of randomly selected genes.
This will generate a new population, as shown in Figure 6.
In this example, we dont keep the best organism of the old population, and all organisms are replaced with new ones. (Another
option is to keep the fittest organisms of the old population and
19 December 2002 Delphi Informant Magazine

Figure 4: Fitness distribution for the population.

only replace the weak ones.) So, at the


end of the first iteration, our new population looks like that in Figure 7.

Population
00010
00111

So, even after only one generation, with


00111
no knowledge except for the relative
01011
fitness value, the genetic algorithm has
Figure 5: Couples
begun to converge to the optimal value
of chromosomes
of 4. This is startling, considering the
(red indicates
genetic algorithm knows nothing about
one couple, blue
the problem space in which it searches; it another).
is effectively blind. Yet, just by examining
a measure of goodness,
Mating Pool
New Population
having a large number
of points to examine
0001|0
00011
simultaneously, and
0011|1
00110
having a large amount of
01|011
01010
randomization thrown
00|010
00011
in, the genetic algorithm
efficiently searches the
Figure 6: Forming the new generaproblem space for postion with some gene splicing.
sible answers.
Feel free to evolve the
population a couple more
generations and see what
happens.

Population

00011

00110

01010

10

00011
3
We usually check at the
end of each generation
Figure 7: The new set of organisms
for some predefined
(or solutions).
termination criteria thats
based on the quality of the solution found, or the number of
generations evolved. Its up to you to decide which one will suit
your problem best.

The Traveling Salesperson Problem


Now that we have a better idea of how genetic algorithms operate,
we can roll up our sleeves and do some real work with our favorite
development tool. The problem well try to solve consists of
helping a salesperson to visit n cities without passing through the
same city more than once, using the shortest possible path.
Well encode our organism (TSolution) in the form of an object
containing an array of integers. This array will represent our chromosome. Each integer in the array represents one city, or gene. The cities
will start from 1 (as opposed to the

arrays zero-based index).


For example, in the case of a five-city
trip, we would have something like
Figure 8. Figure 9 shows the organism
as a Delphi language class declaration.

Figure 8: A solution array


or chromosome.

Sound+Vision
TSolution = class(TObject)
private
FProblem: TTSP;
FCount: Integer;
FTogo: Integer;
FCities: array of Integer;
FPath: array of Integer;
procedure InitSolution;
procedure Swap(i, j: Integer);
function FirstAvail: Integer;
public
constructor Create(const Problem: TTSP);
destructor Destroy; override;
procedure RandomRoadTrip;
procedure ForwardRoadTrip(City: Integer);
procedure BackwardRoadTrip(City: Integer);
procedure RandPath;
procedure Mutate;
function Distance: TDistance;
function Visited(City: Integer): Boolean;
function Complete: Boolean;
function GetCity(Index: Integer): Integer;
function GetPath(City: Integer): Integer;
function PlaceCity(City, Index: Integer): Boolean;
end;

Figure 9: TSolution class declaration representing one organism


in the population.
TTSP = class(TObject)
private
FSize: Integer;
FTerrain: TDistMatrix;
procedure InitTerrain(const Cities: array of TPoint);
public
constructor Create(const Cities: array of TPoint);
property Size: Integer read FSize;
property Terrain: TDistMatrix
read FTerrain write FTerrain;
end;

Figure 10: TTSP class declaration representing the problem


search space.

As you can see, an object of the type TSolution represents one


of the many possible solutions to the problem. You create an
instance of TSolution by calling its constructor and passing a
TTSP object as a parameter.
As you can see in the code accompanying this article (see end of
article for download details), TTSP represents the terrain where all
the cities can be found (more precisely, our search space). It contains
a two-dimension array with the distances between any two cities. Its
definition is extremely compact (see Figure 10).
Finally, we need another type to represent the genetic algorithm
itself. This representation is delegated to TGA. Together TTSP,
TSolution, and TGA represent the bulk of our project. The remaining
part will be the user interface.
TGA is the engine of this project and will set everything in
motion (see Figure 11). All you must do is provide it with the
problem to solve.
The TGA class constructor will take five parameters:
Problem. A TTSP representing the problem to be solved.
Pop. An integer number representing the size of the population
to be created.
20 December 2002 Delphi Informant Magazine

TGA = class(TObject)
private
FProblem: TTSP;
FPopulation: array of TSolution;
FPopSize: Integer;
FRankings: array of TOrganism;
FGen: Integer;
FMutRate: TPercentage;
FNatSelRate: TPercentage;
FBestSol: TDistance;
FWorstSol: TDistance;
FBestOverall: TDistance;
FWorstOverall: TDistance;
FOnNewGenComplete: TNewGenEvent;
procedure BigBang;
procedure Genocide;
procedure Rank;
procedure NaturalSelection;
procedure Reproduce;
function Crossover(Mom, Dad: Integer): TSolution;
procedure Mutate;
procedure NewGenComplete(Gen: Integer);
public
constructor Create(const Problem: TTSP;
Pop, Gen: Integer; const MutRate: TPercentage = 20;
const NatSelRate: TPercentage = 30);
destructor Destroy; override;
procedure Evolve(var StopFlag: Boolean);
property BestSol: TDistance read FBestSol;
property WorstSol: TDistance read FWorstSol;
property BestOverall: TDistance read FBestOverall;
property WorstOverall: TDistance read FWorstOverall;
property OnNewGenComplete: TNewGenEvent
read FOnNewGenComplete write FOnNewGenComplete;
end;

Figure 11: TGA class declaration representing the genetic


algorithm itself.

Gen. Another integer representing the number of generations to


which to evolve.
MutRate. The mutation rate for the population.
NatSelRate. The natural-selection rate for the population, i.e.
how many organisms will die in each generation and make space
for the offspring.

Once you create your TGA object, you must attach an event handler
to its NewGenComplete event. This event will fire at the end of each
generation, and provide you with information about the generation
just created. Well use this to extract the current generation number
and the best solution of that generation.
Our fitness function will be the total distance our salesperson travels.
The shorter the distance, the fitter the organism will be. Well also
use elitism to choose which organisms will reproduce, and which
ones will die. This way, we can guarantee that the best solutions will
survive to the next generation.

The Evolve Method


The Evolve method is the Delphi language representation of the
classical outline for a genetic algorithm. It will evolve for FGen
generations or until the StopFlag is set (see Figure 12). Another
valid alternative for the termination test is to use a minimal quality
requirement for the solution we consider acceptable. Once we reach
that level of precision or better, we know we can stop searching.

Crossover and Mutation


Other critical parts of a genetic algorithm are the crossover and
mutation operations. Crossover tries to pass the best of each

Sound+Vision
{ Evolves the population through the generations. }
procedure TGA.Evolve(var StopFlag: Boolean);
var
i: Integer;
begin
for i:=1 to FGen do begin
NaturalSelection;
Reproduce;
Mutate;
Rank;
NewGenComplete(i);
if StopFlag then
Break;
end;
end;

Figure 12: The Evolve method of the TGA class.

parent to the children in the hope that the children will be


stronger (better solutions) than their parents. Mutation will help
us avoid falling into premature convergence (local optima of the
search space).
These two operations usually are time-consuming and must be
balanced carefully. In real biological systems, the ratio mutation per
organism is extremely low, and, generally, it is meaningful only in
large populations. In genetic algorithms, this number does not have
to follow nature, and were free to increase it. I usually set it to something between 5 percent and 10 percent, depending on the overheads
and the benefits it brings.
The genetic algorithm itself usually has no knowledge of either operation. They are implemented in the representation of the organism
(TSolution, in our case). This facilitates reuse of the genetic algorithm
in different problems.

The User Interface


Figure 13 shows a snapshot of our GUI in action. To add new
cities to the problem, just click on the white area on the left side.
This represents the terrain where all cities must be located. You
can modify the parameters of your problem by setting a new value
to the size of the population, the generation count, and mutation
and selection rates. To restart, press the Reset button. I also added
a way of saving and loading predefined problems: Just use the
Load Cities and Dump Cities buttons.

Conclusion

Figure 13: The user interface for our traveling-salesperson solution showing the best path for our traveling salesperson.

TSolution = class(TChromosome)
// Specific code goes here...
end;

and:
TGASolver = class(TGA)
// Specific code goes here...
end;

I will leave the actual design details to you.


Genetic algorithms are very robust and easy to adapt to fit a large
variety of problems. This makes them appealing for areas such
as systems optimization, economics, chemistry, physics, neural
networks, and machine learning.

Further Reading
Genetic Algorithms in Search, Optimization and Machine

Learning by David E. Goldberg.


A Fast TSP Solver Using GA on JAVA by Hiroaki Sengoku
and Ikuo Yoshihara, a paper presented in the Third International Symposium on Artificial Life and Robotics, 1998.
A tutorial named Introduction to Genetic Algorithms by
Marek Obitko http://cs.felk.cvut.cz/~xobitko/ga
A site by Matt Sullivan named An Introduction to Genetic
Algorithms http://www.cs.qub.ac.uk/~M.Sullivan/
ga/ga_index.html
Evoweb http://evonet.dcs.napier.ac.uk/evoweb/resources/
links/linktype16.html

I hope this article gives you a feel for what can be done with
genetic algorithms. The main difficulties in developing genetic
algorithms are probably encoding the chromosomes and the way
we build the analogy between the natural and artificial systems.
However, after a few tries, this process becomes easier.

A few improvements to our TGA and TSolution classes could


make them more general (in the true spirit of genetic algorithms).
For example, you could create an abstract TChromosome class
that would have all the basic data and behavior common to all
organisms, such as Mutate, Crossover, and Reproduce, and that
would have no specific feature that tied it to a particular problem,
as we created in TSolution. In that case, TSolution would inherit
from TChromosome and implement its abstract methods in a way
thats specific to the traveling-salesperson problem. The same
would have to be done with TGA. The final result could look
something like this:

The example application referenced in this article is available


on the Delphi Informant Magazine Complete Works CD located in
INFORM\2002\DEC\DI200212FV.

21 December 2002 Delphi Informant Magazine

Fernando Vicaria is a quality-assurance engineer at Borland Software Corporation in Scotts Valley, CA. Hes also a freelance technical author for Delphi and
C++Builder issues. Fernando is specialized in VCL and CLX frameworks, and,
when hes not at work, hes probably surfing at some secret spot in Northern
California. He can be reached via e-mail at fvicaria@borland.com.

Delphi 101
Classes / Components / Delphi 4-7

By Rick Spence

From Class to Component


Part I: Creating a Non-visual Component

n July, I showed you a class that I use to manage the SQL statements in my applications. I
keep the SQL statements external (in a database), and the class gives me access to these
statements (see Separate, but SQL in the July 2002 issue of Delphi Informant). In that
article I mentioned that it was a trivial matter to convert the class to a component. Since
then, Ive received requests to supply the component, hence this article.
First Ill explain the general approach to converting
a class to a component, why you would want to do
so, some rules for component writing in general,
and some things to be careful of. Then Ill take the
SQL class from the previous article and transform
it into a non-visual component. After that, Ill
show how to sub-class TQuery to allow it to use the
external SQL statements. Next month, Ill show
you how to write a property editor that will allow
the component user to select the appropriate stored
SQL statements.

Why Components?
Why would you want to convert a class to a
component? Im certainly not proposing that you
blindly take all your classes and remake them as
components. Some classes are fine as they are,
and you wont gain anything by making them
components. In general, components offer three
advantages over classes:
1) Delphi takes care of creating and destroying
components for you, whereas classes must be
explicitly instantiated and destroyed using
code.
2) You can set properties at design time, rather
than only using code at run time.
3) With visual components, you can position and
size the component at design time.
22 December 2002 Delphi Informant Magazine

So the benefits arent as great with non-visual components, such as a Query, or FileOpenDialog. Since
youre not concerned with positioning or sizing the
component, the only advantages are that you can set
properties at design time, and that Delphi will create
and destroy the component for you.
However, dont underestimate the advantages of
setting properties at design time. In addition to
the ease with which you can set properties (and
thereby link components), you can also write
property editors that allow only valid values. As an
example of a property editor, consider a data-aware
control such as DBEdit. After you have set the
DataSource property, you must set the DataField
property to reference a field from the associated
data sources data set. The property editor will list
only valid fields, preventing you from entering a
bad field name. Thats a great advantage, and next
month Ill show how to do that with our TQuery
subclass. Well link it to our external SQL manager,
and allow users to select the SQL statement to use,
restricting them to entries in the table that stores
the SQL statements.
Its also important to note that developing
components can be a time-consuming process,
and most of the time someone will have created a

Delphi 101
component that fills your need, or is close to filling your need. These
may be commercial components such as those you see advertised in
this magazine, or even freeware. Whether you create components
yourself, use others components, or modify others components for
your purposes, this article will get you started.

the Object Inspector will display DataSet in the Object Inspector.


Review the Borland documentation on properties if youre not
familiar with them, or read my article Object Oriented Programming in Delphi: A Guide for Beginners, available at
http://www.webtechcorp.com.

Rules for Component Writers

Rule Four. To integrate your components into the IDE, they must
be stored in design-time packages. You must create the package, add
your components (Pascal files) to it, compile the package, and install
it into the IDE.

There are five hard and fast rules you must adhere to when writing
your own components. There are also some conventions you should
follow, which Ill discuss in the next section.
Rule One. All components must descend from the VCLs TComponent
class. They dont have to directly descend from TComponent; your
component could descend from another class which ultimately
descends from TComponent. For example, you would inherit
from TWinControl for a visual control that can receive focus. The
class your component inherits from is called your components
ancestor, and your first decision when creating a new component
is to decide its ancestor.
Rule Two. If you write a constructor for your component, the constructor must be declared using the override directive, it must accept
one parameter (of type TComponent), and it must call its ancestors
constructor. This is because your constructor is overriding the constructor in TComponent, which is declared as:
constructor Create(AOwner: TComponent); virtual;

The owner is the component which owns this component. If you


drop your component onto a form, the owner will be the form itself.
Note that when you write components, your code is executing at both
design time and run time. That is, when the user drops your component
onto a form, your components constructor is run at that moment. And
as youll see later, when you set properties at design time, your code is
also executed. Having your code execute within the IDE will take some
getting used to. Also, if your code generates exceptions, there is a chance
you can crash the IDE. For this reason its best to first test your components by instantiating them in code, before you add them to the IDE.
This second rule also means that when youre converting classes to
components, any classes that have their own constructors must be
changed. As youll see in a later section, the class were converting has
a constructor defined like this:
constructor Create(dbName, tblName: string);

When making this class a component, the constructor must receive


one parameter of type TComponent (its owner). We still need to allow
for two string parameters, however, so well make them properties the
user can assign using the Object Inspector. This also means that code
which uses the classes must be changed as well. Usually, however, the
reason for converting classes to components is so you can drop them
onto a form (rather than instantiating the class in code), so that code
will have to be changed anyway.
Rule Three. For variables to appear in the Object Inspector, they
must be declared in the published section of the component, and
they must be properties, not simple instance variables (or fields as
Borland likes to call them). For example, if a component has the following property declaration in its published section:
property DataSet: TDataSet
read GetTDataSet write SetTDataSet;

23 December 2002 Delphi Informant Magazine

Rule Five. The final rule concerns the placement of your components
in the IDE. You must decide which Component palette page they
will appear on, either an existing page (such as Samples), or (preferably) your own page.
Heres how this works. When you install your component into
the IDE, Delphi calls a procedure in your components file named
Register (note the initial uppercase R, this is case sensitive). You
must declare the Register procedure in your programs interface
section so its visible to other units.
You must write this Register procedure to call another routine named
RegisterComponents. You must pass RegisterComponents the name of
the page on which you want your components installed, and an array
of components (if youre registering just one component your array
will have only one entry). Heres an example:
procedure Register;
begin
RegisterComponents('Samples', [TWTExternalSQL]);
end;

The component is named WTExternalSQL (of class


TWTExternalSQL), and it will be stored on the Samples page
of the Component palette.

Conventions
The first convention concerns naming your component. You already
know (from writing classes) that all class names should be prefixed
with the letter T. When you write components, theres an additional
convention to prefix the component name with a few letters to indicate
the author/company responsible for writing the component. Component
names must be unique, so this helps avoid the possibility of duplicate
names. I use the letters WT (for WebTech), for example, while
Woll2Woll Software (developers of InfoPower and 1stClass) use WW.
The Delphi Prefix Registry Web site (http://www.delphiprefixregistry.net)
is an attempt to centralize the registration of component prefixes (and I
just realized my prefix is not registered there).
Another convention concerns the names of properties, the fields
used to store those properties, and the get/set functions you can
optionally use to read from or write to those properties. If the
property you are publishing is named DataSet, the convention is to
name the field (instance variable) which actually stores this property
as fDataSet, and to store this in the class private or protected section, as shown here:
TWTExternalSQL = class(TComponent)
private
All material onTDataSet;
this CD-ROM 1995-2001 Informant Communications Group, Inc. Elk Grove, CalifDataSet:
fornia, United States of America. Informant is a registered trademark of Informant Communications
published
Group, Inc. Delphi is a trademark of Borland Software Corporation.
property DataSet: TDataSet read fDataSet write fDataSet;
end;

Delphi 101
TWTExternalSQL = class(TComponent)
private
fdataSet: TDataSet;
protected
function GetTDataSet: TDataSet; virtual;
procedure SetTDataSet(const Value: TDataSet); virtual;
published
property DataSet: TDataSet
read GetTDataSet write SetTDataSet;
end;

Figure 1: This example type declaration features a property with


read and write specifiers.

type
TExternalSQL = class
public
constructor Create(dbName, tblName: string);
function GetSQL(SQLName: string): string;
destructor Destroy; override;
protected
fTable : TTable;
end;
implementation
{ TExternalSQL }
constructor TExternalSQL.Create(dbName, tblName: string);
begin
fTable := TTable.Create(nil);
fTable.DatabaseName := dbName;
fTable.TableName := tblName;
fTable.Open;
end;
destructor TExternalSQL.Destroy;
begin
fTable.Close;
fTable.Free;
inherited;
end;
function TExternalSQL.GetSQL(SQLName: string): string;
begin
if not fTable.Locate('SQLName', SQLName,
[loCaseInsensitive]) then
Raise Exception.Create(SQLName + 'Not found');
Result := fTable.FieldByName('sqlCode').AsString;
end;

Figure 2: The original ExternalSQL class.

Some developers prefer to place the field in the protected section, so its visible to subclasses. I prefer to place it in the private
section, then use protected virtual accessor functions so those
functions can be accessed and/or overridden by subclasses. If you
use accessor functions (functions which Delphi calls to read/write
the properties) rather than a simple redirection (as we used in the
previous example), the convention is to prefix the read function
name with Get, and the write function name with Set, as
shown in Figure 1.

Sample Class to Convert


Figure 2 shows the ExternalSQL class from my previous article. This
is the class were going to convert to a component. The idea behind
this class is that SQL statements are stored in a database table, and
this class provides access to those SQL statements. The table has two
columns: SQLName, the user-defined name of the SQL statement, e.g.
AllCustomers; and, SQLCode, the actual SQL statement.
24 December 2002 Delphi Informant Magazine

To use this class, the user must first instantiate it, passing the name of the
database and the name of the table which stores the SQL statements:
externalSQL :=
TExternalSQL.Create('ExternalSQL', 'SQLStatements.db');

To retrieve a particular SQL statement call the GetSQL method,


passing the name of the SQL statement you require:
sql := externalSQL.GetSQL(sqlStatementName);

The class is simple, but its a great example of encapsulation. The


actual mechanism by which the SQL statements are located is hidden
from the user. In this case, the class uses a TTable component to
locate the required records. It would be easy to modify this class,
without any impact on the class user to use a TClientDataSet
component, and have the entire set of SQL statements stored in
memory.

Converting to Component
The first issue to consider is which class our component should
inherit from. This ones easy; because were creating a non-visual
component, well inherit directly from TComponent, thus:
TWTExternalSQL = class(TComponent)

The second issue is what to do with the constructor. As you know,


since we inherit from TComponent, our constructor must receive
an owner as a parameter, but in this case our constructor must also
accept two string parameters. The class uses these strings to create a
Table component so we can access the table.
We could make these strings properties, so the component user can
set them in the Object Inspector, but a better approach is to allow
the class user to create this Table component by dropping it onto
the form, and have a property in our component to reference this
Table. This would work, but we are then tying our class user to use a
Table component. What if they want to use an ADO component or a
dbExpress component to access the SQL statements?
The best solution is for our class to declare a property of type
TDataSet, then the class user can associate any type of dataset with
our component. Note that when you declare a components property
as some type of class, the IDE will only allow you to associate an
object of that class, or an object of a subclass of that class, with the
property. So, if we declare a new property of type TDataSet, users can
drop any type of dataset onto the form, and make our components
property reference that dataset.
One complication (isnt there always one?) arises when you have
a property of one object reference another object. Consider what
happens in the following scenario. The user drops an ADOQuery
component onto the form, makes our components DataSet property
reference it, then deletes the ADOQuery component. Our property
now references a non-existent object. Delphis own DataSource
component has the same problem if you delete a DataSet its
referencing. In the latter case, however, if you delete the dataset, the
DataSource.DataSet property is reset. We need to handle this the
same way Delphi does.
The key is in a method we can choose to override named Notification.
Delphi automatically calls a components Notification method every
time a component is added to, or removed from, a form. Notification

Delphi 101
type
TWTExternalSQL = class(TComponent)
private
fdataSet : TDataSet;
protected
function GetTDataSet: TDataSet; virtual;
procedure SetTDataSet(const Value: TDataSet); virtual;
public
function GetSQL(SQLName: string): string;
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
destructor Destroy; override;
published
property DataSet: TDataSet
read GetTDataSet write SetTDataSet;
end;
procedure Register;
implementation
{ TExternalSQL }
function TWTExternalSQL.GetSQL(SQLName: string): string;
begin
if not fDataSet.Active then
fDataSet.Open;
if not fDataSet.Locate('SQLName', SQLName,
[loCaseInsensitive]) then
Raise
Exception.Create(SQLName + 'Not found');
Result := fDataSet.FieldByName('sqlCode').AsString;
end;
function TWTExternalSQL.GetTDataSet: TDataSet;
begin
Result := Self.fDataSet;
end;
procedure TWTExternalSQL.SetTDataSet(
const Value: TDataSet);
begin
fDataSet := Value;
end;
procedure TWTExternalSQL.Notification(
AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (AComponent = Self.DataSet) and
(Operation = opRemove) then
DataSet := nil;
end;
procedure Register;
begin
RegisterComponents('WebTech', [TWTExternalSQL]);
end;

Figure 3: The class reworked as a component, including a


Notification method.

takes two parameters: the component being inserted or removed, and


a variable indicating the operation (opInsert or opRemove). Our class
needs to override this method, so that if a component is removed,
and that component is referenced by our DataSet property, then our
DataSet property is set to nil.
Figure 3 shows the entire class declaration of TWTExternalSQL,
including this Notification method. Since the class user is now creating
the dataset, our component doesnt need a constructor or destructor.
Note how you can associate any type of dataset with our
component. Indeed, the sample code for this article shows how to
use a TClientDataSet so the entire set of SQL statements is loaded
25 December 2002 Delphi Informant Magazine

into memory and remains there while the application runs. (Code
accompanying this article is available for download; see end of
article for details.)

Subclassing TQuery
Now that we have ExternalSQL as a component, we need to create
a subclass of Delphis TQuery class. The new subclass must be able
to access the ExternalSQL object, and allow the user to specify the
name of the external SQL statement to use. Initially well develop it
so the user can enter any name for ExternalSQL, i.e. we wont check
for validity. In next months article, Ill show you how to develop a
property editor that will display the names of the SQL statements
from the table and even allow users to change and test the SQL
statements from within the form designer.
The previous section showed how to create a component from
scratch. What I think is more common is to inherit from an existing
component and change it. And thats exactly what were going to do
in this section. Well create a new component based upon TQuery,
and add two new properties: one named ExternalSQL of type
TWTExternalSQL, and another named SQLName of type TSQLName.
In other words, ExternalSQL will be a reference to our
TWTExternalSQL object, and SQLName the name of the SQL
statement stored in the database table. Note that I declare its type as
TSQLName, which in turn I declare as:
type
TSQLName = string

Why bother with this indirection, when TSQLName is simply


redefined as a string? It has to do with the way property editors
work. We can write a property editor that will allow the user to
edit properties of type TSQLName, and thus distinguish between
properties of type TSQLName and properties of type string. More on
this next month.
When the query is activated, we need to use the ExternalSQL
manager to locate the SQL referenced by the SQLName property, and
copy the text of the SQL statement into the TQuerys SQL property.
As you no doubt know, there are two ways to activate a TQuery: you
can set its Active property to True, or you can call its Open method.
We should probably hook in to both these operations, but a quick
peek into the TQuery source code shows the following public method
in TDataSet (from which TQuery descends):
procedure TDataSet.Open;
begin
Active := True;
end;

So, if we can have our code execute when Active is set to True, we
can ignore Open. The question is how can we get our code to execute
when the Active property is set? Further digging in TDataSet reveals
the public property Active declared as:
property Active: Boolean
read GetActive write SetActive default False;

Active is implemented using accessor functions rather than


redirection. SetActive is declared in the protected section as:
procedure SetActive(Value: Boolean); virtual;

Delphi 101
Its protected, which means we can override it. Its also virtual,
so if we do override it our version will still be called even when
it is referenced from within its own class (as when Open calls
it). Thats a key point you need to understand regarding the
virtual directive. If SetActive wasnt declared as virtual, our
version wouldnt be called when Open sets active to True. This
is because the call would be statically bound (i.e. the compiler
would generate code to directly access the SetActive method in
TDataSet), whereas we need it to call the method in our subclass.
We need the call dynamically bound, which is what we get when
we declare it as virtual. Refer back to the section on conventions
earlier in this article, and note that I recommend you declare your
own properties this way.
Our class, then, only needs to override the SetActive method,
and when called, retrieve the external SQL and load it into the
components SQL property. Listing One shows the complete
type declaration and implementation. As you can see, theres not
much to it. We declare the two new properties as private and
write protected accessor functions. We also need to override the
Notification event, so we can reset the wtExternalSQL property if
the user deletes it. And we override the SetActive method, so we
can copy our SQL from the database table into the SQL property.

Summary
This article demonstrates that its a fairly straightforward task
to convert simple classes into components. The biggest problem
youll encounter is changing the class constructor. There are
some rules you must follow when creating components, and some
conventions you should follow. These were discussed in detail.
We also saw how to create a new component based on an existing
component. We were fortunate in that the ancestor component
declared the method we needed to override as virtual. Sometimes
we arent as lucky; either a key piece of information we need in an
ancestor class is private (rather than protected), or a method we
need to override isnt declared as virtual. In these cases I suggest
you locate the component author and send him or her threatening
e-mail, or make disparaging comments about his or her parents
(just kidding about the e-mail).
Next month, Ill show you how to create a property editor that will allow
the component user to select the SQL statement from a list, and to test
the statements from inside the IDE. See you then.
The package and demonstration project referenced in this article are
available on the Delphi Informant Magazine Complete Works CD
located in INFORM\2002\DEC\DI200212RS.

Begin Listing One Subclassing TQuery


type
TSQLName = string;
TWTExternalQuery = class(TQuery)
private
fwtExternalSQL : TWTExternalSQL;
fSQLName : TSQLName;
protected
procedure SetActive(Value: Boolean); override;
function getSQLName: TSQLName;
function getWTExternalSQL: TWTExternalSQL;
procedure setSQLName(const Value: TSQLName);
procedure setWTExternalSQL(
const Value: TWTExternalSQL);
public
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
published
property wtExternalSQL: TWTExternalSQL
read getWTExternalSQL write setWTExternalSQL;
property SQLName: TSQLName
read getSQLName write setSQLName;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('WebTech', [twtExternalQuery]);
end;
{ TWTExternalQuery }
function TWTExternalQuery.GetSQLName: TSQLName;
begin
Result := fSQLName;
end;
function TWTExternalQuery.getWTExternalSQL: TWTExternalSQL;
begin
Result := fWTExternalSQL;
end;
procedure TWTExternalQuery.Notification(
AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (AComponent = Self.wtExternalSQL) and
(operation = opRemove) then
Self.wtExternalSQL := nil;
end;
// TDataSet.Open is simply setting active to True.
procedure TWTExternalQuery.SetActive(Value: Boolean);
begin
// Seems to get called twice...
if Value then
if (Self.SQLName <> '') and
(Self.wtExternalSQL <> nil) then
Self.SQL.Text := wtExternalSQL.GetSQL(Self.SQLName);
inherited;
end;
procedure TWTExternalQuery.SetSQLName(
const Value: TSQLName);
begin
fSQLName := Value;
end;

Rick Spence (RSpence@WebTechCorp.Com) is the technical director of Web Tech


Training and Development (http://www.webtechcorp.com). The company has
offices in Florida and England, and specializes in Delphi training and development.

26 December 2002 Delphi Informant Magazine

procedure TWTExternalQuery.SetWTExternalSQL(
const Value: TWTExternalSQL);
begin
fwtExternalSQL := Value;
end;

End Listing One

New & Used


By Robert Leahey

AQtime 2.0
Profiler Makes You Look Like a Guru

ext time Im in Las Vegas, I have an obligation: Ill be buying lunch for the team
at AutomatedQA. In the April 2002 issue of Delphi Informant, I had great things
to say about AQtest, now known as TestComplete (see http://www.delphizine.com/
productreviews/2002/04/di200204rl_p/di200204rl_p.asp). AQtest has completely revolutionized my testing processes. Im also quickly becoming dependent on AQdevTeam,
although its still in beta at the time of this writing. Similarly, I sense that it will change
my project management methods. And, the subject of this review, the second version of
AQtime, is quickly making me look like an absolute guru to my clients.
Are your ordering procedures taking too long? Is
your legacy application leaking memory? Do you
want to make sure your program works with Windows XP? AQtime is the answer for positive results.
Im also using it behind the scenes to improve and
watchdog many other performance areas of my
current projects, to great advantage.
AQtime is a profiler what exactly is that? The
quick answer is that a profiler monitors code performance. A function profiler can track how much

time is spent in each function of an application


and how many times those functions are called,
thus giving you an idea of where performance
bottlenecks may be. Likewise, a memory allocation
profiler tracks the allocation and de-allocation of
memory and reports on any memory leaks it finds.
Profilers are nice, but theyre only as good as their
reporting systems. In other words, how does the
profiler present the results of its tests? As youll
learn in this review, AQtime does a marvelous job
of making its results available and meaningful.

Overview
Getting started with AQtime is straightforward.
For Delphi applications and modules, your
project must be compiled with certain compiler
settings that make your module transparent to
AQtime. Then you need to open your compiled
executable within AQtime and select one of its
19 profilers. Some profilers will require your
module to be executed, so they can trace the
executing code. Others get what they need from
the debug information embedded in the module.

Figure 1: AQtimes Setup tab.


27 December 2002 Delphi Informant Magazine

Some profilers can seriously degrade the performance of an application, and you may not
wish to profile your entire application at once.
AQtime offers the ability to specify which parts
of your module will be profiled and at what
points during execution the profiler will be
active. The interface provided to make these
specifications is found on the Setup tab of the

New & Used


main AQtime window (see Figure 1). On
this tab, you can see the Modules, Areas,
and Triggers panes.
The Modules pane provides a hierarchical
listing of all modules available for profiling,
units, classes contained within those units,
and the methods of those classes. The
elements in this tree can be dragged and
dropped into the Areas and Triggers panes
to narrow your profiling efforts.
The Areas pane allows you to define what
areas of your modules will be profiled.
The areas you define can contain whole
units, classes, or individual methods, each
of which can be individually enabled or
disabled. Thus, you can specify categorical
areas (drawing routines, calculations, UI
events, etc.) and profile particular parts of
your modules.
The Triggers pane provides a way to turn
profiling on and off while your module is
executing. Triggers only work for three of
AQtimes 19 profilers, but theyre useful if
you want to profile only a specific part of
your code under certain circumstances. For
instance, if you want to activate the profiler
only after a certain UI element has been
clicked, or if you want to turn off profiling
while a long recursive section executes, you
can set those conditions here.

Figure 2: AQtimes Results page after a profiling session using the Function Profiler.

Getting the Results


As I said, a profiling tool is only as good
as its results dissemination, and AQtimes
is great. If anything, it gives too much
information. Trying to find the results you
Figure 3: The Function Profilers Details panel.
need and what they mean is sometimes
challenging. Fortunately, with the Setup
page and the robust options of the Results page (see Figure 2), you
The second tab, Graph, displays a customizable graph based on the
can narrow the flood of data to pinpoint what you need. As with
current profiler. In most cases, it displays values based on each row from
any powerful tool that displays a great deal of information, the
the Report panel. The graph can be customized by dragging columns
interface can be rather daunting and it may take some time to
from the Report panel to create new series within the graph.
figure out the results.
Tab number three, Disassembly, provides the binary source, address, and
The screen displayed in Figure 2 is the result of a profiling session
size information for the currently selected method. This panel is also
using the Function Profiler. AQtime retains the results of previous handy for those who might not know assembly as well as they would like
profiling sessions, visible in the Explorer panel in the upper left it will display a line of source code, and then display the Assembler
hand corner of the Results page. When you select the results of a
instructions that result from that line of code.
session, the results are displayed in the rest of the window. In the
case of the Function Profiler, the upper right-hand pane displays a Editor, the fourth tab, displays source code. It will display the code of
sortable, groupable listing of every profiled function in a module,
the currently selected method, along with information from the current
and details as to how long each function took to execute this is profiler. The Details panel, on the fifth tab, displays detailed information
the Report panel. In the bottom half of the Results page, youll see a specific to the current profiler. See Figure 3 for a Details panel from a
page control with six tabs. These tabs are detail views: Event View,
Function Profiler session.
Graph, Disassembly, Editor, Details, and Call Graph.
Youll notice that the panel in Figure 3 is split into Parents and
The first tab, the Event View panel, displays events that occur
Children sections. Keep in mind that this panel is showing detail
within AQtime and the profiled module during profiling. It will
about the currently selected method in my example,
also display summary profiler results, which can be useful in
TtsCustomMMAPIExplorer.LoadMemberList. Parents are any
making sense of a profiling sessions results.
methods that called LoadMemberList, while Children are any
28 December 2002 Delphi Informant Magazine

New & Used


an idea of how many times a routine was
called. This information is also included in
the Function Profiler, but because Function
HitCount doesnt track timing information,
it operates much faster. Also, because of its
reduced result set, it can be easier to analyze
its results.

Figure 4: The Call Graph panel.

methods called by LoadMemberList. The Function Profiler is all about


time spent in each method, so these views are showing you timing
information for the calling, current, and called methods. Once you get
used to it, this panel is ripe with information waiting to be gleaned.
The last panel, Call Graph, is valuable for watching control
flow, and is just plain cool (see Figure 4). This graph shows the
current method and its relation to the methods that surround it
in the control flow of your module. Calling and called methods
are displayed along with the current method and the hit count
of each of those methods. The graph is also hot moving
the mouse over the control flow lines will cause the methods
connected by that line to be highlighted.
This brief overview of the detail panels in the Results tab should give you
a good idea of AQtimes robust result display capabilities.

Nineteen Profilers
The current version of AQtime (2.04) contains 19 profilers (up from 17
in version 2.0). The following is an abbreviated look at these profilers.
Static Analysis: This profiler analyzes the debug information in the
profiled module to discover information about sizes and addresses of
routines and units, binary code, number of source lines per routine, etc.
It doesnt actually run the module to perform this discovery, so its quick.
Besides the obvious uses of such information, this particular profiler is
helpful when your application raises an exception with nothing but an
address; you can run this speedy profiler to find the offending method.
Function Coverage: This profiler simply checks to see whether each
routine set to be profiled actually executed during the profiling
session. The Report panel displays a red or green dot for each listed
routine, indicating whether it executed.
Line Coverage (Grouped by Function): Similar to Function Coverage,
this profiler goes much deeper and determines whether each line of code
for the profiled routines executes during the profiling session. In the
Report panel, the results are grouped by source functions.
Line Coverage (Grouped by File): Similar to the previous profiler,
this profiler runs a little quicker since it groups by source file rather
than function. This profiler is an excellent way to quickly see if
sections of your code are executing.
Function HitCount: A full function profile can sometimes be
overkill, so use Function HitCount when you simply want to get
29 December 2002 Delphi Informant Magazine

Line HitCount: This performs in a similar


manner to Function HitCount, but tracks
hit counts for single lines of code rather
than just routines. When viewing the
Editor panel for Line HitCount, each line
of code will have its hit count next to it in
the Editor panels gutter.
Function Profiler: This is the traditional
profiler what many people think of when visualizing a profiler.
Function Profiler tracks much information about calls to the
profiled routines during a profiling session. It tracks a count
of calls to each routine (HitCount), execution time for each
routine, and a hierarchy of calls (calling methods, called methods,
etc.), which is displayed from Call Graph. Like most of the other
profilers, Function Profiler handles multi-threaded applications,
tracking information for each thread in a module.
Function Trace: This is an unusual profiler in that it combines with a
separate product (CodeSite by Raize Software; http://www.raize.com)
to create a real-time call stack. AQtime sends CodeSite EnterMethod
and ExitMethod calls to the CodeSite viewer for each profiled routine.
You also have the option of sending the value of each routines
parameters with the EnterMethod calls.
Exception Trace: This profiler outputs information for any
exceptions raised during the profiling session. It will report exception
type, address, call stack, etc.
LoadLibrary Tracer: Use this profiler to track information about any
DLLs being loaded or unloaded by the profiled module. It will report
how many times each DLL is loaded and unloaded, DLL addresses,
load and unload times (in milliseconds), etc.
VCL Class Profiler: The VCL Class Profiler tracks information on all
VCL objects created during the profiling session. Information such as
leaked instances, call stack state when each object was created, instance
sizes, instance addresses, and instance counts are available.
VCL Reference Count Profiler: This profiler tracks information about
any interfaced classes for which references were added during the profiling session. Details include _AddRef and _Release history, as well as the
call stack state for each of these calls.
Unused VCL Units Profiler: Although Delphis optimizing compiler
removes any unused routines from your code, a unit that resides in
the uses clause will still have its initialization and finalization sections
linked into your application, even if none of its functions are called.
The Unused VCL Units Profiler scans all units included in a project
and checks whether they are actually used it then displays a list of
any units that arent.
Function Sampling: This profiler gives an indication of which routines may be performance hogs by polling the application at regular
micro-intervals as to what code is executing at that moment. Rou-

New & Used

AutomatedQAs AQtime 2.0 is a performance profiler and memory


allocation debugger for Borland, Microsoft, and GNU compilers.
AQtime offers 19 profilers and a variety of screens for viewing the
resultant data. If youre concerned with improving the speed of your
applications, or their memory management, the profiling power of
AQtime and its robust data presentation should make it an obvious
choice for your development toolkit.
AutomatedQA Corp.
2200 E. Patrick Lane, Suite 1
Las Vegas, NV 89119
E-Mail: sales@automatedqa.com
Web Site: http://www.automatedqa.com
Price: US$349.99. Discounts are available for multi-user licenses.

tines that execute quickly will go unnoticed, while slow routines will
be caught. Since this profiler doesnt noticeably slow the executing
module, its a good profiler to run over the whole application, prior
to running the Function Profiler.
Line Sampling: Similar to Function Sampling, the Line Sampling profiler counts the number of times each line of code is executing when the
profiler polls the profiled module.
Platform Compliance: This profiler runs quickly (the profiled application need not be executed) and provides information about whether the
profiled application can work on a specific operating system.
Memory and API Resource Check Profiler: Run this profiler to track
your modules calls to API functions and memory allocation. It will
check whether the profiled application correctly creates and releases allocated resources, analyze the use of GetMem, FreeMem, ReallocMem, New,
and Dispose, and trace other memory-related functionality.
BDE SQL Profiler: The BDE SQL Profiler times the execution of SQL
queries and stored procedures through the BDE.
ATL Reference Count Profiler: This last profiler isnt available for
Delphi applications because it applies only to applications compiled
using Borland C++Builder or Microsoft Visual C++.

Conclusion
AQtime will change the way you approach application performance. If
youre concerned with improving the speed of your applications, or their
memory management, the profiling power of AQtime and its robust
data presentation should make it an obvious choice for your development toolkit. If youre still not sure AQtime will revolutionize your
development, AutomatedQA is they offer a 60-day unconditional
money-back guarantee.

Robert is a Delphi developer who has been writing code since he discovered AppleBasic on his Apple II+. He now provides custom creative solutions and Delphi tool
training via his company, Thoughtsmithy (http://www.thoughtsmithy.com). He is
working on his Masters degree in Music Theory at the University of North Texas
and currently resides in Texas with his wife and daughters.

30 December 2002 Delphi Informant Magazine

New & Used


By Alan C. Moore, Ph.D.

WPTools Version 4
Text Manipulation Library for Delphi and C++Builder

PTools is a comprehensive text manipulation library from wpCubed GmbH


(formerly Julian Ziersch Software). WPTools consists of about 20 components
for working with formatted text (mainly Rich Text Format). As you may know, Rich Text
Format (RTF) is the common language on which modern word processors such as
Word and WordPerfect rely to communicate with each other.
WPTools has grown considerably in recent years.
Initially released in 1996, version 3 of WPTools
was released in March, 2000, and version 4
came out in the summer of 2002. Version 3
introduced layout view, separate control of
headers and footers, easy manipulation of tables,
and support for numerical calculations in tables.
Version 4 adds versatile numbering and bullet
handling (multilevel), HTML/CSS support,
XML-based localization, multilevel undo and
redo, and more. Of course, each new version
features increased speed in processing.

Component

Purpose

TWPRichText

A memo control for editing formatted text.

TDBWPRichText

Similar to TWPRichText but data-aware.

TWPEdit

A single-line RTF edit field.

TWPRichTextLabel

A label that displays formatted text; it


can be transparent.

TWPPreview

A component to build a print preview for


the contents of a TWPRichText or
TDBWPRichText control.

Figure 1: The main RTF components.


31 December 2002 Delphi Informant Magazine

You would expect this type of library to be able to


create sophisticated word-processing functionality,
and it does. Among other things, it provides page
layout view, print preview, WYSIWYG, headers
and footers, subscript and superscript, mail merge
capabilities, and named paragraph styles. But
what about uses beyond word processing, such
as programmatic text manipulation? WPTools is
strong here also. A couple of years ago I wrote a
syntax highlighting application that used some of
wpCubeds low-level functions to produce output
that resembled the text we see in Delphis editor.

An Arsenal of Word-processing
Components
WPTools consists of about 20 components that
provide everything you need to add modern
word-processing support to any application.
At the heart of the library are the five RTF
components summarized in Figure 1. All
of these display, and most allow editing of,
formatted text.
Chief among the main RTF components and
at the heart of any word processing support
is TWPRichText. Since it relies on WPTools

New & Used


RTF engine, this component is completely independent from
Microsofts RichEdit DLL. Because of that, it behaves in a
consistent and predictable manner across the various Windows
platforms.
New to version 4 is the ability to export text in HTML/CSS
format. This makes it possible to come closer to WYSIWYG than
using an HTML approach. WPTools uses the information from
the paragraph style table to create the CSS information, creating
additional styles automatically. The library uses the built-in
shared XML classes for HTML reading and writing. It includes a
simplified XML reader and writer that you can use independently
from the rest of the library. WPTools also uses these classes for the
integrated localization control.
But the versatility goes even further. In building an application,
you can logically connect other WPTools controls to TWPRichText
to give users the same level of control
available in commercial word-processing
Purpose
applications. Users can set a wide range of
Display and print WPTools text inside a QuickReport form.
text properties, including the many font
qualities. The library includes a specialized
Provide mail-merge capabilities.
toolbar to support this functionality. You
Modify tabs, indents, and table sizes.
can also use the action classes or the lowShow page height and scroll when in layout mode.
level API functions to control paragraph
and text attributes.
Connected to the WPToolBar property of a TWPRichText

Figure 2: An example program with a tiled background and


numerical calculations in a table.
Component
TWPQUICKPRINT
TWPMMDATAPROVIDER
TWPRuler
TWPVertRuler
TWPToolBar

object, provides over 60 predefined buttons.


TWPToolPanel

A more basic toolbar than TWPToolBar; with this one you


need to create and position the buttons yourself, giving you
more control over the design of your application.

TWPToolCtrl

Provides an automatic interface with WPToolButton components on the same panel.

TWPToolsBasicCustomAction

Ancestor class of all the predefined action classes that can be


used with WPTools. There are more than 90 action classes.

TWPComboBox

A combo box that can be used with TWPToolPanel or


TWPToolCtrl to select fonts and colors.

TWPToolButton

A button that can be used with TWPToolPanel or TWPToolCtrl


to set character and paragraph attributes or to start operations like Load or Print.

TWPValueEdit

A flat spin edit that automatically converts from cm, inch,


points, and twips.

TWPShadedPanel

A TPanel to enable creation of attractive forms.

Figure 3: Miscellaneous GUI and support components.

Figure 4: An example program showing various text formats and integration with Addict
Spelling Checking library.
32 December 2002 Delphi Informant Magazine

Other tools include vertical and horizontal


rulers, table tools, and print preview
capability. There are sophisticated display
capabilities, including transparency and
tiled background images (Figure 2 shows
an example of a tiled background), and
cursor hover events and effects. For
example, its possible to display the file
fieldname in a popup hint when the
cursor is moved over a mail-merge letter.
Closely related to the TWPRichText
control is the data-aware TDBWPRichText.
This component allows users to edit text
stored in a database. It works in a manner
similar to other data-aware controls. But
unlike Delphis TDBMemo, it doesnt
automatically refresh the dataset when the
focus changes, nor does it reload text when
the database is set to edit mode you
must set various properties to enable such
functionality.
TWPEdit is a single-line edit control
for working with formatted text. If you
have several such controls, WPTools
provides the TWPContainer component
to store and load the data in all of these
controls. The TWPRichTextLabel is
simply a formatted label. Descended from
TGraphicControl, it provides the ability to
create attractive labels in any application.
These labels can also be transparent.
Finally, TWPPreview allows you to display
a multi-page preview for the text in the
attached TWPRichText control.

New & Used


GUI Controls, Dialog Boxes,
and Miscellaneous Components
In addition to its main components, WPTools includes many
support components that greatly enhance the power and flexibility
of the library (see Figure 3). For example, TWPQUICKPRINT lets
you display and print WPTools text inside of a QuickReport form.
There are various GUI controls to support the environment of a
full-featured word processor: panels, toolbars, rulers, and so on.
Figure 4 shows a demonstration application that uses all of these
components. The library also includes many built-in dialog boxes
to perform the following tasks (and more):
Change bullet or numbering styles
Change page size and margins
Modify paragraphs or table cell borders
Modify paragraph indentation and spacing
Modify, add, and delete paragraph styles
Insert symbols into the text
Create or modify tab stops

Integration with other Libraries,


Documentation, Support, etc.
With the optional WPReporter (included in the WPTools Bundle),
you can use bands and groups in mail-merge templates. These addon components also support formulas in tables, sequentially stored
text, enhanced mail merge, and a formula evaluation engine. There
are also connections to wpCubeds wPDF library (reviewed in the
May, 2002 issue of Delphi Informant). WPTools also integrates
well with their WPForm product, allowing you to create and print
forms. Theres also good integration with other libraries, such
as Toolbar2000 and Addict Spelling Checker (note the Addict
Spelling Checker dialog box in Figure 4).
The documentation is good and getting better. I was impressed
with the current draft of the PDF manual. It provides a very
good overview of the entire library and a helpful tutorial. The
author is currently adding information on some of the lowlevel functionality I mentioned previously, which will be a very
welcome addition. I have always found WPTools e-mail support
to be completely satisfactory; generally the author answers
questions within 24 hours. WPTools version 4.x Standard
includes source code for the components but not the RTF engine.
However, this can be purchased separately and is included in the
Professional version.

Conclusion
WPTools is an outstanding word processing library for Delphi
or C++Builder. It contains everything you need to add word
processing support or to work with formatted text in any
application. I recommend it highly.

Alan Moore is a professor at Kentucky State University, where he teaches music theory and
humanities. He was named Distinguished Professor for 2001-2002. He has been named
the Project JEDI Director for 2002-2003. He has developed education-related applications
with the Borland languages for more than 15 years. Hes the author of The Tomes of
Delphi: Win32 Multimedia API (Wordware Publishing, 2000) and co-author (with John
Penman) of an upcoming book in the Tomes series on Communications APIs. He also has
published a number of articles in various technical journals. Using Delphi, he specializes
in writing custom components and implementing multimedia capabilities in applications,
particularly sound and music. You can reach Alan on the Internet at acmdoc@aol.com.

33 December 2002 Delphi Informant Magazine

WPTools 4 from wpCubed GmbH is a superb collection of Rich


Text Format components. At its heart is a powerful RTF engine
that provides developers with considerable control over formatted
text. In creating word-processing applications, it supports standard
paragraph properties (indentation, spacing, alignment, margins,
and tabs), character attributes (bold, italic, superscript, subscript,
color, etc.) and support for hyperlinks, protected text, hidden text,
footnotes, headers, footers, bookmarks, mail-merge, and much
more. It has strong support for tables and includes easy integration
with a number of related tools, including Addict Spelling Checking
components and other wpCubed products.
wpCubed GmbH
(formerly Julian Ziersch Software)
c/o Julian Ziersch
St. Inbert Str. 30
81541 Munich
Germany
E-Mail: support@wptools.de
Web Site: http://www.wptools.de
Price: WPTools Standard, 225; Professional, 385. WPTools Bundle
Standard (includes WPReporter), 340; Professional 488. WPReporter
Add-on (requires WPTools 3.x or 4.x), 115. Optional support units for
ReportBuilder, 75. Free upgrade for 3.x owners who purchased on
or after October 1, 2002. For the latest prices, other bundles, and site
license information, visit the wpCubed Web site.

TextFile

The Tomes of Delphi: Win32 Core API, Windows 2000 Edition


In 1998, fellow Delphi Informant contributing writer Alan
Moore highly recommended
the original edition of this
book, written at the time for
Delphi 3 developers (visit
http://www.DelphiZine.com/
bookreviews/1556225563_b/
1556225563_b.asp for Alans
original review). Since then,
plenty has happened in the
world of Windows 32-bit
development. Most notably,
Microsofts determination
to build the future of the
Windows platform on the
.NET Framework has turned
the Windows development
community in a new direction.
As more low-level legacy
Windows APIs are converted
to their .NET equivalents, the
need to know Win32 Core
APIs is waning. Yet, at least
for the near term, there are
certainly aspects of the Windows desktop that cannot be
tinkered with using the managed code employed by .NET.
For those instances, knowing
the Win32 Core API is still a
Delphi programmers best bet
for controlling some aspects of
Windows.
Like the original version,
this Windows 2000 Edition

takes readers through window


creation, message processing, memory management,
DLL, process, thread, timer,
error, GDI, painting, drawing, region and path, bitmap,
and metafile functions. Each
function is discussed in detail
regarding syntax, parameters,
return value, cross references,
etc. Then its C or C++ library
call is converted into an Object
Pascal equivalent and an example of the call is provided. The
book also includes a CD-ROM
containing each chapters
source code in ready-to-run
Delphi project format.
But is the new edition worth
$60? Particularly for those who
already own the previous edition? Unfortunately, there arent
enough Windows 2000-specific
API examples to warrant an
upgrade. In fact, there isnt even
an easy way to discern from the
book which API references are
specific to the Win2K platform
besides its occasional bolding
of value name descriptions.
And this books discussion only
includes the Win9x, ME, and
NT/2K family of Microsoft
operating systems; no XP-specific APIs are mentioned. Given
the fact that the book was pub-

34 December 2002 Delphi Informant Magazine

lished in early 2002, its too bad


theres not even an appendix
highlighting a quick listing of
new XP-specific functions.
What about Delphi developers
ready to take the plunge into
the world of deeper Windows
API understanding? Four years
ago, this book would have
been invaluable. Today, given
the amount of information
available on the Internet and
Microsofts MSDN Web site,
its an expensive proposition. As
readers become more confident
with the books approach, they
quickly realize that once they
see how a few C/C++ libraries are converted into Delphi
syntax, the others are just as
straightforward. I recommend
that those interested in purchasing this book first download the
Core help file freely available
from Wordwares Web site, and
visit a few Delphi programming
sites for specific Windows API
programming articles. This is
particularly recommended for
those simply searching for a
handful of Windows API calls
to address a few programming
problems.
On the other hand, if the help
file creates more questions than

The Tomes of Delphi: Win32


Core API, Windows 2000 Edition by John Ayres, Wordware
Publishing, www.wordware.com.
ISBN: 1-55622-750-7
Cover Price: US$59.95
(731 pages, CD-ROM)

answers for your Windows API


Delphi programming project, I
strongly recommend purchasing this title, especially if
your objective is to live in the
Windows API as a Delphi programmer for the next year or
two. Theres no other Delphispecific book on the market
today as deeply dedicated to
the subject.
Mike Riley

File | New
Directions / Commentary

Delphi 7: The Lucky One?

here might be something to numerology the notion that numbers have mystical meaning or power. For example,
in some Asian cultures the number four is considered unlucky. Recalling the initial problems with Delphi 4, especially
its premature release with those annoying bugs, numerology seems worth considering. What about the number seven?
It seems that seven is considered auspicious in many cultures. So, will Delphi 7 prove to be a great success for Borland?
Its much too soon to say for sure. But with that question in mind, well explore Delphi 7s new features, its expanded
component library, and a few early user reactions.
Delphi and Windows. One important issue that comes up with each
new version of Delphi is its level of support for current Windows
incarnations and other important new technologies. Delphi 7 supports many of the new features in recent Windows versions, such as
XPs Themes and common controls. In terms of emerging technologies, support for Web Services seems particularly important. As in the
previous version, Delphi 7 includes seven Web Services components.
However, there are additional enhancements and even more example
programs (something I find especially helpful). As many readers are
aware, Simple Object Access Protocol (SOAP) is one of the most
popular technologies for packaging Web Services messages. Delphis
SOAP support has increased considerably; Delphi 7 now includes
classes and interfaces to support reading or inserting headers into the
SOAP envelopes that transmit messages between clients and servers.
And thats just one of several enhancements.
Providing Web Services is also one of the main elements of Microsofts
.NET strategy. Not only does Delphi 7 provide good support for Web
Services in general, it provides incipient support for .NET, including
a Migration Kit for moving to that platform. There is also an early
version of the Delphi for .NET compiler; unfortunately, this compiler
is apparently not integrated into the development environment. When
I shared this with Borlands John Kastner, he informed me that there
is an unsupported OTA (Open Tools Add-in) for using the Delphi for
.NET compiler preview thats available on BDN (Borland Developer
Network). The Delphi dcc32 compiler itself has also been enhanced with
.NET in mind. That compiler includes three additional compiler warnings to help developers prepare code for porting to .NET: Unsafe_Type,
Unsafe_Code, and Unsafe_Cast.
Of course, Delphi continues to provide strong support for its three
traditional development areas: Windows GUI desktop applications,
database applications, and Web applications. Be aware that in the latter
two categories some of the new and continuing support is limited to the
high-end versions; a small subset is found only in the Enterprise Edition.
There are many changes in the area of database development, with some
components being enhanced and others being depreciated. DbExpress
is becoming increasingly important with each new Delphi version. Its
drivers have been updated to support Informix SE, Oracle 9i, DB2
7.2, InterBase 6.5, and MySQL 3.23.49, and a driver has been added
to support MSSQL 2000. There are also new and enhanced database
components. A new unit, DBClientActns, includes three new action
components for working with client datasets: TClientDataSetApply,
TClientDataSetUndo, and TClientDataSetRevert.
35 December 2002 Delphi Informant Magazine

The world beyond Windows. Borland is enabling cross-platform


Windows/Linux development even more than in the past. The Professional Edition of Delphi and above now include comparable versions
of Kylix 3 (Delphi version only), making cross-platform development a
reality for more programmers. Of course, the cross-platform developer
must use Kylix to compile the Linux version of any CLX application
written in Delphi 7. But thats not a big deal, is it?
Theres also news on the CLX front. Delphi 7 includes a new CLX-only
version of the System page displayed when a CLX application is opened
in Delphi (previously the System page was displayed only for VCL
applications). The new System page includes several directory and file
components specific to the Linux operating system.
New components and routines. For many developers, new and
enhanced components are as important as any other aspect when
evaluating a new version of Delphi. Again, be aware that many of the
components I will list arent available in all versions. In the area of
Web development, Delphi 7 includes AToZed Softwares IntraWeb
components that take up four palette pages: IW Standard, IW Data,
IW Client Side, and IW Control. These components support development of Internet server applications using standard form tools.
They also support the development of pages for WebBroker and
WebSnap applications.
Heres a sampling of some of the other new components that have been
added. There are new Indy components. In addition, Nevronas Rave
Reports, which supports report creation by users, is included in some
versions. And, there are additional routines that have been added to
SysUtils, the Math unit, and other units.
Changes to the editor and debugger and more. The code editor
is the central element of the IDE for most developers. There have
been many enhancements to Code Insight, especially code completion. Code completion now applies to HTML code. You can create
customized code completion managers using expanded capabilities
of the OpenTools API. The editor is now more friendly toward
source code other than Delphi (formerly referred to as Object
Pascal); you can now also view C++, C#, HTML, and XML code
with appropriate syntax highlighting.
What about changes to the Integrated Debugger? First, the Watch List
has a new look. It now uses multiple tabs so you can organize watches
into groups for easier debugging. To add a watch group, right-click the
Watch List and select Add Group.

File | New
The Enterprise Edition includes ModelMaker, a UML-based add-on to
simplify working with classes and interfaces (see Robert Leaheys review
of ModelMaker in the August issue of Delphi Informant Magazine).
This is one of my favorite new features, and something I expect to be
working with a great deal. Although there is less printed documentation than in earlier Delphi versions, electronic documentation in the
form of PDF, HTML, and INT files has mushroomed. In fact, theres
so much that Borland had to put it on one of the Companion Tools
CDs. There are now two!
Should you upgrade? This was a particularly hot topic in some of the
discussions I saw online. Some developers complained about the price
of Delphi compared to Microsoft products. Others pointed out that
upgrade prices are much less than purchasing it new. And when the
question of bugs came up, most developers indicated they had not
encountered anything serious.
Quite a few folks were interested in comparing Delphi with Microsofts
Visual Studio .NET. One developer pointed out that upgrades to
Microsofts development tools occur much less frequently than those
to Borlands tools. (Im not convinced that less frequent upgrades are
really desirable; I think it makes a lot of sense to keep up to date with
the newest technologies.) Another developer talked about positive
experiences with C#. Another Delphi columnist suggested that Borland
should consider making Delphi for .NET a plug-in that could be used
in that environment. Again, Im not convinced this would be a good
business move for Borland, although it has a certain superficial appeal. A
few in the discussion groups were quite candid about their past Delphi
upgrade histories; here are two examples: 1, 2, 3, 5 and 1, 3, 5, with a
strong intention of upgrading to Delphi 7. It struck me that this second
individual might be a student of numerology who believes in the magic
of odd-numbered Delphi versions.

36 December 2002 Delphi Informant Magazine

What factors should one consider before deciding whether to


upgrade and/or which version to purchase? An upgrade to one of
the high-end versions constitutes a major investment, so it seems
important to examine the feature matrix of each version and compare it against specific needs and available funds. Someone developing Windows GUI desktop applications will have less of a need
to upgrade than a database or Internet developer. Those in the
latter two groups should examine the features in the Professional
and Enterprise editions, being mindful of the work they plan to
undertake in the coming year. And anyone planning to do serious
work in the .NET platform should give serious consideration to
upgrading. In the coming months, I plan to write a column on
.NET, so there will be much more on that topic then.
Will Delphi 7 be lucky for Borland? I think it could be. But to a
great extent the answer lies with you. Next month well explore
something completely different, an old language called Prolog
that is now available in Delphi. Until next time.
Alan C. Moore, Ph.D.

Alan Moore is a professor at Kentucky State University, where he teaches music


theory and humanities. He was named Distinguished Professor for 20012002. He has been named the Project JEDI Director for 2002-2003. He has
developed education-related applications with the Borland languages for more
than 15 years. Hes the author of The Tomes of Delphi: Win32 Multimedia API
(Wordware Publishing, 2000) and co-author (with John Penman) of an upcoming book in the Tomes series on Communications APIs. He also has published
a number of articles in various technical journals. Using Delphi, he specializes
in writing custom components and implementing multimedia capabilities in
applications, particularly sound and music. You can reach Alan on the Internet at
acmdoc@aol.com.

S-ar putea să vă placă și