Você está na página 1de 35

November 2001, Volume 7 Number 11

Cover Art By: Arthur Dugoni

ON THE COVER
5

On the Net

Web Services Made Plain Keith Wood


Keith Wood shows how to create a Web Service, from invoking the
wizards, to writing interface method declarations and implementations. The sweet part is that Delphi automatically handles the incoming and outgoing SOAP messages.

FEATURES
10

Columns & Rows

Using the XML Features of


SQL Server 2000: Part IV Alex Fedorov
Alex Fedorov demonstrates how to use extracted XML-formatted metadata and the ClientDataSet component to create a bridge between
alien data sources and Delphis data-aware controls.

15

Bruno Sonnino shares three tips: putting a menu in a toolbar so it


can be detached and moved around, and how to add tab stops to a
Memo component and the very different RichEdit component.

REVIEWS
27

Kylix Tech

An Open Invitation Ray Lischner


Ray Lischner makes it easy for the uninitiated to evaluate Kylix Open
Edition by exploring topics such as selecting the Linux distribution,
installation, FreeCLX issues, dbExpress, licensing, and more.
1 November 2001 Delphi Informant Magazine

SysTools for Kylix


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

30

Building Kylix Applications


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

31

The Tomes of Delphi:


Algorithms and Data Structures
Book Review by Tom Lisjac

Delphi at Work

User-modifiable DBGrids Ron Nibbelink


Ron Nibbelink shares the techniques and code necessary to provide
users with highly versatile DBGrid components that allow them to
control the appearance, order, and width of columns at run time.

19

23 At Your Fingertips
Floating Menus, Etc. Bruno Sonnino

DEPARTMENTS
2
32
35

Delphi Tools
File | New by Alan C. Moore, Ph.D.
Symposium by Jerry Coffey

Delphi
T O O L S

New Products
and Solutions

Book Picks
XML by Example,
Second Edition
Benot Marchal
QUE

Greatis Software Presents TFormDesigner


Greatis Software released
TFormDesigner, a run-time form
designer component for Delphi
3 through 5 and C++Builder 3
through 5.
TFormDesigner allows you to
move and resize any control on
your form. There is no need to
prepare your form; simply place
TFormDesigner into your form
and set the Active property to True.
Unlike the standard form
designer, which works only at
design time, TFormDesigner
works at run time. TFormDesigner replicates the face of the
standard Delphi and C++Builder
form designer, providing ease of
use and familiarity. After it has
been activated, you can select
any control in your form with
the T key or mouse-click to
move or resize it.
TFormDesigner features

multi-selection, ActiveX forms


compatibility, MDI forms compatibility, locking and protecting
any controls, an align dialog
box, a size dialog box, alignment
palette, customizable grab handles, customizable design grid,
and size/coordinates hints.
The compiled demonstration,
printable documentation, and trial
version for both Delphi and

C++Builder 3 through 5 are


included in the demo kit, available
at the Greatis Software Web site.
Greatis Software
Price: Single-user license, US$29.95; team
license (10 users), US$149.95; site license
(unlimited), US$299.95.
Contact: b-team@greatis.com
Web Site: http://www.greatis.com/
formdes.htm

Epsylon Technologies Releases SuperBEAST 2.1 and TAXXI Technology

ISBN: 0-7897-2504-5
Cover Price: US$29.99
(495 pages)
http://www.quepublishing.com

UML for Database Design


Eric J. Naiburg
and Robert A. Maksimchuk
Addison-Wesley

ISBN: 0-201-72163-5
Cover Price: US$39.99
(300 pages)
http://www.awl.com/cseng

2 November 2001 Delphi Informant Magazine

Epsylon Technologies, Inc. has


released SuperBEAST, an application server for Delphi/C++
applications. SuperBEAST
includes TAXXI Communicator,
an XML browser that makes
remote Delphi/C++Builder applications work as though they are
local. Delphi applications should
be hosted under SuperBEAST
Server control.
TAXXI applications look and
feel local; with a Windows
interface, they can be launched
through the Windows Start
menu or by double-clicking a
desktop icon. It is easy to
transform Delphi-written applications into TAXXI by adding
the TaxxiControl component to
the main form. After recompiling, the remote application is
accessible to users via the Internet, modem, or LAN. The connection can be encrypted.
Developers previously could
create TAXXI applications only
using standard Delphi/
C++Builder visual components.
Now, remote Delphi applications
can consist of any third-party
components by adding code for
TAXXI support of the components. All applications run as a
.exe under SuperBEASTs control.
To deploy an application you only

need to change the .exe file.


A developer can create sophisticated, multithreaded applications
running in single or multi-user
mode. An application in single
mode launches after a user request;
another copy will be started
for another user. The application
exists in memory between sessions
until timeout. Multi-user mode
means an application serves many
users at the same time, minimizing
memory demands.
SuperBEAST Server may
play the role of stand-alone
Web server with SSL/TSL support, virtual server support, FTP,
Gopher, Finger, Telnet support,
and full-featured administration
utility. SuperBEAST Server may
work with Apache as a pure
application server. SuperBEAST
eases the creation of applications
for HTML browsers (HTML
components) and for wireless
devices (WML components).
SuperBEAST consists of
TAXXI Communicator; BEAST,
which includes HTML Lib
run-time server and administration utility; HTML Lib
components inherited from standard Delphi components for
WYSIWYG HTML development; WML Lib, a set of
components for WYSIWYG

development for wireless devices;


TAXXI Lib, a set of components
for supporting thin client technology for Delphi/C++Builder
applications; BFC, a C++ library
for writing sophisticated serverside applications in Visual C++;
Protocol SDK, a C++ library for
writing additional protocols for
SuperBEAST; Transport SDK, a
C++ library for writing transport-level packages; and BEAST
Guardian source code for faulttolerance application.
TAXXI Communicator runs
on Windows 95/98/ME/
NT/2000. SuperBEAST Server
runs on Windows NT/2000
(full mode) and Windows
95/98/ME (debug mode).
The SuperBEAST Developer
license includes one unit license,
four profile licenses, and one
developer license. There are no
limitations on the number of concurrent users per server. Trial versions of SuperBEAST and TAXXI
can be downloaded from the
Epsylon Technologies Web site.
Epsylon Technologies, Inc.
Price: SuperBEAST Developer license,
US$2,495; TAXXI Communicator, free.
Contact: info@epsylontech.com
Web Sites: http://www.epsylontech.com,
http://www.taxxi.com

Delphi
T O O L S

New Products
and Solutions

Book Picks
User Interface Design
for Programmers
Joel Spolsky
Apress

BoldSoft Releases Bold for Delphi 3.1


BoldSoft AB announced the
release of its application framework, Bold for Delphi 3.1, a
development product that uses
industry-standard Unified Modeling Language (UML) models
to move application design into
final applications.
Bold for Delphi offered tight
integration with Rational Softwares design tool Rational Rose.
Bold for Delphi 3.1 also offers
tight integration with all other
major modeling tools via the
XML Metadata Interchange
(XMI) format. Bold for Delphi
3.1 uses UML models in design
time to create databases, optionally generate source-code, and
design a model-powered GUI. In
run time, the Bold Model-VM
technology offers a model-powered application infrastructure
providing services such as persistence mapping, multi-user
propagating, and display mechanisms.
To offer scalability, Bold for
Delphi 3.1 comes with more

than 400 possible architectures


using HTTP, COM/DCOM,
XML, and SOAP to communicate between layers. A tool that
automatically migrates fat-client
to thin-client applications is also
included.
Bold for Delphi 3.1 offers features to automatically upgrade
databases with preserved data.
Objects created by an earlier
version of the model can be
upgraded on the fly, or via the

batch upgrader mechanisms.


Bold for Delphi 3.1 includes
built-in support for multiple
object level undo/redo, as well
as Extended Locking, a modelpowered mechanism that takes
care of advanced pessimistic
locking in run time.
BoldSoft AB
Price: Contact BoldSoft for pricing.
Contact: info@boldsoft.com
Web Site: http://www.boldsoft.com

eHelp Ships RoboHelp Enterprise 1.0


ISBN: 1-893115-94-1
Cover Price: US$29.95
(144 pages)
http://www.apress.com

Extreme Programming Explored


William C. Wake
Addison-Wesley

ISBN: 0-201-73397-8
Cover Price: US$29.99
(159 pages)
http://www.awl.com/cseng/xp

eHelp Corp. is shipping


RoboHelp Enterprise 1.0, serverpowered Help software for
developers, Help authors, and
technical writers who need to
deliver online Help and assistance with desktop and Webbased applications.
RoboHelp Enterprise 1.0s
server-based technology gives
development teams direct feed-

back about the questions and


problems end users experience
with applications and their Help
systems. Enterprise 1.0 also
includes an array of Help technology and supports all major
Help formats.
RoboHelp Enterprise 1.0 features built-in usability testing
through reports, including unanswered user questions, most fre-

quently asked user questions,


application pages/screens requiring the most Help, most frequently viewed Help topics,
Help requests over time, and
Help system errors.
RoboHelp Enterprise 1.0 offers
the ability to pinpoint problem
areas of an application or Help
system, advanced project merging for improved team development, updates to the Help
content, and integration with
HTML editors, such as FrontPage, Dreamweaver, HomeSite,
etc.
RoboHelp Enterprise 1.0
includes the features of RoboHelp Office for creating, editing,
and organizing online Help content, superior natural language
search for end users, and XMLbased navigation for significantly
faster performance of serverbased Help systems.
eHelp Corp.
Price: US$1,898. Discounted pricing for
current RoboHelp Office users.
Contact: (800) 459-2356
Web Site: http://www.ehelp.com

3 November 2001 Delphi Informant Magazine

Delphi
T O O L S

New Products
and Solutions

Book Picks
Mastering Delphi 6
Marco Cant
SYBEX

ISBN: 0-7821-2874-2
Cover Price: US$59.99
(1,129 pages, CD-ROM)
http://www.sybex.com

The Linux Cookbook: Tips and


Techniques for Everyday Use
Michael Stutz
No Starch Press

Palmsoft Releases Jazmine Calendar/PIM Widgets 2.0


Palmsoft, Inc.
released Jazmine
Calendar/PIM
Widgets 2.0
(JCW 2.0), a
visual component
suite providing
reliable, featurerich scheduling/
PIM type components for Borland
toolsets.
JCW 2.0 is
a collection of
components for
delivering robust
scheduling or
task-oriented
applications.
Standard views,
such as Microsoft
Outlooks
Calendar/Task
and Lotus
Organizers Planner are available;
multi-column day
view, week view, monthly calendar, yearly calendar, custom
view, alarms, and date and time
edit control all use common
properties and events for seamless integration into any application. JCW is resource-centric
to ease creating resource schedulers (conference room, doctor/
patient, operating room, etc.).
JCW 2.0 is built around
an architecture with the sole
responsibility of presenting
your scheduling and task data.
It is not responsible for modifying or updating your datasource. All events necessary to
manage and persist the data
are included. For data-aware

functionality, a DataStore component allows you to connect


your database in the usual
datasource/dataset manner.
In JCW 2.0, every event
belongs to a resource, which
can be a person, place, or
thing, or anything else you can
imagine. You can drag events
across resources. It allows you
to view your events in many
styles. JCW 2.0 offers full support for XML so that you can
load or save your data to and
from XML.
JCW 2.0 includes appointments, tasks, to dos, view by
day, view by week, view by
month, view by year, view
by planner, view by your def-

inition, view by user/owner,


persistent sort/settings, custom
color/fonts, drag and drop,
bitmap background, set
reminders, data aware
(optional), XML aware, and
run time customization.
JCW 2.0 is compatible with
Delphi 5, 6, and C++Builder 5.
Free demonstrations and trial
versions of Jazmine Calendar/
PIM Widgets 2.0 are available.
Palmsoft, Inc.
Price: Jazmine Calendar/PIM Widgets
2.0, US$379 with source; without source
US$249.
Contact: sales@palmsoft.com
Web Site:
http://www.jazminecomponents.com

PocketStudio Professional 1.0 Released


ISBN: 1-886411-48-4
Cover Price: US$29.95
(396 pages)
http://www.nostarch.com

4 November 2001 Delphi Informant Magazine

Pocket Technologies, Inc.


announced the release of PocketStudio Professional 1.0, a
powerful RAD development
tool for developing applications for the Palm OS. Pocket
Technologies will showcase the
new product at PalmSource
2001 in San Jose, CA from
October 23 to 26, 2001.
PocketStudio Professional Edition is a complete development
solution for embedded and
handheld devices. PocketStu-

dio Professional 1.0 works on


Palm OS 2.0 and higher. PocketStudio Professional Edition is
a powerful combination of a
32-bit compiler with an integrated development environment that allows you to build
sophisticated applications for
the Palm OS. PocketStudio
provides the power of Pascal
to make developing for the
PalmOS simpler, quicker, and
more powerful than ever
before.

PocketStudio is easy to
learn and use, makes creating
complex and powerful applications simple and intuitive,
helps developers get products
to market quickly, and allows
PC developers to easily migrate
to Palm OS development.
Pocket Technologies, Inc.
Price: US$499.99
Contact: info@pocket-technologies.com
Web Site:
http://www.pocket-technologies.com

On the Net

Web Services / SOAP / WSDL / Delphi 6 Enterprise

By Keith Wood

Web Services Made Plain


Delphi 6 Handles the SOAPy Details

n the August issue of Delphi Informant Magazine, I introduced you to the Simple Object
Access Protocol (SOAP) as a way of calling methods on remote objects. I presented a
series of components that allowed your Web application to respond to requests encoded
in SOAP. Thus, you could write a SOAP application by dropping two components on the
Web module, creating an action, and writing an event handler for it.
With the advent of Delphi 6 Enterprise Edition,
creating SOAP applications is just as easy. Delphi
6 goes further to provide direct support for parameter types other than strings, including structured
types. It also automatically generates another essential part of using SOAP: the Web Services Description Language (WSDL) file that allows others to
make use of your service.
This article describes the full process of creating your
own Web Service, one that finds movies given a
certain rating, and creating a client application that
makes use of it. Since Web Services are languageand platform-neutral, you also can call on other
services to add functionality to your application. To
demonstrate, the client imports and then invokes a
service that sends out e-mails, so you can inform
friends about the results of your movie search.

Wizards to Web Services


Before starting your new Web Service, you
should go to the Borland Delphi Web site
(http://www.borland.com/delphi/webreg/
registeredusers.html) and pick up the Invokamatic
Wizard, one of the eXtreme Toys for Delphi 6.
Once compiled and installed, the wizard adds an
entry (Invokable Wizard) on the WebServices tab of
the New Items dialog box. Youll use this to help
build a Web Service.
After installing the wizard, you can start your new
Web Service by opening the New Items dialog box,
going to the WebServices tab, and selecting the SOAP
Server Application icon. The resulting dialog box (see
Figure 1) appears exactly like the normal Web server
application, except for the mention of SOAP in the
title bar. The SOAP specification describes how to
use HTTP as the transmission protocol, to make a
SOAP application just another Web application.
Choose the appropriate style of application for your
purposes. Clicking on OK generates a Web module
with three components on it (see Figure 2). The
THTTPSoapDispatcher registers itself with the Web
module as an auto-dispatching object, thus intercepting all SOAP-related calls (those starting with soap
in their path), without the need for creating an appropriate Web action. Then, the THTTPSoapDispatcher
passes these calls along to the second component.

Figure 1: Starting a new Web Service.

5 November 2001 Delphi Informant Magazine

The THTTPSoapPascalInvoker receives the incoming


SOAP message and converts that into a call to a Pascal
method. THTTPSoapPascalInvoker makes use of the

On the Net

Figure 2: The Web Service module.

XML DOM framework built into Delphi 6 to perform the parsing of


the SOAP XML into variables and objects that are easier to work with in
Pascal. By default, this makes use of the Microsoft MSXML parser.
Like magic, the THTTPSoapPascalInvoker uses interfaces to define
the available services, and then registers both these and their implementations, so they can be retrieved by name later. The interfaces
need to have RTTI (runtime type information) details available,
which is achieved by deriving them from IInvokable.
Finally, the TWSDLHTMLPublish component generates the WSDL
that describes your Web Service. The component also uses the invocation registry to determine which services are available, and scans
those interfaces to create the corresponding WSDL. As another autodispatching object, this component looks for incoming requests that
start with wsdl in their path, and responds to them automatically.
Without any further request information, the component generates
an HTML page that lists all the available interfaces and links to their
WSDL documents. When a request comes in and names a particular
interface, TWSDLHTMLPublish returns the WSDL for it directly.
Amazingly, you dont actually change anything on this Web module.
All the necessary functionality is built into the module to handle the
incoming SOAP or WSDL requests, and to pass the SOAP requests
off to an appropriate handler. Instead of altering this unit, you create
additional units and then register your interfaces and classes with the
invocation registry, which is then queried by the components here to
complete the operation.

Invocation Wizard
Now, you get to use the new wizard you downloaded to create the
interface and implementing class for your Web Service. Again, in
the New Items dialog, on the WebServices tab, open the Invokable
Interface & Class Wizard (see Figure 3) and enter your details. What
youre creating is an interface that defines the functionality available
through your Web Service, and a class that implements those abilities.
By deriving from TInvokableClass, you gain a virtual constructor
that lets you initialize your objects easily. As in the previous SOAP
article, the Web Service involves finding the names of movies with a
particular rating for a nights entertainment.
The result of using this wizard is two units: one for the interface, and
one for its implementation. Its good practice to separate the interface
from the implementing class, because this lets you reuse the interface
without reference to a particular way of performing its actions. Youll
make use of the interface in the client part of this Web Service later.
Within the interface declaration the wizard has created, you just enter
functions and procedures that define the required functionality. Make
sure they all have the stdcall directive, so other processes can easily
access them.
Any types to which you refer in these methods must be encodable
within SOAP. The references to the Types and XSBuiltIns units
provide you with all the basic types (such as strings, integers, and
6 November 2001 Delphi Informant Magazine

Figure 3: The Invokable Interface & Class Wizard dialog box.

dates) and dynamic arrays of them as well. If you need something


more, you can define your own types, but you must register them
before using them within the SOAP framework.
In this example, the function takes one string value (the rating) and
returns an array of strings (the names of the matching movies). The
code in the initialization section is what allows the other components
to know about your interface via registration. The first parameter is
required, because its the reference to the interface itself. Additional
parameters let you specify the namespace for this interface, an encoding scheme to use, a description of the interfaces purpose, and an
external name for the WSDL port type.
The namespace in SOAP action headers for this interface indicates it
is the target of a call. A unique string is generated if you dont supply
a value. The external name is useful when the port type has a name
that isnt valid in Object Pascal, such as a keyword.
Figure 4 shows the completed interface for the example Web Service.
The only changes from the generated source are the addition of the
function declaration and a namespace for the registration call. The
FindMovies method takes a string rating, and returns a dynamic string
array of movie names.
The second unit generated by the wizard contains a class that implements this interface, along with a registration call at the end for that
class. Now, add the method declaration from the previous unit, and
write its implementation, as shown in Listing One (on page 9). For
this example, you also need to create a TQuery object you can use
to extract matching movies from the database (using the BDE alias,
Movie-watcher).
Using a parameterized query lets you plug in the value of the rating
easily at a later stage. This isnt much of an issue in this example,
because the application is set up as a CGI program, meaning that a
new instance is invoked each time. If you were to use an ISAPI DLL
instead, you would benefit from the parameterized query, but then
youd have to deal with concurrency issues in database access.
As an aside, have a look at the WHERE clause for the query. This
enables you to pass in a blank rating and have the query return
everything, or a specific rating and select only those that match. The
term :rating refers to the parameter passed in, whereas rating is
the field within the database table:
SELECT Name FROM Movie
WHERE :rating = ''
OR :rating = rating

On the Net
FindMovies Client

{ Web Service interface. }


unit FindMoviesIntf;
interface
uses Types, XSBuiltIns;
type
IFindMovies = interface(IInvokable)
['{46773976-C1C0-4138-9276-3154DDEA70B4}']
function FindMovies(Rating: string):
TStringDynArray; stdcall;
end;
implementation
uses InvokeRegistry;
initialization
InvRegistry.RegisterInterface(TypeInfo(IFindMovies),
'http://www.the_movies.com/FindMovies', '',
'Find a list of movies with a given rating.'#13#10 +
'If the rating is blank, all movies are returned.');
end.

Figure 4: Defining the Web Service.

Note that the code raises an exception if there are no matching


movies for the given rating. This demonstrates how the SOAP framework handles exceptions and translates them into an appropriate
SOAP response with a fault.
To enable this interface and its implementation to be served by
the SOAP server, all you need to do is add these two units to
the project. Their initialization sections automatically register the
appropriate parts and the components on the Web module, and
then query the registry to find them. Compile the project and copy
the executable to your Web server scripts directory to deploy it.

WSDL
You can immediately see the WSDL associated with your Web
Service by accessing the following URL from your browser
(assuming a default deployment):
http://localhost/scripts/MovieSvc.exe/wsdl

The response is an HTML page generated by the TWSDLHTMLPublish


component that lists all the interfaces supported by this application. It
shows the IWSDLPublish one, as well as your own. Clicking on the
link for your interface brings up the corresponding WSDL, as shown in
Listing Two (on page 9).
This lays out the types used in the calls, the message types
(FindMoviesRequest and FindMoviesResponse), and the binding of
these two to form a complete remote call using SOAP. Finally,
the service is defined as this binding at a particular location
(URL). Fortunately, all of this is generated for you from your
interface definition, and you dont need to concern yourself with
the details. If you want more information, check out the references at the end of this article.
With this definition, a prospective client has enough information
to establish a connection to your service, present a properly formatted request for it, and know what to expect in return. Later,
you use this to talk to a third-party Web Service to provide
additional functionality.
7 November 2001 Delphi Informant Magazine

Figure 5: A Web Service client.

Now that the Web Service is up and running


on your local Web server,
you need a client to
access it. Because you
defined the interface
yourself, you can use
it directly in the client
application, which makes
the code simple.

Create a new application and put a label, combo box, button,


and memo on the form (see Figure 5). The combo box holds the
possible ratings, the button initiates the request, and the memo
displays the results. (The label is just there for show.) To make the
connection to the Web Service, you need to add a THTTPRIO
component (a remotely-invokable object via HTTP) from the
WebServices tab on the palette to the form.
Within the THTTPRIO component, you need to set the
WSDLLocation property to the URL you looked at for your interface: http://localhost/scripts/MovieSvc.exe/wsdl/IfindMovies.
Then, you can select the Service and Port property values from
the drop-down lists. Its possible for a Web Service to offer many
services at the one location, but in this case, theres only one.
Finally, in the OnClick event handler for the button, enter the
code shown in Figure 6. This casts the RIO component to your
interface, and then calls the method on it directly. Behind the
scenes, the request is formatted into a SOAP message, sent to the
Web Service provider, decoded and acted upon there, reformatted
as a SOAP response, returned to the client, decoded again, and
used within your program. Thank goodness, Delphi hides all this.
If there are no matching movies, an exception is generated on
the server side. This is passed across as a SOAP fault in the
response message and is reconstituted on the client. You trap
and deal with it just like any other exception. As far as your
program is concerned, theres no difference between invoking a
remote method and invoking a local one. Because the remote call
may take a while, however, you should turn the cursor into an
hourglass at the start of the button code and restore it to the
default at the end.
So, by dropping a component onto the form, setting a few properties, and casting it as an interface, you have immediate access to
a remote object and its functionality via SOAP. Delphi does all
the hard work for you. Also, because you have an interface defined
that encapsulates the remote call, you benefit from Code Insights
code and parameter completion in your coding for that service.

Third-party Services
This is great for Delphi Web Services, but what about other ones?
One of the big promises of Web Services is that you can write
them in any language on any platform and can access them from
anywhere. Can Delphi use these other services too? Of course.
To start, you need to locate the WSDL for the required
service. One place to look is at the XMethods site (http://
www.xmethods.com), a repository of Web Service definitions.
Looking down the list, you can see quite a few already implemented using Delphi.

On the Net
{ Retrieve array of movies matching specified rating.
Trap any exception from the Web Service and display it.
Otherwise place the movie names into the memo. }
procedure TfrmMovieServices.btnSearchClick(
Sender: TObject);
var
Results: TStringDynArray;
Index: Integer;
begin
Screen.Cursor := crHourGlass;
try
memResults.Lines.Clear;
try
Results := (rioMovies as IFindMovies).FindMovies(
cmbRating.Items[cmbRating.ItemIndex]);
for Index := 0 to High(Results) do
memResults.Lines.Add(Results[Index]);
except on e: Exception do
MessageDlg('This problem occurred:'#10#13 +
e.Message, mtError, [mbOK], 0);
end;
finally
Screen.Cursor := crDefault;
end;
end;

Figure 6: Searching for movies.

unit EmailIntf;
interface
uses Types, XSBuiltIns;
type
Email_cemailPortType = interface(IInvokable)
['{05D28B3D-05FA-4240-BFB7-1CDCC0415442}']
function SendAnonymousEmail(const sFrom: WideString;
const sTo: WideString; const sSubject: WideString;
const sMessage: WideString): Boolean; stdcall;
end;
implementation
uses InvokeRegistry;
initialization
InvRegistry.RegisterInterface(TypeInfo(
Email_cemailPortType), '', '', '',
'Email.cemailPortType');
end.

Figure 7: The e-mail Web Service interface.

{ Send list of movies to specified e-mail address. }


procedure TfrmMovieServices.btnEmailClick(Sender: TObject);
begin
Screen.Cursor := crHourGlass;
try
(rioEmail as Email_cemailPortType).SendAnonymousEmail(
'a_friend@the_movies.com', edtEmail.Text, 'Movies',
'These movies match the selected rating (' +
cmbRating.Items[cmbRating.ItemIndex] + '):'#10#13 +
memResults.Lines.Text);
MessageDlg('Email sent', mtInformation, [mbOK], 0);
finally
Screen.Cursor := crDefault;
end;
end;

Figure 8: Telling a friend.

8 November 2001 Delphi Informant Magazine

As an example, you can add e-mail capability to the movie


client so you can tell a friend about the movies you just found.
A couple of such services are listed at XMethods, but, for
the demonstration, you can use the Lucin version (WSDL at
http://sal006.salnetwork.com:83/lucin/email/cemail.xml) to show the
interoperability of these services. (Presumably, its not Delphi-based.)
For the find-movies service, you had an interface for the service to
code to, and it would be great if you had the same thing for this
other service. Fortunately, another wizard comes to the rescue. In the
New Items dialog box, on the WebServices tab, select the Web Services
Importer icon. Then enter the URL for the WSDL for the selected
service and click Generate. The wizard reads the WSDL and produces
the corresponding interface for you (see Figure 7).
Now you can just use it in your program like the find-movies one
you created directly. Drop another THTTPRIO on the form and set
its properties based on the new services WSDL. Add an edit field
to specify an e-mail address, and a button to send it. The code for
this button appears in Figure 8. As before, you cast the RIO to the
interface, and then use its methods directly.
Now you can run the client, find a list of movies based on their
ratings, and send the results to a specified e-mail address. And you
can do all of this with only a few lines of code.

Conclusion
Support for SOAP and Web Services is an important new feature of
Delphi 6. Writing a service consists of invoking two wizards and writing your interface method declarations and implementations. Delphi
automatically handles the processing of the SOAP messages coming
in and going out.
For a Web Service client, you can reuse the server-side interface in
conjunction with a THTTPRIO component to access the service
remotely. Then, the rest of your code is no different from calling
something locally. You even deal with exceptions the same way you
usually do. Third-party Web Services are processed through another
wizard to generate a Pascal interface for them, which is then used
with a THTTPRIO component as before.
Delphi 6 makes it easy to write Web Services applications and their
clients, and still have the full power of the Object Pascal language
at your disposal.

References
For SOAP references, go to http://www.w3c.org/TR/SOAP/ and
http://www.w3c.org/TR/soap12/. For information about WSDL,
see http://www.w3.org/TR/wsdl. The X-Methods Web Services
repository is at http://www.xmethods.com.
The files associated with this article are available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\NOV\
DI200111KW.

Keith Wood is a system engineer with Ellora Software, based near Boston. He
started using Borlands products with Turbo Pascal on a CP/M machine. Often
working with Delphi, he has enjoyed exploring it since it first appeared. Readers
may reach him via e-mail at kbwood@compuserve.com.

On the Net
Begin Listing One Implementing the Web Service

Begin Listing Two Web Service description

unit FindMoviesImpl;

initialization
InvRegistry.RegisterInvokableClass(TFindMovies);
end.

<?xml version="1.0"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
name="IFindMoviesservice"
targetNamespace="http://www.borland.com/soapServices/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="urn:Types">
<types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:Types">
<xs:complexType name="TStringDynArray">
<xs:complexContent>
<xs:restriction base="soapenc:Array">
<xs:sequence/>
<xs:attribute ref="soapenc:arrayType"
n1:arrayType="xs:string[]"
xmlns:n1="http://schemas.xmlsoap.org/wsdl/"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:schema>
</types>
<message name="FindMoviesRequest">
<part name="Rating" type="xs:string"/>
</message>
<message name="FindMoviesResponse">
<part name="return" type="ns1:TStringDynArray"/>
</message>
<portType name="IFindMovies">
<operation name="FindMovies">
<input message="FindMoviesRequest"/>
<output message="FindMoviesResponse"/>
</operation>
</portType>
<binding name="IFindMoviesbinding" type="IFindMovies">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="FindMovies">
<soap:operation
soapAction=
"http://www.the_movies.com/FindMovies#FindMovies"/>
<input>
<soap:body use="encoded"
encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/"
namespace=
"http://www.the_movies.com/FindMovies"/>
</input>
<output>
<soap:body use="encoded"
encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/"
namespace=
"http://www.the_movies.com/FindMovies"/>
</output>
</operation>
</binding>
<service name="IFindMoviesservice">
<port name="IFindMoviesPort"
binding="IFindMoviesbinding">
<soap:address
location=
"http://localhost/scripts/MovieSvc.exe/soap/IFindMovies"/>
</port>
</service>
</definitions>

End Listing One

End Listing Two

interface
uses
SysUtils, Types, FindMoviesIntf, InvokeRegistry,
DB, DBTables;
type
TFindMovies = class(TInvokableClass, IFindMovies)
private
FQuery: TQuery;
public
constructor Create; override;
destructor Destroy; override;
function FindMovies(Rating: string):
TStringDynArray; stdcall;
end;
implementation
constructor TFindMovies.Create;
begin
inherited Create;
FQuery := TQuery.Create(nil);
with FQuery do begin
DatabaseName := 'Movie-watcher';
SQL.Add('SELECT Name FROM Movie');
SQL.Add('WHERE :rating = '''' OR :rating = rating');
ParamByName('rating').DataType := ftString;
Prepare;
end;
end;
destructor TFindMovies.Destroy;
begin
FQuery.Free;
inherited Destroy;
end;
function TFindMovies.FindMovies(Rating: string):
TStringDynArray;
var
Index: Integer;
begin
with FQuery do
try
ParamByName('rating').AsString := Rating;
Open;
if RecordCount = 0 then
raise Exception.Create(
'No movies found for rating ' + Rating);
SetLength(Result, RecordCount);
Index := 0;
while not EOF do begin
Result[Index] := FieldByName('name').AsString;
Inc(Index);
Next;
end;
finally
Close;
end;
end;

9 November 2001 Delphi Informant Magazine

Columns & Rows

Microsoft SQL Server 2000 / XML / Metadata / Delphi 5, 6

By Alex Fedorov

Using the XML Features of SQL


Server 2000
Part IV: Delphi Data-aware Controls

n the previous articles of this series (in the August through October 2001 issues),
we looked at basic data querying techniques that allow us to use the XML features
of Microsoft SQL Server 2000 from our Delphi applications. This months article will
concentrate on using Delphi data-aware controls with XML data sources.
As mentioned in Part I of this series, the XMLDATA
option allows you to extract XML-formatted metadata along with regular data. This month, well use
this feature to connect XML-based data with Delphi
data-aware controls.

XMLDATA Option Revisited


As pointed out previously, you can add the XMLDATA option at the end of a FOR XML statement
to produce an XML document that contains metadata along with regular data. Currently, theres no
way to extract just metadata using the FOR XML
statement. For example, the following SQL statement executed against the Products table in the
Northwind database:

assigned a dt prefix automatically. Supported


XML data types are shown in Figure 2.
Now youre armed with not only data, but the metadata contained in one XML document. You know the
structure of the XML metadata and the meanings of
its elements and attributes. The only problem is that
this data (along with metadata) comes from a source
unknown to Delphi. Theres no direct way to specify
the source of data in a Delphi DataSource component,
and use it with data-aware controls. However, Delphi
does contain a powerful ClientDataSet component
that can be used as a bridge.

Bridging the Gap

There are two distinct parts: The first is the description of the table itself along with a column list,
and the second describes the columns. For each
column theres an <ElementType> element with four
attributes. For this example, were only interested
in the name and dt:type attributes. The name
attribute contains the name of the column, while
dt:type specifies the XML data type of the column.

Client data, implemented as the ClientDataSet


component, is a DataSet component thats
intended to work without the connectivity support
of the BDE or ADO. This component uses
MIDAS.DLL and provides all the data access, editing, navigation, data constraint, and filtering support introduced by the TDataSet class. The only
thing your application should do is provide the way
to read and write data. It takes five steps to do this:
1) Extract data with metadata from SQL Server as
an XML document.
2) Create the client dataset.
3) Create fields, using metadata from the schema
part of an XML document.
4) Fill in the dataset with data.
5) Attach the dataset to DBGrid.

The urn:schemas-microsoft-com:datatypes
namespace is used to declare the data types within
the schema document. Data type namespaces are

Extracting data and metadata. As weve already


seen, the XMLDATA option is used to extract metadata. This option can be attached to the end of FOR

SELECT * FROM Products


FOR XML AUTO, ELEMENTS, XMLDATA

returns an XML document that contains the representation of metadata shown in Figure 1 (the data
part is omitted).

10 November 2001 Delphi Informant Magazine

Columns & Rows


<Schema name="Schema1"
xmlns="urn:schemas-microsoft-com:xml-data"
xmlns:dt="urn:schemas-microsoft-com:datatypes">
<ElementType name="Products" content="eltOnly"
model="closed" order="many">
<element type="ProductID"/>
<element type="ProductName"/>
<element type="SupplierID"/>
<element type="CategoryID"/>
<element type="QuantityPerUnit"/>
<element type="UnitPrice"/>
<element type="UnitsInStock"/>
<element type="UnitsOnOrder"/>
<element type="ReorderLevel"/>
<element type="Discontinued"/>
<element type="Photo"/>
</ElementType>
<ElementType name="ProductID" content="textOnly"
model="closed" dt:type="i4"/>
<ElementType name="ProductName" content="textOnly"
model="closed" dt:type="string"/>
<ElementType name="SupplierID" content="textOnly"
model="closed" dt:type="i4"/>
<ElementType name="CategoryID" content="textOnly"
model="closed" dt:type="i4"/>
<ElementType name="QuantityPerUnit" content="textOnly"
model="closed" dt:type="string"/>
<ElementType name="UnitPrice" content="textOnly"
model="closed" dt:type="fixed.14.4"/>
<ElementType name="UnitsInStock" content="textOnly"
model="closed" dt:type="i2"/>
<ElementType name="UnitsOnOrder" content="textOnly"
model="closed" dt:type="i2"/>
<ElementType name="ReorderLevel" content="textOnly"
model="closed" dt:type="i2"/>
<ElementType name="Discontinued" content="textOnly"
model="closed" dt:type="boolean"/>
<ElementType name="Photo" content="textOnly"
model="closed" dt:type="uri"/>
</Schema>

Data Type

Description

bin.base64

Binary BLOb represented as MIME-style Base64


encoded string.
Binary value represented as hexadecimal digits.
0 for False, or 1 for True.
One character long string.
Date without time part in a subset ISO 8601
format.
Date with optional time and no optional zone in
a subset ISO 8601 format.
Date with optional time and optional zone in a
subset ISO 8601 format.
Equivalent of number, but no more than 14
digits to the left of the decimal point, and no
more than four to the right.
Real number with a leading sign, fractional
digits, and, optionally, an exponent.
Number with optional sign, no fractions, and no
exponent.
Number with no limit on digits that can
potentially have a leading sign, fractional digits,
and, optionally, an exponent.
Time with no date and no time zone in a subset
ISO 8601 format.
Time with no date, but optional time zone in a
subset ISO 8601 format.
A one-byte integer with optional sign.
A two-byte integer with optional sign.
A four-byte integer with optional sign.
An eight-byte integer with optional sign.
Real number with seven-digit precision that can
potentially have a leading sign, fractional digits,
and, optionally, an exponent.
Equivalent of float real number with 15-digit
precision that can potentially have a leading
sign, fractional digits, and, optionally, an
exponent.
A one-byte unsigned integer.
A two-byte unsigned integer.
A four-byte unsigned integer.
An eight-byte unsigned integer.
Universal Resource Identifier (URI).
Hexadecimal representation of GUID with
optional embedded hyphens.

bin.hex
boolean
char
date
dateTime
dateTime.tz
fixed.14.4

float
int
number

time
time.tz
i1
i2
i4
i8
r4

r8

Figure 1: XML metadata example.

XML RAW, FOR XML AUTO, and FOR XML AUTO, ELEMENTS
statements. Depending on which type of the statement you choose,
youll get a different representation of metadata (representation of data
was discussed earlier in this series). For example, in RAW and AUTO
mode, you get <AttributeType> elements (see Listings One and Two
on page 13), and in AUTO, ELEMENTS mode, <ElementType>
elements. (For this example, youll use AUTO, ELEMENTS mode.)
First, place a button into an empty Delphi form, and write the OnClick
event handler shown in Figure 3.
This code creates an instance of Microsoft XML Parser and executes the SQL query against the IIS virtual directory that maps
to the Northwind database on SQL Server. The resulting XML
document is loaded into XML Parser and is ready to be processed.
Creating a client dataset. Next, prepare an empty client dataset so
it can be filled with the data extracted from SQL Server. You need
to reference the DBClient unit by adding it to the uses clause. The
following code creates and initializes a new empty client dataset,
where the DS variable is declared of TClientDataSet type:
// Create client dataset.
DS := TClientDataSet.Create(Self);

Creating dataset fields. The client dataset doesnt know anything


about its fields, names, and data types. So, youll provide this
11 November 2001 Delphi Informant Magazine

ui1
ui2
ui4
ui8
uri
uuid

Figure 2: XML data types


procedure TForm1.Button1Click(Sender: TObject);
var
URL, SQL : string;
Nodes
: IXMLDOMNodeList;
Node
: IXMLDOMNode;
I,J
: Integer;
begin
// Extract XML data and metadata.
XMLDoc := CoDOMDocument.Create;
URL := 'http://terra/northwind?sql=';
SQL := 'SELECT * FROM ' + TableName;
SQL := SQL + ' FOR XML AUTO, ELEMENTS, XMLDATA';
URL := URL + StringReplace(
SQL, ' ', '%20', [rfReplaceAll]);
URL := URL + '&root=Northwind';
XMLDoc.Async := False;
XMLDoc.Load(URL);
...

Figure 3: Part of the example applications buttons OnClick


event handler.

Columns & Rows


SQL XML

SQL Server

Delphi

i4
string

int
nvarchar, varchar,
char, ntext
money
smallint
bit
image
datetime
uniqueidentifier
real

ftInteger

fixed.14.4
i2
boolean
uri
datetime
uuid
r4

ftString
ftCurrency
ftSmallInt
ftBoolean
ftString
ftDateTime
ftGUID
ftFloat

Figure 4: Mapping XML data types to Delphi field types.

function XD2DF(XD: string): TFieldType;


begin
if XD = 'i4'

then XD2DF := ftInteger

else if XD = 'string'

then XD2DF := ftString

else if XD = 'fixed.14.4'

then XD2DF := ftCurrency

else if XD = 'i2'

then XD2DF := ftSmallInt

else if XD = 'boolean'

then XD2DF := ftBoolean

else if XD = 'uri'

then XD2DF := ftString

else if XD = 'datetime'

then XD2DF := ftDateTime

else if XD = 'uuid'

then XD2DF := ftGUID

else if XD = 'r4'

then XD2DF := ftFloat

else

XD2DF := ftString

end;

information using metadata from the XML document that exists


in an instance of XML Parser in the XMLDoc variable.
You need to extract a collection of nodes that contain the metadata definitions. As already mentioned, this collection is stored
in the XML documents Schema/ElementType node and its subnodes. To extract this set of nodes, use the SelectNodes method of
XMLDomDocument object, specifying the following path:
//Schema/ElementType

The Nodes variable (of IXMLDOMNodeList type) will now contain all <ElementType> nodes found in the XML document. You
can iterate this collection and extract any particular node that will
represent one field in the table. The first node contains a table
definition, so skip it:
for I := 1 to Nodes.Length-1 do begin
Node := Nodes[I];
...
end;

Create a single field at each iteration by extracting a single node and


using its name and dt:type attributes. Again, the name attribute
specifies the name of the field, and dt:type specifies its data type.
Figure 4 shows all the XML Data data types found in the Northwind
database, along with their SQL Server and Delphi equivalents.
Note: For other XML data types and their SQL Server equivalents,
refer to the SQL Server OLEDB Provider documentation,
which is available in SQL Server Books Online, currently
found at http://msdn.microsoft.com/library/default.asp?URL=/
library/psdk/sql/portal_7ap1.htm.
We need to create a function that maps the XML data types shown
in Figure 4 to their corresponding Delphi field types. The function,
XD2DF (for XML Data to Delphi Field), is shown in Figure 5.
Note that the SQL Server image data type is returned as a link to the
database object itself, if you dont use the BINARY BASE64 option.
(Images stored in SQL Server are of no use in Delphi applications.)

Figure 5: The XD2DF function.

// Create field definitions.


Nodes := XMLDoc.SelectNodes('//Schema/ElementType');
with DS do begin
for I := 1 to Nodes.Length-1 do begin
Node := Nodes[I];
with FieldDefs.AddFieldDef do begin
Name := Node.Attributes.GetNamedItem('name').Text;
DataType := XD2DF(Node.Attributes.GetNamedItem(
'dt:type').Text);
end;
end;
CreateDataSet;
end;

Figure 6: Creating field definitions.

// Fill dataset with data.


Nodes := XMLDoc.SelectNodes('//' + TableName);
with DS do begin
for I := 0 to Nodes.Length-1 do begin
DS.Append;
for J := 0 to Nodes[I].ChildNodes.Length-1 do
FieldValues[Nodes[I].ChildNodes[J].NodeName] :=
Nodes[I].ChildNodes[J].Text;
DS.Post;
end;
end;

Figure 7: Filling fields with data.

At the very least, we can create a dataset by calling the CreateDataSet


method of the ClientDataSet component. The whole process
involved in creating dataset fields is shown in Figure 6.
Now weve got the client dataset, and it knows something about the
data. The next step is to supply this data.
Filling fields with data. To extract your data into the Nodes collection, use the SelectNodes method of the XMLDomDocument object,
specifying the following path:
'//' + TableName

Armed with the conversion function, we can now specify fields of


the client dataset, thus:
with FieldDefs.AddFieldDef do begin
Name := Node.Attributes.GetNamedItem('name').Text;
DataType := XD2DF(Node.Attributes.GetNamedItem(
'dt:type').Text);
end;

12 November 2001 Delphi Informant Magazine

// In our example -> //Products

This will fill the Nodes collection with data that can be iterated
by your code. Use two iterations: one for iterating an individual
record (appending a new record by using the Append method of
the client dataset), and the other for iterating the fields in one
record. The entire process involved in filling your dataset with
data is shown in Figure 7.

Columns & Rows

Begin Listing One Schema for XML RAW mode

Figure 8: The example application at run time.

Attaching the dataset to a DBGrid component. Attach your


newly created dataset to a Delphi data-aware control, by placing a
DataSource, DBNavigator, and DBGrid component on a Delphi
form. In the DataSource property of both data-aware controls,
specify DataSource1, then add the following code:
// Move to the first row.
DS.First;
// Attach DataSet to DataSource.
DataSource1.DataSet := DS;

Now run the application, and use the DBNavigator and DBGrid
to navigate the data extracted from SQL Server using its XML
features. The sample application is shown in Figure 8. Listing
Three (on page 14) shows all of the source for the main form.

Conclusion
Weve seen how to extract XML metadata from SQL Server using
XML documents, and we covered processing this metadata to use
Delphi data. Weve also observed how the powerful Delphi client
dataset component can be used as a bridge between an alien data
source and Delphi data-aware controls. Mapping XML Data data
types to Delphi field types was discussed, as well.
Next month, well complete this five-part series by learning how
to insert, update, and delete data, as well as how to process errors.
See you then.
The example project referenced in this article is available on the
Delphi Informant Magazine Complete Works CD located in INFORM\
2001\NOV\DI200111AF.

Alex Fedorov is a Chief Technology Officer for Netface SA, based in Lausanne,
Switzerland (http://www.netface.ch). He was one of the co-authors of Professional
Active Server Pages 2.0 (Wrox, 1998) and ASP Programmers Reference (Wrox,
1998), as well as Advanced Delphi Developers Guide to ADO (Wordware, 2000).

13 November 2001 Delphi Informant Magazine

<Schema name="Schema1"
xmlns="urn:schemas-microsoft-com:xml-data"
xmlns:dt="urn:schemas-microsoft-com:datatypes">
<ElementType name="row" content="empty" model="closed">
<AttributeType name="ProductID" dt:type="i4"/>
<AttributeType name="ProductName" dt:type="string"/>
<AttributeType name="SupplierID" dt:type="i4"/>
<AttributeType name="CategoryID" dt:type="i4"/>
<AttributeType name="QuantityPerUnit" dt:type="string"/>
<AttributeType name="UnitPrice" dt:type="fixed.14.4"/>
<AttributeType name="UnitsInStock" dt:type="i2"/>
<AttributeType name="UnitsOnOrder" dt:type="i2"/>
<AttributeType name="ReorderLevel" dt:type="i2"/>
<AttributeType name="Discontinued" dt:type="boolean"/>
<AttributeType name="Photo" dt:type="bin.base64"/>
<attribute type="ProductID"/>
<attribute type="ProductName"/>
<attribute type="SupplierID"/>
<attribute type="CategoryID"/>
<attribute type="QuantityPerUnit"/>
<attribute type="UnitPrice"/>
<attribute type="UnitsInStock"/>
<attribute type="UnitsOnOrder"/>
<attribute type="ReorderLevel"/>
<attribute type="Discontinued"/>
<attribute type="Photo"/>
</ElementType>
</Schema>

End Listing One

Begin Listing Two Schema for XML AUTO mode


<Schema name="Schema1"
xmlns="urn:schemas-microsoft-com:xml-data"
xmlns:dt="urn:schemas-microsoft-com:datatypes">
<ElementType name="Products" content="empty"
model="closed">
<AttributeType name="ProductID" dt:type="i4"/>
<AttributeType name="ProductName" dt:type="string"/>
<AttributeType name="SupplierID" dt:type="i4"/>
<AttributeType name="CategoryID" dt:type="i4"/>
<AttributeType name="QuantityPerUnit" dt:type="string"/>
<AttributeType name="UnitPrice" dt:type="fixed.14.4"/>
<AttributeType name="UnitsInStock" dt:type="i2"/>
<AttributeType name="UnitsOnOrder" dt:type="i2"/>
<AttributeType name="ReorderLevel" dt:type="i2"/>
<AttributeType name="Discontinued" dt:type="boolean"/>
<AttributeType name="Photo" dt:type="bin.base64"/>
<AttributeType name="Highlight" dt:type="i4"/>
<attribute type="ProductID"/>
<attribute type="ProductName"/>
<attribute type="SupplierID"/>
<attribute type="CategoryID"/>
<attribute type="QuantityPerUnit"/>
<attribute type="UnitPrice"/>
<attribute type="UnitsInStock"/>
<attribute type="UnitsOnOrder"/>
<attribute type="ReorderLevel"/>
<attribute type="Discontinued"/>
<attribute type="Photo"/>
</ElementType>
</Schema>

End Listing Two

Columns & Rows


Begin Listing Three Using Delphi data-aware controls
unit CDSUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, MSXML2_TLB, StdCtrls, DBClient, DB,
Grids, DBGrids, ExtCtrls, DBCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{ $R *.DFM }
const
TableName : string = 'Products';
var
XMLDoc : IXMLDomDocument;
DS
: TClientDataSet;
// Convert XML data to Delphi field type.
function XD2DF(XD: string): TFieldType;
begin
if XD = 'i4'
then XD2DF :=
else if XD = 'string'
then XD2DF :=
else if XD = 'fixed.14.4' then XD2DF :=
else if XD = 'i2'
then XD2DF :=
else if XD = 'boolean'
then XD2DF :=
else if XD = 'uri'
then XD2DF :=
else if XD = 'datetime'
then XD2DF :=
else if XD = 'uuid'
then XD2DF :=
else if XD = 'r4'
then XD2DF :=
else
XD2DF :=
end;

ftInteger
ftString
ftCurrency
ftSmallInt
ftBoolean
ftString
ftDateTime
ftGUID
ftFloat
ftString

procedure TForm1.Button1Click(Sender: TObject);


var

14 November 2001 Delphi Informant Magazine

URL, SQL : string;


Nodes
: IXMLDOMNodeList;
Node
: IXMLDOMNode;
I, J
: Integer;
begin
XMLDoc := CoDOMDocument.Create;
// Extract XML data and metadata.
URL := 'http://terra/northwind?sql=';
SQL := 'SELECT * FROM ' + TableName;
SQL := SQL + ' FOR XML AUTO, ELEMENTS, XMLDATA';
URL := URL + StringReplace(
SQL, ' ', '%20', [rfReplaceAll]);
URL := URL + '&root=Northwind';
XMLDoc.Async := False;
XMLDoc.Load(URL);
// Create client dataset.
DS := TClientDataSet.Create(Self);
// Create field definitions.
Nodes := XMLDoc.SelectNodes('//Schema/ElementType');
with DS do begin
for I := 1 to Nodes.Length-1 do begin
Node := Nodes[I];
with FieldDefs.AddFieldDef do begin
Name := Node.Attributes.GetNamedItem('name').Text;
DataType := XD2DF(Node.Attributes.GetNamedItem(
'dt:type').Text);
end;
end;
CreateDataSet;
end;
// Fill DataSet with data.
Nodes := XMLDoc.SelectNodes('//' + TableName);
with DS do begin
for I := 0 to Nodes.Length-1 do begin
DS.Append;
for J := 0 to Nodes[I].ChildNodes.Length-1 do
FieldValues[Nodes[I].ChildNodes[J].NodeName] :=
Nodes[I].ChildNodes[J].Text;
DS.Post;
end;
end;
// Move to the first row.
DS.First;
// Attach DataSet to DataSource.
DataSource1.DataSet := DS;
end;
end.

End Listing Three

Delphi at Work

User Interface / DBGrid / Database / Delphi 2-6

By Ron Nibbelink

User-modifiable DBGrids
Setting Column Appearance, Order,
and Width at Run Time

ere was my problem: Some of my customers like to resize the screens, and others
like to be able to specify the order and widths of the columns. But I hate to see
unfilled space at the edge of the grid, and whenever possible Id rather avoid a
horizontal scroll bar across the bottom of the grid.
At first, I just didnt make resizing available to users.
I carefully set the column widths, and there they
stayed. In the end, however, the customers won,
and I began to develop the techniques described
in this article techniques for adding, sizing, and
otherwise modifying a DBGrids columns at run
time. It also contains a way of saving and restoring
user selections, so you can make them persistent.

Setting Up DBGrid Columns at Run Time


Of course, you can use Delphis Columns editor to
generate the columns for a DBGrid component. As
with all VCL components, Delphi stores information regarding properties that you change. If you
view the form as text, you can see the columns
listed with each item reference containing properties; for example:
...
Columns = <
item
Alignment = taCenter
FieldName = 'LastName'
Title.Alignment = taCenter
Title.Caption = 'Last Name'
Width = 128
end>
...

15 November 2001 Delphi Informant Magazine

Its possible to put the essence of this information


in the source code of your application and control
it programmatically. This technique uses arrays of
records of the following type:
type
TGridColDesc = record
FldName : string;
Alignmt : TAlignment;
ColCapt : string;
CaptAlign : TAlignment;
MinWidth : Integer;
VarWidth : Integer
end;

The names of the fields have been modified to


differ from the VCL column properties. The last two
fields, MinWidth and VarWidth, need explanation.
MinWidth is the nominal width of the column in
pixels. It corresponds to the Width property in the
TDBGrid column item property list. MinWidth is
the minimum width to which a column should be
set. If the value is 0, the column is not displayed.
The VarWidth field tells the system how to divide
any extra space in the grid. The routine that sets up
the columns starts by determining the minimum

Delphi at Work
const
EmplGridCols: array[TEmplGridCols] of TGridColDesc =
{ egcEmpNo } ((FldName: 'EmpNo';
Alignmt: taRightJustify; ColCapt: 'Emp #';
CaptAlign: taCenter; MinWidth: 0; VarWidth: 0),
{ egcLastName } (FldName: 'LastName';
Alignmt: taLeftJustify; ColCapt: 'Last Name';
CaptAlign: taCenter; MinWidth: 135; VarWidth: 6),
{ egcFirstName } (FldName: 'FirstName';
Alignmt: taLeftJustify; ColCapt: 'First Name';
CaptAlign: taCenter; MinWidth: 117; VarWidth: 4),
{ egcPhoneExt } (FldName: 'PhoneExt';
Alignmt: taCenter; ColCapt: 'Phone Number';
CaptAlign: taCenter; MinWidth: 97; VarWidth: 2),
{ egcHireDate } (FldName: 'HireDate';
Alignmt: taCenter; ColCapt: 'Hired';
CaptAlign: taCenter; MinWidth: 75; VarWidth: 0),
{ egcSalary } (FldName: 'Salary';
Alignmt: taRightJustify; ColCapt: 'Salary';
CaptAlign: taCenter; MinWidth: 75; VarWidth: 2));

Figure 2: The main form of the sample application shows the


default column arrangement.

Figure 1: A typed constant array of TGridColDesc records.

width of the columns. It adds the MinWidth values, plus the width of
separator lines between the columns. The remaining width is available
for distribution among the columns.
The routine then uses the VarWidth values to give each column its
proportion of the remaining space. For example, suppose a grid has
four columns and the VarWidth values are 0, 3, 1, and 6, for a total
of 10. The first column gets no extra space, because its VarWidth is
0. The second column gets three-tenths of the remaining width, the
third one-tenth, and the fourth six-tenths. Any extra pixels left from
rounding are given to the last column. Conversely, if rounding takes a
few too many pixels, they are taken from the last column.
To use the TGridColDesc records, add the VCL_Util unit to your
project and include it in the uses clause of each unit that contains
a DBGrid. Then add an enumerated type for each grid, listing
all its columns that you may want to display in the grid. (This
can include calculated fields as well as normal data fields.) Create
the enumerated type value names by adding a common prefix of
lowercase characters to each database field name. At least the first
character of the field name part of the type value name must
be capitalized. (This naming convention becomes crucial if you
want to save and restore user column settings.) For the sample
employee.db table that ships with Delphi, the enumerated type
declaration might look like this:
type
TEmplGridCols = (egcEmpNo, egcLastName, egcFirstName,
egcPhoneExt, egcHireDate, egcSalary);

Figure 3: When the sample program starts, it omits the EmpNo


column and widens the other columns to fill the available space.

using various minimum column widths. Since the LastName


column may require the most space, it has a VarWidth value of
6. If the form allows extra space, it will get the most additional
pixel width. Column FirstName will receive a smaller portion of
the extra space, and so forth. The HireDate column, however, has
a VarWidth of 0, so it will not be widened.
Once the data is set up, you need to add a couple of lines of
code to make it work. First, have Delphi set up an OnResize
event handler for either the DBGrid or the form. Give it a local
integer variable, such as nMinGridWidth in the example. Then call
the GridColsMinWidth function, passing it the array of column
descriptor records:
nMinGridWidth := GridColsMinWidth(EmplGridCols);

Then build a typed constant array of TGridColDesc records, such


as that shown in Figure 1.
Note: The Delphi Help topic Writeable typed constants recommends turning off the compiler option ({$J-}) that allows the program to change these pre-set variables. However, depending on how
you implement these techniques, you may need the typed constant
arrays of TGridColDesc records to be modifiable. If so, you should
leave the option on ({$J+}), at least for this part of your code.
In this example, EmpNo isnt included in the grids list of columns, since its MinWidth is 0. The other fields are to be displayed
16 November 2001 Delphi Informant Magazine

GridColsMinWidth returns the sum of the MinWidth fields, plus the


combined widths of one-pixel column separator lines. This gives you
the minimum width for the columns. If the DBGrids ClientWidth
property value is less, you may want to force it to the minimum.
Otherwise, the columns will be made too narrow. Then call:
SetGridCols(grdDB, EmplGridCols);

where grdDB is the DBGrid on your form, and EmplGridCols


is your column descriptors array. This procedure removes
any existing column objects for the grid and rebuilds them

Delphi at Work
according to the values in EmplGridCols. Now your grid
columns fit your grid, and your application looks professional.
The net effect of this effort is that you or your user can set
the form width at will, and the columns will shrink or widen
to take up the available space, all with very little effort on your
part. Figure 2 shows the default column layout for the sample
database. In Figure 3, the column widths have been specified
and the program has called procedure SetGridCols to rebuild
the columns list.

Saving and Restoring Users Settings


The DBGrid components Options property includes an element
named dgColumnResize. Its True by default, which allows the user
to move or resize individual columns in the grid by dragging the
column titles or their separators, respectively.
If you allow the user to modify the columns, you may also
want to be able to display the grid with the same settings the
next time the user brings up that screen. The VCL_Util unit
has two routines for making this happen. You can store your
users grid column specifications in an INI file, in the Windows
registry, or in your database. The GetGridColList function
returns a string that contains enough information to allow the
SetGridColsFromList procedure to rebuild your grids columns.
When you set up the form, either in the FormCreate or
FormActivate routine, retrieve the string the program stored the
previous time. If this is the first use, let the program use your
default values. Then pass that string to SetGridColsFromList along
with the grid, the TypeInfo for the enumerated type for your
columns, and the array of column descriptors.
To get the TypeInfo, include unit TypInfo in the uses clause
of your form. The TypeInfo function returns a pointer to your
enumerated types runtime type information (RTTI). The reason
for this requirement will become clear later.
Finally, when you close the form, either in the FormClose or
FormDestroy routine, call the GetGridColList function to create the
string containing the users current column settings. Then store it
with the rest of the users specifications for the application. This
functions parameters also reference the grid, the TypeInfo for the
enumerated type, and the array of column descriptors, as well as
a Boolean flag that lets you specify whether to save the column
widths or just the order. Heres an example of the contents of an
INI file, which stores the column specifications:
[General]
ColSpecs=egcHireDate 56,egcLastName 239,
egcFirstName 186,egcPhoneExt 156

How It Works
The VCL_Util unit contains the two routines that retrieve and
set column information. The first one is a function that returns a
string containing the names, and optionally widths of the
columns. The second routine uses a string in the same format to
set the columns: which fields, in what order, and how wide.
The GetGridColList function starts by capturing the lowercase letters that prefix the enumerated type value names. In the example
above, the prefix is egc. The routine then extracts the database
field name from each column in the grid. It concatenates the
prefix and the field name, and adds it to the function result string.
17 November 2001 Delphi Informant Magazine

Figure 4: The example SetCols form provides a visual way for the
user to select the columns to be displayed in a grid. It also allows
the user to change the order in the Visible Columns list box.

If you pass True to the InclWidths parameter, the function also


adds the columns current width in pixels. Commas separate the
column entries.
At the other end of the process, the SetGridColsFromList procedure takes a string, typically the one created by the GetGridColList
function the previous time the user ran the program. First, the
routine makes a local copy of the array of column descriptors.
It then uses the SplitStr function (also included in the VCL_Util
unit and described below) to parse the string at the delimiting
commas into the separate items of a TStringList. Each item represents a column to be displayed.
For the first string, the routine uses the GetEnumValue RTTI
routine to get the enumerated type value of the column. It then
moves the columns descriptor for that enumerated type value into
the first element of the array of column descriptors and sets its
MinWidth value, if specified.
The process is repeated for all the desired columns. Afterward
any unused column descriptors are added to the end of the
column descriptors array, but they are given a MinWidth value
of 0 so the program wont try to display them later. Essentially,
this routine sorts the source codes array to match the users
previous arrangement.

Letting the User Specify Which Columns to Display


The final technique allows the user to add or remove columns
entirely. The SetCols form (shown in Figure 4) provides a couple
of ListBox components with arrow buttons between them. In the
sample provided here, the form is based on the TfrmGenrForm
object described in Ron Nibblelinks article, A Hierarchy of
Forms, from the August 2000 issue of Delphi Informant Magazine.
The calling routine stores a reference to the grid columns enumerated RTTI and the column descriptor array in public variables
in the form object. It then loads the title names of the columns
with widths greater than 0 in the Visible Columns list box, and the
rest in the Available Columns list box.
Users can manipulate the columns several ways. They can select a
column name in one box, and click on the arrow button to move
it to the other. Double-clicking the item has the same effect. The
names can also be dragged from one box to the other. Within the
Visible Columns box, users can drag names up or down to change
the display order. When users have finished moving the column
names, the form modifies the column descriptor array with the
results. The calling routine can then retrieve the changes and reset
the grid columns.

Delphi at Work
Other Goodies
The SplitStr function, a utility in the VCL_Util unit, was mentioned earlier in this article. It accepts a string and a delimiter
character as parameters and returns a TStringList. The routine
breaks the string at each delimiter character, and adds the substrings to the list. If two delimiter characters are adjacent, the
list gets an empty string for that element. Similarly, if the first or
last character is the delimiter, the list begins, or ends, respectively,
with an empty string.
This routine has proven very useful in numerous situations. For
example, I frequently store the location and size of a form so
the program can put it back there the next time the user starts
the application. In the users INI file (or in the user database),
I store a string containing the forms Left, Top, Width, and
Height properties as numbers separated by commas, for example,
120,86,450,275. When I retrieve the string, I pass it to the
SplitStr function. I can then call the forms SetBounds method with
Strings[0], Strings[1], etc. as the parameters.
Another use for SplitStr has been in passing a short list of IDs,
codes, or other values as a parameter to a routine. I can build up
the list as a comma-delimited string, and let the called routine
separate it out and use it as needed. This is especially useful if
the program needs to present the list to the user for approval in a
MessageDlg-generated dialog box.
Also in the VCL_Util unit is the declaration of an object named
TStringObj. Its simply a descendant of TObject that holds an
integer and a TStringList. In programming, I use a lot of short
keys or codes to reference longer strings. For example, we may
have manufacturing defect codes along with full descriptions, or a
key based on a persons name along with the full name. Sometimes
Ill want to view the one, but need to have the other readily
accessible. At other times, I may need them switched around.
In these situations, its very handy to associate a TStringObj object
with each string in a TStringList. Instead of calling the Add(S1)
or Insert(S1) method, I load strings by calling AddObject(S1,
TStringObj.Create(S2)), or InsertObject(...). For every Strings[i] in
the list, I can then access the associated string by referencing
TStringObj(Objects[i]).S. For example, the SetCols unit (described
earlier) uses TStringObj objects to keep the columns database field
names associated with the visible column titles. It uses the objects
integer to store the original width of the column.

18 November 2001 Delphi Informant Magazine

The VCL_Util unit has a few other handy utilities:


 The SubString function is useful when you know you want
a particular substring from a delimited string, but you dont
want the whole SplitStr result. It calls SplitStr and returns the
nth substring (zero based).
 The FreeStringListObjects and FreeStringList procedures are
helpful in freeing the objects associated with TStringList
strings, such as TStringObj described earlier.
 The ListToStr function accepts a TStringList and some formatting flags, and returns a comma-delimited string containing
the TStringLists strings.
 The StrListByParagraph function accumulates the individual
lines in a TStrings list into fewer, longer strings as paragraphs
that it returns in a TStringList. It may be useful, for example,
in converting the lines of a TMemo in preparation for sending
them as an e-mail message. The routine recognizes paragraphs by blank lines between groups of contiguous lines.

Conclusion
These techniques have allowed me to provide more powerful,
better-looking programs in less time. If nothing else, I no longer
have to carefully size and align the individual columns or work
with the Columns editor. They also provide users with the ability
to customize the grid columns to meet their needs. The capability
also helps when the same form is used to list data from various
tables. Rather than have a separate grid on each of several pages,
I just swap the column descriptions. When the user switches from
one table to the other, the program just resets the columns.
All source referenced in this article is available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\NOV\
DI200111RN.

Ron Nibbelink develops PC database applications for the Quality and Process
Improvement organization of the Wichita, Kansas Division of the Boeing Commercial Airplane Group. He has developed applications on a variety of platforms
and in several languages since 1983.

Kylix Tech
Kylix Open Edition

By Ray Lischner

An Open Invitation
An Introduction to Kylix Open Edition

s a Delphi programmer, you probably have been deluged with information about
Kylix. But your main focus as a programmer has always been Windows, and may
remain Windows for some time to come. Nonetheless, you owe it to yourself and your
career to keep abreast of developments in the Linux world. Kylix is a natural next step,
and Borland has made Kylix Open Edition available as a free download, so theres no
reason not to take a closer look. This article guides you through the steps necessary
to install and run Kylix OE.

Which Linux Distribution?

Installing Kylix OE

If you have not yet installed Linux, you must first


decide which distribution to install. I recommend
avoiding Red Hat. The Kylix newsgroups are full
of problems people have with Red Hat for one
reason or another. For instance, it uses the wrong
version of the GNU C compiler, which produces
incompatible libraries. Users also report problems
with the Red Hat package manager, which makes
it hard to install Kylix, and so on. I use SuSE 7.2,
and Im happy, and people have had success with
Mandrake and other distributions.

Download Kylix OE from the Borland Web


site (http://www.borland.com/kylix). Youll need
to fill in a registration form to obtain a serial
number and registration key. Download and
unpack the files. If you have an older Linux
distribution, cd to the borpretest directory and
read the PREINSTALL file. That tells you how
to run the testsystem program, and what to do
if your libraries are out of date.

The advantage of choosing a recent distribution,


such as SuSE 7.2, is that it already has the latest
C run-time library (glibc), which includes the bug
fixes that Kylix requires. If you have an older distribution, you might need to install a different glibc
before you can install Kylix. The Kylix installer will
inform you if your distribution is up to date, or
you can run the borpretest program that comes
with all editions of Kylix.
Feel free to run KDE, GNOME, or any other
desktop. Kylix doesnt depend on the specifics of
your desktop. In theory, the Kylix installer adds
a menu item for Kylix to the KDE or GNOME
menus, but in my experience, this rarely works.
Its easy to add manually, though, so this shouldnt
be a problem.
19 November 2001 Delphi Informant Magazine

Once you have the latest libraries, read


README_OE, which tells you what Borland
took out of the Open Edition. If you want to try
out all the features of Kylix, you should download the Server Developer Trial Edition instead.
The installation instructions are pretty much the
same either way.
You can install Kylix as a non-root user, but the
typical scenario is for root to install Kylix in a
world-readable directory (such as /opt/kylix) so
everyone can use it. Unlike past versions of Delphi,
all the users on your system can run Kylix from a
single directory, without needing to run a registryonly installation or copy registry keys.
If you must use Red Hat, install Kylix as a nonroot user, and use the -m switch to setup.sh (which
avoids bugs with Red Hats package manager).

Kylix Tech
Of course, the biggest difference
between Kylix and Delphi is CLX.
Instead of the VCL, Kylix has a
new component library, CLX (pronounced clicks). The main system
and utility units also have been
reorganized to accommodate CLX
and the Linux environment. As you
might expect, theres no Windows
unit on Linux. Instead, there is libc.
(Irrelevant aside: Linux is written in
C, so the C run-time library is the
Linux run-time library. In Linux, as
with all flavors of UNIX, libraries
are named with an initial lib followed by the rest of the name.
By convention, all names are lowercase. Thus, the C library is called
libc.) The Windows unit held
some useful type declarations, so
the portable types were extracted
into the Types unit. Support for
the Variant type was moved into its
own unit, Variants. These changes
also took place in Delphi 6, which
makes it easier to port code between
Kylix and Delphi 6.

Figure 1: Kylix Open Edition.

If you already have a commercial edition of Kylix, and youre curious


about Kylix OE, you can install Kylix OE as a non-root user (so
it doesnt interfere with your installed edition of Kylix). You should
also create a new user just for running Kylix OE. All versions of
Kylix store information in your login directory (under the .borland
subdirectory). You dont want the different versions of Kylix trying
to overwrite each others files. By maintaining separate users, you
maintain separate configuration files as well.

Running Kylix
Once youve installed Kylix, run startkylix as your self (not root).
The first time you run Kylix, it asks for your serial number and
authorization key. Borland sends them to the e-mail address you supplied when you downloaded Kylix. You need these to continue. Once
youve entered your key, Kylix asks for a Borland Community name
and password. If you arent a member, you can create a username on
the fly. If you skip this step, Kylix exits immediately.
Once youre actually running Kylix, youll see a dialog box telling you
its generating a Font Matrix. It should show a percent progress. Once
it reaches 100 percent, the dialog box disappears, and you can see
the normal Kylix splash screen. If you dont see the percent progress,
dont worry. Just wait about a minute, and then close the dialog box.
Finally, youll see the familiar Delphi interface, except this is Kylix
running on Linux (see Figure 1).
Kylix 1 is a port of Delphi 5, so you can apply your Delphi experience
to customize the Kylix environment. Choose a different editor font or
editor colors. For example, I always turn on hints and warnings in the
default project options, and the options are in the same options dialog
box in Kylix 1 as in Delphi 5. Almost everything works the same as
it does in Delphi 5. Some things are missing, such as Delphi Direct
(raise your hand if you think this is an improvement). Some Delphi 6
features found their way into Kylix, such as the new, improved Code
Insight and support for environment variables.
20 November 2001 Delphi Informant Magazine

Where Are the Components?


One other key difference between Delphi 5 and Kylix OE is the
dearth of components. Kylix OE has the standard VisualCLX components, such as labels, edit boxes, dialog components, and common
controls. It lacks database, Internet, and Web components. Some of
these problems are easy to remedy.
The first thing you should do is download the Indy components
from http://www.nevrona.com/indy/. These components are the
same ones that are included with the commercial Kylix and Delphi
6 editions. They are free and open-source. Version 8 is the current
release, and version 9 is in beta. (This article describes how to use
version 8.) Follow the Download link at the top of the page, and
then choose the version you want. Download at least the source
code, although you probably want to download the documentation
and demos, too. (Note that if you have a commercial Kylix release,
you should download the updated demos to replace the ones that
came on the Kylix CD.)
Create a directory for the Indy files, such as /opt/indy/. Unpack
the HTML documentation archive in its own directory, such as
/opt/indy/html. The other archives include proper subdirectory
names (e.g. source/) so they can be unpacked in the parent Indy
directory. There are two packages to build: indy.dpk (run time)
and dclindy.dpk (design time). Build indy.dpk first. When I built
it, I got two error messages when compiling IdStackLinux.pas that
said the types of actual and formal var parameters must be identical. These problems are easy to fix: Simply change the declarations
from Integer to Cardinal. After compiling, copy the bplindy.so.6
and bplindy.so.6.0 files to the Kylix bin directory. If you use cp,
be sure to use the -d or -a option. Otherwise, youll end up with
two identical files instead of one file and one symbolic link. (You
can choose a different directory on your LD_LIBRARY _PATH
instead of the Kylix bin directory. If you dont know what that
means, use the Kylix bin directory.)

Kylix Tech
Next, build dclindy.dpk. You can choose to install it from the Indy
source directory, or you can copy it to the Kylix bin directory
and install it from there. Lets say you copied (using cp -d) the
dclindy.so.* files to the Kylix bin directory. To install the package,
choose Component | Install Packages from the menu bar, click the
Add button, and find the Kylix bin/dclindy.so.6 file. Click OK, and
you should see three new tabs on the Component palette: Indy
Clients, Indy Servers, and Indy Misc. Indy comes with demos for a
variety of Internet utilities, from e-mail clients to Web servers.

FreeCLX
Borland has released the source code for BaseCLX (the core runtime library, such as System.pas and Classes.pas) and VisualCLX
(the basic visual components), plus the database components and
data-aware visual components. They call this collection of code
FreeCLX. Anyone may download the sources from SourceForge
(http://freeclx.sourceforge.net) under the GNU General Public
License. If you own a commercial edition of Kylix, you may download and use FreeCLX under your commercial Kylix license.
By downloading FreeCLX, you can take advantage of the latest
bug fixes without waiting for the official Kylix 1.01 update. The
downside is that the only way you can use FreeCLX is to link
statically with your application. You cannot build new packages
for use in the IDE, nor can you take advantage of the database
components that are included in FreeCLX. Despite the limitations,
you might find that you need one of the bug fixes in FreeCLX. (See
the Web site for a list of bug fixes and patches.)
To download FreeCLX, start at the main FreeCLX page on SourceForge, and click the CVS link. Read and follow the directions. The
files are downloaded into a directory named freeclx. You also will
need to download or build the latest libqtintf.so library (which is
version 2.2.4.3 as I write this article). You can download it from the
main FreeCLX page. Copy it into the Kylix bin directory. Fix the
symbolic links as follows (change /opt/kylix to the actual directory
where you installed Kylix):
cd /opt/kylix/bin
mv libqtintf.so.2.4.4{,.0}
ln -s libqtintf.2.4.4{.3,}

The braces expand into multiple command-line arguments. The


argument, pre{x,y}end, expands into the two command-line arguments prexend and preyend. Thus, the mv command is a more
convenient, and less error-prone, way to write:
mv libqtintf.so.2.4.4 libqtintf.so.2.4.4.0

These links ensure that any Kylix application that tries to load
version 2.4.4 will find the new version 2.4.4.3. To make sure you
got it right, type ls -lL libqtintf.so. It should show you the
details of the newly downloaded file.

You can enter the paths as they appear, in which case you must
define the FREECLX environment variable to the absolute path
where you downloaded the FreeCLX files, or you can substitute the
actual path in place of $(FREECLX).
The next time you compile your application, Kylix will compile the
FreeCLX source files and link them into your application. Note that
the IDE is still using the original source code, so the components
might behave differently at design time than at run time. If that
causes problems for you, the only solution is to get rid of FreeCLX.

Problems with FreeCLX


In theory, you should be able to use the database components in
FreeCLX to gain a small measure of database capability in Kylix
OE. You dont get the fields editor or any other database property
or component editor, but at least you get the core components.
FreeCLX also includes the dbExpress client for MySQL.
The problem is that you cannot load the database components
into the IDE. The FreeCLX database components depend on the
FreeCLX visual components. You cannot compile the database units
with the visual and core units that ship with Kylix OE. That problem is surmountable by building new BaseCLX and VisualCLX
packages. (You wouldnt be allowed to redistribute the new packages,
but you could use them on your own, to be loaded by the IDE.)
The problem is that the IDE was written for the original VisualCLX, and doesnt like the updates in FreeCLX. When I built
a new VisualCLX package from the source code in FreeCLX,
the IDE was unable to open its form editor and reported an
access violation every time. I could not find any way to get the
IDE to load the updated visual components. And, without those
components, there is no way to load the database components
into the Component palette.
If you want another solution to writing database clients in Kylix
OE, you need to find some third-party database components. One
popular suite is Zeos (http://www.zeoslib.org). This suite requires
FreeCLX, but it avoids the problems described earlier because it
doesnt use the visual data-aware components that require VisualCLX. Instead, the suite substitutes its own components. Follow the
directions at the Web site for downloading, building, and using the
Zeos database objects.

Building dbExpress
FreeCLX comes with the dbExpress driver for MySQL. Its not much
use without any database components, but if you have a project you
wrote with a commercial edition of Kylix, and you give the source
code to a friend who has only the Open Edition, and your application uses MySQL, your friend might want to build the MySQL
dbExpress library. To build it, change to the dbExpress directory,
and type:
make FREECLX=`pwd` -k

The easiest way to install the FreeCLX files is to add the FreeCLX
source directories to the library and browsing paths under the
Environment options. Be sure to move the FreeCLX directories so
they appear first in the list. Include the following directories in the
search path:
$(FREECLX)/clx
$(FREECLX)/rtl/linux
$(FREECLX)/rtl/sys

21 November 2001 Delphi Informant Magazine

(Note the backward apostrophes around pwd. The result of `pwd`


is the complete path to the current working directory. That is, its
the directory where FreeCLX is installed.) You might get a couple
of minor errors, but you can ignore them. The makefile is not
robust, but it manages to get the job done in most cases. The result
should be two files in the dbExpress/bin directory: dbxres.en.1.0,
and libsqlmy.so.1.0. Copy these files to the Kylix bin directory and
create symbolic links, as follows:

Kylix Tech
ln -s libsqlmy.so.1.0 libsqlmy.so.1
ln -s dbxres.en.1.0 dbxres.en.1

Applications
The main reason to use Kylix OE is to write applications, or to
compile applications others have released under the GPL. Thats
easy; just open the project and compile it, the same way you do
in Delphi.
Every application built with Kylix OE automatically includes a
splash screen that displays the text: This application was built
with Borland Kylix Open Edition(tm). The application must be
distributed under the terms of the GNU General Public License
(GPL), version 2. A copy of this license can be found at: http://
www.borland.com/kylix/gpl.html.
To skip the splash screen, use the -ns switch. You cannot change
the text of the splash screen or translate that text to another language. You may customize the splash screen with two bitmaps.
A title bitmap may appear across the top of the splash screen,
and an icon bitmap may appear to the left of the text, under
the title. To include the bitmaps in your application, put them in
a resource file with the resource names OSB_TITLEIMAGE and
OSB_ICONIMAGE.
If you have Delphi, you can use Delphis image editor on Windows
(or Borlands Resource Workshop) to create the resource files. Alternatively, you can use any bitmap editor (such as the GNU Image
Manipulation Program, or GIMP, which comes with all major
Linux distributions) to create bitmap files, and compile a resource
script into a binary resource file. A sample resource script is:
OSB_ICONIMAGE BITMAP "icon.bmp"
OSB_TITLEIMAGE BITMAP "title.bmp"

Linux. The --with-windres option tells the configure script to add


windres to the binutils makefile. For your convenience, you can
download a prebuilt windres (built on SuSE 7.2, using glibc 2.2.2)
from http://www.tempest-sw.com/kylix/.
If you build a console application, the splash text is printed to the
standard output device. Unlike the GUI splash screen, you cannot
use a command-line option to skip the introductory text. This is
unfortunate, because CGI applications and UNIX filters must not
print extraneous text to the standard output. Fortunately, there is
an easy way to circumvent Kylix in this case: remove the $AppType
compiler directive. Unlike Windows, in Linux theres really no difference between a console application and a GUI application, but the
splash text is written only when the {$AppType Console} compiler
directive is used. Note that the terms of the GNU General Public
License require some kind of notice concerning the license, so you
must make other suitable arrangements to display the license text.

Conclusion
Kylix Open Edition is a unique product. For Borlands other products, the company offers a Personal edition as a free download. The
Personal edition, as the name suggests, is for personal use only. No
distribution is allowed. With Kylix OE, Borland acknowledges the
importance of open source in the Linux community, and permits
distribution under the license. The companion project, FreeCLX,
also is distributed under the license. Now, any Linux user can start
taking advantage of Delphi-style rapid application development,
and distribute the source code under the license.
To take full advantage of Kylix OE, you need some additional tools,
especially a resource compiler. Such tools are readily available, and
it doesnt take much work to use them productively with Kylix. In
time, I hope Borland will work out the glitches that prevent the full
use of FreeCLX in Kylix Open Edition.

To compile the resource script in Linux, you have two choices:


wrc and windres. If you have Wine installed, you can use Wine
Resource Compiler (WRC). Alternatively, you can build the GNU
binutils package to get windres. (If you already have Wine, use
WRC and save yourself the hassle of downloading binutils. If youre
short on disk space, use windres.)
If you choose the binutils route, you must first download the latest
release of binutils. It contains crucial bug fixes. (Bugs that were
not discovered until Kylix developers starting using windres in earnest.) Visit http://www.gnu.org/directory/binutils.html where you
can find the CVS root and anonymous password. After downloading the sources, run ./configure --with-windres, and then make.
By default, the makefile builds windres only on Windows, not on

22 November 2001 Delphi Informant Magazine

Ray Lischner is the author of Delphi in a Nutshell and other books. His current
project is a book about Kylix, to be published next year by OReilly. When
he isnt writing, he often speaks at conferences and user-group meetings
across the country.

At Your Fingertips

Customizing Menu, Memo, and RichEdit Components / Delphi 4-6

By Bruno Sonnino

Floating Menus, Etc.


Plus: Setting Tab Stops in Memo and RichEdit Components

sually, a main menu is positioned at the top of a form, fixed above the toolbar. But
there are some applications that put the menu in a toolbar, so it can be detached
and moved around. Delphi is one of these applications. Delphi is written in Delphi, so
there must be a way to put a menu in a detachable toolbar. Lets see how.
Initially, we must know something about the
ToolBar and ControlBar components (introduced in Delphi 4). These two components
encapsulate their namesake Windows common
controls. To use them fully, you must have the
Windows DLL, COMCTL32.DLL, updated to
version 4.72 or greater.
The ControlBar component (on the Additional tab
of the Component palette) acts as a container for
other controls just drop them into it. Each
contained control gets a small handle to drag it,
represented by two vertical bars. If you wish to
drop many controls into a ControlBar, but want
just one handle, you must drop another container,
such as a ToolBar or another ControlBar, and drop
the controls there.
We can drop other controls in the ToolBar or
create new buttons in it. These buttons are of
the TToolButton class. The first step to make a
ToolBar dockable is to drop a ControlBar on a
form and align it by changing its Align property.
You can align to the side of the form where
you wish to dock the toolbar. If you want to
allow the toolbar to dock on every side of the
form, you must drop four ControlBars, one for
each side of the form. Then you can change
the ControlBars AutoSize property to True. This
way, when the ToolBar is not docked, the ControlBar will shrink, leaving space for the rest of
the controls in the form. To make a ToolBar
dockable, change the properties DragMode to
dmAutomatic, and DragKind to dkDock.
23 November 2001 Delphi Informant Magazine

There are some properties in the ControlBar that


must be adjusted to get the ideal appearance.
The RowSize and RowSnap properties can control the height of the toolbar. If RowSnap is
True, the ToolBar height is adjusted to fit the
ControlBar row size. If False, you can set the
ToolBar to any height. Set the RowSnap property
to False, so theres no need to adjust the row size
of the ControlBar. Because were using only one
row, this isnt a problem, but if we wanted more
toolbars it would be better to set RowSnap to
True, for ease of organization.
Each ToolButton has a property named ImageIndex
that points to the ImageLists image index to
retrieve its glyph. There are two other properties
that can be assigned to ImageLists: HotImages
and DisabledImages. The first one has the images
shown when the mouse is over a button, and the
second has the images shown when the button
is disabled. When there is no ImageList assigned
to these properties, the button remains the same
when the mouse is over it. The disabled image
is calculated by graying the enabled glyph. This
way, if you want to have different bitmaps to
show the button states, you must drop three
ImageLists on the form and add the bitmaps in
the same order. The 0 index bitmap in the three
ImageLists will be used by the button that has
ImageIndex 0.
After this brief introduction to ControlBars,
ToolBars, and ToolButtons, we can go to our
real problem: floating menus. The first step to

At Your Fingertips

Figure 1: The docked menu.

Figure 2: The undocked menu (with no close button).

create a floating menu is to create a real menu. You must drop a


TMainMenu on the form and create the menu as you normally
would. Double-click the main menu to open its editor and add
options to it. Then drop a ControlBar on the form, and set its
Align property to alTop. Set its AutoSize property to True, and its
RowSnap property to False.

When a ToolBar is undocked, a new window of type TToolDockForm


is created to house the undocked ToolBar. When the ToolBar
is docked again, this window is freed. We can create a new
window class derived from TToolDockForm, override its Create
method, remove the close button, and then tell Delphi that the
new kind of toolbar window is the one weve created. The new
class declaration is:

Drop a TToolBar in the ControlBar, setting the properties


DragMode to dmAutomatic and DragKind to dkDock. Usually, the
ToolBar buttons display only images; the associated caption isnt
shown. To show the captions like a menu, you must set the
property ShowCaptions to True. Another property that must be
modified is Flat, so the buttons have the raised effect only when
the mouse is over them. Change the Caption property to Floating
Menus. This caption will be shown when the ToolBar is undocked.
The next step is to add new buttons to the ToolBar, one for
each option in the main menu. Ive put four options (File, Edit,
Window, and Help), so I must create four buttons in the ToolBar.
Then we must assign the menu items to the ToolButtons. This is
done using the MenuItem property. When you assign this property
to a menu item, its caption gets the menu items caption. You may
have noticed that all buttons have the same size, but this isnt the
usual behavior of a menu: each option has its size. To get this kind
of behavior, you must set the buttons AutoSize property to True.
This way, the buttons take only the space of their caption.
The floating menu is ready, but there are still two menus on the form: the
fixed menu and the floating menu. We must remove the fixed menu so
that there is only one menu. This is done by clearing the Menu property
of the form. When you drop a MainMenu component on the form and
it doesnt have a main menu, the Menu property is assigned to it, so we
must clear it. After clearing this property, only the floating menu remains.
You can execute the program and enjoy the new floating menu (see
Figure 1).
But theres still a problem. If the toolbar is undocked and the
user clicks the close button, the main menu is gone and the only
way to recover it is to close and reopen the program. Thats not a
friendly way to recover a menu. One alternative is using a popup
menu with an option to show the toolbar again, but its rather
strange to use a second menu to reopen the first one. There should
be a better way to prevent the user from closing the window.
24 November 2001 Delphi Informant Magazine

TNewToolDockForm = class(TToolDockForm)
public
constructor Create(AOwner: TComponent); override;
end;

This new class only redefines the Create constructor. In this new
constructor we remove the system menu from the BorderIcons
property, removing the close button:
constructor TNewToolDockForm.Create(AOwner: TComponent);
begin
inherited;
BorderIcons := BorderIcons - [biSystemMenu];
end;

Finally, we must tell Delphi that the class that will house the
toolbar when undocked is a TNewToolDockForm. This is done in
the OnCreate method of the main form:
procedure TForm1.FormCreate(Sender: TObject);
begin
ToolBar1.FloatingDockSiteClass := TNewToolDockForm;
end;

FloatingDockSiteClass is a property that tells Delphi which class


should be instantiated when the toolbar is undocked. This way
when the toolbar is undocked, a TNewToolDockForm will be created with no close button, as shown in Figure 2.

Tab Stops in a Memo Component


To create a table in a Memo or RichEdit component, you must
use the tab character (a decimal 9, or #9 in Pascal) in the middle
of the string. This is better than using spaces, because with
proportional fonts the text never gets aligned properly. In a
Memo, you cannot set the tab stops for a single line of text or

At Your Fingertips
procedure TForm1.FormCreate(Sender: TObject);
const
TestStr = 'abcdefghijklmnopqrstuvwxyz' +
'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var
i : Integer;
Size : TSize;
begin
// Get average width of character.
GetTextExtentPoint32(Canvas.Handle, TestStr, 52, Size);
BaseUnits := Size.cx div 52;
// Fill memo.
for i := 1 to 100 do
Memo1.Lines.Add('C1'#9'C2'#9'C3'#9'C4'#9'C5'#9'C6');
end;

procedure TForm1.HeaderControl1SectionResize(
HeaderControl: THeaderControl; Section: THeaderSection);
var
Tabs : array[0..4] of DWord;
i : Integer;
begin
// Calculate tab stops.
for i := 0 to 4 do
Tabs[i] :=
HeaderControl.Sections[i+1].Left * 4 div BaseUnits;
// Set tab stops.
Memo1.Perform(EM_SETTABSTOPS, 5, Integer(@Tabs));
end;

Figure 5: Setting the new tab stops after a section resize.

Figure 3: Calculating the base units of a form.

procedure TForm1.Button2Click(Sender: TObject);


var
Distance : Integer;
begin
try
Distance := StrToInt(InputBox(
'Tab Distance', 'Enter Tab Distance', '32'));
Memo1.Perform(EM_SETTABSTOPS, 1, Integer(@Distance));
SetHeaders(Distance);
except
on EConvertError do
ShowMessage('Invalid value');
end;
end;

and may have their relative position moved. Well use one as a guide to
the tab settings in the Memo. If the sections are resized, the tabs will be
changed. To reset all tabs to the original 32 dialog units, use the code
shown here:
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Perform(EM_SETTABSTOPS, 0, 0);
SetHeaders(32);
end;

The SetHeaders procedure sets all header sections to the same width. This
is necessary when you change the tab settings, so the header control aligns
with the tabs:

Figure 4: Setting all tabs to a constant value.

paragraph; the settings apply to all of the text. The default tab
setting in a Memo is 32 dialog units. To convert a dialog unit to
pixels, you can use the formula shown in these statements:
Pixelsx := (Dialogx * BaseUnitX) div 4;
Pixelsy := (Dialogy * BaseUnitY) div 8;

Usually, the base units are retrieved with the API function,
GetDialogBaseUnits, but this doesnt work with forms that have
variable-pitch fonts. In this case, the base units are obtained using
the average width and height in pixels of the characters in the
Memo font. To get the average width, we can measure a string
made of all characters in uppercase and lowercase in pixels with
the GetTextExtentPoint32 function, and divide the result by 52,
the number of characters in the string. To alter the standard tab
setting, you must send the message EM_SETTABSTOPS to the
Memo handle. This is done with something like this:
Memo1.Perform(EM_SETTABSTOPS, WParam, LParam);

where WParam has the number of tab stops that will be set. If
it is 0, then the tab stops will be reset to the default 32 units.
If it is 1, all tab stops will be set to the spacing set by LParam.
LParam must be set to an array of DWord with the tab stop values. These
values must be in dialog units.
As an example, well put a Memo and a HeaderControl component on
the form. The HeaderControl component acts as a header for controls
that dont have one. Its like the header in a ListView in report mode, but
can be positioned at the top of other controls. Its divided into sections,
which have individual captions and widths. The sections can be dragged
25 November 2001 Delphi Informant Magazine

procedure TForm1.SetHeaders(Distance: Integer);


var
i : Integer;
begin
for i := 0 to HeaderControl1.Sections.Count - 1 do
HeaderControl1.Sections[i].Width :=
Distance * BaseUnits div 4;
end;

The value for BaseUnits is calculated in the forms OnCreate event


handler, and stores the average width of its font. This calculation is shown
in Figure 3. It gets the size of a string made of all uppercase and lowercase
characters, divided by 52.
To change all tabs to a constant value you can use the code shown in
Figure 4. The new tab value is requested with the InputBox function. This
function presents a dialog box that prompts for the distance, and returns
it as a string when the user clicks OK. We convert it to an integer and then
set the tab stops and the header widths. The code is in a try..except block,
in case the user enters an invalid value.
The last thing we must do is trap for when the user resizes the sections
of the HeaderControl. To do this, well use the OnSectionResize event to
set the new tabs. OnSectionResize is fired when the user finishes dragging
the section borders and the section is resized. We must declare an array
of DWord, set it to the tab values, and send the EM_SETTABSTOPS
message. Since the widths of the sections of the header are measured in
pixels, we must convert them to dialog units. The code in Figure 5 shows
how to convert the pixels to dialog units, and set the new tab stops.
Figure 6 shows the memo with the tabs set.

Tab Stops in a RichEdit Component


The TRichEdit doesnt work like a TMemo. You can format each
paragraph or even each character individually, so you dont set the

At Your Fingertips
tab stops for the whole text at once. Paragraph formatting is done
using the Paragraph property of TParaAttributes class. With this
property you can set the tab stops, alignment, indents, or bullets.
The TParaAttributes class has two members, TabCount and Tabs,
that are used when setting the tab stops. TabCount has the number
of tab stops set for the current paragraph, and Tabs is an indexed
property pointing to the tabs positions.
The tab stops of the underlying Windows control encapsulated by
the TRichEdit component are measured in twips (1/1440), but
Delphi multiplies the value thats set in the Tabs property by 20,
perhaps to make the values closer to the tab stops of the TMemo.
The result is that you dont get the same thing and must calculate
to get the correct result. To convert pixels to the units used to set
the RichEdit tabs, use the formula in this statement:
TabUnits :=
Pixels * 1440 div (Screen.PixelsPerInch * 20);

To set the first tab to 50 pixels, you can use this statement:

Figure 6: Memo component with tabs set.

procedure TForm1.HeaderControl1SectionResize(
HeaderControl: THeaderControl; Section: THeaderSection);
var
i, OldStart, OldLen : Integer;
begin
with RichEdit1 do begin
// Store old selection.
OldStart := SelStart;
OldLen := SelLength;
// Prevent flickering.
LockWindowUpdate(Handle);
try
// Select all the text.
SelStart := 0;
SelLength := -1;
// Calculate and set tab stops.
RichEdit1.Paragraph.TabCount := 5;
for i := 0 to 4 do
RichEdit1.Paragraph.Tab[i] :=
HeaderControl.Sections[i+1].Left * 1440 div
(Screen.PixelsPerInch * 20);
finally
// Restore selection.
SelStart := OldStart;
SelLength := OldLen;
// Restore window update.
LockWindowUpdate(0);
end;
end;
end;

Paragraph.Tab[0] :=
50 * 1440 div (Screen.PixelsPerInch * 20);

Because all the tab settings in a RichEdit are done paragraph by


paragraph, there is no such thing as setting the tabs of the whole
text. To do this, you must select all the text, set the tab stops, and
reset the original selection. Figure 7 shows the code to do this.
You may have noticed the calls to LockWindowUpdate before and
after selecting the text. This is done to prevent updating the
RichEdit window. If these calls werent made, you would see a
flicker while the selection is changed, the text is selected, and the
tabs are set. This way, the RichEdit is only redrawn when the new
tabs are set, as shown in Figure 8.
The example projects referenced in this article are available on the
Delphi Informant Magazine Complete Works CD located in INFORM\
2001\NOV\DI200111BS.

Figure 7: Setting the tab stops for the whole memo.

A Brazilian, Bruno Sonnino has been developing with Delphi since its first
version in 1995. He has written the books 365 Delphi Tips and Developing
Applications in Delphi 5, published in Portuguese. He can be reached at
sonnino@netmogi.com.br.
Figure 8: RichEdit with tab stops set.

26 November 2001 Delphi Informant Magazine

New & Used


By Alan C. Moore, Ph.D.

SysTools for Kylix


TurboPowers Low-level Library Now Available for Linux

or nearly a decade, TurboPowers low-level Windows development libraries, SysTools and the earlier WinSys, have provided outstanding support for Borlands
Windows development tools. Now the award-winning SysTools Library has been ported
to the Linux platform to aid Kylix developers. Like the SysTools 3 library I recently
reviewed (in the July 2001 issue of Delphi Informant Magazine), the Kylix incarnation
can also be divided into three general groups: data manipulation classes, container and
streaming classes, and system support classes. Well examine each of the three groups.
Then well examine other factors such as documentation and support.
Having run most of the sample programs, I can
affirm that the vast majority of the routines perform
well with Kylix. A few example programs and one
or two units showed minor problems, but I was able
to easily fix most of them. TurboPower provides free
update patches on their Internet site whenever the
number and/or severity of outstanding fixes reaches
a certain level. Before that, they post a manual fix
text file that lists fixes that users can apply to the
source code included with their libraries.

Working with Data


The following list provides a quick overview of
the various types of data manipulation support
included in this library (the same as those included
in SysTools 3):
 Parser and evaluator for arithmetic expressions
similar to those accepted by Object Pascal.
 Routines for manipulating astronomical data
including sun, moon, and planet positions,
times of rising and setting, lunar phases, and
more.
 Binary Coded Decimal provides exceptional
precision for floating point number calculations.
 Financial routines and scientific statistical
routines augmenting those in the Borlandsupplied Math unit. Both are modeled on
functions in Microsoft Excel.
Five
popular checksum algorithms including:

two 32-bit CRCs, two 16-bit CRCs, and the
Internet Checksum.
27 November 2001 Delphi Informant Magazine







Routines for compactly storing, converting,


formatting (strings), and performing arithmetic on dates and times (includes international
support).
Support for adding and extracting file or
stream attachments to documents while converting the attachments according to the most
widely accepted MIME formats.
SysTools sort engine uses a non-recursive
quicksort with merge sort facilities.
String conversion routines, and special routines for string comparisons and many other
manipulations.
Traditional file functions made available for
text files (finding and moving to a specific position, finding the current logical file pointer,
and determining the files size).

As in the Delphi library, the Kylix version has


extensive support for many data types, including
all types of strings (Pascal length-byte strings, nullterminated strings, AnsiStrings, and WideStrings).
Most routines support all these types. There are
routines to parse and manipulate filenames (appropriate for Linux, of course) such as AddBackSlash,
DefaultExtension, and JustName. Further, there are
routines for counting words, word wrapping, and
finding the location of a word (a substring surrounded by delimiters) in a string. Support for
searching includes the Boyer-Moore searching algorithm, which is fully implemented. It also includes a
powerful sorting engine, as shown in Figure 1.

New & Used








Figure 1: A few of the sorting engine capabilities are shown in


this demonstration of SysTools for Linux.

As in the Delphi library, this Kylix version includes strong support


for the manipulation of date and time data, with methods supporting Julian dates (any date 1/1/1600 to 12/31/3999) and a wide
variety of picture masks. Also included are date/time testing functions like ValidDate and IsLeapYear, and many conversion methods
such as DayOfWeek, MonthToString, TimeStringToSTTime, and
RoundToNearestMinute. Going beyond date and time functions,
the library includes many astronomical functions that return
phases of the moon, equinoxes, and much more.
For users with Delphi editions that dont include the Math unit, SysTools
provides much of that functionality. For users who do, it extends
that functionality to include business math and scientific math. Some
of the functions included under business math are CumulativeInterest,
DiscountRate, EffectiveInterestRate, VariableDecliningBalance, and
YieldDiscounted, to name just a few. Similarly, statistical functions include
Correlation, Covariance, Frequency, HarmonicMean, NormDist, and
Percentile. Again, this is just a small sampling.
Finally, SysTools for Kylix includes an Expression Evaluator that
allows you to register custom constants and functions along with
a visual Expression Edit component. Theres one more math area
that deserves special mention: Binary Coded Decimal. This highprecision, floating-point class is particularly useful in financial or
accounting applications. It supports as many as 36 significant digits
with a range from 1E-64 to slightly less than 1E+64. Finally, there are
a plethora of other math functions.

Container Classes and Specialized Streams


Ive always appreciated SysTools container classes, and have used
some in my own applications. As with traditional container classes,
they provide storage for collections of related data elements. Of
course, Delphi and Kylix provide some of the more common ones
like arrays and records, but SysTools provides many others:
 TStBits provides the same functionality as an array of Boolean
values where each element in the array can be either True (1)
or False (0).
 TStCollection is special collection class similar to the old
TCollection object provided in Borland Pascal 7.
 TStContainer is an abstract container class from which all the
others descend.
 TStDictionary is a string dictionary similar to a traditional
dictionary in which you look up items by name and retrieve
information about them.
28 November 2001 Delphi Informant Magazine






TStDQueue is a descendant of TStList that extends it, implementing a double-ended queue (and also a stack and a queue).
TStPQueue is an implementation of a priority queue in which the
elements are stored as pointers. Typically these point to a record
or class of your own, holding fields that describe the priority and
any other data needed by the application. In the classic operating
system example, a process identifier would be needed to start the
highest-priority process once it was identified.
TStHashTable is very similar to the string dictionary but differs
in various ways. For example, the keys in the internal hash table
are not limited to strings, but are passed as untyped parameters,
allowing the use of many data types.
TStLArray and TStLMatrix support two large array classes, similar to Delphis array type but capable of holding many more
elements. One is a large array in one dimension (the TStLArray
class), the other a large array in two dimensions, also known as a
matrix (the TStLMatrix class).
TStList is an implementation of a doubly-linked list manager
with TStListNode nodes that hold the data objects.
TStSortedCollection is a TStCollection descendant that adds the
functionality of automatically ordering data objects in the collection as they are inserted.
TStTree is a binary search tree data structure storing a sorted
collection of data objects in memory. There are no preset limits.
TStVMatrix is a virtual matrix that differs from TStLMatrix by
providing the programmer with more control over swapping data
to and from the hard disk.

All of these descend from TPersistent, making them all streamable.


These container classes fall into two general groups: those based
on pointers (linked lists, trees, and collections), and those based
on un-typed variables (arrays). SysTools container classes share these
advantages: you can store any kind of data in each, and you can adjust
data element size and number of elements at run time. Using base classes,
you can also create your own specialized container classes.
If youd like to consider specific examples, please refer to my review
of SysTools 3 for Delphi. These include the TStBits, TStList class,
and TStDQueue classes. This library also includes two collections that
include much of the functionality of the Borland Pascal 7 TCollection,
one sorted, one not (see Figure 2). Both are sparse arrays of pointers
where most of the pointers are assumed to be empty. It also includes a
String Dictionary and a Tree.
SysTools for Kylix also includes useful stream classes. Delphis own
stream classes are powerful and useful, but they have some limitations. For example, the TFileStream class doesnt implement any
buffering; similarly, Delphi streams do not allow you to access stream
data in terms of lines and characters. SysTools provides this functionality in its TStBufferedStream and TStAnsiTextStream classes. These
classes act as filters, providing this additional functionality along with
normal stream processing.

System Support
From the beginning, SysTools has provided strong support for working
with the operating system and low-level data. The StUtils unit includes
routines for setting, filling, clearing, testing, or exchanging values in
various data types, including SetLongFlag (sets one or more bits in the
parameter Flags) and FillStruct (fills a given region of memory with values
of a specified size, i.e. not just byte-size as in FillChar). Others include
ClearByteFlag, LongFlagIsSet, SwapNibble, and SetFlag. There are lowlevel file-handling routines to retrieve all kinds of information about files
and directories and perform various operations. The Regular Expression

New & Used

SysTools for Kylix is a large library of Delphi classes for manipulating


data of all kinds, powerful container classes, and low-level routines
to work with the Linux operating environment. It includes example
programs and full source code. The manual is well organized, comprehensive, and very well written. You can download a trial version and
the product has a 60-day money-back guarantee. In sum, SysTools
establishes itself as a contender to become the premier Kylix Library.
TurboPower Software Company
15 North Nevada Avenue
Colorado Springs, CO 80903-1708

Figure 2: One of SysTools powerful collection classes, which is


very close to the Windows version.

class and component allow you to perform search and replace operations
on text files using a powerful Regular Expression engine. If you need
to convert text to an HTML-compatible file with embedded HTML
commands you can use the SourceToHTML component.
As you might expect, not everything from the Delphi version is included,
particularly those features that are Windows-specific, such as support for
the Windows registry. And you wont find those powerful new Windows
Shell components. But the core SysTools classes are all here, including
some we havent discussed in this review (such as Bar Code support).

Documentation and Support


As with the Delphi version, the documentation is excellent, even if
its several hundred pages less than SysTools 3s 1,000-page manual. It
includes all of the key properties and methods, each defined with clear
examples. Each chapter includes example code demonstrating features
and capabilities. Like other TurboPower products, the library includes
full source code at no additional cost. Excellent support is provided by
e-mail, in online newsgroups, and by phone. Questions posted to the
newsgroups are usually answered the same day. Minor upgrades can be
downloaded free of charge.

Conclusion: A Truly Excellent Library


SysTools for Kylix is an arsenal of low-level tools for the Linux developer.
You should consider purchasing it even if you need just a small portion
of its units. You can learn more about this library by visiting the TurboPower Web Site. You can download a trial version from there (http://
www.turbopower.com/trialrun). And dont forget TurboPowers 60-day

29 November 2001 Delphi Informant Magazine

Phone: (800) 333-4160 or (719) 471-3898


Fax: (719) 471-9091
E-Mail: info@turbopower.com
Web Site: http://www.turbopower.com
Price: SysTools for Kylix, US$249; upgrade for owners of SysTools
for Delphi and C++Builder versions 3.0 or later, US$149; owners of
any other TurboPower product, US$199 (20% discount).

money-back guarantee, the most generous one I know of in the industry.


With its newest incarnation of SysTools, TurboPower has staked its claim
as king of the mountain in the brave new world of Kylix development.
I recommend it highly.
Before closing, Id like to present my personal wish list for TurboPower,
both in what I would like to see in later versions of this library and other
support for Kylix. Since there are so many window managers for Linux,
it seems impractical to try to support the unique features of each.
However, SysTools for Kylix add-ons from KDE and GNOME might
be popular; perhaps TurboPower should investigate this in one of their
online polls. For future porting consideration, I would especially like
to see a Kylix version of Sleuth QA Suite (quality assurance knows no
platform boundaries) and Orpheus. But with this version of SysTools
and several other Kylix products recently released, TurboPower is already
demonstrating strong leadership in this exciting new arena.

Alan Moore is Professor of Music at Kentucky State University, teaching music theory
and humanities; he was named Distinguished Professor for 2001-2002. He has been
developing education-related applications with the Borland languages for more than 15
years. He 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 at acmdoc@aol.com.

TextFile

Building Kylix Applications


Building Kylix Applications, by
Cary Jensen, Ph.D. and Loy
Anderson, Ph.D., is the first
Kylix book Ive had a chance
to examine. I predict it will be
among the most popular. Let
me explain why.
Building Kylix Applications is
appropriate for developers
completely new to Kylix and
Delphi, as well as those moving
from Delphi to Kylix. The
authors are careful to point
out those areas of difference
between Kylix and Delphi.
But, there are so many similarities that I feel this book could
serve equally well as an introduction to Delphi. Since little
is assumed regarding the readers background, the authors
take great care explaining the
Kylix IDE, and other basic
issues, in detail.
Although Building Kylix Applications is accessible to novice
Delphi/Kylix developers, theres
much here that will be of interest to more advanced programmers. The authors take time to
explain some of the important
features that have appeared in
recent Delphi versions, such as
Frames and Action Lists. The
chapter on Using and Configuring the Editor is excellent, and includes a discussion
of working with Kylixs Open

Tools API to create your own


key bindings.
Even more impressive is the
chapter on Debugging Kylix
Applications. In it the authors
take you on a detailed tour of
Kylixs many debugging windows, and explain why and
how to disable the integrated
debugger. They also provide a
thorough explanation of the
various types of breakpoints.
No book can cover every
facet of a topic. One area
that I felt could have been
included was a chapter on
Object Pascal, which would
be particularly beneficial for
developers moving to Kylix
from other languages. However, Kylix (like Delphi) comes
with a thorough introduction
to the underlying language.
Furthermore, every topic they
have chosen to include is
treated thoroughly.
The comprehensive treatment of
database programming is especially welcome. Having read Dr
Jensens articles and book contributions, and having attended
some of his presentations, this
did not surprise me. I consider
him to be among the most
knowledgeable and effective writers on Delphi database topics.
Even intermediate-level develop-

30 November 2001 Delphi Informant Magazine

ers could benefit from his and Dr


Andersons clear explanations of
this important topic.
Starting with basic topics of
working with database components, they broach some rather
advanced topics. Theres an
entire chapter on TFields, a
topic that is sometimes covered
rather quickly in Delphi books.
There are two chapters on
datasets, and an entire chapter
on writing dbExpress drivers.
Since dbExpress is the heir
apparent to the BDE (Borland
Database Engine), this will be
valuable to many Kylix and
Delphi developers.
Among the other advanced
topics included in Building
Kylix Applications are component creation, multithreading,
interfaces, and shared object
(SO) libraries. For Delphi
developers not familiar with
the latter, SO libraries are similar to Windows dynamic link
libraries. The authors conscientiously recommend that readers
study third-party Linux programming books to learn more
about this latter topic (Ill have
more to say about this in a
future File | New column).
Like Delphi, Kylix excels in
both database and Web development. The final section of

Building Kylix Applications


explores Kylix support for this
latter area, discussing working
with Web Broker and Internet
Direct. All of the source code
is available for free download
at the authors Web site. If
you download the source code,
I think youll be inspired to
buy this extremely well-written
book, so you can understand
the valuable principles demonstrated. I recommend this book
highly and without reservations.
Alan C. Moore, Ph.D.
Building Kylix Applications
by Cary Jensen, Ph.D.
and Loy Anderson, Ph.D.,
Osborne/McGraw-Hill,
http://www.osborne.com
ISBN: 0-07-212947-6
Cover Price: US$59.99
(832 pages)

TextFile

The Tomes of Delphi: Algorithms and Data Structures


Shopping for a good technical
reference can be a formidable
task. And judging them solely
by weight, thickness, or
number of pages is a great
way to take home an expensive
doorstop. Once in a while,
however, an obvious and easy
pick comes along; Julian Bucknalls The Tomes of Delphi:
Algorithms and Data Structures
is one such find.
The book provides an all
Delphi, all the time perspective to a wide variety of classic
topics. If youre thinking that
this theme sounds old, conventional, and potentially boring,
dont be misled the presentation in this book is anything
but ordinary.
While some classical texts do
tend to be stodgy, Julian Bucknall makes building B-trees,
hash algorithms, and linked
lists seem like high adventure.
Topic development is clear,
consistent, and engaging where
each paragraph draws you into
the next. Once opened, this
book is difficult to put down.
There are 12 chapters and 500+
pages covering arrays, lists,
searching and sorting, random
number generation, hashing,
B-trees, queues, state machines,
regular expressions, and a very

interesting chapter on data


compression. While theres just
enough history and theory presented to thoroughly ground
each topic, the dominant focus
of the book is the building of
optimized and practical implementations in Delphi.
The Tomes of Delphi: Algorithms
and Data Structures targets an
Advanced audience, but dont
pass it by if youre a Delphi
beginner with a basic understanding of OOP. Classical
algorithms and data structures
are a perfect place to begin
building a solid foundation
in any programming language.
Getting to the next level is
always a stretch, but the entertaining and well ordered presentation coupled with the
cleanly written examples in this
book can be a big help on the
path to the intermediate and
advanced ranks.
The CD contains the book
sources, the authors EZDSL
freeware library, and a commercial trialware package.
Open any of the source files
and youll find optimally written and lavishly commented
code. Theres not a component
to be found as the library routines are a collection of classes
ideal for building reference
implementations and tutorials.

31 November 2001 Delphi Informant Magazine

This lean and mean approach


left something to be desired,
however, when it came to the
demos, which were packaged
mostly as console applications.
While DOS-like demos were
fine in the days of Turbo Pascal,
a top-level GUI would have
provided far better support for
the Delphi-centric theme of this
book. A form and graphical controls would have also worked
better with Kylix where the IDE
doesnt spawn console apps into
a separate shell like Delphi does
under Windows. Since I had
Kylix configured to start without
a command shell, the demos
didnt have access to standard
I/O and appeared to hang as
they waited for keystrokes that
couldnt be entered. This was
annoying but easily fixed by running startkylix from the command line of an XTerm.
The demos compiled and ran
fine under Win32. There were
a few minor cosmetic issues
under Kylix, and one of the
demos couldnt be compiled on
either platform due to a missing file on the CD. A note
to Julian brought an immediate
response and an update to the
books support Web site.
Virtually all of the classical topics
in The Tomes of Delphi: Algorithms
and Data Structures have been

covered in other books, magazines, and Usenet threads. So why


pay $59.95 for information that
you can get at the library, download, or cull out of old magazines? Because this book puts it
all together, and does so with
mastery and a lively presentation.
The Tomes of Delphi: Algorithms
and Data Structures is a practical,
well written, and comprehensive
resource. Im glad its on my shelf
next to the space where I hope
to put Volume 2!
Tom Lisjac
The Tomes of Delphi: Algorithms and Data Structures
by Julian Bucknall,
Wordware Publishing,
http://www.wordware.com.
ISBN: 1-55622-736-1
Cover Price: US$59.95
(525 pages, CD-ROM)

File | New
Directions / Commentary

Interview with Charlie Calvert

riginally pursuing a career in journalism, Charlie Calvert developed an interest in computing while attending college.
He learned Turbo Pascal in the mid-1980s, then went to work for Borland, his favorite company, in tech support. In
less than a year, he was asked to write his first book, on Turbo Pascal. A decade later, after writing several editions of
Delphi Unleashed, Calvert became one of the best known and most widely respected people at Borland. Hes also helped
a number of other aspiring Delphi authors, and was instrumental in strengthening the ties between Borland and Project
JEDI. Since leaving Borland, Calvert has done much development in Delphi, JBuilder, JavaScript, and Perl all related to
Internet programming with lots of emphasis on streaming. His Kylix book will be released soon.
Delphi Informant: Could you share with us how you became involved
with development, and how you came to join Borland?
Calvert: Two things happened when I started computing. I was in
school in the mid-1980s, learning how to be a journalist. One of
my teachers took us down to the computer lab and showed us a
PC. He had us type in a few words. Ho hum. Then he said, Hit
the backspace key. I reached out one Wite-Out speckled finger and
pushed backspace, and miracle of miracles the character I had
just typed disappeared. That was it. I was hooked!
I actually became a journalist and practiced journalism for a while. I
decided on a career change when the editor came into the office one
day and found I had spent the whole morning writing WordPerfect
macros instead of writing articles for him. Im not sure you are in the
right profession, he said. I was a good journalist, but I could see his
point. I sure was spending a lot of time writing WordPerfect macros.
I found a little community college where nobody knew me.
Sneaking about with my head down and collar up around my face,
I signed up for a programming course. The first day, I sat in the
back. Once I was sure there was no one there who knew me, I
decided I could risk falling flat on my face around a bunch of total
strangers, so I stuck with it. Fortunately, the teacher was using
Turbo Pascal, so I got off on the right foot. There were some other
programming projects I had been working on for some time. I
translated them into Pascal and had them all up and running in
no time. Id found my computer language. After getting through
the programming course at the community college, I signed up
for a second degree from Evergreen State College, this time in
computer programming. I had a great time getting a computer
science degree. I never wanted to graduate; I was having too much
fun hacking all night in the computer lab.
After graduating, I sent rsums everywhere. On a lark, I even
responded to Borland via my CompuServe account. They had
posted an ad for jobs on a CompuServe forum, and I thought,
What the heck? It cant hurt to apply. Then I promptly forgot
the whole thing, figuring I could never get to work at my dream
company. Low and behold, about a month later, David Wilhelm,
who is still on Borland Delphi R&D, called me up and asked if
I was really interested in a job. Yes, I said, not trusting myself
32 November 2001 Delphi Informant Magazine

to say more. He said, Okay, well send you a plane ticket and set
you up in a hotel here during the interview. Somehow, I managed
not to drop the phone on the floor. About three weeks later, I was
working in Borland tech support.
DI: Youve been involved with Delphi since the very beginning I
suspect even during its earliest development. Please share with readers
some of the highlights of Delphis genesis.
Calvert: No one who was present at the inception can ever forget
the role Anders Hejlsberg played in Delphis development. A lot of
the crunch in the development of Delphi came about because of the
struggle to add database support to the product. Most sources agree
that Zack Urlocker, the product manager at the time, was the guy who
really sold the company and developers on the need to add database
support to this new visual version of Turbo Pascal. Zack saw the success
of other products, such as PowerBuilder, and he was sure Borland
needed a similar product. The only problem was how to create it. The
Turbo Pascal team knew nothing about databases.
Initially, a programmer who wasnt on the Delphi team was assigned the
task. Im not sure how, but I had a chance to sit in on some of the
R&D meetings when the Delphi team conversed with that programmer.
Anyone who knows the key figures on the Delphi team can easily
believe most of them are not inclined to extravagant displays of temper.
Nonetheless, I can remember Anders and other members of the team
shouting during these discussions. I dont really want to pass judgment,
but the months were rolling by, and the database tools werent falling
into place.
The situation reached a crisis, when finally Anders went into
hiding with the database source code. He was gone for a month, perhaps
two. It was during the summer, and we all sat around chewing our nails,
wondering if the product would ever have database support. We also
hoped that it wouldnt have the dreaded word builder in its name.
Meanwhile, Zack was trying to promote the product. Somehow, he
reached down into the lower and most disreputable rungs of David
Intersimones developer-relations group and got me involved in the
process. We were going to go to PC Magazine and show them that
Delphi was the product for database development. This was back in the
days when PC Magazine was still important to developers.

File | New
About a week before we were to leave, Anders strolled back onto the
scene with the fruits of his labor. Someone gave me the first Delphi
build that included the database tools and told me to get the demo
ready for PC Magazine. There was only one problem: Absolutely
nothing worked! That first build with Anders database tools was
one of those builds where trying to do much more than open the
File menu was courting disaster.
I labored deep into the night, certain I had seen, at last, the dark
side of the great Anders Hejlsberg. He, too, could not solve the
database quandary, and who knew what was to be the fate of our
favorite product?
Then, of course, came build two of Anders code. I was one of the
first people to get that build, and so I was one of the first people
to understand the glory that is Delphi. In the end, I probably had
less than 48 hours to put the whole demo together. Zack and I
took it to PC Magazine, where I stared at the wonders of big-time
journalism while our fearless product manager did all the talking.
In short, I sweated, and he talked. In the long run, we prevailed;
PC Magazine gave Delphi a thumbs up. The real hero, of course,
was Anders.

DI: Lets examine the flip side of that last question. If you could add
any feature to the next version of Delphi, what would it be?
Calvert: That ones easy: better support for graphics. Where are
the good DirectX, multimedia, and OpenGL wrappers? Objectoriented databases also sound like a natural for Delphi, as would
something like Javadoc. Finally, I was a big supporter of the shelved
attempts to get Delphi to run on top of the Java virtual machine.
On a related note, Ive spent the last several years tearing my hair
out watching Macromedia develop tools for Web developers. Every
time I see a bit of Flash on a Web site, or whenever I hear people
talking about Dreamweaver, Fireworks, or even HomeSite (which
was built in Delphi), I cant help but believe all that money belongs
in the Borland coffers. No one at Borland wrote a line of code for
those products, but they were such natural Borland products that
I still feel that having the name Macromedia on them was some
kind of mistake.
Its not too late, Borland! As I write, Flash is taking the world by
storm. Borland could make a better version of Flash with one hand
tied behind its back.

DI: There have been many new features and enhancements in each
succeeding version of Delphi, beginning with 32-bit support in
Delphi 2. Besides this obvious one, what do you feel was the most
important feature added to Delphi since the original version?

DI: As well as being an expert in Delphi and Object Pascal, you


have equally impressive expertise in C/C++. Tell us about your
experiences with various programming languages throughout the
years.

Calvert: I also assume Kylix is off limits as the answer to this


question. Leaving the Linux world aside, I think the two most
important features added to Delphi are interfaces and COM support. Im a strong believer in the kind of abstraction that can be
provided by interfaces. Who cares how an object is implemented?
Just give us the interface to it and let the programmer or third
parties plug in various implementations. COM takes this promise
one step further by placing the objects in discrete packages that
can exist either on the local machine or on remote machines. The
addition of type libraries helped make this come true. When I talk
about COM support, I am very much including the ability to take a
type library and convert it into a Delphi source file.

Calvert: The great languages are Object Pascal, Java, and C++.
These are the only real programming languages that are in wide use
today. My favorite is Object Pascal, but if Java could overcome its
performance problems, it would provide very stiff competition.

Someone defines the interface for a COM object, and then we can
go out and find an implementation of the interface, whether its
on the local machine, or on a remote machine. Its paradise for
programmers who are looking for scalability and a way to reuse
code. Find an interface and then just plug in the implementation.
Its a beautiful way to build software.
The wrappers around COM in Delphi were brilliantly designed
and surpassed anything that Microsoft had on the table before
.NET. The only fly in the ointment was COM itself, which I dont
believe really delivers on the promise of a robust and easily reusable,
distributed architecture. A lot of people like to put down Remote
Method Invocation (RMI), perhaps because its free. But I believe
its much more robust than COM, and much easier to use at
least as it stands now. The security model in COM is a particularly
nasty rats nest. Lets hope SOAP will turn out to be a better
solution to this kind of problem.
Another important development in the history of Delphi has been
the emergence of open-source communities such as Project JEDI
and the Indy Project. They have made huge contributions to the
product.
33 November 2001 Delphi Informant Magazine

C++ is an amazing beast. No one, however, should be allowed to


use it until they have been programming for at least five years.
C++ has everything a programmer needs, and I love the language.
The problem with C++, however, is not what was included in the
language, but what they didnt have the sense to leave out.
The big development in recent years has been the emergence of
little languages. Perl and Python are extraordinary tools and a huge
boon to the programming community. I love working in both of
those languages, although I prefer Python.
JavaScript is a very useful language as well. You can write real
programs in JavaScript, if you architect them carefully. Ive seen
some developers just cut and paste little chunks of JavaScript into
their Web pages without ever quite seeming to grasp that there is a
real language between those funny looking curly braces. It doesnt
have the power of Perl or Python, but JavaScript is another great
lever for moving large objects.
There must be other great languages out there, but I dont have time
to explore them all. My heart, however, belongs to Object Pascal.
Like Java, it seems to have found a way to make complex tasks
relatively simple. Perhaps it would be better to say that it allows us
to get real work done without unnecessarily complicating the task.
I like simplicity and elegance, and I think Nicholas Wirth has the
patent on those concepts when it comes to programming languages.
In the long run, I think he, and not Bjarne Stroustrup, will prove
to be the language architect who came closest to the ideal structure
for a programming language.

File | New
DI: Weve seen some encouraging developments at Borland profitability, for example. Whats your perspective on the companys health?

to enter into the discussions in good faith. If one side has resentment,
or if one side has a secret agenda, they wont make any progress.

Calvert: I think the company is doing much better than it was a


few years ago. Borland is a very technical company, and finding
managers who can understand what a compiler is all about has
been difficult. I remember one vice president in charge of R&D
who finally had to turn the mouse to drive his PowerPoint presentation over to his administrative assistant. It appeared he just
couldnt figure out how to work it. But the company seems much
more on track now. Managers seem to understand, at last, that
tools like Delphi and JBuilder are the basis on which the company
is built. Borland is about technology, and not just any kind of
technology, but arcane technology. Its future is in building great
software for the tools market.

Fortunately, a lot of the nonsense appears to be in the past. Management appears to understand, finally, that Delphi and JBuilder
are the heart of the company. If progress is going to be made, it
can only happen if R&D sits down with management and works
out solutions everyone can believe in.

I think Borland has a place in what is called the enterprise tools


market, but getting there has been and will continue to be
a slow and difficult process. Each company has its own character, just as each person has his or her own personality. Borland
is a compiler and tools company, and it can only move into
new markets when its developers are inspired by, and thoroughly
understand, the tools they want to create. If you have a writer who
produces great science fiction, you cant suddenly sit down with
her and say, Hey, the markets moved. Its all about historical
romances now. Get rid of the lasers and the rockets and start with
the crinoline and broken hearts. That doesnt work.
Management and R&D have to sit down together, listen to one
another, and chart a plausible course into the future. Both parties need

34 November 2001 Delphi Informant Magazine

In saying this, I dont mean to rule out the possibility that Borland
could make successful commercial software. The Borland name
still carries a lot of weight, and a well-made consumer product
from Borland could be a serious force in the market.

This is the first half of a 5000-word interview. To read the entire


interview, please visit http://www.DelphiZine.com/opinion.
Alan C. Moore, Ph.D.

Alan Moore is Professor of Music at Kentucky State University, where he teaches music
theory and humanities. He was named Distinguished Professor for 2001-2002. He
has developed education-related applications with the Borland languages for more
than 15 years. He is 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.

Symposium
Directions / Commentary

Hatred Is the Enemy


Unless they involve Borland or Microsoft, current events have never intruded
into these pages. Ive made a concerted effort to keep politics and religion
out of this magazine including the quasi-religious feelings some harbor
about software (of all things). Even when it comes to Borland and Microsoft,
I prefer to keep my views to myself most of the time. This is a technical
journal, and I think it important to keep the noise down so as not to
interfere with this magazines mission. And when I do speak out its usually to
try to moderate the tone. Ive criticized Borland for Microsoft bashing (in the
September 1997 issue), and just last month criticized Microsoft for its
suppression of Borland-related speakers and topics at its trade shows.
Of course, all of this pales into trivial insignificance when compared with the
monstrous, maniacal, and dedicated hatred we saw unleashed on September
11th. If you find this editorial out of place, I apologize. In fact, I agree with
you; it is out of place. But given the circumstances, I simply cannot write
an editorial that ignores the recent hellish events. Nor can I remain silent.
Things are different now.
Things are different now, and I feel compelled to speak of hate. Certainly
we must track down and eliminate those responsible for these crimes. But
this requires a new kind of war, and despite what many say, were perfectly
capable of winning it. A country that pulled off the Manhattan Project and
the Apollo missions is certainly capable of taking out these hateful cells of
terrorists, wherever they are without carpet bombing entire countries. And
to those who take the nuke Afghanistan approach, I would say there is
essentially no Afghanistan to bomb, and refer you to http://www.salon.com/
news/feature/2001/09/14/afghanistan/index.html. The Soviets and the U.S.backed Mujahadeen have already dismantled what was once a country. This
is no time for Jingoism; we must conduct this new war carefully, and cut out
just the cancer not the healthy tissue.
Most of the United States has united, first in horror, then in grief, and
increasingly in resolve to eradicate terrorism. I say most of the United States
because there are always the radicals. Theres nothing like an extreme event

35 November 2001 Delphi Informant Magazine

to flush out the kooks. If George W. and Hillary can get along, if Gephardt
and Armey can find unity, then those who still want to play the blame game
must be of the lunatic fringe.
The United States has its own purveyors of hate. Theres a memorial
where once a building stood in Oklahoma, because of the hatred of one
Timothy McVeigh. Jerry Falwell and Pat Robertson are home-grown haters as
well (http://www.nytimes.com/2001/09/18/national/18FALW.html). Theyre
trying to distance themselves from their statements now, but they spoke
clearly enough when they blamed the September 11th attacks on gays, the
ACLU, abortion, etc. Creator of TVs The Awful Truth, Michael Moore is
repelling even his fans with his particular stripe of hate. He hates American
business and the Bush Administration so much that he cant see straight
(http://www.michaelmoore.com/2001_0917.html).
People who claim to know the truth make me very nervous. Pravda means
truth, and Hitler spoke the absolute truth as he saw it. Those on the far
right and the far left are both far too certain about the way things should
be. And they hate too much.
If we must hate something, let us hate hatred itself. And lets also hate its
bedfellows of ignorance, intolerance, and close-mindedness. Theres a better
world on the other side of this: a world with less hate.

Thanks for reading.

Jerry Coffey is Editor-in-Chief of Delphi Informant Magazine. He can be contacted at


jcoffey@DelphiZine.com.

Você também pode gostar