0% found this document useful (0 votes)
126 views48 pages

Delphi Informant Magazine (1995-2001)

This document is the October 1996 issue of the Delphi Informant newsletter. It contains articles about using OLE Automation to control one application from another, building DLLs in Delphi, creating customizable expert systems, working with object properties and relationships, and preparing for Delphi client/server development. It also announces new products like BoundsChecker 4.0 for error detection and an integration between Mercury Interactive's testing tools and Delphi.

Uploaded by

reader-647470
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
126 views48 pages

Delphi Informant Magazine (1995-2001)

This document is the October 1996 issue of the Delphi Informant newsletter. It contains articles about using OLE Automation to control one application from another, building DLLs in Delphi, creating customizable expert systems, working with object properties and relationships, and preparing for Delphi client/server development. It also announces new products like BoundsChecker 4.0 for error detection and an integration between Mercury Interactive's testing tools and Delphi.

Uploaded by

reader-647470
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 48

October 1996, Volume 2, Number 10

OLE Automation
Controlling One Application with Another

Cover Art By: Tom McKeith

ON THE COVER
7 OLE Automation — Cary Jensen, Ph.D.
35 Delphi at Work — Douglas Horn
Using Delphi and Microsoft Word for Windows, Dr Jensen explains how to
You may not have noticed it; its very ubiquity makes it invisible.
use OLE Automation to have one application control another. He provides
Nevertheless, the humble Sender parameter can make many Object
all the information needed to turn any Delphi 2 application into an
Pascal programming tasks easier. Mr Horn explains how to put the
automation server.
neglected parameter to work.
FEATURES 39 Inside OP — Dan Miser
12 Informant Spotlight — Ray Lischner Using Delphi’s data-aware components as a model, Mr Miser has created
Mr Lischner explains the nuances of building DLLs in Delphi 1 TINISource, an INI-aware component that takes the drudgery out of inter-
and 2. The discussion includes a DLL refresher course, exporting a acting with Windows .INI files.
class from a DLL, importing a class into an application, and using
objects that cross module boundaries. REVIEWS
42 With Class — Product Review by James Callan
19 Visual Programming — Keith Wood
Mr Wood helps you become a pro at creating experts; step-by-
step, he teaches you how to use the TExpert class to build a 46 Delphi 2 Unleashed — Book Review by Cary Jensen, Ph.D.
customizable expert for creating screen savers — then there’s the 46 The New Delphi 2 Programming EXplorer
screen saver itself. It’s two articles in one. Book Review by Tim Feldman

26 OP Tech — Robert Vivrette DEPARTMENTS


Need to know an object’s container? Or which object is
responsible for creating and destroying another? Mr Vivrette 2 Delphi Tools
demonstrates how to find out with a sample Delphi 2 application 5 Newsline
using Object Pascal’s Controls array and Components array. 48 File | New by Richard Wagner
29 Delphi C/S — Bill Todd
Are you ready to commit? Before jumping into Delphi/InterBase
client/server computing, you must understand the issues affecting
concurrent access to data. Mr Todd explains the InterBase
difference: versioning.

1 October 1996 Delphi Informant


Delphi NuMega Technologies Supports Delphi 2 with BoundsChecker 4.0
NuMega Technologies,
T O O L S Inc., of Nashua, NH has
released BoundsChecker
New Products 4.0, with enhancements to
and Solutions BoundsChecker’s error
detection solutions.
Version 4.0 supports
Delphi 2 and ApiGen, a
utility for the validation of
user-extensible API calls.
The newest version of
BoundsChecker supports
ActiveX, and Smart
Debugging (background
error detection).
BoundsChecker is the
only error detection tool OLE interface leaks and Price: BoundsChecker Standard
New Delphi Book
that can evaluate Internet invalid parameters, and Edition, US$299.
Delphi Programming for APIs, interfaces, and con- return codes for over 70 Contact: NuMega Technologies, Inc.,
Dummies 2nd Edition
Neil J. Rubenking trols, including URLMON OLE interfaces. 9 Townsend West, Nashua, NH 03063
IDG Books and HLINK, while validat- BoundsChecker can also Phone: (800) 468-6342 or
ing hundreds of DLL func- identify more than 85 (603) 889-2386
tions. This includes the errors in eight categories by Fax: (603) 889-1135 or
Internet’s WinSock API. type, stack trace, and exact (603) 889-2386
BoundsChecker’s new location in source code. E-Mail: Internet: [email protected]
OLECheck features detect Web Site: http://www.numega.com

Mercury Interactive and Borland Announce Client/Server Testing Solution


Mercury Interactive Corp. Delphi objects; and stressing a Web server capacity
of Sunnyvale, CA, and Verification Testing, ensuring by recreating HTTP traffic
Borland have announced a Delphi testers know when the through multi-tasking Web
ISBN: 1-56884-621-5
Price: US$24.99 (418 pages) new client/server testing application is performing as virtual users.
Phone: (800) 762-2974
solution that integrates expected. Delphi extensions for
WinRunner 4.0 and It also has SMARTest (Script WinRunner and LoadRunner
LoadRunner 4.0 testing Mapping for Adaptable and will be available this month
tools with Delphi Reusable Test technology) to for Windows 95 and
Client/Server Suite 2. handle application changes Windows NT. Mercury
The new Delphi extensions automatically. SMARTest Interactive will offer the
built into WinRunner 4.0, a maintains Delphi object spe- Delphi extensions to existing
GUI testing tool, and cific data independently of customers free of charge.
LoadRunner 4.0, a client/serv- individual scripts. Its architec-
er and Web Load Testing tool, ture separates variable object Price: LoadRunner 4.0 Standard edi-
will integrate a Visual Testing data from scripts, ensuring tion (Windows 3.1), US$14,990;
environment that organizes the same scripts can be reused Professional edition (Windows 3.1,
automated testing facilities even as the application Windows 95, and NT), starts at
and makes them available to changes during development. US$40,000; Professional edition
Delphi testers. The Delphi The integration of Delphi (UNIX), starts at US$50,000.
extensions also include auto- and LoadRunner 4.0 includes: WinRunner 4.0 Standard edition
mated record, replay, and veri- Client Load Testing, simulat- (Windows 3.1),US$2,850; Professional
fication for Delphi GUI ing multiple Delphi client edition (Windows 3.1, Windows 95,
object testing. users across the network; and NT), US$3,995.
The integration of Delphi Server Load Testing, providing Contact: Mercury Interactive, 470
and WinRunner 4.0 includes: a way to stress a server’s capac- Potrero Ave., Sunnyvale, CA 94086
Visual Testing, enabling users ity by simulating the activity Phone: (800) 759-3302 or
to visually record and replay generated by multiple Delphi (408) 523-9900
test scenarios using a point- applications on a single PC; Fax: (408) 523-9911
and-click method of selecting and Web Load Testing, for Web Site: http://www.merc-int.com

2 October 1996 Delphi Informant


Delphi SkyLine Tools Introduces ImageLib@theEdge for Delphi
In North Hollywood, CA,
T O O L S SkyLine Tools announced
the debut of ImageLib-
New Products @theEdge. It sells as a stand-
and Solutions alone component for manip-
ulating images in Delphi, or
as an add-on to current
ImageLib packages. These
include ImageLib Portfolio
3.1, ImageLib ’95, and
ImageLib Combo.
theEdge produces imaging
effects including mosaic,
hole punch, page curl,
whirlpool, and wave.
Additionally, theEdge fea-
tures color and reduction vide effects for presenta- Contact: SkyLine Tools,
New Delphi Book
properties; gamma correc- tions. 11956 Riverside Dr. Suite 107,
teach yourself... Delphi 2
tion; contrast, brightening, A free trial version is avail- North Hollywood, CA 91607
Devra Hall
MIS Press sharpening features; and able from Skyline’s Web site. Phone: (818) 766-4561
can scale, crop, and rotate Fax: (818) 766-9027
images by degree. It also Price: theEdge stand-alone version E-Mail: Internet: 72130.353@-
features five new transitions US$129; ImageLibCombo@theEdge compuserve.com
(wipes and fades) that pro- US$299. Web Site: http://www.imageLib.com

Eschalon Development Releases Power Libraries for Delphi 2


Eschalon Development ing, timing, and debugging. the custom structured file
Inc. of Coquitlam, BC, Eschalon Power Libraries class for unique file formats.
Canada has released feature streams for memory
Eschalon Power Libraries. mapped files, classes for con- Price: US$149.95
ISBN: 1-55828-457-5
Price: US$29.95
Developed specifically for tainers (integer, string, Contact: Eschalon Development Inc.,
(322 pages, Disk) Delphi 2, and compatible indexed, variables, etc.), sec- 24-2979 Panorama Drive, Coquitlam,
Phone: (800) 488-5233
with Windows 95 and tion and log files, and reg- BC, Canada V3E 2W8
Windows NT, Eschalon istry access. They also have Phone: (604) 945-3198
Power Libraries provides asynchronous/synchronous Fax: (604) 945-7602
over 450 functions for execution of programs, as E-Mail: Internet: [email protected]
Delphi 2 applications. well as a “sendkeys” feature, Web Site: http://www.eschalon.com
Included in the product are and allow developers to use
functions for string manipu-
lation, string parsing and Open Window Releases OWShare
tokens, file and disk access, Open Window of ables appropriate for a pro-
date and time, hashing/- Colorado Springs, CO has ject, edit the supplied forms
checksums/CRC, MD5 mes- released OWShare, a source with Delphi’s built-in visual
sage digest, sorting, search- code package that provides editors, and then “drop in”
screens and support code to his or her program.
turn program functionality OWShare coincides with a
into a shareware product. revision of the requirements
OWShare is available in for shareware programs
Delphi and Visual Basic ver- released by members of the
sions that support ASP-com- Association of Shareware
pliant time and usage limiting, Professionals.
branding, escalating reminder A demonstration program
screens, site licenses, evalua- is available on the WINU-
tion period extensions, and TIL CompuServe Forum
other shareware features. (OWSHR.ZIP), and
With OWShare, a developer
“Open Window Releases OWShare”
can initialize the global vari- continued on page 4

3 October 1996 Delphi Informant


Delphi Objective Software Supports Delphi 2 with Product Updates
Objective Software
T O O L S Technology of Canberra,
ACT Australia has
New Products announced Delphi 2
and Solutions updates for their ABC for
Delphi and TRANSFORM
products. Both products
are now available for
Delphi 1 and 2.
ABC for Delphi is a library
of 30 visual components
including exception handling,
user interface, and data com-
ponents.
TRANSFORM is a Delphi
component that allows gen- Price: ABC, US$89 or US$179 (with Contact: Outside the US: Objective
CD Cleaner eration of aggregate compo- source). TRANSFORM, US$125. Software Technology Pty Ltd., PO Box E138
303 Products, Inc. of Palo nents from Delphi forms. It’s Contact: In the US: ZAC Catalog, Kingston, ACT 2604 Australia
Cedro, CA is shipping 303 suited for prototyping and 1090 Kapp Drive, Clearwater, FL 34625 Phone: (+61) 6-273-2100
Sonic Blast. This anti-static
agent repels dust, fingerprints,
iterative development of Phone: (800) 463-3574 or Fax: (+61) 6-273-2190
and smudging on CDs. Discs complex components. Patch (813) 298-1181 E-mail: Internet: 100035.2441@-
treated with this biodegrad- updates for current users are Fax: (813) 461-5808 compuserve.com
able formula are protected
for up to one year.
available on the Informant E-mail: Internet: sales@zac- Web Site: http://ourworld.com-
Each Kit contains a spray CompuServe forum (GO catalog.com puserve.com/homepages/objsoft or
bottle and an applicator ICGFORUM). Web site: http://www.zaccatalog.com http://www.obsof.com
pad, and treats
approximately 300 CDs.

Price: US$12.95
Sax Ships Basic
Contact: 303 Products, Inc., Engine Pro
PO Box 966,
Palo Cedro, CA 96073 Sax Software, of Eugene,
Phone: (916) 549-5617 OR is now shipping the lat-
Fax: (916) 549-5577
Web Site: http://www.303-products.com est version of Sax Basic
Engine Pro 3.0. An ActiveX
control, Sax Basic Engine
Pro integrates with Delphi,
Visual C++, and Visual Basic
applications, and supports
events, classes, and multiple
modules.
Sax Basic Engine Pro
offers syntax highlighting, Price: US$495; requires no royalties Phone: (800) 645-3729 or
and includes an integrated or run-time fees, and ships with a 30- (541) 344-2235
editor and debugger that day money-back guarantee. Fax: (541) 344-2459
can call a stack, set a vari- Contact: Sax Software, 950 Patterson E-Mail: Internet: [email protected]
able watch, or single-step St., Eugene, OR 97401 Web Site: http://www.saxsoft.com
through code. It also allows
users to manipulate events Open Window Releases OWShare (cont.)
by treating everything as an America Online in the Price: US$59.95
object. Whenever an object Development Forum Contact: Open Window, PO Box 49746,
receives an action, an event (OWSHR_10.ZIP). Colorado Springs, CO 80949-9746
is fired. In addition to the online Phone: (800) 531-0403 or
Sax Basic Engine Pro also Help, support is available via (719) 531-0403
works with multiple mod- CompuServe, America Fax: (719) 531-0403
ules in macro code. Users Online, Internet, or US E-Mail: Internet: 75236.3243-
can create complex macros Mail. OWShare requires @compuserve.com
and customize common dia- Windows 3.1 or Windows CIS Forum: GO WINUTIL
log boxes. 95, and Delphi. Web Site: http://www.openwindow.com
4 October 1996 Delphi Informant
News EDS Chooses Delphi to Develop Case Tracking System
Scotts Valley, CA —
Electronic Data Systems
Integration Services contract,
selected EDS to help convert
system is scheduled to begin
full deployment in 1997.
L I N E Corp. (EDS) has selected a mainframe case tracking For more information visit
Delphi to develop a system to a client/server EDS’ Web site at
October 1996 client/server-based Case architecture, and develop a http://www.eds.com.
Activity Tracking System new prototype system for Borland Ships Trial,
(CATS) for the National legal case tracking.
Labor Relations Board EDS used Delphi and the Learning, and
(NLRB). The NLRB will use Borland RAD Pack to create Low-Price Editions
CATS to manage over 40,000 preliminary CATS screens of Delphi
new legal cases annually, as currently being demonstrated Scotts Valley, CA — Borland
well as to report statistics. to NLRB staffers around the has released three new ver-
The NLRB, under the country for design review sions of Delphi, including a
Defense Enterprise and user input. The CATS trial edition that users can
download free from Borland
Borland Presents Golden Gate Internet & Online; a “Learn to Program
Intranet Strategy with Delphi” package for stu-
Anaheim, CA — In a With its Golden Gate dents and beginning pro-
keynote presentation to strategy, Borland hopes to grammers; and an easy-to-use,
ROI Systems Chooses more than 1,500 developers assist users building low-price version of Delphi 2
Delphi at the 7th annual Borland Internet-based applications for developers to evaluate.
ROI Systems, Inc., an Developers Conference, by integrating existing
Enterprise Resource Planning The “Learn to Program with
(ERP) software supplier, Borland presented its overall investments in client/server Delphi” package includes the
has selected Delphi to create Internet strategy and architectures with the
the interface for its next 16-bit Delphi 1 software,
generation of integrated demonstrated new technolo- emerging Internet technolo- Teach Yourself Delphi in 21
business systems. gies, including products gies. The strategy includes
ROI Systems is changing its Days, Computer-Based
GUI presentation, instead of from the planned acquisition combining object-oriented Training (an online curricu-
updating their ASCII screens of Open Environment Corp. development tools, Delphi,
with screen scraper products lum), and new sample appli-
that give a GUI appearance, Referred to as the Golden Latte, IntraBuilder, Borland cations with source code.
but still require multiple steps Gate initiative, Borland C++, as well as Open
to move from one function The new special version of
to another to complete a plans to merge both phases Environment’s Entera tech- Delphi 2 has been re-designed
transaction. ROI’s new client of its Internet plans: release nology and related Internet
interface will in some cases with features making it more
combine 15 separate func- Internet-enabled versions of products. accessible to new developers.
tions on one screen. its existing products; and For more information, con-
For more information, call: In addition, Borland has
(800) 544-7849, e-mail: then add Intranet solutions tact Borland at (408) 431- included new functionality to
[email protected], or visit for the workgroup market 1064 or visit Borland Online
ROI’s Web site at show how Delphi can be an
http://www.roisysinc.com. segment. at http://www.borland.com. add-on tool for Visual Basic
and C++ developers. This ver-
VCL Contest Winners Announced sion of Delphi 2 includes
Scotts Valley, CA — SAMS subscription to Delphi Teach Yourself Delphi 2 in 21
Publishing and Borland Informant. Days, an online Visual Basic-
Press have announced the VCL entries were judged to-Delphi command refer-
winners of Delphi by representatives from ence, and an online Delphi
Informant’s VCL Contest. Borland International, reference for C++ developers.
Due to the extended dead- SAMS Publishing, and The Delphi trial version is
line and delay in posting Informant Communications available from Borland
results, SAMS and Borland Group, Inc. for originality Online at http://www.bor-
are awarding an extra book and functionality. Entries land.com/delphi20/ or on
to each of the winners. were received from around CD (shipping and handling
Database Developer’s Guide the world in each of the five charges additional).
with Delphi 2 will be award- categories. The “Learn to Program with
ed in addition to Delphi The winner in the Delphi” package and the low-
Developer’s Guide, Second Internet/Communications price version of Delphi 2 are
Edition as part of the VCL category was Jower Garcia available now. “Learn to
contest prize package. Top Toppin of Venezuela. Program with Delphi” is
winners will also receive US$49.95; the modified of
“VCL Contest Winners Announced”
US$100 and a six-month continued on page 6 Delphi 2 is US$99.95.

5 October 1996 Delphi Informant


News Borland Announces Availability of its Latest Version of InterBase
Scotts Valley, CA — Borland
International Inc. has
mized for Microsoft NT
4.0, and tuned for use with
95 (a multi-user server for up
to four concurrent users),
L I N E announced version 4.2 of Borland’s forthcoming Java US$599.95; and InterBase
InterBase. This version of JDBC driver for InterBase Server for Windows NT (a
October 1996 InterBase has improved per- and InterClient. departmental-to-enterprise
formance and resource usage, The InterBase 4.2 family of server including five user
due to an enhanced version of products includes: Local licenses), US$850.
the SuperServer Architecture. InterBase (a single user version For more information visit
InterBase 4.2 features of the server), US$249.95; Borland Online at
ODBC 2.5 drivers for InterBase Server for Windows http://www.borland.com.
Windows 95 and Windows
NT, and thread-safe 32-bit
client libraries. It is also VCL Contest Winners Announced (cont.)
compatible across the Toppin submitted Mail TPanel and acts as a dock-
Windows and UNIX envi- eXtension, a visual compo- able toolbar with customiz-
ronments maintaining one nent adding e-mail features able behavior. The Dockbar
consistent API, database to Delphi applications, package contains both 16-
format, and SQL language. supporting Lotus Notes, and 32-bit versions of the
This consistency across cc:Mail, and Microsoft control.
platforms enables develop- Mail and Exchange. Kenneth Clubb of
Borland Announces
Resignation of CFO ers to write a server applica- Lance Leverich of Kansas Maryland submitted the
Borland has announced tion once, and deploy it to submitted the winning winning entry in the Other
the resignation of David entry in the Database cate- category, with
Mullin, vice-president and
either Windows or UNIX
chief financial officer. Mullin operating systems. gory. His TMailLabel com- TConnections, a special
will to continue his duties ponent is used to define TPanel that shows master-
until mid-September.
By adhering to industry
Borland is currently seeking standards such as SQL 92, and print mailing labels, detail relationships by
a replacement for Mullin. allowing developers to set drawing lines between
and offering 32-bit ODBC
drivers for Windows 95 and the LabelDefinition to pro- TTables and TDataSources.
Windows NT, users can inte- vide a pre-defined label lay- Clubb was also the Grand
grate third-party software out. Using the various link Prize Winner for his Master-
products. In addition, fields, developers can set Detail Explorer, which pro-
Borland partnered with the interface to the data- vides a view of master-detail
Visigenic Software earlier this base(s) for retrieval of mail- information for program-
year to develop the InterBase ing address information. mers and users. Each level of
ODBC drivers. Jan Dekkers of SkyLine the tree expands into
The InterBase SuperServer Tools in CA submitted detailed information when
Architecture offers thread-safe ImageLib, the winning selected, displaying up to
client and multi-threaded entry in the Multimedia eight levels of detail.
server. Its unique versioning category. ImageLib 3.1, In addition to the prizes for
engine ensures data availabili- from Skyline Tools, is a winning the Other category,
ty for both transaction pro- software development tool Clubb will receive a
cessing and decision support allowing programmers to US$1,000, a copy of Delphi
style applications. implement BMP, CMS, Informant Works ’96 and
InterBase 4.2 also includes GIF, ICO, JPG, PCX, Delphi Client/Server, and an
performance enhancements PNG, SCM, THB, TIF, and additional copy of each of
for large multi-user systems; WMF images into an appli- the Delphi books from
32-bit GUI tools for interac- cation. In addition, AVI, Borland Press for winning
tive SQL, server, and license MOV, MID, WAV, and the Grand Prize.
management; 32-bit ODBC RMI formats can be imple- A list of winners is available
drivers for Windows 95 and mented to or from a file or on SAMS’ Web site
Windows NT; license man- database BLOB field. (http://www.mcp.com/sams),
ager expert to ease user The winning entry in the Borland’s Web site
license management; identi- Interface category was (http://www.borland.com),
cal code base and feature set received from Peter Andrew SAMS’ CompuServe forum
across Windows 95 and Van Lonkhoyzen of South (GO SAMS), and Borland’s
Windows NT platforms. It Africa. Van Lonkhoyzen’s CompuServe forum (GO
is also certified and opti- TDocPanel is derived from BORLAND).

6 October 1996 Delphi Informant


On the Cover
Delphi 2 / Object Pascal / OLE

By Cary Jensen, Ph.D.

OLE Automation
Controlling One Application with Another

LE automation is a convention by which one application can control


O another. The controlling application is referred to as the automation
client, and the one being controlled is referred to as the automation server.
The 32-bit applications you build with Delphi 2 can be automation clients
or servers, or both.
In general, there are two types of automation OLE automation servers can make proper-
servers: in-process and local. In-process servers ties and methods available to the automa-
are DLL-based, and local servers are .EXE- tion client. Provided the automation server
based. Again, you can easily create both of developer designs the server appropriately,
these with Delphi 2. the automation client can exert detailed
control over the server.

Using an Automation Server


The four steps to using an automation
server are:
1) Declare a variant. A variant is a loosely-
typed variable that you employ to access
and control the automation server.
2) Open the automation server using the
CreateOLEObject function.
3) Set the properties and/or call the methods
of the server.
4) Release the server when you are done.

Declaring a Variant for Use with a Server


Delphi 2 introduces a new data type called a
variant, a variable whose type does not have
to be known at compile time. In most cases,
variants are used to control automation
servers. The following is an example of how
you can declare a variable named MyServer to
be of the type variant:

var
MyServer: Variant;

Opening an Automation Server


You open an automation server by calling
CreateOLEObject. This function is declared
in the OLEAuto unit. Therefore, you must

7 October 1996 Delphi Informant


On the Cover
include the OLEAuto unit in the uses clause of the unit from
which you call CreateOLEObject.

CreateOLEObject requires a single string parameter containing


the name of the automation server. This is the name the server
registers in the HKEY_CLASSES_ROOT key of the Registry,
and it associates the server with a CLSID (class ID), a value
assigned to the server by Windows when the server is registered.

All automation servers have a unique name, as well as a


unique CLSID. The server name is case-insensitive. Also,
Delphi does not require you to reference the CLSID when
using an automation server.

Figure 1 shows Registry Editor, a Windows 95 application


that allows you to view and edit the Registry. Notice that Figure 2: The SPELL project Memo object.
the server name for WINWORD.EXE is Word.Basic.
Consequently, to open Microsoft Word for Windows Releasing the Server
(Word) as a local automation server with the variable There are two ways to release an automation server. One way
MyServer, you would execute the following statement: is to permit the variant to go out of scope. For example, if
you declare a variant local to a procedure, and then exit the
MyServer := CreateOLEObject('word.basic'); procedure, the server opened with the variant will close
(assuming that some other process, such as an application, is
not also using the server).

The second way to close an automation server is to assign the


constant UnAssigned to the variant. To use the same example,
for instance, to release Word as a server, you would make the
following assignment:

MyServer := UnAssigned;

Using an Automation Server Example


The project SPELL.DPR (see Figure 2) displays a simple
Memo object. When you click the button labeled Check
Spelling, the project opens Word as a local automation
Figure 1: Viewing the Registry with Registry Editor. server, copies the memo to the Windows Clipboard, and
then uses a series of method calls to instruct Word to spell
check the document.
Controlling an Automation Server
After you have opened an automation server, you can con- When the spell check is complete, additional server
trol it by setting values to its properties, or calling its method calls are used to select the text and copy it back
methods. Delphi does not provide you with the tools nec- into the Clipboard, from which it is pasted back into the
essary to discover a particular server’s properties and meth- Memo object. Figure 3 is associated with the button’s
ods. (You could write such a tool using API calls, but that OnClick event handler.
is outside the scope of this discussion.)
SPELL.DPR is a simple example of using a local server.
Normally, you will refer to the documentation provided by However, SPELL.DPR is provided for demonstration purpos-
the server’s developer to learn what properties and meth- es only. If you attempt to use this example and Word is
ods are available, and how to use them. already running, an exception will be raised when the code
attempts to close the server.
Word publishes a method for creating a new document.
This method, FileNew, takes no parameters. Therefore, If you need to add spell checking to your Delphi applica-
once FileNew is opened, you can instruct Word to create a tions, you are much better off using one of the third-party
new document by including the following statement in spell checkers available, instead of a large and sometimes ill-
your code: behaved server like Word. Delphi 2, for example, includes
the VCSpeller OCX that you can use to spell check text
MyServer.FileNew; without the problems associated with using Word.

8 October 1996 Delphi Informant


On the Cover

procedure TForm1.Button1Click(Sender: TObject);


var
WinWord: Variant;
begin
StatusBar1.SimpleText := 'Spell checking...';
Application.ProcessMessages;
// Open the OLE link to WinWord.
WinWord := CreateOLEObject('word.basic');
// Select all of the memo text.
Memo1.SelectAll;
// Copy the selected text to the Clipboard.
Memo1.CopyToClipboard;
// From WinWord, create a new file.
WinWord.FileNew;
// Paste Clipboard contents into the new file.
WinWord.EditPaste;
try
try
try Figure 4: The Repository with the Automation Object selected.
// Initiate spell checking.
WinWord.ToolsSpelling;
except
// WinWord creates a message indicating that the
// spell check is complete. This generates an
// exception. Ignore this exception.
end;
// Select the spell checked text.
WinWord.EditSelectAll;
// Copy it to the Clipboard.
WinWord.EditCopy;
finally
// Close WinWord.
WinWord.Cancel;
WinWord := UnAssigned;
end; // Try-finally
except
Exit; Figure 5: You define the class and program name of your
StatusBar1.SimpleText := 'Could not spell check' automation server using the Automation Object Expert.
end; // Try-except
// Bring the spell checked text back.
Memo1.PasteFromClipboard;
StatusBar1.SimpleText := 'Spell checking complete'
Adding a TAutoObject Unit
end; You add a TAutoObject unit to your project by selecting
File | New. Select the Automation Object from the Object
Figure 3: The OnClick event handler for the Check Spelling Repository (see Figure 4). Delphi then displays the
button. Automation Object Expert (see Figure 5).

Creating an Automation Server In Class Name, enter a name for your derived class. Set OLE
Delphi 2 makes it easy to create automation servers from Class Name to the complete name that you want to register
your existing applications and DLLs. This allows you to as the automation server in the Registry. Delphi will auto-
expose some or all of your existing application’s features to matically complete this for you, based on your chosen class
automation clients. name, but you can change it if you wish.

The four steps to convert an existing application into an In Description, provide a description of the automation
automation server are: object. This description will also be stored in the Registry.
1) Add a unit to your project that derives a new class from Finally, set the Instancing. Normally, applications use a
the TAutoObject class. Single Instance value, and DLLs use Multiple
2) Within this derived class, declare any methods you want Instance.
to expose to automation clients within the automated
section. Once you accept the Automation Object Expert, it will
3) Within this derived class, declare any properties you want create and add a new unit to your project. Figure 6 is an
to expose to automation clients within the automated example of how this unit will appear. (For more informa-
section. These properties must use access methods to read tion on registering new applications, see the sidebar
and write the properties. “Registration Note” on page 11.)
4) Implement the class and access methods.
In this generated code, there are two critical elements to
This process is demonstrated with the project note. First, the expert has inserted a registration procedure
AUTOEX.DPR, an automated version of the local table that adds the server to the Registry. This procedure is
packing utility described in the article “BDE Basics” [see called from the unit’s initialization section. After you ini-
the March 1996 Delphi Informant]. tially run this project, do not modify this code.

9 October 1996 Delphi Informant


On the Cover
clause. While this process is similar to how you declare prop-
unit Unit1;
erties in objects, in general, there are several restrictions:
interface Automated properties cannot use direct access. You must
declare and use read and write access methods for each
uses
OleAuto;
property you declare.
Only certain property types are permitted. These are Byte,
type Currency, Double, Integer, LongInt, Single, SmallInt, string,
TOleDemo = class(TAutoObject)
private
TDateTime, Variant, and WordBool.
{ Private declarations } You cannot use the index, stored, default, or nodefault
automated specifiers in automated property declarations.
{ Automated declarations }
end;
To allow the automation client to define which table to
procedure RegisterOleDemo; pack using a property, you can add a TabName property to
const
AutoClassInfo: TAutoClassInfo = (
the automation server. The following is an example of how
AutoClass: TOleDemo; the type declaration may appear after doing so:
ProgID: 'OleAutoDemo';
ClassID: '{3EE99F30-F141-11CF-82A3-444553540000}'; type
Description: ''; TOleDemo = class(TAutoObject)
Instancing: acSingleInstance); private
begin { Private declarations }
Automation.RegisterClass(AutoClassInfo); FTabName: string;
end; protected
function GetTabName: string;
initialization procedure SetTabName(Value: string);
RegisterOleDemo; automated
end. { Automated declarations }
procedure PackTab; virtual;
Figure 6: The Automation Object Expert creates a new unit and property TabName: string
adds it to your project. read GetTabName write SetTabName;
end;
Second, a new clause, automated, appears in the type dec-
laration. You use automated to surface properties and Implementing Automation Methods
methods to automation clients. You implement automation methods the same way that you
implement methods for any type of object. The following is
Declaring Server Methods how the GetTabName, SetTabName, and PackTab methods
You declare methods for the server by adding them to the might appear:
automated clause of the type declaration. For example, to
add a method that can be called by the client, and have function TOLEDemo.GetTabName: string;
begin
this method pack a table, you can modify the type decla- Result := FTabName;
ration as follows: end;
procedure TOLEDemo.SetTabName(Value: string);
type begin
TDelphiOLEServer = class(TAutoObject) if FTabName <> Value then
private FTabName := Value;
{ Private declarations } end;
automated procedure TOLEDemo.PackTab;
{ Automated declarations } var
procedure PackTab; virtual; Tab: PChar;
end; begin
GetMem(Tab,144);
try
These methods can be declared virtual, but not dynamic. StrPCopy(Tab,FTabName);
Furthermore, they must use the register calling conven- Form1.PackTable(Form1,Tab);
finally
tion, which is the default. [Virtual and dynamic methods FreeMem(Tab,144);
are described and compared in Ray Lischner’s article end;
“Virtual or Dynamic” in the May 1996 Delphi Informant.] end;

If you want, you can include the dispid directive, followed The GetTabName, SetTabName, and PackTab methods appear
by an integer. This will register the method using the spec- as they would in any object to which you are adding meth-
ified OLE Automation dispatch ID. If you do not include ods. However, the PackTab method deserves special attention.
dispid, the compiler will automatically assign one.
PackTab is called by the automation client to produce an
Declaring Server Properties action by the server. From within PackTab you can set
You declare properties that you want to expose to your properties of objects within the server application, call
automation clients by declaring them within the automated object methods, and execute functions and procedures.

10 October 1996 Delphi Informant


On the Cover

Registration Note Calling Your Automation Server


The automation server created in the preceding example
demonstrates how easily you can convert an existing appli-
I’ve created OLE Automation servers from my existing applica- cation into an automation server.
tions for some time. However, since the release of Delphi 2.01,
I could not add an automation server unit to an existing appli- Using your new server from an automation client is even
cation and have it successfully register with the Registry. I have easier. Simply use the technique described earlier in the
only been able to register new applications. section “Using an Automation Server.”

The only way I can successfully register an existing application This is demonstrated by the code in the project
is by performing the following steps. I begin by creating a new, CALLAUTO.DPR. The following shows how to open the
blank application. After saving this application to a directory, I OLEAutoDemo automation server, set the TabName prop-
add the automation unit, and again save the application. I then erty of the server, and then call the PackTab method, all
run the application to register it. from a button’s OnClick event handler:

Once the automation server is registered, I remove the default implementation

Form1 from this application, and add each unit from my exist- uses OleAuto;
ing application that I want to convert into an automation serv-
{$R *.DFM}
er. When I am done, I have a new application with all the units
and forms from my existing application, in addition to the OLE procedure TForm1.Button1Click(Sender: TObject);
automation unit. Furthermore, this application is a registered var
PackServer: Variant;
automation server. begin
PackServer := CreateOLEObject('OLEAutoDemo');
PackServer.TabName := Edit1.Text;
I want to point out that this may not actually be a problem PackServer.PackTab;
with Delphi. Instead, it may be because of the large number of PackServer := UnAssigned;
end;
example applications I create while writing training materials. I
may have damaged my Registry, or somehow affected Delphi in
a way that prevents it from performing correctly. I haven’t tried Conclusion
re-installing Delphi 2.01, but doing so may clear up this prob- Delphi makes it easy to convert any of your Delphi 2 appli-
lem. If you can add an automation unit to an existing applica- cations into automation servers. By doing so, you enable
tion, you do not need to worry about the above steps. other programs, even those not written in Delphi, to leverage
the features of your existing applications. ∆
— Cary Jensen
The demonstration projects referenced in this article are available
on the Delphi Informant Works CD located in
INFORM\96\OCT\DI9610CJ.
In this example, the PackTab method calls the method
Form1.PackTable, passing to it the name of the table
stored in the TabName property. Cary Jensen is President of Jensen Data Systems, Inc., a Houston-based database
development company. He is author of more than a dozen books, including
Delphi In Depth [Osborne/McGraw-Hill, 1996]. He is also Contributing Editor of
Delphi Informant. You can reach Jensen Data Systems at (713) 359-3311, or via
CompuServe at 76307,1533.

11 October 1996 Delphi Informant


Informant Spotlight
Delphi 1 / Delphi 2 / Object Pascal

By Ray Lischner

Classy DLLs
Exporting a Class and Its Methods from a Delphi DLL

ynamic link libraries (DLLs) are essential to Windows programming.


D Fortunately, Delphi has excellent support for creating and using DLLs,
making it easy to employ a DLL to export a function or procedure. Moreover,
with just a little more work, you can export a class and its methods.

This article describes how to export a class At run time, any application or DLL can
from a DLL, import a class in an applica- load another DLL, calling its functions
tion, and use objects that cross module and procedures. What makes DLLs so
(application and DLL) boundaries. attractive is that Windows keeps a single
copy of the DLL’s code in memory, no
There are two facets of mixing classes, matter how many applications load the
objects, and DLLs: same DLL.
the static, compile-time nature of export-
ing and importing classes, and For example, a spelling checker implemented
the dynamic, run-time nature of using in a DLL can be used by many applications,
objects and Run-Time Type Information such as a word processor, spreadsheet, or e-
(RTTI). mail tool. If all three applications use the
same spelling checker, a single copy of the
The general principles for dealing with these spelling checker’s code is loaded in memory,
aspects of DLLs are the same for Delphi 1 not three. And with a little more work, the
and 2, but the syntax can be different. This spelling checker can share its data as well,
article addresses the problems and solutions using a single copy of the dictionary, saving
for both versions. even more memory.

Review of DLLs A DLL can also be effective when used by a


To understand how to use classes with DLLs, single application. In this case, the benefit of
you must first understand how DLLs work using a DLL is not the code sharing, but the
without classes. Earlier this year, Delphi DLL ability to load the DLL only when it’s need-
basics were covered in a series of articles by ed. For example, a large application might be
Andrew Wozniewicz [see “DLLs” Parts I divided into multiple DLLs, each imple-
through IV in the March through June 1996 menting a key area of functionality. When
issues of Delphi Informant.] Here’s a quick the function is needed, the application loads
review, in case you missed that series or just the DLL into memory. When the function is
want a refresher. no longer required, the application unloads
the DLL, freeing its memory. This can
A DLL can be implemented in most pro- reduce the total amount of memory required
gramming languages. In Delphi, DLLs are by the application.
created using the library keyword in the
project’s .DPR file, instead of the program As a more specific example, a word proces-
keyword (used for creating an application). sor application might use a separate DLL

12 October 1996 Delphi Informant


Informant Spotlight

library WPDoc; var


DllInst: THandle;
procedure ReadWP(const Filename: string; ReadDoc: procedure( const Filename: string;
Doc: TDocument); export; Doc: TDocument);
begin begin
... { Load the DLL. DllInst = 0 for an error. }
end; DllInst := LoadLibrary('WPDOC.DLL');
{ Import the procedure. Test Assigned(ReadDocument)
procedure WriteWP(const Filename: string; to check for errors. }
Doc: TDocument); export; if DllInst <> 0 then
begin begin
... @ReadDocument := GetProcAddress(DllInst,'READDOC');
end; ReadDocument(Filename, Document);
{ Unload the DLL when you are finished. }
exports FreeLibrary(DllInst);
ReadWP name 'READDOC', end;
WriteWP name 'WRITEDOC'; ...
end.
Figure 3: Calling a DLL routine dynamically.
Figure 1: Exporting routines from a Delphi DLL.
DLL is loaded. The application calls the interface procedures
for each file format it can read or write. Without DLLs, according to their exported names. This makes it easier to
the application would consume memory for read and write add additional file formats: just add DLLs.
routines for file formats the user never encounters. With
each file format in its own DLL, the application loads An external declaration, however, ties the application to a par-
only the DLL of the file format in use. ticular DLL. In this case, the word processor needs to load the
file format routines from different DLLs at different times,
For an application to use a DLL, the DLL must export one depending on the user’s file format choice. Instead of using an
or more routines. The DLL contains a table of all the rou- external declaration, the application can use the Windows API
tines it exports. Each exported routine has a unique index, routine, LoadLibrary, to load a specific DLL, and then call
and can also have a name that can be different from its GetProcAddress to locate a particular routine. Figure 3 illus-
procedure name. For example, the word processor might trates how the word processor can load and call the READ-
require the file format DLL to have two routines. The rou- DOC routine. The path to the DLL is given by the variable
tine with index 1 has the exported name of “READDOC”, DllPath, which contains the name of the DLL the application
and the routine with index 2 has the name “WRITE- wants to read the bitmap (e.g. WPDOC.DLL).
DOC”. A DLL that reads and writes WordPerfect files
might use the name ReadWP internally, but export the Delphi 1 vs. Delphi 2
routine under the name “READDOC”. The exports state- In the examples so far, an exported procedure has been
ment specifies which routines are exported, with their declared with the export directive. An export directive
export names and indexes (see Figure 1). informs Windows that the routine is being called across a
module boundary, i.e. from an application to a DLL, from a
The word processor application imports routines from the DLL to an application, or from one DLL to another.
DLL so it can call the routines as though they were part of the
application. When calling a routine from a DLL, the applica- In Delphi 1, the export directive is required because
tion typically uses an external declaration which includes the Windows 3.1 uses a segmented memory architecture. When
name and arguments of the routine, as well as the DLL name calling a routine across a module boundary, Windows must
and the exported index or name (see Figure 2). Thus, the ensure that it is using the correct data segment. The export
application never knows the internal routine names used by directive tells the compiler to generate special code at the
the DLL; it only sees the exported interface. start of a routine to establish the correct data segment. Code
is also added to the end of the routine, restoring the data seg-
procedure ReadDocument(const Filename: string;
ment register to its previous value.
Doc: TDocument); external 'WPDOC' name 'READDOC';
procedure WriteDocument(const Filename: string; The export directive is not required in Delphi 2. The applica-
Doc: TDocument); external 'WPDOC' name 'WRITEDOC';
tion and DLL share a flat memory architecture, so there are
Figure 2: Importing a routine from a DLL.
no memory segments to worry about. For purposes of back-
ward compatibility, the Delphi 2 compiler ignores the export
directive.
Another benefit of this approach is that separate DLLs can
implement the same interface, that is, the same exported Instead of export, consider using the stdcall directive in
indexes and names. After the word processor has loaded the Delphi 2. One of the Delphi 2 compiler’s new features is its
proper DLL that corresponds to the desired file format, the faster, register-based calling convention. When you call a
rest of the application’s code doesn’t need to know which function or procedure, Delphi doesn’t push the first three

13 October 1996 Delphi Informant


Informant Spotlight
arguments onto the stack. Rather, it loads them into registers,
unit SpellChk;
resulting in faster code execution. However, this calling con- ...
vention is only compatible with Delphi. type
{ Called the suggestion callback for
suggested corrections. }
The stdcall directive tells the compiler not to use Delphi’s TSuggestProc = procedure(Word: PChar) of object;
register calling convention, but to use the Windows standard {$ifdef WIN32} stdcall; {$endif}
calling convention, which pushes all arguments onto the TISpellChecker = class
public
stack in a standard order. All Windows API routines use the function OpenDictionary(Path: PChar): Boolean;
stdcall calling convention. To ensure maximum flexibility, use virtual; export;
stdcall for all routines exported from a DLL. The application {$ifdef WIN32} stdcall; {$endif} abstract;
procedure ClearDictionary;
and DLL must declare the same calling convention, so virtual; export;
remember to use stdcall when importing a routine as well. {$ifdef WIN32} stdcall; {$endif} abstract;
function CheckSpelling(Word: PChar;
Suggest: TSuggestProc): Boolean; virtual; export;
The stdcall directive is not recognized by the Delphi 1 compil- {$ifdef WIN32} stdcall; {$endif} abstract;
er, which poses a challenge when writing a DLL for Delphi 1 end;
and 2. The only way to write a simple DLL source file for
both Delphi 1 and 2 is by using conditional compilation (i.e. Figure 5: Simple spelling checker interface class.
$ifdef and $endif statements). The example in Figure 4
demonstrates how the pre-defined symbol, WIN32, distinguish-
es between Delphi 1 (WIN32 is not defined) and Delphi 2
(WIN32 is defined).

procedure ReadWP(const Filename: string; Doc: TDocument);


export; {$ifdef WIN32} stdcall; {$endif}

Figure 4: Exporting a routine for Delphi 1 and Delphi 2.

Exporting a Class
As we’ve seen, Delphi makes it easy to use normal functions
and procedures stored in a DLL. Classes and objects, however,
work slightly differently. This section describes how to export
a class from a DLL, and import a class into an application.

In Delphi 1, the first step is to declare a class’ methods


Figure 6: Using the same unit in an application and a DLL.
with the export directive. Use the export or stdcall direc-
tive in the class declaration. (Again, when using Delphi 2,
you might want to use the stdcall directive, to assure the Note that every method is declared as virtual and abstract.
DLL can be called from applications written in other lan- An abstract method has no implementation, so a derived class
guages.) It isn’t necessary to repeat the directive in the must override and implement every abstract method. This
method implementation, or for derived classes that over- must be done for every method you want to export from a
ride the method. DLL. To understand why, consider how classes and units
work in Delphi.
If you are using conditional compilation to declare the class
for Delphi 1 and 2, you don’t need to repeat the export or The Interface Class
stdcall directive. The conditional compilation is kept in one A class must be implemented in the same unit that it is
location. You can usually keep derived classes and method declared. If the TISpellChecker class was implemented in the
implementation portable, without conditional compilation. SpellChk unit, then the application would be linked with the
full implementation of the spelling checker (see Figure 6).
Figure 5 shows the declaration for the TISpellChecker class in the However, the point is to implement the spelling checker in a
SpellChk unit. This class defines the interface of a rudimentary separate DLL. So, the application needs a way to access the
spelling checker. An application calls the OpenDictionary method class declaration for TISpellChecker without linking its meth-
to load a spelling dictionary. To load multiple dictionaries, the ods. The solution is to declare an interface class.
application calls multiple instances of OpenDictionary. The
ClearDictionary method empties the entire dictionary, and the An interface class, such as TISpellChecker, is a class with
CheckSpelling method checks the spelling of a word. If the word abstract methods. Because an abstract method has no imple-
is misspelled, the CheckSpelling method obtains a list of suggest- mentation, the SpellChk unit doesn’t define any methods for
ed corrections. These methods are exported from a DLL for use TISpellChecker. Both the application and DLL can link with
in an application, such as a word processor. the SpellChk unit to ensure they are using the same class dec-

14 October 1996 Delphi Informant


Informant Spotlight

type
TSpellChecker = class(TISpellChecker)
...
public
function OpenDictionary(Path: PChar): Boolean;
override;
procedure ClearDictionary; override;
function CheckSpelling(Word: PChar;
Suggest: TSuggestProc): Boolean; override;
end;

Figure 8: Concrete implementation of the interface spelling


checker class.

function InitSpellChecker: TISpellChecker; export;


{$ifdef WIN32} stdcall; {$endif}
begin
try
Result := TSpellChecker.Create;
Figure 7: Sharing an interface between an application and a except
DLL. Result := nil;
end;
laration for the TISpellChecker class. The DLL implements a end;
derived class, TSpellChecker, overriding all the abstract meth-
ods of TISpellChecker. Figure 7 illustrates the DLL imple- exports InitSpellChecker;

menting the spelling checker, where the application and DLL Figure 9: InitSpellChecker creates and returns a spelling check-
share the abstract interface class. er object.

The derived class (e.g. TSpellChecker) is often called a concrete


class because it provides a concrete implementation of an
abstract interface (see Figure 8). Remember: do not repeat
the export or stdcall directives in a derived class.

With the TISpellChecker and TSpellChecker classes defined,


the next step is to create a TSpellChecker object in the DLL,
and return it to the application. In other situations, you
might create an object in the application, and pass it to the
DLL as a parameter to a function or procedure. In this case,
the DLL exports the InitSpellChecker function, which creates
and returns a TSpellChecker object (see Figure 9). Note that
the type of InitSpellChecker is the abstract class,
TISpellChecker. The application isn’t aware of the concrete
class, TSpellChecker, only the abstract class, TISpellChecker. Figure 10: Calling a virtual method.

How Exported Methods Work You can think of the VMT as a means of exporting methods
An application can call the exported method of the DLL which replaces the table of indexes and names. In the case of the
because every class has a table of virtual method pointers. spelling checker, the only routine listed in the exports statement
This Virtual Method Table (VMT) contains a pointer to the is InitSpellingChecker. All other exported routines are methods.
code for every virtual method declared by a class and its base
classes. As such, the VMT for the interface class, Memory Management
TISpellChecker, contains a pointer for each virtual method Not all the methods of an interface class need to be exported.
declared by the class. When the DLL creates an instance of Sometimes it’s useful to declare a static method to take
the concrete class, TSpellChecker, it also creates a VMT that advantage of the fact that such a method is implemented in
points to all exported methods of the class. the application, and isn’t called across a module boundary.

The VMT for TSpellChecker originates in the DLL, so its The most notable example is the Free method, which frees
method pointers all point to the methods in the DLL. When the memory for an object. This method is declared static by
the application calls any virtual methods, it knows that it must the TObject class. Every class inherits from TObject, there-
get the method pointer from the VMT. The application is fore every class inherits the Free method, which calls the
unaware of whether the VMT comes from the application or object’s destructor, Destroy. Destroy is virtual, so the applica-
DLL. Figure 10 illustrates how an application calls a virtual tion gets the method pointer from the VMT, meaning the
method. DLL’s destructor is called from the application.

15 October 1996 Delphi Informant


Informant Spotlight

type
TISpellChecker = class
public
procedure Free;
procedure Release; virtual; export;
{$ifdef WIN32} stdcall; {$endif}
...
end;

{ Replace TObject.Free to call the exported


Release method. }
procedure TISpellChecker.Free;
begin
if Self <> nil then
Release;
end;

{ Call the destructor from the DLL. } Figure 12: Run-Time Type Information.
procedure TISpellChecker.Release;
begin
Destroy;
end;

Figure 11: Exporting the Release method for memory manage-


ment.

The problem in Delphi 1 is that the destructor is not


declared with the export directive. In Delphi 2, the
destructor is not declared with the stdcall directive. If you
create the object in the DLL and free it from the applica-
tion, you can run into serious trouble. The solution is to
declare an exported method, say, Release, that calls the
destructor (see Figure 11).

Because Release is exported, it can be safely called across a


module boundary. Once in the context of the DLL, it’s safe to
Figure 13: How the Object Pascal is operator works.
call the destructor. The interface class must then redeclare the
Free method to call Release instead of Destroy. With this simple
change to the interface class, you can create an object in the follows the base class pointers in the object’s VMT, until it
DLL and free it in the application, or create it in the applica- finds a match or runs out of base classes to compare — until
tion and free it in the DLL. (Note: The Release method dis- it reaches TObject, which has no base class. If Delphi finds a
cussed here is unrelated to the native TForm Release method.) match for the VMT, then the is operator returns True, other-
wise it returns False (see Figure 13).
Run-Time Type Information
Using the VMT to export a method makes it easy to The problem is that this scheme doesn’t work with an object
export a class in Delphi, but it also introduces complica- that crosses a module boundary (i.e. is passed as a parameter
tions. If you try to use the is or as operator, or the to a DLL, or returned by a function in a DLL). This is
InheritsFrom method, you’ll run into problems. Let’s look because each module has its own copy of the RTTI for its
at what’s happening. classes. Delphi compares VMT pointers for exact equality,
which can’t happen if the VMTs being compared are from
A class’ VMT is part of its RTTI. The RTTI is a set of tables different modules (see Figure 14).
that describes a class. These tables include the VMT: a table
for dynamic methods and message handlers; information Unfortunately, no simple solution exists for this problem.
about the published properties, methods, and fields; the name Delphi must assume the classes are different since it has no
of the class; a pointer to the parent class; and other informa- way of knowing otherwise. Just because two classes have the
tion. Figure 12 shows the organization of a class’ RTTI. same name doesn’t mean they are identical. You can imple-
ment a DLL that accidentally uses the class name,
Delphi uses the parent class pointer to implement the is and as TISpellChecker, but with completely different methods.
operators, as well as the InheritsFrom method. Compiling a class Therefore, Delphi can’t determine if two classes are identical
reference as a pointer to the class’ VMT, Delphi compares based solely on the class name.
VMT pointers to test whether one class is derived from another.
The solution is to build any type checking and type cast-
For example, the is operator compares an object’s VMT ing required by your class into the class itself. For
pointer to the test class’ VMT. If they aren’t the same, Delphi instance, you might expand the spelling checker interface

16 October 1996 Delphi Informant


Informant Spotlight

type
TISpellChecker = class
public
function IsEnglishSpellChecker: Boolean;
virtual; export;
{$ifdef WIN32} stdcall; {$endif} abstract;
function IsFrenchSpellChecker: Boolean;
virtual; export;
{$ifdef WIN32} stdcall; {$endif} abstract;
end;

{ In the DLL ... }


type
TEnglishSpellChecker = class(TISpellChecker)
public
function IsEnglishSpellChecker: Boolean; override;
function IsFrenchSpellChecker: Boolean; override;
end;
Figure 14: Objects and RTTI crossing a module boundary.
function TEnglishSpellChecker.IsEnglishSpellChecker:
Boolean;
to include different classes for different languages. Each begin
class implements the rules for plurals, verb conjugations, Result := True
end;
and so on. Every class uses the same interface, but with
different implementations. To provide a common interface function TEnglishSpellChecker.IsFrenchSpellChecker: Boolean;
for the word processor, all classes inherit from the same begin
Result := False
base class, TISpellChecker. To test whether a spelling end;
checker object is an instance of TEnglishSpellChecker, the
TISpellChecker class declares virtual methods that return
this information. Figure 15: Virtual methods for testing the type of an exported
object.
Figure 15 shows the new class declarations and virtual
methods that can be used instead of the is operator. To
test whether a spelling checker object is an instance of
TEnglishSpellChecker, call the IsEnglishSpellChecker
method, which is implemented by each concrete spelling
checker class.

Version Control
Version control presents a similar problem. Imagine what
happens if there is a new version of the application with a
different interface for the TISpellChecker class. Suppose a
problem arises when installing the new DLL. In this case, the
VMT for the TISpellChecker class in the DLL doesn’t match Figure 16: Conflicting VMTs because of different versions of
the VMT in the application. TISpellChecker.

As another example, let’s say that adding support for multiple it is also necessary to change the expected version number.
languages required the addition of several new methods. The When the application obtains a TISpellChecker object
application and DLL must have compatible VMTs. from the DLL, it checks the version numbers, and if they
Otherwise, the application can easily call the wrong method don’t agree, the application reports the appropriate error
(see Figure 16). message (see Figure 17).

To reiterate, Delphi has no way of knowing if the two ver- TInterface Class
sions of TISpellChecker have identical interfaces by looking at To make your job a little easier, Delphi defines the
the class names. It is your responsibility to ensure the applica- TInterface class in the VirtIntf unit. This class declares the
tion and DLL are using the same version of the Release and GetVersion methods, as described in this article.
TISpellChecker class.
Although the TInterface class is undocumented, every ver-
To solve this problem, define a GetVersion method to sion of Delphi 1 and 2 has the source code for the VirtIntf
return the current version of TISpellChecker. A constant, unit. However, each version’s source file is in a different
ExpectedVersion, records the version the application directory. Try looking in \DOC, \SOURCE\VCL, or
expects. When you change the interface of TISpellChecker, \SOURCE\TOOLSAPI. Your interface class must imple-

17 October 1996 Delphi Informant


Informant Spotlight
face class, declaring all exported methods as virtual,
const
ExpectedVersion = 2;
abstract methods. In Delphi 1, you must supply the export
type directive; in Delphi 2, you might want to use stdcall.
TISpellChecker = class Derive the interface class from Delphi’s TInterface class to
public
function GetVersion: Integer; virtual;
automatically get version checking and memory manage-
export; {$ifdef WIN32} stdcall; {$endif} abstract; ment (i.e. so objects can be created in one module and
... freed in another). Type checking and type casting are not
end;
generally permissible with exported objects, but you can
function GetSpellChecker: TISpellChecker; write additional methods to support this functionality, even
begin if in a slightly restricted form. ∆
{ Get the spelling checker object from the DLL. }
Result := InitSpellChecker;
{ Check the version number. }
if Result.GetVersion <> ExpectedVersion then The demonstration project referenced in this article is available
raise Exception.Create(
'Incorrect version of SPELCHEK.DLL');
on the Delphi Informant Works CD located in
end; INFORM\96\OCT\DI9610RL.

Figure 17: Implementing version control.

ment ExpectedVersion as a constant, so it is not part of the


TInterface declaration. Ray Lischner is the founder and president of Tempest Software (http://www.tem-
pest-sw.com), which specializes in object-oriented components and tools. Mr
Conclusion Lischner has been a software developer for over a decade, at large and small
DLLs can be extremely useful in Delphi programs. And as firms across the United States. He has recently finished Secrets of Delphi 2, a
we’ve seen, you can even export a class from a DLL by fol- book that reveals undocumented aspects of Delphi, to be published by the Waite
Group Press this autumn. You can reach Mr Lischner at [email protected].
lowing a few simple rules. You must first define an inter-

18 October 1996 Delphi Informant


Visual Programming
Delphi 1 / Object Pascal

By Keith Wood

Saved by an Expert
An Expert to Generate a Screen Saver Application

elphi 1 provides many ways for developers to reuse code with mini-
D mal effort. These include the components we drop on our forms and
the other objects that Delphi defines. In addition, Delphi 1 provides us with
the Gallery, a container of templates and experts for forms and projects.

Templates allow us to copy static code that .EXE. It’s expected to interact with the
we have previously developed, while experts Windows Desktop through the Control
allow us to interact with the code generation Panel, and display its configuration or
to customize it for our needs. In this article, saver screen when requested. It should also
we’ll develop a Delphi expert to generate a define a title appearing in the list of avail-
project framework. To make this worthwhile, able screen savers.
we need an application that’s interesting,
hopefully useful, and not too trivial. A screen From the Command Line
saver seems to fit the bill nicely — it’s a fairly The program determines which mode it
simple application with code that is common should start in — configuration or display —
(indeed required) across all screen savers, and based on command line parameters passed to
features several extras that we can have our it. For example, if a /S switch is present, the
expert automatically include or exclude. screen saver opens its display window to
cover the entire screen and begins drawing
Screen Saver Basics on it. The screen saver must then monitor
Screen savers serve two functions on a com- the system for any user activity, via the
puter system: mouse or keyboard, which then causes it to
1) They are used to protect the monitor close itself.
from phosphor burn that can be caused
by leaving an image on the screen too Conversely, if a switch isn’t present, the pro-
long. This is not really a problem any- gram opens its configuration form. This
more with newer monitors. allows the user to control the display screen’s
2) They can be used to hide and possibly pro- appearance. These parameters can be stored
tect information on the screen from other in an .INI file for use when the display
people when the system is unattended. screen appears. Typically the form features a
Test button that the user can press to view
all right — maybe there’s a third function: the effects of the configuration selections.
3) Entertainment! For example, some screen
savers play vignettes of a longer story, or Some screen savers allow users to specify a
show selections from collections of graph- password to prevent unauthorized users from
ics or cartoons. halting it and returning to the original pro-
grams. To implement this, two additional
A screen saver is really just an ordinary forms are required. One to allow the pass-
program that has been renamed with an word to be changed, and another to ask for
.SCR extension instead of the normal the password when the saver is running. An

19 October 1996 Delphi Informant


Visual Programming
option on the configuration screen allows the user to enable
begin
or disable this feature, and has a button that allows the user { Only one instance is allowed at a time }
to change the password. if hPrevInst = 0 then
begin
{ Display }
The actual drawing of the display screen can show anything if (ParamCount > 0) and
imaginable. One possibility is to manipulate the original (UpperCase(ParamStr(1)) = '/S') then
begin
screen image, causing it to dissolve, re-arrange, or be “eaten”
{ fmDisplay needs to be the main form }
by something. To do this, we need to capture an image of the Application.CreateForm(TfmDisplay, fmDisplay);
screen just before the screen saver starts, and use the image as Application.CreateForm(TfmConfiguration,
fmConfiguration);
the starting point for our activities.
end
else
Sounds fairly simple doesn’t it? We could just write one { Configure }
screen saver and then copy the code when we want to write begin
{ fmConfiguration needs to be the main form }
another, adding or deleting the bits that we need in the new Application.CreateForm(TfmConfiguration,
version. fmConfiguration);
Application.CreateForm(TfmDisplay, fmDisplay);
end;
The workable solution is to create a screen saver expert. We’ll Application.Run;
then install the expert into the Delphi environment, allowing end;
us to quickly generate a generic screen saver and add code to end;

customize it. Figure 1: This project source code allows the screen saver to
swap main forms based on a command-line parameter.
Forms: First Things First
The application should create the configuration and display and can only be present in the project source itself — not one
forms automatically. In configuration mode the user will of the units. Only one of these entries can exist, per project.
probably want to test the saver with the new parameters,
requiring the display screen to be available. While in display Here’s a screen saver name example:
mode, we must also create the configuration form to provide
{ Description that appears in drop-down list }
access to the user parameters. {$D SCRNSAVE Dropping Off}

Regardless of the mode we’re in, the configuration form pro- The configuration form loads the screen saver parameters from
vides a single point for loading the parameters. Normally, their location — in our case from an .INI file — and displays
each form that is auto-created is constructed in the code of them onscreen. These can be updated and saved back to the file
the project source, with the main form as the first to appear. for later use. The parameters themselves depend on the screen
This form is made visible while the others are hidden. saver being implemented and cannot be automatically generated.
However, we want different forms to be the main form in
each of the screen saver’s modes. The form contains three standard buttons:
1) OK saves the parameters and closes the form,
To accommodate the different ways of invoking the screen 2) Cancel closes the form without saving, and
saver we must modify the code in the project source. We want: 3) Test makes the display form visible.
the display form to be the main form when the program
is invoked with a /S parameter, and If the screen saver is password protected, the form also con-
the configuration form to be the main form at all other tains a check box controlling whether to ask for the pass-
times. word, and a button allowing the user to enter a password.

Therefore, depending on the mode, we must alter the order The display form is a borderless window that maximizes on
of creation of these two forms. creation and stays on top of all other windows. It contains a
single Timer component that allows for the periodic updat-
The code in Figure 1 shows this form selection based on the ing of the display for the screen saver. This Timer is enabled
mode. It also includes a check to ensure that only one copy when the form is shown, and disabled when it’s hidden.
of the screen saver program is running. The hPrevInst variable
contains the handle of the previous instance of this program. To allow the form to monitor all user activity, we create a
If it’s zero, then there is no previous instance and we can con- customized message handler and direct all messages for the
tinue. If it’s non-zero, the program takes no further action form to it using:
and terminates.
Application.OnMessage := DeactivateScreenSaver;
The screen saver’s title is also encoded in the project source.
The {$D} compiler directive is used to insert the name into Within this message handler, we check if the message relates to
the executable. It must appear with an identifier of SCRNSAVE a mouse movement, mouse click, or key press. If one of these is

20 October 1996 Delphi Informant


Visual Programming
The change password and password request forms are
{ Check for any user activity and stop when found }
procedure TfmDisplay.DeactivateScreenSaver( straightforward. The former implements several checks to
var msg: TMsg; var bHandled: Boolean); ensure that the password is only altered by an authorized
var
person and that the new password is confirmed and differ-
bDone: Boolean;
begin ent from the original. The latter simply asks the user to
StopMonitoring; { Don't monitor further messages } enter a password.
{ Check for largish mouse movement }
if msg.Message = WM_MOUSEMOVE then All the edit fields on these forms have their PasswordChar
bHandled := (Abs(LOWORD(msg.lParam) - ptOrig.x) > 5) or property set to *. This causes the characters entered to be dis-
(Abs(HIWORD(msg.lParam) - ptOrig.y) > 5)
played as this character, hiding the actual values.
else
{ Or for key presses or mouse clicks } Additionally, the edit fields have their MaxLength property
bHandled := (msg.Message = WM_KEYDOWN) or set to 8 to limit the password’s length.
(msg.Message = WM_SYSKEYDOWN) or
(msg.Message = WM_ACTIVATE) or
(msg.Message = WM_NCACTIVATE) or Note that in the password request form, all key strokes are
(msg.Message = WM_ACTIVATEAPP) or previewed by the form, through setting its KeyPreview prop-
(msg.Message = WM_LBUTTONDOWN) or
erty to True and adding a method to the OnKeyDown event.
(msg.Message = WM_RBUTTONDOWN) or
(msg.Message = WM_MBUTTONDOWN); This method discards the AF and AE sequences
since we don’t want the user to switch to another program
bDone := bHandled;
while the screen saver runs.
if bDone then
begin
{ Check whether screen is password controlled } Capturing the Image
if fmConfiguration.cbxPassword.Checked then
To have the screen saver manipulate the screen image, we
begin
fmPassword := TfmPassword.Create(self); need to copy and draw it on the display form. Delphi pro-
try vides no direct way of accessing the screen image, so we must
if fmPassword.ShowModal = mrOK then
resort to the appropriate Windows API calls to achieve this.
bDone := (fmPassword.edPassword.Text =
fmConfiguration.sPassword)
else We call the GetDC function to obtain a handle to the screen
bDone := False;
image. Normally we would pass the handle of a window to
finally
fmPassword.Free; GetDC and then receive a handle to the device context (usu-
end; ally encapsulated by Delphi’s TCanvas) of its client area. If,
end;
instead, we pass a handle of zero, we receive a handle to the
if bDone then device context of the screen itself. This can then be copied to
{ Password OK or no password } a bitmap for later use. Figure 3 shows the Object Pascal
Close;
end;
required to capture the screen image.

StartMonitoring; { Monitor all messages } The timing of this activity is critical. We must capture the
end;
screen before the screen saver is made visible or we’ll only get
an image of ourselves. The place to capture the screen, there-
Figure 2: The message handler for the display screen.
fore, is in the OnCreate event of the form. Unfortunately, at
detected, the user has returned and the screen saver is termi- this stage the form itself has not yet been created and thus
nated. Figure 2 shows the complete message handler code. cannot be referenced. This means that we cannot transfer the
screen image directly to the Canvas of the form.
The message type is available through the handler’s
Message property. Small mouse movements — less than For this reason, we create a bitmap to hold the image and
five pixels — are ignored, as it’s assumed that these are not copy it there instead. This is then transferred to the form
intentional. The bHandled parameter is set to indicate the first time that its OnPaint event is invoked using the
whether this handler responded to the message sent. Note canvas’ Draw method. This also means that the screen
that the call to StopMonitoring at the beginning deactivates image is available throughout the screen saver’s execution.
this message handler for the duration of its execution. Of course, both the device context and the bitmap must be
freed (when possible) to return their resources to Windows.
And the Password Is ...
If the screen saver is password protected, we must ask for Now that we have all the pieces necessary to build a generic
the password when we detect the user’s return. This is screen saver, we can incorporate them into an expert to auto-
done by the fmPassword form that is created and displayed mate the building process.
in response to any user activity. If the password entered
matches that held in the configuration form, then the Delphi Experts
screen saver terminates as before. Otherwise the message Delphi experts are a mysterious breed. Online Help contains
handler is reactivated and the screen saver continues. entries that describe how to use them, but it does not con-

21 October 1996 Delphi Informant


Visual Programming
{ Capture screen to bitmap. Must do this before displays the expert’s form and requests input from the user.
the form shows! } Once this has been completed, it performs some initializa-
procedure TfmDisplay.FormCreate(Sender: TObject);
var
tion, generates the source and .DFM files required by the
h: HDC; application, and opens the completed project, allowing the
begin programmer to further customize it.
bFirst := True;
bmpScreen := TBitmap.Create;
bmpScreen.Height := Screen.Height; The form displayed to the user employs a notebook compo-
bmpScreen.Width := Screen.Width; nent to hold the different pages of information being request-
{ Get handle to device context for the screen }
h := GetDC(0);
ed. The user can navigate these pages using the Next and Prev
{ And copy to internal bitmap } buttons. An image on the form provides visual feedback on
try the purpose of the current page, and can be used to indicate
BitBlt(bmpScreen.Canvas.Handle, 0, 0,
Screen.Width, Screen.Height,
the effects of various options on that page (see the Dialog
h, 0, 0, SRCCOPY); Expert for an example of this).
finally
{ Must release device context back to Windows }
ReleaseDC(Handle, h);
On the final page, the Next button becomes a Create button.
end; When it’s pressed, Create generates the project. The user can
end; also press the Cancel button to terminate the process at any
Figure 3: Capturing the screen image for the display form. time. To allow our expert to better integrate with Delphi, we
should follow this layout and behavior.
tain information on how to develop a new one. Fortunately,
the source code for the Application and Dialog experts is Our expert uses only two pages. The first requests the screen
available in DELPHI\DEMOS\EXPERTS under the saver’s title and the interval for the Timer, and provides options
ExptDemo project. From these we can divine how they work to capture the screen image at startup and/or to password protect
and implement one of our own. the screen saver. The second page requests the name of the pro-
ject for this screen saver and the directory in which to store it.
First, we find that an expert is implemented as a DLL with
certain pre-defined entry points. So all we must do is fol- After the user enters the appropriate details, the expert gener-
low this formula and generate our own expert in a DLL. ates the requested code. First it needs to load the snippets of
code that comprise the project. These are held in a string
From examining the ExptDemo project, we see an expert is a resource file in the example experts and are read into an array
class derived from TIExpert, which defines a number of of PChars by the InitCodeGeneration procedure.
methods that must be overridden. These return various pieces
of information about the expert, including its name and Resource files allow an application’s various components to be
description, an image to represent it in the Gallery, whether separately maintained and easily altered (e.g. when changing
it’s a form, project, or standard expert, and its current state. to another language). The string resource can be changed
using Borland’s Resource Workshop, or by compiling
An id string must also be defined. This consists of a two-part resource source files using Borland’s Resource Compiler. The
name: the first part typically identifies the vendor or author, example experts take the latter approach. This is invoked
and the latter identifies the expert itself. Together these through the BUILD.BAT batch file.
should be unique within Windows. Finally, there is the
Execute method that actually runs the expert when request- Bitmaps that the experts can display are also stored in a
ed. Further comments on all of this are available in the resource file, and can be altered using Delphi’s Image Editor
ExptIntf unit (located in your DELPHI\DOC directory). (you can also use Borland’s Resource Workshop to alter
bitmaps). Both of these resources are included in the final pro-
For the expert to be correctly linked into Delphi’s IDE, it ject with the {$R} compiler directive in the project source file:
must be registered. This is achieved by the InitExpert func-
tion that is exported from the DLL. InitExpert is called by { Bitmaps for the expert }
{$R SCRSAVBM.RES}
Delphi while the IDE loads. After some initialization,
InitExpert makes calls to RegisterProc (it’s passed in as a para- { String resource for the expert }
meter) with an instance of each expert to register. {$R SCRSAVST.RES}

Copying this project and modifying the various pieces to The string resource containing the code snippets must be
implement our own expert results in the ScrSavEx project file located and made available for use within the program. This
that accompanies this article. is done by the LoadResource and FindResource Windows API
functions, which copy the named resource into memory and
The application expert (the one we’re copying) is actually produce a handle to it. The resource is then locked to prevent
implemented in the APP.PAS file. The interface procedure, it from being overwritten in memory, and a pointer to it is
the one invoked by the Execute method of the expert class, returned so that we can access it directly.

22 October 1996 Delphi Informant


Visual Programming

{ Generate the source for the project file, { Generate the .DFM file for the screen display form }
with the specified name } procedure GenerateDisplayFormFile(
function GenerateProjectSource( fmExpert: TfmScrSavExpert);
fmExpert: TfmScrSavExpert): TFileName; var
var sFormName: TFileName;
stmSourceFile: TFileStream; stmFormFile: TFileStream;
begin stmTextFile: TMemoryStream;
{ Create the full path and name for the project file } begin
Result := fmExpert.edPath.Text; { Create the full path and name for the screen
if (Result > EmptyStr) and display form resource file }
not Result[Length(Result)] in [':', '\']) then sFormName := GetFileName(fmExpert.edPath.Text,
Result := Result + '\'; iDisplayFile, sResourceExt);
Result := Result + fmExpert.edName.Text + sProjectExt;
{ Check whether file can be created }
{ Check whether file can be created } CheckFileOverwrite(sFormName);
CheckFileOverwrite(Result);
{ Create text version of file and write object
{ Create the file and write the code to it } descriptions to it }
stmSourceFile := TFileStream.Create(Result, fmCreate); stmTextFile := TMemoryStream.Create;
try try
WriteSnippetFormat(stmSourceFile, csProjectBegin, WriteSnippet(stmTextFile, csDisplayForm);
[fmExpert.edName.Text]); if fmExpert.cbxCapture.Checked then
if fmExpert.cbxPassword.Checked then WriteSnippet(stmTextFile, csDisplayFormCapt);
WriteSnippetFormat(stmSourceFile, csProjectPwd, WriteSnippetFormat(stmTextFile, csDisplayFormEnd,
[LoadStr(iPasswordFile), [fmExpert.spnInterval.Value]);
LoadStr(iChgPswdFile)]); { Return to the beginning of the file for conversion }
WriteSnippetFormat(stmSourceFile, csProjectEnd, stmTextFile.Position := 0;
[LoadStr(iDisplayFile), { Create resource version of file and convert
LoadStr(iConfigFile), from text version }
fmExpert.edTitle.Text]); stmFormFile := TFileStream.Create(sFormName, fmCreate);
finally try
stmSourceFile.Free; ObjectTextToResource(stmTextFile, stmFormFile);
end; finally
end; stmFormFile.Free;
end;
finally
Figure 4: The function to generate the project source for the stmTextFile.Free;
screen saver and return the file name to the caller. end;
end;

The code snippets consist of text blocks that are each ter-
Figure 5: The procedure to generate the .DFM file for the dis-
minated by a vertical bar ( | ). This delimiter is searched play form.
for within the resource, with each section being pointed to
by the next position in the array. The delimiter is changed To generate the .DFM files, we capitalize on the fact that
to a null character so the array elements appear to be they can be represented as a “straight” ASCII text file. To see
PChars. Note that the for loop controlling this assignment this we can select File | Open File from the menu, change the
uses the Low and High functions to avoid hard-coding the file type to .DFM, and load any form file. Delphi then pre-
first and last values in the TCodeSnippet type. sents us with a list of all the objects comprising the form with
those properties that do not have default values being set.
The code for use in generating the basic screen saver can Objects that are contained within another on the form
be taken from an actual screen saver project. Code from appear within the definition of that object. This file can be
each of the units is combined into a single text file with altered as text and is then converted back into its normal
vertical bars terminating the different sections, including binary format by Delphi.
the optional parts. During the generation process, place-
holders act as substitutions for variable values. This is We can do the same with the ObjectTextToResource procedure.
done using the StrLFmt function wherein string positions It takes two parameters, being streams that connect to the
are marked by %s and integer values by %d. text source for the file and another for the binary output.
Figure 5 is the Object Pascal code that generates the .DFM
Loaded with Code file for the display form.
With the code snippets loaded, the code can now be gen-
erated. First we produce the project source by construct- The code snippets to construct the .DFM files are included
ing its name from the details (provided by the user) and in the string resource as described earlier. Each is opened in
then writing the necessary code from the code resource its text format, and cut and pasted into the combined text
into that file, substituting values where appropriate (see field of all snippets.
Figure 4). Then each of the program units follows, gener-
ating the Object Pascal source and the corresponding For all files being generated, we check that they do not
.DFM file. already exist, and ask permission to overwrite them if they

23 October 1996 Delphi Informant


Visual Programming
do. If permission is refused, the expert terminates without
completing the new project. Because we have locked the
string resource into memory, we must remember to unlock it
and free the space it was using at the end of the process. This
is done in the DoneCodeGeneration procedure.

Open the Project


Finally, after all the code is generated in its appropriate
format, we tell Delphi to open the new project with the
OpenProject method of the ToolServices object. This loads
in the nominated project and opens its main form for
editing (in our case the display form, since this appears
first in the project source). We also open the configuration
form directly with OpenFile since both these forms are
typically amended by the programmer (more information
on the ToolServices object is available in the ToolIntf unit
in the DELPHI\DOC directory). The programmer can
now customize the project knowing that the pieces
required for the screen saver to correctly interface with Figure 6 (Top): The Options screen from the Screen Saver Expert.
Windows are already in place. Figure 7 (Bottom): The Create screen from the Screen Saver
Expert.

Before it can be used, the expert must be compiled into a


DLL. This assumes that the string resource has already been Click the Next button to advance to the second page. Enter
compiled from its own source files. However, searching the name of the project, DropOff, and select a directory in
through the IDE reveals no way of adding our expert to the which to store it (see Figure 7). Press the Create button and
Gallery. Templates can easily be added from the Gallery itself watch the code appear.
by clicking on the Add button. However, this is disabled
when we move to the Experts tab. On the configuration form add two spin edit controls
along with their labels. These control the size of the sec-
Some further searching provides the answer. The experts are tions dropped and their speed down the screen. Set their
notified to Delphi through its initialization file, properties appropriately: range 1 to 10, initial values 4 and
DELPHI.INI. DELPHI.INI has a section called Experts, 10 respectively. Change the labels to meaningful prompts
with a list of the DLLs that contain them. To install another and provide for accelerator keys if required. In the code,
one, simply add its name to the list, for example: update the LoadConfig and SaveConfig procedures to read
and write the values for these parameters. Figure 8 is the
[Experts]
ExptDemo=C:\DELPHI\BIN\EXPTDEMO.DLL
code for the resulting LoadConfig procedure.
ScrSavEx=C:\DELPHI\EXPERT\SCRSAVEX.DLL

Delphi must then be re-started to acknowledge these changes.


{ Load configuration parameters from .INI file }
Note that the expert must be unloaded — by removing its procedure TfmConfiguration.LoadConfig;
entry and re-starting Delphi — before it can be re-compiled var
following alterations. inifile : TInifile;
begin
inifile := TInifile.Create(sConfigFile);
Generating the Screen Saver try
Now we’ll use the Screen Saver Expert to help us write our with inifile do begin
spnSize.Value :=
screen saver. It’s named Dropping Off and appears to shave off ReadInteger(sIniSection,sIniSize,4);
sections of the screen and drop them down and off the screen. spnSpeed.Value :=
ReadInteger(sIniSection, sIniSpeed, 10);
cbxPassword.Checked :=
From Delphi’s menu, select Options | Environment and ReadBool(sIniSection, sIniProtected, False);
click on the Preferences Tab. In the Gallery group, make sPassword :=
sure the Use on New Project option is checked. Click OK ReadString(sIniSection, sIniPassword, '');
end;
when you’ve verified this. Now, from Delphi’s menu, select finally
File | New Project to display the Project Gallery. Click on inifile.Free;
the Experts tab and double-click on the Screen Saver end;
end;
Expert. On the Options screen, check the Capture screen
at start and Password protected check boxes. Leave the
Timer interval at its default value and enter the title of the Figure 8: The LoadConfig procedure after being amended. Note
screen saver (see Figure 6). that all but two lines are generated by the expert.

24 October 1996 Delphi Informant


Visual Programming
start the entire process again (see Figure 10). We use the con-
{ Enable timer and start screen saver } figuration’s size parameter to determine how much of the
procedure TfmDisplay.FormShow(Sender: TObject);
screen to move at any one time. The Brush of the form’s
begin
iCurLine := Screen.Height; Canvas is initialized in the FormCreate method to be solid
iCurPos := iCurLine; black, and is used in the FillRect routine to blank out the sec-
tmrDraw.Interval :=
tion that we have just moved.
500 - fmConfiguration.spnSpeed.Value * 45;
tmrDraw.Enabled := True;
StartMonitoring; After the project is compiled it can be incorporated into
end;
Windows along with the other screen savers. Copy the exe-
cutable to the directory containing your screen savers (typi-
Figure 9: The FormShow procedure after being amended. About cally \WINDOWS\SYSTEM) and then rename it to have
half of this is generated by the expert.
an extension of .SCR. Now open the Desktop from within
the Control Panel and our new screen saver should appear
in the list. Select it and press Setup to display the configu-
{ Each timer tick draw the next dropped line } ration form. Change the parameters if required and test it.
procedure TfmDisplay.tmrDrawTimer(Sender: TObject);
var
iSize: Integer; Conclusion
begin One of Delphi’s key features is its ability to reuse code. Using
iSize := fmConfiguration.spnSize.Value;
or inheriting from components is one way to achieve this, as
with Canvas do begin
{ Drop the current line one position } is the use of templates and experts. Templates provide static
CopyRect(Bounds(0, iCurPos + iSize, code, while experts allow generation of customized code from
Screen.Width, iSize), Canvas,
a standard base.
Bounds(0, iCurPos, Screen.Width, iSize));
FillRect(Bounds(0, iCurPos, Screen.Width, iSize));
Refresh; We have built a project expert — without too much diffi-
Inc(iCurPos, iSize);
culty — for a useful application, and have incorporated it
{ If at the bottom, go to the next line to drop } into the Delphi environment. It ensures that required val-
if iCurPos > Screen.Height then ues are not omitted, and allows us to easily include or
begin
exclude sections of code as we see fit. It removes the repet-
Dec(iCurLine, iSize);
{ If at the top, start again } itive part of the code generation and allows us to concen-
if iCurLine < 0 then trate on the main purpose of the application. Along the
begin
way we’ve constructed a solid base for building custom
iCurLine := Screen.Height;
Draw(0, 0, bmpScreen); screen savers in Delphi.
end;
iCurPos := iCurLine;
end;
end; I’d like to thank Mark Johnson for his screen saver article in
end; the July 1995 issue of the Delphi Connection (available on the
Internet at http://www.pennant.com/delphi.html). The code
Figure 10: The tmrDrawTimer procedure moves the screen sec-
generated by the screen saver expert is based on the code
tions down and off the screen. The expert generates only the from this source. ∆
skeleton of this procedure.

In the code for the display form, we add the functionality The demonstration files referenced in this article are available on
described above: to drop sections of the screen image down the Delphi Informant Works CD located in
and off the screen. To start, add two variables to the private INFORM\96\OCT\DI9610KW.
section of the form. These record the current section being
dropped and its position on the screen. Update the
FormShow code to initialize these variables (see Figure 9). To
alter the speed of the section’s movement we set the Timer’s Keith Wood is an analyst/programmer with CSC Australia, based in Canberra.
Interval property from the configuration’s speed parameter (a He started using Borland’s products with Turbo Pascal on a CP/M machine.
larger value produces a smaller interval). All this is done in You can reach him via e-mail at [email protected] or by phone
the FormShow event so that it is re-initialized each time the (Australia) 6 291 8070.
screen is displayed. This may happen several times during
configuration, with different parameters each time.

Finally, add code to the Timer event to actually draw the


screen. At each tick of the Timer, we move the current sec-
tion one spot further down the screen. When it disappears we
start on the next section, and when they have all gone we

25 October 1996 Delphi Informant


OP Tech
Delphi 2 / Object Pascal

By Robert Vivrette

Parentage and Ownership


Understanding the Controls and Components Arrays

ike any programming language, Object Pascal has a few features that
L are obscure and not very well documented. One such feature is Delphi’s
use of the Controls and Components arrays. These arrays are internal list
structures used to keep track of components and their relation to one
another. This article clarifies their function and use in Delphi programs.
First, let’s take a brief look at these arrays. Show ...
The Components array is a TList structure As they say, a picture is worth a thousand
that is a part of TComponent and all of its words. Here then, is a simple application that
descendants. Similarly, the Controls array is a illustrates the relationship between the Controls
TList structure, and a part of TWinControl and Components arrays (see Figure 1).
and its descendants. Therefore all descen-
dants of TComponent will have a Components By clicking on an empty area of the main
property, and all descendants of form, this sample application shows which
TWinControl will have a Controls property. items are in the form’s Controls array and
Because most visual controls in the Delphi which ones are in its Components array. As
VCL are descendants of TWinControl, you’ll you can see, the Components array holds all
find most have both properties. items that are physically on the form, while
the Controls array holds only those items
that are immediate children of the form.

If you look carefully, you’ll notice the button


and label that are on Panel1 are not listed in
the Controls array. In addition, the
Checkbox, RadioButton, EditBox, and Label
in GroupBox1 are also not listed in the
Controls array. This is because the form is
not the parent of each of these items. Rather,
their respective containers (Panel1 and
GroupBox1) are.

Two concepts — parentage and ownership —


might make things simpler to understand.

... and Tell


Figure 1: This application illustrates the relationship between A component’s parent is its holder, or con-
the Controls and Components arrays. tainer. For example, CheckBox1 is a child of

26 October 1996 Delphi Informant


Op Tech

Figure 3: Click on the CheckBox item within GroupBox1 to


observe who is the Parent and Owner.

Figure 4: Click on BitBtn1 to identify Panel1 as its Parent.

Now if we click on an item within GroupBox1 — say the


CheckBox — observe who’s the Parent and who’s the Owner
(see Figure 3). As you can see, GroupBox1 is CheckBox1’s
Parent, but the Owner is unchanged — it’s still Form1.
Figure 2: The result of clicking on the frame of GroupBox1. Again, look at it from the reverse direction: when we had
clicked on GroupBox1, it listed CheckBox1 as one of its chil-
dren (i.e. in its Controls array). Therefore, when we clicked
GroupBox1; conversely, GroupBox1 is therefore the parent of on CheckBox1, it identified GroupBox1 as its Parent.
CheckBox1. Similarly, when we click on BitBtn1, it identifies Panel1 as its
Parent (see Figure 4).
One primary function of a component’s parent is to define
that component’s coordinate system. If we were to exam- In both cases, Form1 is still responsible for the creation and
ine CheckBox1’s coordinates, we would see that its Left destruction of these components. In fact, you’ll find that all
coordinate is 16 and its Top coordinate is 32. CheckBox1 the components will list Form1 as their owner. Remember,
is obviously not at 16,32 on the form; rather, these coor- all the components were listed in the Components array of
dinates are relative to its parent, GroupBox1. A compo- Form1, which therefore identifies Form1 as the owner of all
nent’s parent also controls other drawing-related steps, the components.
such as the stacking order, tab order, and clipping.
The Sample Program
A component’s owner is the object ultimately responsible The source code for this sample application is simple.
for the component’s creation and destruction. When a Essentially, it consists of only two event handlers: FillLists
component is destroyed, all other components that list the and Reset. The Reset handler clears the two list boxes, and
first component as their owner will be destroyed as well. identifies the item clicked, its parent, and its owner. The
This simplifies the programmer’s job — you don’t need to FillLists handler is used only when Form1, Panel1, or
concern yourself with the destruction of every component GroupBox1 are clicked. It first calls the Reset handler, and
on a form. then populates the right list box with the contents of the
Components array and the left list box with the contents
Back to Controls and Components of the Controls array:
How does this relate to the Controls and Components
arrays? Basically, the Controls array manages parentage and procedure TForm1.FillLists(Sender: TObject);
var
the Components array manages ownership. I : Integer;
begin
Let’s use another example to illustrate. Look at what happens Reset(Sender);
for I := 0 to (Sender as TComponent).ComponentCount -1 do
when we click on the frame of GroupBox1 (see Figure 2). As ComponentsListBox.Items.Add(
expected, the Controls property for GroupBox1 shows only its (Sender as TComponent).Components[I].Name);
children. However, note that the Components array is empty. for I := 0 to (Sender as TWinControl).ControlCount -1 do
ControlsListBox.Items.Add(
This is because Label3, CheckBox1, RadioButton1, and (Sender as TWinControl).Controls[I].Name);
Edit2 are in the form’s Components array, rather than in that end;
of GroupBox1.
The object passed in (Form1, Panel1, or GroupBox1) is
Note also the program indicates at the top that first cast as a TComponent. Then the ComponentCount
GroupBox1’s Parent is Form1, and its Owner is also Form1. property determines how many items are in the
When we had clicked on just the form (in the previous Components array. Then, it’s simply a matter of looping
example), GroupBox1 was in Form1’s Controls and through the Components array as many times as there are
Components array. This is why Form1 is listed as items, extracting the name of each component, and
GroupBox1’s Parent (in Form1’s Control array) and Owner adding it to the list box. The Controls array is treated in
(in Form1’s Components array). the same manner.

27 October 1996 Delphi Informant


Op Tech
What Good Are They? procedure TForm1.Panel1Click(Sender: TObject);
var
These arrays can be very useful in a program. Say you I : Integer;
want to change some property on all components on a begin
for I := 0 to Panel1.ControlCount -1 do
form (or perhaps just certain components like TPanels for with (Panel1.Controls[I] as TControl) do
example). Using the Components array of the form is an Left := Left + 1;
excellent technique to accomplish this. Consider the fol- end;

lowing code:
Every time the Panel is clicked, each of the controls within it
procedure TForm1.FormClick(Sender: TObject); are moved one pixel to the right.
var
I : Integer;
begin Conclusion
for I := 0 to ComponentCount -1 do The Controls and Components arrays can be very power-
if Components[I] is TPanel then
(Components[I] as TPanel).Color := clLime;
ful, if you know how to tap their power. Unfortunately,
end; the Delphi online Help files are a bit sparse in their dis-
cussion of these structures. Hopefully this article has
With this code, when the mouse is clicked on the form, cleared up some of the confusion, and will help you
all Panels are changed to Lime Green. Not a particularly understand how to best access and manage these arrays in
useful example, but it illustrates how you can easily filter your Delphi programs. ∆
through controls on a form and selectively make changes
to them. The demonstration project referenced in this article is available
on the Delphi Informant Works CD located in
The Controls array can also achieve some interesting effects. INFORM\96\OCT\DI9610RV.
You may want to move all controls within a Panel a relative
amount, but don’t want to disturb other controls outside the
Panel. The following code accomplishes this: Robert Vivrette is Technical Editor for Delphi Informant. He has worked as a game
designer and computer consultant, and has experience in a number of program-
ming languages. He can be reached on CompuServe at 76416,1373.

28 October 1996 Delphi Informant


Delphi C/S
Database Servers / InterBase / Delphi

By Bill Todd

Versioning:
The InterBase Advantage
An Examination of Database Concurrency Models

s you rush headlong into the world of client/server computing, one of


A the first things you must do is select a database server. The architec-
tures of database servers vary widely, and as a result, their behavior in a
given situation also varies widely.
This means that to select the appropriate 20 rows. With row-level locking only a single
server for your Delphi database application row would be locked, and other users could
you must understand two things: how data freely access other records on the page. Thus,
will be accessed and modified in your appli- row-level locking provides better concurrency.
cation, and how the server will behave in
each data access or update situation. Pessimistic Locking. If your background is
in desktop databases (e.g. Paradox), you’re
In this article, we’ll explore the issues that probably familiar with pessimistic locking.
affect concurrent access to data, as well as This scheme is so named because it assumes
how they may impact your application. there’s a high probability that another user
will try to modify the same object in the
Locking Schemes database you’re changing. In a pessimistic
The oldest and most common method of locking environment, the object you want to
controlling concurrent access to data by sev- change is locked before you begin changing
eral users is locking. When a user locks an it, and the object remains locked until your
object in a database, he or she restricts other change is committed. The advantage of pes-
users’ ability to access that object. simistic locking is that you’re guaranteed the
ability to commit the changes you make.
How much a lock affects concurrency depends
on the lock’s granularity. For example, a lock Let’s say you need to change a customer’s
placed on an entire table will restrict other address. Using pessimistic locking, you first
users’ access to all the records in the table. lock the customer information at either the
Therefore, a table-level lock has very low gran- page or row level. You can then read the cus-
ularity. A lock placed on a single page in a table tomer’s record, change it, and be guaranteed
limits access to all the records on that page. A that you can write your changes to the data-
page-level lock is more granular than a table- base. Once you commit your changes, your
level lock. By contrast, a lock placed on a single lock is released and others are free to change
row is very granular, and provides the mini- the customer’s record. Locks can persist for a
mum restriction to concurrent data access. long time when pessimistic locking is used. For
example, you could begin a change and then
Most database servers support either row- or take a lunch break. After returning, you can
page-level locking. The problem with page- then commit the change and release the lock.
level locks is easy to understand by looking at
an example. Suppose a page’s size is 2KB Clearly, you want to use locks with high
(2048 bytes) and a row’s size is 100 bytes. granularity if you’re going to use pessimistic
Thus, each page can hold 20 rows, and each locking in a multi-user environment. If you
time a page is locked, access is restricted to all must lock an entire page of customer

29 October 1996 Delphi Informant


Delphi C/S
records while changing a single row, then no other user can column. A clustered index provides superior performance
change any other customer record on that page. Row-level when retrieving adjacent records. This is because the records
locks are best when pessimistic locking is used. This is are physically stored in key order within the database pages.
because they impose the least restriction on access by other
users. Page-level locks are much less satisfactory, because as Unfortunately, this design will probably produce poor perfor-
long as they persist, they restrict access to many rows. mance if the database uses page-level locking. Because
sequential adjacent keys are being assigned and a clustered
Optimistic Locking. The most common locking scheme index is being used, each new record added will probably be
found in database servers (e.g. Oracle, Sybase, SQL Server) placed on the same page as the preceding record. Because the
is optimistic locking. The locking mechanism is optimistic in database locks at the page level, two users cannot add new
that it assumes it’s unlikely another user will try to change orders to the same page simultaneously. Each new order must
the same row you’re changing. An optimistic lock is not wait until the page lock placed by the preceding order is
placed until you’re done committing your changes. released. In this case, you would get much better perfor-
mance by randomly assigning the keys. This will reduce the
To understand optimistic locking, consider two users — Fred chance that successive records will be added to the same page.
and Ethel — who are trying to change a customer’s record.
First, Fred reads the record and begins to make changes. Next, Transactions
Ethel reads the same record and begins to make changes. This Database servers also require the ability to group changes to
is possible because, in the optimistic locking scheme, no lock the database into transactions. Transactions consist of one or
is placed when a user reads a record and begins changing it. more changes to one or more tables in the database that must
be treated as a single unit. This is so that either all or none of
Then Fred completes his changes and attempts to commit the changes that comprise the transaction occur.
them. The database locks the record, commits the changes, and
releases the lock. When Ethel tries to commit her changes, the Transaction processing occurs in three steps: First, tell the
software detects that the record has been changed since she’d database you want to begin a transaction. This informs the
read it. Ethel’s change is rejected, and she must re-read the database that all changes — until further notice — are to be
record and begin again. treated as a single unit. Next, the changes are made to the
tables in the database. Finally, notify the database system that
Optimistic locking has a clear advantage because locks are only you want to either commit or rollback the transaction. If you
held for a brief period while the data is updated. This means commit the transaction, the changes become permanent. All
that with an optimistic locking scheme, you can achieve ade- the changes are “undone” with a rollback.
quate concurrency with less lock granularity. Therefore, data-
bases that use optimistic locking may lock at the page level and Transaction processing is vital to ensure the database’s logical
not at the row level. Conversely, optimistic locking does not integrity. Let’s say that Fred transfers $100 from his savings
fare well in an environment where there’s a high probability account to his checking account. This transaction would pro-
that two users will simultaneously try to update the same row. ceed as follows: start a new transaction, update the savings
account balance to show a withdrawal of $100, update the
From the database vendor’s point of view, page-level locking checking account balance to reflect an increase of $100, and
is advantageous because fewer locks must be placed — partic- either commit or rollback the transaction.
ularly during batch operations that affect many rows. This
means the resource requirements of the lock manager module Suppose the system crashes after step one, but before step
in the database management system are lower, and this can three. Without transaction control, Fred would have lost
help improve the performance of the database server. $100. With transaction control, when the system is restarted,
However, users are invariably the slowest part of any database the database management system (DBMS) will automatically
application, so you’ll usually get better overall performance in rollback any transactions not committed at the time of the
an environment where one user cannot block another. system’s crash. This guarantees that the database will be left
in a consistent state.
Why You Should Care
Understanding how your database manages locks can be criti- You also need transaction control for read transactions that
cally important. Consider an Orders table. New records are will read more than a single record. This is to ensure that the
added continuously as new orders are received. Because the read returns a consistent view of the data. We’ll discuss this
Order data does not include a field (or fields) that would requirement in more detail in the next section.
form a natural primary key, you decide to use an artificially
generated Order Number as a surrogate key. Order Numbers Transaction Isolation
will be assigned sequentially as orders are received. Transaction isolation governs how simultaneously-executing
transactions interact with each other. Many of today’s data-
Because your application must frequently select groups of base servers were originally designed to process short update
orders, you create a clustered index on the Order Number transactions intermixed with single row reads.

30 October 1996 Delphi Informant


Delphi C/S
The perfect example of this is an automated teller machine Let’s reconsider the preceding example. The read transaction to
(ATM). An ATM reads the balance in a single account, or produce the inventory valuation report begins. When the update
updates the balance in one or more accounts. In this environ- transaction to move the pallet of platinum from warehouse A to
ment, transactions are short, and reads involve a single row at a warehouse B is committed, a new version of each updated record
time, so transaction isolation is not a serious concern. However, is created. However, the old versions still exist in the database.
many of today’s database applications do not fit this model.
In a versioning database, each transaction is assigned a
Short update transactions are still the norm. However, the sequential transaction number. In addition, the DBMS main-
advent of executive information systems has introduced tains an inventory of all active transactions. The transaction
long-running read transactions that span entire tables — inventory pages show whether the transaction is active, com-
sometimes entire databases. mitted, or rolled back.

Let’s consider the following scenario. An executive requests When an update transaction commits, the DBMS checks if
the total value of the company’s inventory by warehouse. there are transactions with lower transaction numbers that
While the query is scanning the inventory table, a user are still active. If so, a new version of the record is created
moves a pallet of platinum bars from warehouse A to that contains the updated values. Each version also contains
warehouse B and commits the transaction. It’s possible for the transaction number of the transaction that created it.
the query to count the platinum in both warehouses, thus
producing an erroneous inventory valuation report. When a read transaction begins, it retrieves the next transac-
The question becomes, “Which updates should a read transac- tion number and a copy of the transaction inventory pages
tion see, and when should it see them?” This is what the transac- that show the status of all uncommitted transactions. As a
tion isolation level controls. There are three basic isolation levels: read transaction requests each row in a table, the DBMS
Dirty Read — This isolation level allows any record in the checks if the transaction number for the latest version of the
database to be read whether or not it has been committed. row is greater than the transaction number of the transaction
Read Committed — This level allows read transactions to that’s requesting it. The software also checks if the transac-
see only those changes that were committed. tion was committed when the read transaction started.
Repeatable Read — A repeatable read allows the read
transaction to immediately see a snapshot of the database Let’s say the transaction number of the row’s latest version is
when the transaction began. Neither committed nor greater than the requesting transaction’s number; or, the
uncommitted updates that occur after the read transac- transaction which created the latest version was active when
tion starts will be seen. the read transaction started. With either scenario, the DBMS
looks back through the chain of prior versions. The software
Note that the TransIsolation property of Delphi’s continues until it encounters a version with a transaction
TDatabase component allows you to set all three of these number that is less than the transaction number of the trans-
isolation levels. However, this doesn’t mean that your serv- action that is trying to read the row, and whose transaction
er supports the isolation level you have selected. In addi- status was committed when the read transaction started.
tion, if you’re using an ODBC driver, the driver must also
support the isolation level you set. Search on When the DBMS finds the most recent version that meets
“Transactions | Transaction Isolation Levels” in the Delphi these criteria, it returns that version. The result is repeatable
online Help to view a table showing what each of these read transaction isolation without preventing updates during
isolation levels maps to on your server. the life of the read transaction.

In the example above, you need a repeatable read isolation Consider the following example of a row for which four ver-
to ensure the accuracy of your inventory valuation report. sions exist:
The problem is the price you must pay to get repeatable
read in a database with a locking architecture. With the Tran=100 (status=committed)
Tran=80 (status=active when read started)
locking model, the only way to ensure that data does not Tran=60 (status=rolled back)
change during a long read transaction is to prevent any Tran=40 (status=committed when read started)
updates from occurring until the read transaction ends. In
many situations, the effect on users of stopping all updates Assume that a read transaction with transaction number 90
for the duration of a long read transaction is unacceptable. attempts to read this row. The read transaction will not see
the version of the row created by transaction 100 because the
Versioning update that created this version took place after transaction
Versioning is another model for concurrency control. It over- 90 began. Also, transaction 90 cannot read the version creat-
comes the problems that locking model databases have when ed by transaction 80, even though it has a lower transaction
the environment consists of a mixture of update and long read number. This is because transaction 80 isn’t yet committed.
transactions. This model is called the versioning model. To date, Although the version for transaction 60 still exists on disk,
InterBase is the only DBMS to use the versioning model. transaction 60 has rolled back — and rolled back versions are

31 October 1996 Delphi Informant


Delphi C/S
always ignored. Therefore, the version that transaction 90 duces the correct results only if Fred’s transaction commits.
will read is the version created by transaction 40. This illustrates the danger of reading uncommitted data.

Note that in this example, transaction 80 is not allowed to Using locking, Fred reads the balance that places a read
commit. When transaction 80 attempts to commit, the lock, and then commits a withdrawal that places a write
DBMS will discover that transaction 100 has committed, and lock during the update. Ethel reads the balance, which
transaction 80 will be rolled back. attempts a read lock, but must wait because of Fred’s write
lock. Fred cancels the transaction before committing. This
Advantages of Versioning rolls back and releases the write lock. Ethel can now read
For a more complete understanding of how the locking and and get the correct balance.
versioning models compare, you must examine two things:
the types of concurrency conflicts that can occur in a multi- Under versioning, Fred withdraws the money. This updates
user database, and how each model behaves in each case. the balance and creates a new uncommitted version. At her
machine, Ethel reads the balance, but it does not reflect
The following examples assume that the locking model uses a Fred’s uncommitted withdrawal. Fred rolls back, so the ver-
shared read lock and an exclusive write lock to implement sion showing the withdrawal is marked rolled back. This
optimistic locking. Multiple users can place read locks, but illustrates a performance advantage of versioning because
no user can place a write lock if another user has either a read Ethel does not have to wait to read the balance.
or write lock. If one user has a write lock, another user can
neither read nor write the row. This is typical of databases The following is a different example, but it’s the same as
that use locking architecture. our earlier scenario of moving platinum from one ware-
house to another:
Consider the case where a husband and wife go to different Fred requests the total of all accounts.
ATMs at the same time to withdraw money from their joint Ethel transfers money from savings to checking while
checking account. Without concurrency control, the follow- Fred’s transaction is running.
ing sequence of events occurs: Fred receives the wrong total. The analysis of the data is
Fred reads the account’s balance as $1,000. inconsistent because the data’s state was not preserved
Ethel reads the account’s balance as $1,000. throughout the life of the read transaction.
Fred posts a $700 withdrawal.
Ethel posts a $500 withdrawal. Under locking, Fred requests a total of all accounts, thereby
placing a read lock. Ethel transfers money but cannot place a
At this point, the account balance is -$200 and the bank is write lock to commit the transfer because of Fred’s read lock.
not happy. This happened because without a concurrency Ethel must wait until the read transaction finishes. Finally,
control mechanism, Fred’s update is lost as far as Ethel is Fred gets the right total and releases the read lock, and
concerned. She never sees the change in the account balance. Ethel’s transaction can proceed.
However, under the locking model:
Fred reads the account’s balance, causing a read lock. Under versioning, Fred requests the total. At her ATM,
Ethel reads the account’s balance, also causing a read lock. Ethel transfers money from savings to checking, resulting
Fred posts his withdrawal, attempting a write lock that in new versions which Fred’s transaction does not see. Fred
fails because of Ethel’s read lock. gets the correct total and Ethel’s update is not delayed.
Ethel posts her withdrawal, attempting a write lock that
fails because of Fred’s read lock. Another variation of the repeatable read problem occurs if
you must reread the data in the course of the transaction.
A deadlock now exists. Hopefully, the DBMS will detect the For example:
deadlock and rollback one of the transactions. A query is started for all rows meeting certain criteria.
Another user inserts a new row that meets the criteria.
Under the versioning model, Fred reads the account’s balance Repeat the query and you will get one additional row.
and Ethel reads the account’s balance. Then, Fred posts his The appearance of this “phantom row” is not consistent
withdrawal, which causes a new version with a new balance within the transaction.
to be written. When Ethel posts her withdrawal, it’s rolled
back when the newer version is detected. With a database that uses the locking model, the only way
to prevent this inconsistency is to read lock the whole
A different problem occurs if a user does not commit a table for the duration of the transaction. Thus the
transaction. Let’s say Fred withdraws money from the sequence of events is:
account and this updates the balance. Ethel reads the balance Place a read lock on the table.
and Fred cancels the transaction before committing. Now Query for all records meeting certain criteria.
Ethel has seen the wrong balance. In this case, a dependency Another user attempts to insert a record, but is blocked
exists between the two transactions. Ethel’s transaction pro- by the table-level read lock.

32 October 1996 Delphi Informant


Delphi C/S
Repeat the query and you’ll get the same results because Both locking and versioning databases will recover automati-
other users cannot commit changes. cally when the server is restarted. However, there’s a signifi-
Under versioning there’s no problem, because the newly cant difference in the recovery time.
inserted record has a higher transaction number than the
read transaction. Therefore, it’s ignored on the second and Locking-model databases write each transaction to a log file.
subsequent reads that are part of the same transaction. To recover after a crash, the DBMS must read the log file and
rollback all the transactions that were active at the time of the
Disadvantages of Versioning crash by copying information from the log to the database.
So far it looks as if the versioning model handles most con-
currency conflicts better than the locking model. However, A versioning database does not have a log file. The record ver-
this is not always the case. In this example, Fred and Ethel sions in the database already provide all the information
are both told to make their salaries equal: required to recover. No data needs to be copied from one
Fred reads his salary. place to another. Instead, when the DBMS comes back on
Ethel reads her salary. line, it simply goes through the transaction inventory pages
Fred sets Ethel’s salary equal to his. and changes the status of all active transactions to rolled back.
Ethel sets Fred’s salary equal to hers. At most this will take a few seconds, even on a large database
or one with a large number of active transactions. Thus, crash
Under versioning, the result is that their salaries are simply recovery is another area where the versioning model excels.
swapped. Using locking, you can prevent this by locking both
records. For example, both Fred and Ethel read their own Other Issues
salaries and place read locks. Fred sets Ethel’s salary equal to At first it may appear that a versioning database has a signifi-
his, but cannot commit because of Ethel’s read lock. cant disadvantage. This is because the multiple record versions
Likewise, Ethel sets Fred’s salary equal to hers, but cannot will cause the database size to temporarily increase rapidly
commit because of Fred’s read lock. compared to a locking database. While this is true, don’t for-
get that other databases also grow as their log files expand.
Once again, you have a deadlock that the database system
should resolve by rolling back one transaction. Another solu- However, versioning databases will certainly grow rapidly if
tion using locking is to write lock the entire table. For exam- something is not done to control the proliferation of record
ple, Fred write locks the table and reads his salary. Ethel then versions. The DBMS performs some of the housekeeping for
tries to read her salary, but is blocked by Fred’s table-level you automatically. Each time a record is accessed, the DBMS
write lock. Fred sets Ethel’s salary equal to his and releases checks if any prior versions of that record are no longer need-
the write lock. Ethel’s transaction is now free to proceed. ed. A version is obsolete if its transaction rolled back, or if
there is a later committed version of the record and there are
Under versioning, Fred reads his salary and Ethel reads hers. no active transactions with a transaction number less than the
Fred sets Ethel’s salary equal to his and commits. Then Ethel transaction number of the newer committed version. Versions
sets Fred’s salary equal to hers and commits. Once again the that are obsolete are automatically deleted and the space they
salaries are swapped, because versioning allows both transac- occupied in the database pages is reused.
tions to process concurrently. The only way to solve this
problem with the versioning model is as follows: Many rows in many databases are visited infrequently. To
Fred reads his salary. remove unnecessary versions of these rows, the database must
Ethel reads her salary. be periodically “swept.” A sweep operation visits every row in
Fred sets Ethel’s salary equal to his. every table in the database and deletes outdated versions. You
Fred sets his salary to itself, creating a newer version. can run the sweep while the database is in use, but the sweep
Ethel sets Fred’s salary equal to hers, but it rolls back will impact performance while it’s running.
because a newer version exists.
InterBase, by default, will automatically start a sweep after
Here the problem is solved by setting Fred’s salary equal to 20,000 transactions. This isn’t the best way to manage sweep-
itself. This forces the creation of a new record version for ing, because you have no control over when the sweep will
Fred’s salary. Versioning architecture will not allow a change start. In addition, the user who starts the transaction that trig-
to be committed when a version of the record to be updated gers the sweep is locked until the sweep finishes. It’s better to
exists (which was created after the start of the current transac- periodically start a sweep manually when database use is low.
tion). Therefore, Ethel’s update rolls back.
Conclusion
Recovery Selecting the correct database for your application requires a
One very important issue in any database application is clear understanding of the types of transactions the system
recovery time when the server crashes. No matter how robust must process. Many applications today require a mixture of
your hardware and software and/or how reliable your electric multi-row read transactions and updates. In this environ-
power supply, there’s always a possibility the server will fail. ment, versioning has a clear advantage because it can process

33 October 1996 Delphi Informant


Delphi C/S
read and write transactions concurrently while still providing
repeatable read to ensure accuracy.

Versioning also provides rapid crash recovery because there’s


no log file to process. When a versioning database restarts, it
simply marks all open but uncommitted transactions as rolled
back, and it’s ready to go.

As stated earlier, InterBase is the only DBMS to use the ver-


sioning model. In addition to the advantages of the version-
ing model, InterBase has the smallest disk and memory foot-
print (it ships on two diskettes), is self-tuning, and runs on
NetWare, Windows NT, and a wide variety of UNIX plat-
forms. Therefore, InterBase is highly scalable. ∆

Bill Todd is President of The Database Group, Inc., a Phoenix area consulting and develop-
ment company. He is co-author of Delphi 2: A Developer’s Guide [M&T Books, 1996],
Creating Paradox for Windows Applications [New Riders Publishing, 1994], and Paradox
for Windows Power Programming [QUE, 1995]; a member of Team Borland; and a
speaker at every Borland Developers Conference. He can be reached at (602) 802-0178,
or on CompuServe at 71333,2146.

34 October 1996 Delphi Informant


Delphi at Work
Delphi / Object Pascal

By Douglas Horn

Return to Sender
The Lowly Sender Parameter
Can Make Applications Shine

he humble Sender parameter could be one of Delphi’s most useful


T tools for modular, extensible programming. Although Sender is normal-
ly used to simply determine which object called a particular procedure, with
some creativity, this parameter can be extended to allow robust modular
programming with simple, reusable code.

Sender Basics Sender can accommodate any Delphi object


The Sender parameter is so ingrained in that can trigger an event. Sender forms a two-
Delphi programming that it appears in prac- way link between an object and an event han-
tically every program procedure. Even the dler; it tells the procedure what object trig-
Form’s OnCreate event — which has no gered it to be called. In other words, if an
sender — includes the (Sender: TObject) event handler was a letter, the Sender parame-
parameter in its definition. ter would be the return address.

Despite this, the Sender parameter is not Using Sender


necessary for a procedure to function. The simplest way to use Sender is with an
Removing Sender from a procedure that if...then or case statement that performs a
doesn’t use it results in functional code, as certain action depending on what object trig-
long as the parameter is also removed from gered the procedure. Here’s a typical example:
the procedure’s type statement in the form
procedure Form1.ButtonClick(Sender: TObject);
object’s interface section. Delphi will gen-
begin
erate an “incompatible parameter” warning if Sender is StopButton
at compile time, but this can be ignored. then Stop(Sender);
In fact, removing unused Sender parame- end;

ters results in slightly leaner code.


This code examines the Sender parameter to see
Not only can Sender be omitted from any if it’s the object, StopButton. If so, the event
procedure, but when used, it doesn’t even handler calls another procedure, Stop, passing
have to be called Sender. This name is only the Sender parameter to that procedure. The
used by convention. The common term Stop procedure calls another procedure and so
makes understanding code simpler, but on, passing the Sender parameter through the
there’s no reason not to name it Caller, program code. Because each procedure is sim-
Origin, or Banana if the developer prefers. ply passing along the parameter it received,
So the Sender parameter doesn’t have to be wherever the parameter is used, it will reflect
called Sender, and is, in fact, unnecessary. So the object that called the original event handler.
what is it? And why is it important enough
to add to practically every Delphi procedure? Not only can Delphi developers use Sender to
identify the object that called a procedure, but
Sender is a parameter passed from the compo- they can also use it to access that object’s
nent that called the event handler, and is a properties. The form in Figure 1 uses a num-
parameter of the type TObject. Because ber of colored panels as a palette. When the
TObject is the ancestor of all components, user clicks on a panel, the large panel on the

35 October 1996 Delphi Informant


Delphi at Work
left changes to the color of the Sender Impostors
selected panel. This procedure does As procedures pass the Sender parameter from one to another,
not need the name of the panel it never changes. It always points back to the object that called
selected, only its Color property. the original event handler. This is true provided each proce-
Therefore, each panel in the palette dure along the path passes the original parameter. However,
can have its own color, but all share Figure 1: This mini-
program sets the color
you can also substitute another parameter for the original
the following OnClick event handler: of the panel at right to Sender, fooling subsequent procedures into acting on another
the color of the panel object as if it were the true Sender.
procedure
selected by the user.
TForm1.Panel1Click(Sender:
TObject); This is the basis of our sample application, SENDER.DPR.
begin It uses Sender and Tag so that Delphi can be “fooled” into
Panel1.Color := TPanel(Sender).Color;
end;
having similar menu and button commands perform spe-
cial actions on the buttons. Traditionally, this wouldn’t
Since Sender provides a “return address” to the calling object, require special Sender parameters — both buttons and
that object’s properties can be read and set as well. This way, the menu items will share an event handler that can update the
procedure can also change the panel’s Color property without button as part of its functionality:
actually working with the panel object’s name. For example: procedure Button1Click(Sender: TObject);
begin
TPanel(Sender).Color := clRed;
{ Main functionality }
Button1.Font.Style := [fsBold];
Any type of property can be accessed using this framework, end;

provided it’s a property belonging to the specified type. In the


previous example, the procedure specifies that Sender is a However, when several components share a single event han-
TPanel, which therefore has a Color property. While Sender is dler, the programmer cannot simply assume to act on a spe-
declared as a TObject in the procedure header, the following cific object. Modifying the code in question to read:
line would return a compiler error:
TButton(Sender).Font.Style := [fsBold];

TPanel1.Color := TObject(Sender).Color;
is fine if the code is always called by a TButton. However,
As TPanel’s ancestor type, TObject is perfectly valid in other this makes it troublesome to call the same event handler
respects; but it does not contain a Color property. Since from a corresponding menu item because the menu item
TObject contains no properties, developers cannot use it to causes an error each time it calls the event handler.
determine the properties of a wide range of objects. Thus, a
program can’t find the color of a TPanel, TLabel, or TForm The Sample Application’s Framework
all with the same TObject(Sender).Color code. The sample application provides the framework for a modu-
lar, easily-extendible application. It also demonstrates how to
In fact, these three component types all contain the Color make the simple Sender parameter do a lot of high-powered
property. However, because they do not share this property in work, and shows how to get around some of the limitations
common ancestor classes, programmers cannot easily create mentioned earlier.
one block of code to get the Color property from these vari-
ous types of objects. The simplest solution a developer could Most applications contain menu items and buttons that
use would be this: perform the same action. Delphi makes it simple to do
this by routing multiple events to a shared event handler.
if Sender is TPanel then If multiple buttons and menu items all use the same event
Brush.Color := TPanel(Sender).Color; handler, it’s simple to determine which control called the
if Sender is TLabel then
Brush.Color := TLabel(Sender).Color; procedure. However, it becomes more difficult to deter-
if Sender is TForm then mine, say, which menu item corresponds to the calling
Brush.Color := TForm(Sender).Color; event if the control was actually a button. The user may
obviously find that the Word Wrap button and the Word
Fortunately for developers, the Delphi designers added the Tag Wrap menu command are synonymous in functionality.
property. Tag is a little catch-all integer parameter that devel- However, to have Delphi understand this and update the
opers can use for whatever purpose they choose. What makes menu item’s Checked property — regardless of which con-
Tag especially useful is that it resides well up the inheritance trol was the Sender — takes some special programming.
tree in the TComponent class. Just two steps down the ladder
from TObject, TComponent is the ancestor of all controls. This The sample application contains four SpeedButtons, as well as
means that any of these objects will have the Tag property. main and pop-up menu items that correspond to each (see
Unlike the Color property cited earlier, the Tag property of any Figure 2). The buttons and menu items perform similar func-
object can be accessed with a simple line of code: tions, in this case, using a database to track the time spent on
TForm.Tag := TComponent(Sender).Tag;

36 October 1996 Delphi Informant


Delphi at Work
each of a number of tasks. Program users SpeedButtons
can configure any number of tasks, all of Name/Caption AllowAllUP GroupIndex Tag OnClick event handler
which call the same event handler. (For
SpeedButton1 True 1 1 SpeedButtonClick
clarity, the sample application illustrates
SpeedButton2 True 1 2 SpeedButtonClick
only the Sender functions.) SpeedButton3 True 1 3 SpeedButtonClick
SpeedButton4 True 1 4 SpeedButtonClick
While a task is active, its SpeedButton Figure 2: The lay-
remains depressed. Pressing a depressed out of the sample MenuItems
button releases it and ends the task. application’s com- Name/Caption Tag OnClick event handler
ponents.
Pressing a button while another is File1 0 --
depressed releases the first and depresses the new button, causing MainItem1 1 SpeedButtonClick
the timesheet database to be simultaneously updated. The trick MainItem2 2 SpeedButtonClick
is to handle a number of MenuItems and SpeedButtons with MainItem3 3 SpeedButtonClick
one event handler. This technique enables you to allow for any MainItem4 4 SpeedButtonClick
number of tasks without writing a new handler for each. PopupItem1 1 SpeedButtonClick
PopupItem2 2 SpeedButtonClick
Implementing this functionality requires one more ingredient:
PopupItem3 3 SpeedButtonClick
the Components property. Like Tag, Components is a property
PopupItem4 4 SpeedButtonClick
of TComponent, meaning that it’s accessible to all components.
Components is a property of every component, but in most Figure 3: Setting the values of properties for the sample appli-
cases it’s only used on TForms. In this case, the Components cation’s SpeedButton and MenuItem components.
property is an array of all components owned by the form.
procedure TForm1.SpeedButtonClick(Sender: TObject);
var
Build It A : Integer;
To build the sample application, create a single form named begin
if Sender is TSpeedButton then
Form1. Add four SpeedButtons (used here to easily allow
if TSpeedButton(Sender).Down then
two-state buttons), a Label, a MainMenu (with an item called PushButton(Sender)
File1 and four sub-items), and a pop-up menu (also with four else
ReleaseButton(Sender)
menu items). Set the properties as shown in Figure 3. Note else
that all the buttons and menu items have the same event han- for A := 0 to ComponentCount-1 do
if Components[A] is TSpeedButton then
dler, namely SpeedButtonClick (see Figure 4). This procedure with Components[A] as TSpeedButton do
is called from any of the SpeedButtons or menu items on the if Tag = TComponent(Sender).Tag then
form (except MenuItem File1). It references two external pro- begin
Down := not Down;
cedures, PushButton and ReleaseButton. if Down then
PushButton(Self.Components[A])
else
SpeedButtonClick first determines which object sent the event. ReleaseButton(Self.Components[A]);
If it’s a TSpeedButton, it passes that object to the PushButton end;
or ReleaseButton procedure, depending on the button’s state end;

(up or down). These procedures allow convenient spots for Figure 4: Referencing PushButton and ReleaseButton with the
messages to be handled. In the following example, the SpeedButtonClick procedure.
Caption of Label1 is set to report the event that occurred:
procedure TForm1.PushButton(Sender: TObject); event to the SpeedButton with the same Tag. This is han-
begin dled in the SpeedButtonClick handler:
Label1.Caption :=
TComponent(Sender).Name + ' was clicked.'; for A := 0 to ComponentCount-1 do
end; if Components[A] is TSpeedButton then
with Components[A] as TSpeedButton do
procedure TForm1.ReleaseButton(Sender: TObject); if Tag = TComponent(Sender).Tag then
begin
Label1.Caption :=
TComponent(Sender).Name + ' was released.'; This code snippet scans the Components array and examines
end; each component it finds. When a TSpeedButton is found, the
code compares this object’s Tag value with the Tag value of
If the Sender of the event is not one of the buttons, we the object that originally fired the message. If the values are
must determine which button to click. This is where the the same, then the code found the button that ultimately
Tag property and the Components array property come in. receives the event. Since the code has already determined
As you noticed in Figure 3, SpeedButton1 had a Tag value that it’s a TSpeedButton, this object’s up/down state can now
of 1. MainItem1 and PopupItem1 also have Tag values of 1. be toggled. Then, either the PushButton or ReleaseButton
Therefore, if any of the menu items are selected, it’s a sim- procedure is called with the selected SpeedButton passed as
ple matter of viewing their Tag values and transferring the the parameter as follows:

37 October 1996 Delphi Informant


Delphi at Work
but sent a modified one that points to the SpeedButton
begin
Down := not Down;
regardless of how the button was selected.
if Down then SpeedButtonClick’s other procedure call, ReleaseButton, is
PushButton(Self.Components[A]) likewise called to handle whatever events should occur
else
ReleaseButton(Self.Components[A]);
when the button is released.
end;
Conclusion
This statement: Alone, the Sender parameter is a simple return address,
allowing functions to trace which component triggered
PushButton(Self.Components[A]) their event. But used in conjunction with a few other
properties, such as Tag and Components, Sender becomes a
calls the procedure PushButton, passing along the parameter: powerful tool for tracing and filtering program flow. This
ability to work around program limitations allows devel-
Self.Components[A]
opers to create single, modular event handlers for compo-
nents of all types, without the need for multiple if..then
The PushButton procedure interprets this Self statement as
statements. This, of course, is just one way of extending
the original Sender. The Self portion is required because we
Sender’s abilities. There are as many possibilities as there
want to look at the Components property of Form1. Since
are programs to write. ∆
the code uses a with statement to simplify readability, we
must ensure that the code is viewing the correct Components
The demonstration program referenced in this article is available
property. In this case, SpeedButtons also have a Components
on the Delphi Informant Works CD located in
property and without the Self qualifier, it will erroneously
INFORM\96\OCT\DI9610DH.
look there instead.

The PushButton procedure is where the timesheet database


would be updated or other functions would be performed.
Since the previous procedure handled the bulk of the
work, PushButton simply performs the actions necessary Douglas Horn is a free-lance writer and Contributing Editor to Delphi Informant. He
when the button has been pressed. Remember that can be reached via e-mail at [email protected]. Readers may browse a collection
SpeedButtonClick did not send the actual Sender parameter, of his past articles at his Web site, http://www.halcyon.com/horn/default.htm.

38 October 1996 Delphi Informant


Inside OP
Delphi / Object Pascal

By Dan Miser

The INISource Component


Creating INI-Aware Controls

elphi’s ability to interact with databases is second to none. However, there


D is a related feature that is not supplied in Delphi — the ability to interact
seamlessly with .INI files. .INI files are a convenient way for a program to store
user configuration settings that can affect almost any aspect of a program.

Delphi made access to the .INI file easy by Determining the Approach
wrapping almost all existing functionality of The first step in deciding how to create the
.INI files from the Windows API into an .INI-aware controls is to consider how
object named TINIFile. However, this still Delphi’s data-aware controls work. During
leaves a great deal of manual coding. The pro- Delphi’s development, Borland adopted a
grammer must read each item from the .INI standard in which each data-aware control
file and set the corresponding control with that is created as a descendant of its non-data-
value. When the user is done editing these aware ancestor, and any database access
controls, the programmer needs to save the needed would be added in every data-
value of each control back into the .INI file. aware control. For example, the standard
data-aware edit control, TDBEdit,
The entire process is very similar to the way descends from TCustomMaskEdit. It also
Delphi’s data-aware controls interface with has its own data-handling methods that
databases. In that model, the DataSource and interact with the database and various
DataField properties of the data-aware con- properties of the control. Therefore, the
trol determine the value of the data-aware .INI-aware controls should also descend
control. The programmer never needs to from their non-data-aware ancestors, and
worry about reading data from, or writing add methods to interact with the .INI file
data to, the database table again. By creating and control.
a set of .INI-aware controls to mimic the
behavior of the data-aware controls, the pro- An example best reveals the integration of
grammer will be freed from the mundane Delphi’s data-aware controls. Figure 1 shows
task of reading and writing individual .INI a DataSource, Table, and four DBEdit com-
file entries throughout the application. ponents. Note that the TableName property
is assigned in only one place, the Table com-
ponent. The DataSource component
becomes linked to that Table via the DataSet
property. This interaction allows the data-
aware controls to reference a database table
by assigning the appropriate DataSource
component in their DataSource property.

This mechanism is used so data-aware con-


trols can refer to either a Table or Query
component. Since we only need to read and
write from one type of data source (the .INI
Figure 1: A DataSource, Table, and four DBEdit
file), we can have the TINISource component
components. contain the file name.

39 October 1996 Delphi Informant


Inside OP
The INISource Component procedure TIniEdit.Notification(AComponent: TComponent;
The INISource component is the master controller of all Operation: TOperation);
begin
.INI-aware controls. It will be used to associate the .INI file inherited Notification(AComponent, Operation);
and .INI-aware controls. When the programmer assigns an if (Operation = opRemove) and (FIniLink <> nil) and
.INI file name to the FileName property, a TINIFile object is (AComponent = IniSource) then
IniSource := nil;
created. There is an interesting side-effect of this creation: end;
not only will the TINIFile open an existing .INI file, but if
the file does not exist, it will create the file automatically. Figure 2: The TINIEdit.Notification method.
Therefore, the programmer does not need to do anything dif-
ferent if this is the first time the user has run the program, or
procedure TIniKeywordProperty.GetValueList(List: TStrings);
if they have already saved settings from a previous run. var
Instance : TComponent; IniSourceInfo : PPropInfo;
The INISource component will also serve as a centralized SectionInfo : PPropInfo; IniSource : TIniSource;
Section : string;
read/write mechanism for the .INI-aware controls. Using begin
Borland’s TTable object as a model, the Post and Cancel meth- Instance := GetComponent(0);
ods will be used to allow the programmer to easily save and IniSourceInfo :=
TypInfo.GetPropInfo(Instance.ClassInfo,'IniSource');
restore all the .INI-aware controls that have been registered if (IniSourceInfo <> nil) and
with a specific INISource component. (IniSourceInfo^.PropType^.Kind = tkClass) then
begin
IniSource :=
Once the INISource component is in place and an .INI file TObject(GetOrdProp(Instance,IniSourceInfo))
has been chosen, the .INI-aware controls need a way to refer to as TIniSource;
that existing component. This is easily accomplished in Delphi SectionInfo :=
TypInfo.GetPropInfo(Instance.ClassInfo,'IniSection');
by specifying a property of type TINISource in the published Section := GetStrProp(Instance, SectionInfo);
section of the component. Delphi will search the form for all if (IniSource <> nil) and (SectionInfo <> nil) and
components of type TINISource and place them in the list of (Section <> '') then
IniSource.IniFile.ReadSection(Section,List);
the Object Inspector automatically. end;
end;
Of course, no component that references another compo-
Figure 3: The TINIKeywordProperty property editor.
nent is complete without a Notification method. This
method is called every time a component is inserted into or
deleted from a form. Thus, the programmer can take special select from a list of valid possibilities. Of course, the user can
action if the component that is about to be deleted is refer- always type in a value that does not exist to create a new sec-
enced in the current component. This method is also neces- tion or keyword.
sary to ensure the INISource property references a valid
INISource component at all times, thereby avoiding a nasty The property editors rely on a great deal of run-time type
GPF. Figure 2 shows the TINIEdit.Notification method. information (RTTI) in order to communicate directly with
other components on the form. An example of this com-
Each .INI-aware control will have several properties that will munication can be found in the TINIKeywordProperty
describe the appropriate .INI file entry for that control. For property editor (see Figure 3). This method needs to set
example, once the INISource component is set for a TINIEdit, the list of possible keywords that exist in an .INI file. The
the INISection property will have a list of currently defined sec- call to GetComponent retrieves the component that trig-
tion names for the .INI file that the INISource component has gered this property editor. This is necessary to identify the
defined. Lastly, the INIDefault property can be set to a value component throughout the rest of the method. After the
appropriate for its type, in case the user has not yet saved any component has been identified, its other properties can
values to the .INI file. also be accessed in the same fashion. (The VCL source
code has several property editors that are excellent learning
Into the INI aides. For more information, see the file
Displaying existing Section and Keyword names of an .INI file \DELPHI\LIB\DBREG.PAS.)
will be handled by writing property editors to show all the pos-
sibilities available to the programmer at any time. This is partic- When the programmer specifies an .INI file name for the
ularly helpful to complete the total concept of visual program- INISource component, all section names of that .INI file need
ming. By providing this functionality, the programmer does not to be identified. Any lines that are enclosed in brackets can be
need to remember each and every section name for a given .INI identified as section names, and therefore need to be inserted
file, or every keyword that exists inside the section that was just into an awaiting TStringList variable called SectionNameList.
selected.
Since both the Section and Keyword properties are nothing
Another benefit of this visual programming metaphor is the more than a list of possible entries, the paValueList is the cor-
elimination of typing errors by allowing the programmer to rect value to assign in the GetAttributes method. To assign the

40 October 1996 Delphi Informant


Inside OP

procedure TIniEdit.WriteFile(Sender : TObject); procedure TIniSource.NotifyIniLinks(Event : TIniEvent);


begin var
if (IniSource <> nil) and (IniSource.Active) and i : Integer;
(FIniSection <> '') and (FIniKeyword <> '') then begin
IniSource.IniFile.WriteString( for i := 0 to FIniLinks.Count-1 do
FIniSection, FIniKeyword, Text) TIniLink(FIniLinks[i]).IniEvent(Event);
end; end;
...
procedure TIniEdit.ReadFile(Sender : TObject); Figure 5: The Active procedure calls NotifyLinks, which in turn
begin calls the ReadFile method of the .INI-aware controls.
if (IniSource <> nil) and (IniSource.Active) and
(FIniSection <> '') and (FIniKeyword <> '') then
Text := IniSource.IniFile.ReadString(
Conclusion
FIniSection, FIniKeyword, FIniDefault) Now that the components
else are built, it’s time to see
Text := Name;
end;
them in action. The sample
... program provided will allow
constructor TIniEdit.Create(AOwner : TComponent); the user to modify certain
begin
inherited Create(AOwner);
settings in his or her
FIniLink := TIniLink.Create; WIN.INI file. There is a Figure 6: The example program
FIniLink.Control := Self; button on the form to save in action.
FIniLink.OnReadFile := ReadFile;
FIniLink.OnWriteFile := WriteFile;
the user’s changes to the
end; .INI file, as well as a button to revert all the changes to the
current .INI file’s state. A complete .INI file editor in two
Figure 4: TINIEdit routines. lines of code! Notice the effect of setting the
INISource.Active property both at design time and run time
list of possible values, the method GetValues will be called. It (see Figure 6).
is here that the abstract method GetValueList needs to assign
the appropriate items to the list. The reader is left with many different opportunities to
extend the scope of these components. Some examples
Reading and Writing would be:
The work of reading and writing the .INI file is left to the Add other non-basic components to behave in a similar
non-visual component, TINILink. This component acts as fashion (e.g. Orpheus controls).
intermediary between the TINISource component and the Allow access to the registration files of Delphi 2.
.INI-aware control to which it belongs. Setting up the Allow different properties to be saved to the .INI file.
TINILink to communicate properly requires two steps: creat-
ing the TINILink in the .INI-aware control, and adding the Visual programming and component-based design are the
newly created TINILink component to the list of INILinks new paradigms in programming, and Delphi supports those
contained in the appropriate TINISource component. models superbly. The beauty of Delphi’s extensibility is evi-
dent when even a small feature can be automated, allowing
In addition to these prerequisites, each .INI-aware control is the programmer to focus on more difficult problems. The
also responsible for providing methods to read from, and components developed here relieve the programmer from the
write to, the .INI file. By assigning these methods to the tedium of ever dealing with .INI files again. ∆
event-handlers of the TINILink component, each .INI-aware
control is able to respond as it should when a request to read
or write the .INI file occurs (see Figure 4).

All this setup is necessary to allow the INISource component


to communicate back to all of the links that have registered
with it. For example, when the user first set the Dan Miser is a contract programmer who has been writing Windows database
programs since 1991. He is also a Borland Certified Delphi Client/Server
INISource.Active property to True, the INISource component Developer. You can contact him at [email protected].
needs to notify all .INI-aware controls that they can read the
.INI file and display the appropriate value. This architecture
will also allow design-time viewing of the data as it exists at
that moment (see Figure 5).

41 October 1996 Delphi Informant


New & Used
By James Callan

With Class 3.0


MicroGOLD Software Develops OOA & OOD Tool

f you are familiar with Object-Oriented Analysis (OOA) and Object-Oriented


I Design (OOD), but don’t want to spend a fortune for CASE support, then
With Class 3.0 from MicroGOLD Software, Inc. is worth a look. With Class is
the affordable CASE tool many developers have been waiting for, supporting
Delphi 1 and 2. It lacks the polish of a US$1,200 CASE tool, but at one-fourth
the price (US$295), you might be willing to forego some sheen.

Restraining RADicals dynamic interactions between run-time


According to industry accolades and product objects. Properly employed, OOA and OOD
awards, most developers agree Delphi is techniques help refine these RADicals into
leading in Rapid Application Development well-engineered production applications.
(RAD). Unfortunately, prototype systems
developed with RAD are frequently Although many academicians and consultants
deployed as production systems, lacking the have proposed different graphical notations on
endurance and elegance found in their well- requirement analysis, the notations proposed by
engineered counterparts. The difference is Rumbaugh, Coad-Yourdon, Booch, and Shlaer-
proper analysis and design. Mellor are generally used. There are exceptions
(e.g. Jacobson’s method in the Telecom indus-
When designing larger applications, pro- try), but not many. Developers and companies
grammers must comply with some unifying often standardize on their favorites. I prefer the
methodology. Formal OOA and OOD Booch method, yet many CASE tool authors
methodologies enable programmers and ana- find the Booch blobs difficult to automate.
lysts to visualize object-oriented systems by Every method requires the drawing of diagrams
providing graphical notations for capturing to visualize, communicate, and document
the relationships between classes and the designs. Just as the pencil begot the eraser, these
methodologies have spawned CASE tools.

Casing CASE Tools


Accelerating the drawing process is the first
step in automating a methodology. Typically,
graphical drawing tools such as Visio quickly
capture the graphical diagrams. Soon designers
will want to capture the semantics between
class and object relationships, and diagrams
augmented by detailed attribute (property) and
class definitions. Ultimately, the goal is to doc-
ument a system’s requirements, including all
the decisions made until deployment. These
are worthy goals, and CASE tools can help.

Although they serve many additional pur-


poses, these CASE packages are essentially
Figure 1: A class diagram produced from Delphi code using
With Class. smart drawing tools; capturing designs for

42 October 1996 Delphi Informant


New & Used

Figure 2: A Booch methodology class diagram.

communication and documentation are their primary use.


All CASE tools permit users to drag and drop graphical
shapes, connect the shapes with lines, and adorn the lines
with special symbols. CASE tools also provide dialog boxes
for recording semantic and design information about class-
es, objects, properties, methods, events, exceptions, and
such. After accepting the information, the tools assist in
analyzing designs for completeness, consistency, and integri-
ty. With most of the tools, both the diagrams and semantic
information can be printed and distributed to team mem-
bers. Some tools even support design versioning.
Additionally, many tools generate class definitions, include
files (used in C++), and complete source code from the infor-
mation captured in their CASE repositories. Some tools help
perform what-if impact analysis on the labor and costs Figure 3 (Top): A customized class diagram with color added.
required for system changes. Since most CASE tool buyers Figure 4 (Bottom): The CLASS dialog box.
are existing developers who have outgrown one-person pro-
jects, many tools reverse-engineer existing code. Reverse engi- (With Class includes scripts to support C++, Eiffel, Ada, Object
neering automatically creates diagrams and specifications Pascal, Smalltalk, Visual Basic, SQL, and Java.)
from existing source code or database tables. “Marketeers”
often bill such products as “full-cycle” CASE tools. The Five-Minute Miracle
My initial five-minute test was simple: install With Class,
With Class point it at existing Delphi code, and run it. No studying
Recently, MicroGOLD released version 3.0 of their With Class manuals, no perusing online Help, and no peeking at the Tip
product to the Delphi community. Since many developers have of the Day. I wanted to test the reverse engineering as a
been waiting for an affordable CASE tool that supports both beginner, so I took a quick dive in.
versions of Delphi, we arranged a test drive.
With Class passed my five-minute smoke test with flying
With Class includes a 100-page user manual, two diskettes, colors. From my Delphi code, it produced an accurate class
and an online tutorial and code generation primary. For an diagram without a single problem. After a minute of drag-
additional US$35, a larger printed tutorial is available. (I rec- ging things around, I had the diagram shown in Figure 1.
ommend the larger tutorial to learn all the features.)
Since OOD methodologies are largely equivalent, With
With Class creates class, state, and object interaction diagrams Class switches between them easily. After selecting the
that use Rumbaugh, Coad-Yourdon, Booch, and Shlaer-Mellor Booch methodology from With Class’ main menu, a class
notations. It collects information on system, class, attribute diagram was produced in the Booch blobs (see Figure 2).
(property), operation (method), state, transition, and object
interactions. With Class generates source and SQL code, CASE Other With Class options help you customize class diagrams and
reports from the With Class repository (forward engineering), add color (see Figure 3). Double-clicking the TESPSymbol class
and renders class diagrams from existing source code (reverse displays the CLASS dialog box (Figure 4). Add another menu
engineering). Through its scripting mechanism, With Class also click, and With Class regenerates a class definition for
enables the crafting of custom reports and code generators. TESPSymbol. A closer look at the generated source code revealed

43 October 1996 Delphi Informant


New & Used
that With Class had gener-
ated the formal Get and Set
methods that aren’t in the
original (see Figure 5). You
can also customize the cod-
ing style. With Class’ Code
Generation dialog box
allows you to change lan-
guages and port applica-
tions (e.g. from Visual Basic
to Delphi). For example,
prototype applications in
Delphi will use the reverse
engineering facility to gen-
erate Java code. Figure 5: Source code generated
by With Class.
Modeling Dynamics Figure 6: The simple state-transition diagram.
After you’ve mastered modeling static class diagrams, it’s time
to model system dynamics. In OOD, this means state-transi-
tion diagrams and object interaction diagrams, and With
Class supports both. Figure 6 displays a simple state-transi-
tion diagram of the sales with the State Specification dialog
box. Figure 7 shows the Transition Specification dialog box
that displays when you double- click a transition line.

Object interaction diagrams display the message passing


(method invocation) that occurs dynamically between
objects. The interaction diagram in Figure 8 illustrates a
general menu option dispatcher executing the change color
use case.

Open Architecture
Figure 7: The Transition Specification dialog box that displays
A CASE tool’s value increases proportionally to its usefulness for
when you double-click a transition line.
your project. Different projects and teams require varying
degrees of design documentation; one size never fits all.
Realizing this, MicroGOLD created a CASE framework to sup-
port popular methodologies, and opened its architecture.
MicroGOLD achieved this in two ways:
MicroGOLD created an OLE server. Using With Class’
OLE services, you can embed With Class CASE diagrams in
compound documents. With Class supports OLE2 in place
activation of CASE diagrams embedded in other documents.
With Class creates a parameter for each item that users
enter, and uses them in a simple text-replacement merge
tool to generate reports and code. Parametric programming
is the general term for this technique. As shown in Figure 9,
With Class calls its rendition Scripting.

By creating custom scripts, With Class collects design informa-


tion, and through custom scripts, formats the information as Figure 8: An interaction diagram that illustrates a general
you like. You can also export CASE information via scripts to menu option dispatcher.
word processors, databases, spreadsheets, or custom applications
and tools. well-adapted for Delphi (With Class was originally written for
C++ environments). You’ll find the publication standards are
Horrors in Heaven below other similarly-priced software packages. However, a
With Class has an impressive array of basic CASE features, more extensive tutorial that most developers will find helpful
but the tool is not without flaws. Beginners beware. With can be purchased to straighten the learning curve. The good
Class’ documentation is written in an academic style that isn’t news is that the manuals are being redone. In the meantime,

44 October 1996 Delphi Informant


New & Used
When generating code in With Class,
be aware that its parse engine doesn’t
perform a merge generation. It either
generates new files or overwrites exist- For its price, With Class
ing files. Everything must be in the ships with some surpris-
ing features, and is
CASE tool and scripts. If not, you capable of producing
well-documented and
must create additional scripts and run carefully-engineered
a second pass to merge in any code object-oriented applica-
tions. However, the doc-
that was manually added. umentation is poor, and
the interface is quirky.

Conclusion MicroGold Software, Inc.


76 Linda Lane,
For US$295 (the 16-bit version Edison, NJ 08820
Phone: (908) 668-4779
Figure 9 (Bottom): A diagram mapping Parametric programming. retails for US$195) With Class ships Fax: (908) 668-4386
E-mail: 71543.1172@-
with some surprising features, but compuserve.com
the tool has flaws. Without better Web Site:
prepare to labor to become acclimated with these CASE tools. http://www.microgold.com
documentation, plan to spend a Price: With Class 3.0 Professional,
You may also notice additional irritations in With Class. week or two being unproductive. To US$295; With Class 3.0 Enterprise,
US$395.
First, the drawing engine does not double buffer, so you perform real work, you must antici-
get the pixellation effect as the images constantly redraw. pate and overlook With Class’ user
It’s fast, but you may go blind on a large project. This interface quirks. If you can ignore the irritations and learn
method of drawing causes occasional ghost images to With Class’ scripting language, you’ll produce some amaz-
appear, especially in Booch diagrams. Also, the multi-select ingly well-documented and carefully-engineered object-
feature might make you scream. It is used to cut and paste oriented applications. ∆
common design elements, and move designs on the canvas.
It is neither intuitive, nor easy to use. Reworks to the user
interface are also expected in future versions. James Callan, an 18-year computing veteran and former consulting director for
Oracle Corp., is currently president of Gordian Solutions, Inc., an information tech-
nology consulting provider in Cary, NC. A frequent writer and speaker on informa-
tion technology and client/server computing, James specializes in product design.
He can be reached at (919) 460-0555, or [email protected].

45 October 1996 Delphi Informant


TextFile

Delphi 2 Unleashed Aimed at Advanced Developers


It is not uncommon for a good position to discuss databases, Delphi’s object
book published on one ver- these issues. In addition to model, as well as OLE
sion of software to be updat- being the author of Delphi Automation and the
ed and reprinted for a subse- Unleashed, he is the author Component Object Model.
quent version of that soft- of Teach Yourself Windows
ware. Unless you take a look, 95 Programming in 21 Days In addition, Delphi 2
you’ll probably assume Delphi [SAMS Publishing, 1995]. Unleashed ships with a CD
2 Unleashed by Charles loaded with code examples,
Calvert [SAMS Publishing, But the Win32 API coverage demoware, shareware, and
1996] is merely an updated is just the beginning. This freeware. The CD also con-
version of Delphi Unleashed book also includes detailed tains Microsoft Word for
[SAMS Publishing, 1995]. discussions of the new Windows .DOC files of the Consequently, if you are a
Upon inspection, however, Delphi 2 features, as well as text of 15 chapters dropped Delphi 1 developer, you
you’ll discover Delphi 2 a large section on game and from the original edition. should get a copy of Delphi
Unleashed shares little with its Internet development. However, these chapters are Unleashed.
highly successful predecessor. Other sections include dis- formatted for typesetting,
“Delphi 2 Unleashed Aimed at
cussions about Delphi’s data and aren’t nearly as easy to Advanced Developers”
Make no mistake, Delphi 2 types, creating and using read as printed material. continued on page 47

Unleashed is a major new


book on Delphi 2. But be
forewarned, this book won’t The New Delphi 2 Programming EXplorer
be much help if you’re only The New Delphi 2 shipped with a CD-ROM of
working in Delphi 1. If Programming EXplorer by Jeff Delphi-related material. In
you’re not using Delphi 2, Duntemann, Jim Mischel, those reviews, I stated the
you’ll be better served with a and Don Taylor [Coriolis authors had done a great job
book that exclusively covers Group Books, 1996] is a introducing Delphi to begin-
Delphi 1, or even one that revised and expanded edition ning programmers. The
covers both products. of Delphi Programming book was divided into three
EXplorer [Coriolis Group sections: the basic mechanics
If you want to learn about Books, 1995]. The first edi- of using Delphi; object-ori-
Delphi 2, however, this tion was an excellent intro- ented Windows program-
book requires serious con- duction to Delphi program- ming with Delphi; and data-
sideration. The fundamen- ming. In this edition, the base application develop- ond edition. The first three
tal reason is its extensive authors have added 200 ment with Delphi. The final quarters of the book are
coverage of the Win32 pages, covering SQL data- section was presented in an essentially unaltered,
API. If you’re primarily a base programming, custom atypical, yet refreshing man- although the authors added
Windows 3.x developer, Delphi components, ner: Taylor cast it as a detec- small sections describing fea-
this material will give you a Windows messages, and tive story starring Ace tures of Delphi 2 that differ
detailed, in-depth look at Delphi exception handling. Breakpoint, a hard-boiled significantly from Delphi 1.
this powerful new environ- They have done a good job, private eye struggling to turn They also re-shot the screen
ment. While you could and the book adheres to the into a sensitive, new-age captures to show the new
consider a book strictly on standards of the first edition. software consultant. This was look of Delphi 2 running
Windows 95 or Windows a great way to cover a lot of under Windows 95, and cor-
NT programming, Delphi I reviewed the original edi- ground quickly, in a manner rected minor errors in the
2 Unleashed excels because tion twice in Delphi that kept readers engaged. text and program listings.
it discusses these topics Informant: in August 1995 “The New Delphi 2
with respect to Delphi 2. upon its initial release, and The authors retained this Programming EXplorer”
continued on page 47
And Charles Calvert is in a October 1995 when it successful formula in the sec-
46 October 1996 Delphi Informant
TextFile
Delphi 2 Unleashed Aimed at Advanced Developers (cont.)
Delphi 2 Unleashed is aimed ing compared to Delphi 2 book when they’re interested API. Although this book is
strictly at advanced develop- Unleashed. This book is huge in only a few of the topics? big and expensive, it deserves
ers because of its detailed and, I think, unwieldy. I your serious consideration.
discussions of many high- simply cannot take this book Finally, there is the writing And don’t be fooled by the
end topics. By comparison, on the road. At over five style. If you enjoyed Calvert’s title — this book may be for
Delphi Unleashed was an pounds, and a thickness chatty, conversational style in you, even if you own the ear-
excellent book for begin- exceeding two inches, it Delphi Unleashed, you won’t lier edition.
ning/intermediate Delphi seems more like a weapon be let down here. Some read-
developers, while still con- than reading material. ers, however, will find this — Cary Jensen, Ph.D.
taining enough detail to sat- casual style of writing dis-
isfy advanced developers. This book is also expensive. tracting and, given the size of Delphi 2 Unleashed
While you can easily get it for this book, unnecessary. by Charles Calvert, SAMS
I must admit, as much as I less than its US$59.99 cover Publishing, 201 West 103rd
like this book, there are sev- price, it remains the most But don’t misunderstand me. Street, Indianapolis, IN
eral aspects I don’t care for. expensive mass-market com- Delphi 2 Unleashed makes an 46290, (800) 428-5331.
The first is its size. The origi- puter book I’ve seen. Why outstanding addition to the
nal edition, as well as several isn’t this two books? Let’s get library of Delphi 2 develop- ISBN: 0-672-30858-4
of its competitors, were big real. How many readers want ers, particularly those rela- Price: US$59.99
books. But they were noth- to lug around a 1,400-page tively new to the Win32 (1,400 pages, CD-ROM)

The New Delphi 2 Programming EXplorer (cont.)


The new material starts in The fourth part of The New Delphi forms that are their error handling fails.
Part 3 (Chapter 15), where Delphi 2 Programming “aware” of each other; they It’s easy to not have good
Mischel introduces SQL EXplorer contains the com- can broadcast messages and error handling in such lan-
database programming con- plete text of Ace commands to each other. guages; but with Delphi,
cepts. In two well-written Breakpoint’s Database While interesting, I think it’s easy to build it in, and
chapters, Mischel and Adventure. This description this chapter needs a more this chapter demonstrates
Duntemann cover the of the design and imple- concrete example project; it how.
basics of using SQL and mentation of a complete works, but is unrelated to a
Delphi. The first demon- database package covers practical application. I used to recommend the
strates managing data using eight chapters, and more first edition of Duntemann,
SQL, the Borland Database than 200 pages. It is also an Chapter 28 does not suffer Mischel, and Taylor’s book
Engine, and the TQuery, entertaining story; Ace is a from such problems. It to anybody looking to get
TDBGrid, and TDataSource tough guy who experiences clearly shows how to work into Delphi programming.
components. For non-data- some anxious moments as with one of the most useful I don’t do that anymore.
base Delphi programmers, he tries to win a consulting parts of Delphi — the Now I recommend the sec-
this is a treasure-trove of contract — without losing exception mechanism for ond edition. It’s a fine
well-illustrated basic con- his girlfriend. handling errors. Using upgrade to an already excel-
cepts. The next chapter exceptions, you can trap lent work.
treats SQL as a database The original Ace any kind of error condi-
programming API, and Breakpoint story was a tion, from dividing by zero — Tim Feldman
provides in-depth coverage refreshing way to learn a to typing non-numeric
on how to use SQL with serious subject. I was characters in the edit field The New Delphi 2
Delphi. It covers enough delighted to see Taylor of a financial application. Programming EXplorer
material to learn how to added two new Breakpoint However, because many by Jeff Duntemann, Jim
create a real working SQL mini-adventures to this edi- older languages weren’t Mischel, and Don Taylor,
database project. This sec- tion. I won’t reveal the out- designed with error trap- Coriolis Group Books, 7339
tion also contains a tutorial come between Ace and his ping, it’s an unfamiliar con- E. Acoma Drive, Suite 7,
on creating custom Delphi girlfriend, but I will say cept for many program- Scottsdale, AZ 85260,
components by deriving that the new adventures mers. Adding error han- (800) 410-0192 or
them from existing delve into some of the dling in languages such as (602) 483-0192. Web Site:
component classes. Mischel more advanced areas of C is messy and boring, so http://www.coriolis.com.
uses personal anecdotes to Windows and Delphi. In developers often exclude it
keep the reader interested Chapter 27, Ace explores from their program’s first ISBN: 1-883577-72-1
in this moderately advanced the Windows messaging version. Or worse, they do Price: US$44.99
subject. system. He uses it to create an incomplete job, and (806 pages, CD-ROM)

47 October 1996 Delphi Informant


File | New
Directions / Commentary

Borland’s Annual Harbinger

he annual Borland Developers Conference (BDC) tends to be a harbinger of the company’s stand-
T ing each year. In 1994, a rather undisciplined keynote address by former CEO Phillipe Kahn typi-
fied the scatterbrained nature of the Borland of the early 1990s. 1995 was highlighted by a return to
respectability for the company, along with unbridled enthusiasm for the newly-released Delphi. This
year’s BDC, held in Anaheim in July, showcased Borland’s emerging technology in areas where the
software industry is rapidly moving — Internet/intranet and multi-tier architectures. To sum it up, I’ll
discuss four main impressions I have of BDC ’96.

Borland is well positioned for the desktop and two-tier client/server Delphi is maturing in the market-
emerging Internet marketplace. Its models, Delphi 97 aims to bring place. The task of establishing itself
new Web-related products — Latté multi-tier application servers, COM in a market ruled by Visual Basic
and IntraBuilder — drew a great (the Component Object Model), and PowerBuilder has been daunt-
deal of interest and enthusiasm at and Internet deployment into the ing, but Delphi is making definite
the conference. If this duo ships mainstream. You’ll learn more about inroads. Not only was this the
promptly, Borland can take a tech- Delphi 97 next month, but suffice largest BDC ever, of the 2,500 par-
nological lead in the Internet tools to say that as application architec- ticipants, 75 percent of them
market. Additionally, Borland is tures become more complex, Delphi attended Delphi sessions. Another
working closely with Sun and will look to encapsulate such tech- sign of a product’s acceptance is
Netscape on several Web technology nology into its environment. employment opportunities. While
fronts. These relationships have pro- newspapers continue to be dominat-
duced tangible results for Borland, The technology gap between soft- ed by PowerBuilder advertisements,
such as Sun’s adoption of Borland’s ware vendors and customers appears it was encouraging to see many
BAJA specification for the Java to be growing. By the nature of the more Delphi job postings than at
Beans Initiative, as well as business, software vendors will BDC ’95, including a representative
Netscape’s licensing of the Just-In- always be ahead of their customers of a Fortune 100 firm who intended
Time compiler for Navigator 3.0. on the technology curve. This year, to hire several hundred Delphi
Ironically, while firmly entrenched the chasm between Borland, developers during the conference. ∆
in the Sun/Netscape camp, Borland Netscape, and Microsoft and their
may have more to gain if Microsoft customers seems larger than ever. — Richard Wagner
succeeds in the Web wars, due to You have probably heard that an
their Windows orientation with Internet year is equal to three calen-
Delphi, IntraBuilder, and other dar months because of the rapid pace Visit the “File | New” home page at
products. of technology innovation in that http://www.acadians.com/filenew/-
market. While vendors race for the filenew.htm. In addition to down-
Delphi looks well poised to tackle leading edge, many customers are loading past articles, you can get the
the next generation of applications. still dealing with issues that are far latest tips and information from the
After working with Delphi 2, my more mundane. For example, in one world of software development.
immediate impression was “Where session I led on migrating to Delphi
could Borland possibly take this 2, the majority of developers in the Richard Wagner is Contributing Editor
product?” Besides minor enhance- audience were still building 16-bit to Delphi Informant and Chief
ments, how much more do you real- applications. Borland’s current Technology Officer of Acadia Software
ly need in a desktop development online survey of whether to develop in the Boston, MA area. He welcomes
environment? My horizons were a new 16-bit version of Delphi is a your comments at rwagner@-
broadened when I heard where they sign of the company’s realization that acadians.com, or on the File | New
are taking the next version, called the market is not moving as fast as home page at http://www.acadians.-
Delphi 97. Extending beyond the they’d like. com/filenew/filenew.htm.

48 October 1996 Delphi Informant

You might also like