Sie sind auf Seite 1von 39

January 2001, Volume 7, Number 1

Cover Art By: Arthur Dugoni

ON THE COVER
6

On the Net

Surfing from Delphi: Part I Bill Todd


Bill Todd shows us how to use IE as an Automation server, and just
to make it really easy provides a wrapper for many of the methods
in the IWebBrowser2 interface.

FEATURES
13

OP Tech

Pretty Good Privacy Mike Riley


Mike Riley provides an introduction to PGP for the Delphi developer,
from its installation and requirements, to a demonstration program, to
suggestions for its use.

16

Greater Delphi

Easy Pivot Tables Alex Fedorov and Natalia Elmanova


Alex Fedorov and Natalia Elmanova demonstrate the power and flexibility of PivotTable, one of the Microsoft Office Web Components, when
used from Delphi.

21

30

Sleuth QA Suite 2
Product Review by Alan C. Moore, Ph.D.

33

ExpressQuantumGrid and ExpressInspector


Product Review by Ron Loewy

Quick CLX

The Life and Death of TButton Robert Kozak


Borlands Robert Kozak begins his regular Kylix column. Focusing on CLX
(pronounced clicks), the cross-platform component library, he begins
with the humble TButton.

26

REVIEWS

At Your Fingertips

DBGrid Images, etc. Bruno Sonnino


Bruno Sonnino provides us with five tips, including: displaying images
in a DBGrid, Windows taskbar tips, accessing environment variables,
and justifying text.
1 January 2001 Delphi Informant Magazine

DEPARTMENTS
2
5
36
38

Delphi Tools
Newsline
Best Practices by Clay Shannon
File | New by Alan C. Moore, Ph.D.

Delphi
T O O L S
New Products
and Solutions

Book Picks
Learn Object Pascal with Delphi
Warren Rachele
Wordware Publishing, Inc.

ISBN: 1-55622-719-1
Cover Price: US$49.95
(358 pages, CD-ROM)

PGP: Pretty Good Privacy


Simson Garfinkel
OReilly

Starbase Announces StarEstimator


Starbase Corp. announced
the release of StarEstimator,
an automated project-planning,
risk-management, and cost-estimation solution. StarEstimator
is designed to ensure the delivery of software and Web development projects on time and
within budget. StarEstimator
automates the planning process, improving the probability
of aligning management expectations with actual project costs
and completion dates.
StarEstimator provides the
structure for defining a project, analyzing the size of the
application, specifying the
languages to be used, examining the teams capability,
addressing management constraints, and tabulating the
results and testing the methodology employed. Every project
is divided into eight phases:
analysis, design, coding, testing, validation, configuration
management and quality assurance, management, and documentation. Exact percentages
of the project estimation are
pre-allocated, but can be customized to meet individual
program requirements.
StarEstimator creates a
complete estimation of cost,
schedule, tasks, deliverables,
maintenance, and support
requirements for virtually any

type of software or Web project


regardless of language or platform. Once the project model
has been reviewed, rened, and
accepted by the project team, it
can be imported into Microsoft
Project and then into StarTeam,
the companys integrated technical collaboration and software
development management tool.
StarEstimator leverages seven
estimation methods and draws
from a database of more than
20,000 successful development
projects across a variety of projects and types.
StarEstimator allows users
to make environmental adjustments specic to their organization and run what-if
scenarios.

Xceed Software Releases FTP Library

ISBN: 1-56592-098-8
Cover Price: US$34.95
(393 pages)

2 January 2001 Delphi Informant Magazine

Xceed Software, Inc. released


Xceed FTP Library 1.0, a
new COM/ActiveX library that
allows developers to add exible FTP le transfer capabilities
to their Windows or Web applications. The library is designed
to take advantage of the WinSock 2 networking API.
The Xceed FTP Library
includes an easy-to-use programming interface, documentation,
prompt technical support, optimized code, exibility, and reliable operation.
The Xceed FTP Library supports the complete FTP command set and complies with
RFC 959, 1123, and 1579
standards, making it rewallfriendly and compliant with

FTP standards. The library also


includes many advanced features, including silent background processing, support for
both single-threaded (STA)
and multi-threaded apartment
(MTA) threading models,
custom interface advising,
sending and receiving les
to/from memory, and the ability to manually parse server
directory listings.
The product ships with
documentation, technical support, and a 60-day money-back
guarantee.
Xceed Software, Inc.
Price: US$149.95
Phone: (800) 865-2626
Web Site: http://www.xceedsoft.com

Starbase Corp.
Price: US$2,499 for a single user.
Phone: (888) 782-7700
Web Site: http://www.starbase.com

NAG Releases
NAG Components
for Delphi 1.4
NAG Software Solutions
announced the release of NAG
Components for Delphi 1.4,
xing minor bugs, improving
overall functionality, and
including new features and
components, such as the
TNAGSpinEdit component and
TNAGSpeedButton components, and a new TNAGButton
that is glyph-capable.
All registered users of NAG
Components for Delphi v1.3
are eligible for a free upgrade.
NAG Components for Delphi
is an easy-to-use, one-stop suite
of 100 percent native, VCLbased components for Borland
Delphi. The entire package
features over 40 components,
including visual controls for
enhancing the user interface
of applications and non-visual
components that allow developers to interact with the
end-users system without any
knowledge about the low-level
Windows API.
NAG Software Solutions
Price: US$69.95
E-Mail: sales@nagsoftware.com
Web Site: http://www.nagsoftware.com/
products/nagcomponents/

Delphi
T O O L S
New Products
and Solutions

Book Picks
Programming Microsoft
Internet Explorer 5
Scott Roberts
Microsoft Press

ISBN: 0-7356-0781-8
Cover Price: US$49.99
(511 pages, CD-ROM)

Linux in a Nutshell, Third Edition


Ellen Siever, Stephen Spainhour,
Stephen Figgins, Jessica P. Heckman
OReilly

ShazamWare Releases Shazam Power Query and Report Wizard 4.0


ShazamWare Data Systems, Inc.
released Shazam Power Query 4.0
and Shazam Report Wizard 4.0.
Shazam Power Query (SPQ) is the
advanced visual SQL builder component for Borland Delphi and
C++Builder. SPQ seamlessly integrates with the Borland Database
Engine (BDE) and other BDE
alternatives that support SQL. It
also supports all TDataset-compatible controls, charting components, and report writers.
SPQ includes a new Query-ByExample (QBE) interface, which
supports visual table joins, outer
joins, drag-and-drop eld selection, custom eld expressions,
aggregate calculations, and more.
Developers can create their own
query metaphor with SPQs open
architecture.
The Shazam Data Model
component stores database information, allowing developers to
change or alias table and eld
names without affecting the database. Developers can also automate table joins, eliminating the
need for end-users to join tables
themselves.
ShazamWare also released
Shazam Report Wizard (SRW)
4.0, which expands SPQs query
capabilities with visual layout and
presentation tools.
Shazam Report Wizard 4.0
also includes Reports Explorer, a
tool that manages a library of
reports with a Windows Explorer-

like interface. Reports are saved to


a local or client/server table without special coding.
SRW includes a preview
component with 10:1 compression for distributing reports via
e-mail and the Web which does
not require DLLs.
Shazam Power Query 4.0 and
Shazam Report Wizard 4.0 support Delphi and C++Builder 1.0
through 5.0. BDE alternatives

are supported in Delphi and


C++Builder 3.0 through 5.0. Both
products include all of their
Delphi source code, user and
developer help les, and a royaltyfree executable distribution license.
ShazamWare Data Systems, Inc.
Price: Shazam Power Query 4.0, US$249;
Shazam Report Wizard 4.0, US$349.
E-Mail: shazam@shazamware.com
Web Site: http://www.shazamware.com

Zero G Ships InstallAnywhere 3.5

ISBN: 0-596-00025-1
Cover Price: US$34.95
(797 pages)

3 January 2001 Delphi Informant Magazine

Zero G Software, Inc.


announced InstallAnywhere 3.5,
which includes such enhancements as full Java 2, version 1.3
compatibility, and Linux support. InstallAnywhere 3.5 provides software producers with
powerful solutions for installing
and conguring software across
multiple platforms.
The InstallAnywhere 3.5 family
the Enterprise Edition, Standard Edition, and Now! provides complicated multi-system
deployment with an easy-to-use
interface. InstallAnywhere Enterprise Edition 3.5 solves multi-platform deployment problems and
provides customization with international support for 29 languages.

InstallAnywhere builds professional, multi-platform installers


capable of installing and conguring software on virtually any client
or server platform. Each installer
created by InstallAnywhere recognizes the platform under which it
is operating and then tailors its
installation to the target system,
whether by creating shortcuts in
the Windows Start menu, setting
custom icons on the Mac OS,
starting Windows NT/2000 services, setting Unix environment
variables, or modifying le permissions on Unix systems. Software producers no longer need to
build separate installers for each
platform.
Developers can rely on Install-

Anywhere to create universal


installers that can deploy software
to multiple platforms via the
Internet or CD-ROM; build
installers for platforms including
Windows 95, 98, ME, 2000 and
NT, Mac OS, Solaris, Linux,
AIX, HP-UX, and any other
Java-enabled platform; install a
Java virtual machine as part of an
applications install; and automatically build Web Installer HTML
pages for deploying software via
intranets and the Internet.
Zero G Software, Inc.
Price: Enterprise Edition, US$1,995; Standard Edition, US$995; Now! is free.
E-Mail: sales@ZeroG.com
Web Site: http://www.ZeroG.com

Delphi
T O O L S
New Products
and Solutions

Book Picks
Linux System Administrators
Survival Guide, Second Edition
Tim Parker
SAMS

ISBN: 0-672-31793-1
Cover Price: US$49.99
(740 pages)

Learn Red Hat Linux Security


George M. Doss
Wordware Publishing, Inc.

ISBN: 1-55622-773-6
Cover Price: US$39.95
(314 pages, CD-ROM)

4 January 2001 Delphi Informant Magazine

WinA&D Delivers Enhanced Data Models


Excel Software extended the data
modeling and SQL code generation capabilities in its WinA&D
modeling tool. Database designers
can create logical and physical
data models and generate the
SQL schema code for RDBMS
products, including Oracle, SQL
Server, DB2, Sybase, Informix,
and InterBase. Rich data models
can represent tables, views,
constraints, assertions, triggers,
indexes, procedures, and other
SQL elements. Other general
enhancements include diagram
scaling from 25 percent to 200
percent, increased dictionary
capacity, and a convenient Notes
window for attaching notes to diagram objects in any type of model.
WinA&D is a tool for
system analysis, requirement specications, software design, and
code generation. Popular modeling notations and supported
methods include object-oriented
analysis and design with UML
or structured analysis and design
using Yourdon/DeMarco. Information Engineering (Martin) style
data models are used to develop

information systems. Model editors are complimented with verication and balancing reports, code
generation, reengineering capabilities and scriptable HTML
reporting features.
Logical data models often use
expressive names and various presentation options to communicate
information between developers.
The names of database objects like
tables, views, and columns are typically more constrained and cryptic in physical data models due
to the RDBMS limitations and
the legacy systems that they represent. WinA&D allows designers
to easily toggle between logical
and physical models and show
entities (tables) with full attribute
lists, primary and foreign keys
only, or customized to show
specic attributes.
WinA&Ds namespace concept
now supports data modeling, SQL
code generation, and reengineering of different database schemas.
Namespaces simplify team development, enable powerful organizational and reporting tools,
partition complex systems, and

Excel Software
Price: Standard, US$495; Educational,
US$845; Desktop, US$1,295; and Developer, US$1,995.
E-Mail: info@excelsoftware.com
Web Site: http://www.excelsoftware.com

Quasidata Announces
DbAltGrid Suite 1.2

NNWFax compiles directly into


your application without DLLs or
extra printer drivers; it includes
full source code, an extensive
help le, and an instructive demonstration application; can fax
in silent mode without showing
WinFax Pro; and more.

Quasidata announced the


availability of DbAltGrid Suite
1.2, the latest release of their
data-aware grid for Delphi and
C++Builder.
New features include RTF eld
support, auto-scaling of column
widths, in-place editing of memo
elds within a cell, the display of
text in a hint window, and the
indication of a URL in a cell.
DbAltGrid allows free-form
layout (i.e. multiple lines of cells
within a row), hierarchical columns structure, alternate row
coloring, and comfortable editing of numeric, Boolean, date,
time, memo, rich-formatted
memo, and graphic elds. It
also provides the possibility of
displaying record summaries as
RTF text.
DbAltGrid is a DBGrid
descendant, and is therefore
compatible. Converts to DbAltGrid will nd no code conict or
loss in grid functionality.

Fractal Software
Price: US$65
E-Mail: info@fractal.nl
Web Site: http://www.fractal.nl

Quasidata
Price: US$145
E-Mail: info@dbaltgrid.com
Web Site: http://www.dbaltgrid.com

Fractal Releases NNWFax


Fractal Software
announced the
release of NNWFax,
a Delphi/
C++Builder component for adding
fax functionality to
your applications.
Using COM
interfacing,
NNWFax connects
directly to WinFax
Pro. Drop an
NNWFax component on your form,
set the properties,
and call the SendFax method. Its
that simple to send faxes. No
need to bother about com ports
or ISDN settings. It is guaranteed
to work on any system that runs
WinFax Pro.
This is an advantage over
other communication toolboxes
which still need hardware programming and debugging. Using
WinFax Pros client/host functionality faxing over the network
is just as easy.

map design elements to specic


SQL schemas.
Generated SQL can be customized to include primary, foreign, and alternate key constraints,
and check constraints in either
CREATE TABLE or ALTER
TABLE statements. Other SQL
elements can selectively generate
DROP or CREATE statements
and RDBMS specic triggers, procedures, functions, and methods.
WinA&D runs on Windows 95,
98, NT, or 2000. Data models
created in earlier versions are automatically converted to use the
enhanced data modeling features
in the Desktop, Educational, and
Developer editions.

News

Inprise/Borland Expands Application Server


Management Offering with AppCenter 4

San Mateo, CA Inprise/


Borland announced the availability of Inprise AppCenter 4, a
new management platform that
aims to maximize the uptime of
Web-based applications, including those based on CORBA
and Enterprise JavaBeans (EJB).
Through continuous application
component monitoring, load
balancing, and fault recovery,
AppCenter 4 manages, maintains,
and corrects problems when no
technician is available.
Inprise AppCenter 4 allows customers to dene a logical map
of the application, along with
the relationships and dependencies between each element. This
enables the distributed application components to be maintained across the network on any
type of machine. Once dened,
Inprise AppCenter 4 monitors
each component to ensure that
the overall application remains
available. If any one object fails,
Inprise AppCenter will automatically start up a new object on a
separate hardware platform if necessary.
The main new features of
Inprise AppCenter 4 include the
ability to model and manage all
aspects of EJB-based applications
deployed using Inprise Application Server 4, including EJB containers and Web servers; dynamic
load balancing to ensure appli-

January 2001

cation performance; integration


with other major management
platforms through SNMP MIB
compatibility; wizards and autodiscovery to simplify modeling
CORBA infrastructures based on
VisiBroker; and the ability to
import and export XML for
easy sharing of Inprise AppCenter
congurations.
More information is available at
http://www.borland.com.

5 January 2001 Delphi Informant Magazine

Collingwood, Australia
The Australian Delphi User
Group (ADUG) has recently
created an e-mail job advertising service designed especially
for programmers and employers in Australia.
For those who would like
to keep an eye on the Delphi
job market in Australia, visit
http://www.adug.org.au/jobs/
programmers.htm.

Inprise/Borland, Computer Sciences, and CiTR Offer


Rapid Delivery of Customer Service Portals
Sydney and Canberra, Australia
Inprise/Borland, Computer
Sciences Corp. (CSC), and CiTR
announced an Australian alliance
designed to speed the adoption
and deployment of Internet-based
federated portals.
Focusing primarily on the needs
of Australian federal, regional, and
local governments, the alliance
aims to develop, implement, host,
and maintain a minimum of 10
government portals by mid-2001
to become the major supplier
of federated portals to Australian
government.
CSC will provide the alliances
customers with a complete Web
portal hosting and services delivery capability.
CiTRs DECADE AccessPoint
speeds up the process of portal
development and is suited for

Delphi Informant Magazine Readers Offered Special


Extension on Discounts from Monarch Bay Software
Houston, TX Monarch
Bay Software announced special
discounts for HelpTrac 7.0 and
HelpTrac LITE, help desk software programs that enable businesses to manage customer
service desks. HelpTrac 7.0 and
HelpTrac LITE are full-featured
programs for help desk and
customer support management,
allowing for management of help
desk problems and solutions,
tracking of calls, producing management information, and building problem/solution histories.
Readers of Delphi Informant
Magazine will receive a special discount on purchases of HelpTrac

ADUG Provides Job


Advertising Service

7.0 or HelpTrac LITE through


January 31, 2001, with a 20 percent discount on all licenses. In
addition, the Monarch Bay Software support and enhancement
agreement will be free from the
purchase date through March 31,
2001. This special offer includes
all HelpTrac 7.0 and HelpTrac
LITE updates and new releases.
Pricing for HelpTrac 7.0 for
one license is US$960. HelpTrac
LITE for one license is US$320.
There is a price reduction for
multiple licenses. For more information, visit the Monarch Bay
Software Web site at http://
www.helptrac.com/index.htm.

government organizations because


it ensures easy location of the
various services and the vast
amounts of information governments manage, connecting them
seamlessly across all layers and
levels of government.
At the heart of DECADE
AccessPoint is Borland Application Server technology from
Inprise/Borland, providing the
platform on which a scalable,
highly-reliable Web-based architecture is built.

Cast Your Vote for


the Delphi Informant
Magazine Readers
Choice Awards
Elk Grove, CA Delphi
Informant Magazine announced
the ofcial ballot for the 2001
Delphi Readers Choice Awards
is now available online at http://
www.DelphiZine.com.
Each year, Delphi Informant
Magazine recognizes outstanding products and vendors in
the Delphi add-on market.
Please take a moment to select
your favorite Delphi tools.
Simply complete the online
ballot on the Delphi Informant
Magazine Web site at http://
www.DelphiZine.com.
This is your chance to voice
your opinions regarding the tools
and products you use in your
everyday development efforts.
Voting ends January 31, 2001.
The results of this survey will be
published in the April 2001 issue
of Delphi Informant Magazine.

On the Net
Internet Explorer / Delphi

By Bill Todd

Surfing from Delphi


Part I: Using Internet Explorer as an Automation Server

nteracting with the Web is becoming a more common requirement for applications
every day. Part one of this two-part series looks at controlling Internet Explorer via
Automation from your Delphi application. Youll use a custom component to wrap the
IWebBrowser2 interface. In part two, youll look at embedding a browser in your application using the TWebBrowser control.

Using ShellExecute

Controlling Internet Explorer

Before diving into Internet Explorer, lets look at


a simple solution. If you only need to display a
Web page in a browser, the Windows ShellExecute
API function will do the job. Figure 1 shows a
simple example of using ShellExecute to display a
Web page using the users default browser. To use
ShellExecute you have to add the ShellAPI unit
to your uses clause. The straightforward result is
shown in Figure 2.

Microsoft provides the IWebBrowser2 interface


to control Internet Explorer or the Microsoft
Web Browser ActiveX control. Borland makes
IWebBrowser2 a snap to use because the interface
and all of its methods are declared in the ShDocVw
unit. For documentation on IWebBrowser2 go
to http://msdn.microsoft.com/workshop/browser/
webbrowser/reference/IFaces/IWebBrowser2/
IWebBrowser2.asp. However, note that the methods whose names begin with put_ in in the documentation are named set_ in in the ShDocVw unit.

procedure TForm1.Button1Click(Sender: TObject);


var
FileName: string;
begin
FileName := 'www.borland.com';
ShellExecute(Application.MainForm.Handle, nil,
PChar(FileName), nil, nil, SW_SHOW);
end;

Figure 1: Showing a Web page using ShellExecute.

To make working with Internet Explorer easier,


I wrote a TIEController component that serves
as a wrapper for many of the methods in the
IWebBrowser2 interface (all of the code is shown
in Listing One beginning on page 9). Figure 3
shows the public methods of the component, while
Figure 4 shows its public properties. The properties
are public, not published, because they are really
properties of Internet Explorer and cannot be set
at design time.
The first step in using Internet Explorer is to open
it. This is done by the IEOpen method:
procedure TIEController.IEOpen;
begin
FIE := CoInternetExplorer.Create;
end;

Figure 2: The result of the code in Figure 1. Looks familiar.

6 January 2001 Delphi Informant Magazine

This method calls the Create method of the


CoInternetExplorer class declared in the ShDocVw
unit, which opens an instance of Internet Explorer
and returns a reference to its IWebBrowser2 interface. FIE is a private member variable of the

On the Net
Method/Property

Description

GetIEWindowHandle

Returns the window handle of the Internet


Explorer window.
Closes Internet Explorer.
Goes to the previous page.
Goes to the next page.
Goes to the home page.
Goes to the search page.
Opens Internet Explorer.
Prints the current page.
Previews the current page.
Refreshes the current page.
Saves the current page.
Saves the current page under a new name
or type.
Opens Internet Explorer and sets its visible
property to True.
Stops the current operation.
Returns True if Internet Explorer is busy
downloading a page.
Returns True if Internet Explorer is open.

IEClose
IEGoBack
IEGoForward
IEGoHome
IEGoSearch
IEOpen
IEPrint
IEPrintPreview
IERefresh
IESave
IESaveAs
IEShow
IEStop
IsIEBusy
IsIEOpen

Figure 3: TIEControllers public methods.


Property

Description

IEAddressBarVisible
IEFullScreen

Whether the address bar is visible.


Whether the Internet Explorer window
occupies the full screen.
The height of the Internet Explorer
window.
The position of the left edge of the Internet
Explorer window.
Whether the menu bar is visible.
Whether Internet Explorer is offline.
Whether the Internet Explorer window is
resizable.
If True, no dialog boxes will be shown.
Whether the status bar is visible.
The text shown in the Internet Explorer
status bar.
If True, Internet Explorer is in Theater
mode. In Theater mode, no menu, toolbar,
address bar, or status bar are shown.
Whether the toolbar is visible.
The position of the top edge of the Internet
Explorer window.
Whether the Internet Explorer window is
visible.
The width of the Internet Explorer window.
The URL of the current page.

IEHeight
IELeft
IEMenuBarVisible
IEOffline
IEResizable
IESilent
IEStatusBarVisible
IEStatusText
IETheaterMode

IEToolbarVisible
IETop
IEVisible
IEWidth
IEURL

Figure 4: TIEControllers public properties.

TIEController component whose type is IWebBrowser2. If you want


to open an instance of Internet Explorer on another machine using
DCOM, call CoInternetExplorer.CreateRemote and pass the machine
name as the parameter.
Once Internet Explorer is open, you can use the FIE interface reference variable to call any of the methods of the IWebBrowser2 interface. A simple example is the IERefresh method. This method tells
Internet Explorer to refresh the current page just as if the user had
clicked the Refresh button:
7 January 2001 Delphi Informant Magazine

procedure TIEController.IERefresh;
begin
if not IsIEOpen then IEShow;
FIE.Refresh;
end;

The rst line of code requires some explanation. One of the problems
with writing a component that provides an interface to an Automation server is deciding what to do if a programmer calls one of
the methods or accesses one of the properties without rst opening
the server. There are two choices: The component can either create
an instance of the server if one does not exist, or it can raise an
exception. In this case, I decided to open the server and make it
visible by calling the IEShow method.
There is a second problem with Internet Explorer: how to determine
if an instance of Internet Explorer has already been created. Setting
the interface reference variable, FIE, to nil when the IEClose method
is called will not work because the user can close Internet Explorer.
This means the TIEController component must check if Internet
Explorer is still open before calling its methods or accessing its
properties. The IsIEOpen and GetIEWindowHandle methods shown
in Figure 5 meet this need.
The IsIEOpen method simply calls GetIEWindowHandle and
returns True if the window handle is not zero. GetIEWindowHandle
rst checks to see if the interface reference, FIE, is nil. If it is
nil, the method returns zero. If not, the IWebBrowser2
Get_HWND method is called to get the window handle. If
Internet Explorer is not open, calling Get_HWND will cause an
EOleException, which is handled by the try..except block by setting the result to zero.
There are some other surprising cases where error handling is required.
One would assume that the IWebBrowser2 GoForward and GoBack methods would simply do nothing if there was no page in the history list to
which to move. Instead, they raise an exception. Therefore, the IEGo...
methods of TIEController use a try..except block, as shown in Figure 6, to
swallow the exception if you try to go to a page that doesnt exist.
One more method deserves special mention: IsIEBusy. IsIEBusy returns
True if Internet Explorer is busy downloading a page, or engaged in
some other activity. You must check if Internet Explorer is busy before
you try to do anything to the current page, such as print it.
Many of the methods of the IWebBrowser2 interface are getter and
setter methods for properties. TIEController implements these as
Delphi properties whose getter and setter methods call the corresponding IWebBrowser2 methods. A typical example is the IEAddressBarVisible
property with its getter and setter methods, as shown in Figure 7.

Understanding ExecWB
If you look at the methods of the IWebBrowser2 interface, you will
notice that a lot of methods you would expect to nd are missing.
For example, there is no Print or SaveAs method. The reason is
that there are no corresponding functions in Internet Explorer
itself. Internet Explorer is just a simple application that hosts
the Microsoft Web Browser ActiveX control. It is the browser
control inside Internet Explorer that actually provides many of the
functions you nd on Internet Explorer menus and toolbars. To
allow Internet Explorers Automation clients to access the methods
of the embedded Web browser control, the IWebBrowser2 interface
includes a method called ExecWB.

On the Net
function TIEController.IsIEOpen: Boolean;
begin
if GetIEWindowHandle = 0 then
Result := False
else
Result := True;
end;
function TIEController.GetIEWindowHandle: HWND;
begin
try
if Assigned(FIE) then
Result := FIE.Get_HWND
else
Result := 0;
except
Result := 0;
end;
end;

Figure 5: Determining if Internet Explorer is open.


procedure TIEController.IEGoBack;
begin
if not IsIEOpen then IEShow;
try
FIE.GoBack;
except
end;
end;

Figure 6: Trapping the exception in the IEGo... methods.

ExecWB takes four parameters. The rst is an integer that identies


the command you want the browser to execute. The constants for
these commands are declared in the ShDocVw unit, and all begin
with OLECMDID_. The second parameter, which is also an integer,
species a command option. Again, the constants for these options
are declared in ShDocVw, but the command option constants begin
with OLECMDEXECOPT_. The third and fourth parameters contain any input or output parameters.
The problem with using ExecWB is that the version of the browser you
are controlling may not implement all of the OLECMDID constants
dened in ShDocVw. To solve this problem Microsoft provides the
QueryStatusWB method. QueryStatusWB takes an OLECMDID as a
parameter and returns a value that indicates the status of the command. The possible return values are also dened as constants in
ShDocVw and they begin with OLECMDF_. The only ones you need
to use are OLECMDF_SUPPORTED and OLECMDF_ENABLED.
When you call QueryStatusWB, the value returned must equal
OLECMDF_SUPPORTED + OLECMDF_ENABLED or you
cannot use that command ID in a call to ExecWB.
The TIEController component uses these methods in its protected
IEExecWB method, as shown in Figure 8. IEExecWB takes the same
four parameters as ExecWB. It begins by calling QueryStatusWB and
passing the command ID as the parameter. If the value returned
by QueryStatusWB does not equal OLECMDF_SUPPORTED +
OLECMDF_ENABLED, it raises an EIEControlExecError exception.
EIEControlExecError is a custom exception class dened at the beginning of the TIEController type declaration section. If the command
status is correct, ExecWB is called. If ExecWB raises an EOleException,
an EIEControlExecError exception is raised.
Figure 9 shows the IEPrint method which calls IEExecWB with the
appropriate command ID. In this example, the method takes a
Boolean parameter that controls whether the printer selection dialog
box is displayed. If the ShowDlg parameter is True, the method
8 January 2001 Delphi Informant Magazine

property IEAddressBarVisible: Boolean


read GetIEAddressBarVisible
write SetIEAddressBarVisible;
function TIEController.GetIEAddressBarVisible: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_AddressBar;
end;
procedure TIEController.SetIEAddressBarVisible(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_AddressBar(Value);
end;

Figure 7: The IEAddressBarVisible property and its methods.


function TIEController.IEExecWB(CmdId, CmdOption: Integer;
VarIn, VarOut: OleVariant): Boolean;
begin
if FIE.QueryStatusWB(CmdId) =
OLECMDF_ENABLED + OLECMDF_SUPPORTED then
try
FIE.ExecWB(CmdId, CmdOption, VarIn, VarOut)
except
raise EIEControlExecError(
'Command execution failed. (Id: ' +
IntToStr(CmdId) + ')');
end
else
raise EIEControlExecError.Create(
'The function you requested is not ' +
'available. (Id: ' + IntToStr(CmdId) + ')');
end;

Figure 8: The IEExecWB method.


procedure TIEController.IEPrint(ShowDlg: Boolean);
var
V: OleVariant;
begin
if ShowDlg then
IEExecWB(OLECMDID_PRINT,
OLECMDEXECOPT_DODEFAULT, V, V)
else
IEExecWB(OLECMDID_PRINT,
OLECMDEXECOPT_DONTPROMPTUSER, V, V);
end;

Figure 9: The IEPrint method.

Figure 10: This test application demonstrates how to control IE


as an Automation server.

calls IEExecWB with a command ID of OLECMDID_PRINT,


and an option of OLECMDEXECOPT_DODEFAULT. If ShowDlg
is False, the command option constant is changed to
OLECMDEXECOPT_DONTPROMPTUSER. The methods of

On the Net
TIEController do not implement all of the available command IDs, but
do implement the ones youre most likely to need. You can easily add
any others you want to use.

Conclusion
If you need to give users the ability to view information on the Web,
controlling Internet Explorer through Automation provides an easy
solution. It lets you open the browser and take the user anywhere,
and then turn control over to the user to work as they wish. You can
also go to a page on the Web and save or print it without the user
ever seeing Internet Explorer. The sample software that accompanies
this article includes both the TIEController component, and a test
application that shows how to use it (see Figure 10).
The les referenced in this article are available on the Delphi
Informant Magazine Complete Works CD located in INFORM\01\
JAN\DI200101BT.

Bill Todd is president of The Database Group, Inc., a database consulting and
development firm based near Phoenix. He is co-author of four database programming books, author of more than 60 articles, a contributing editor to Delphi
Informant Magazine, and a member of Team Borland, providing technical support
on Borland Internet newsgroups. He is a frequent speaker at Borland Developer
Conferences in the US and Europe. Bill is also a nationally-known trainer and
has taught Delphi programming classes across the country and overseas. He is
currently a speaker on the Delphi Development Seminars Kylix World Tour. Bill can
be reached at bill@dbginc.com. For more information on the Kylix World Tour,
visit http://www.DelphiDevelopmentSeminars.com.

Begin Listing One The TIEController component


unit IEController;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, shdocvw;
type
EIEControlError = class(Exception);
EIEControlExecError = class(EIEControlError);
TIEController = class(TComponent)
private
{ Private declarations }
FIE: IWebBrowser2;
function GetIEAddressBarVisible: Boolean;
function GetIEFullScreen: Boolean;
function GetIEHeight: Integer;
function GetIELeft: Integer;
function GetIEMenuBarVisible: Boolean;
function GetIEOffLine: Boolean;
function GetIEResizable: Boolean;
function GetIESilent: Boolean;
function GetIEStatusBarVisible: Boolean;
function GetIEStatusText: string;
function GetIETheaterMode: Boolean;
function GetIEToolbarVisible: Boolean;
function GetIETop: Integer;
function GetIEVisible: Boolean;
function GetIEWidth: Integer;
function GetIEURL: string;
procedure SetIEAddressBarVisible(const Value: Boolean);
procedure SetIEFullScreen(const Value: Boolean);
procedure SetIEHeight(const Value: Integer);

9 January 2001 Delphi Informant Magazine

procedure SetIELeft(const Value: Integer);


procedure SetIEMenuBarVisible(const Value: Boolean);
procedure SetIEOffline(const Value: Boolean);
procedure SetIEResizable(const Value: Boolean);
procedure SetIESilent(const Value: Boolean);
procedure SetIEStatusBarVisible(const Value: Boolean);
procedure SetIEStatusText(const Value: string);
procedure SetIETheaterMode(const Value: Boolean);
procedure SetIEToolbarVisible(const Value: Boolean);
procedure SetIETop(const Value: Integer);
procedure SetIEVisible(const Value: Boolean);
procedure SetIEURL(const Value: string);
procedure SetIEWidth(const Value: Integer);
protected
{ Protected declarations }
function IEExecWB(CmdId, CmdOption: Integer;
VarIn, VarOut: OleVariant): Boolean;
public
{ Public declarations }
procedure IEClose;
function GetIEWindowHandle: HWND;
procedure IEGoBack;
procedure IEGoForward;
procedure IEGoHome;
procedure IEGoSearch;
function IsIEBusy: Boolean;
function IsIEOpen: Boolean;
procedure IEOpen;
procedure IEPrint(ShowDlg: Boolean);
procedure IEPrintPreview;
procedure IERefresh;
procedure IESave;
procedure IESaveAs(FileName: string);
procedure IEShow;
procedure IEStop;
property IEAddressBarVisible: Boolean
read GetIEAddressBarVisible
write SetIEAddressBarVisible;
property IEFullScreen: Boolean
read GetIEFullScreen write SetIEFullScreen;
property IEHeight: Integer
read GetIEHeight write SetIEHeight;
property IELeft: Integer
read GetIELeft write SetIELeft;
property IEMenuBarVisible: Boolean
read GetIEMenuBarVisible write SetIEMenuBarVisible;
property IEOffline: Boolean
read GetIEOffline write SetIEOffline;
property IEResizable: Boolean
read GetIEResizable write SetIEResizable;
property IESilent: Boolean
read GetIESilent write SetIESilent;
property IEStatusBarVisible: Boolean
read GetIEStatusBarVisible
write SetIEStatusBarVisible;
property IEStatusText: string
read GetIEStatusText write SetIEStatusText;
property IETheaterMode: Boolean
read GetIETheaterMode write SetIETheaterMode;
property IEToolbarVisible: Boolean
read GetIEToolbarVisible write SetIEToolbarVisible;
property IETop: Integer read GetIETop write SetIETop;
property IEVisible: Boolean
read GetIEVisible write SetIEVisible;
property IEWidth: Integer
read GetIEWidth write SetIEWidth;
property IEURL: string read GetIEURL write SetIEURL;
published
{ Published declarations }
end;
procedure Register;
implementation
uses ComObj;

On the Net
procedure Register;
begin
RegisterComponents('DGI', [TIEController]);
end;

function TIEController.GetIEMenuBarVisible: Boolean;


begin
if not IsIEOpen then IEShow;
Result := FIE.Get_MenuBar;
end;

{ TIEController }
function TIEController.IEExecWB(CmdId, CmdOption: Integer;
VarIn, VarOut: OleVariant): Boolean;
var
q: Integer;
begin
if FIE.QueryStatusWB(CmdId) = OLECMDF_ENABLED +
OLECMDF_SUPPORTED then
try
FIE.ExecWB(CmdId, CmdOption, VarIn, VarOut)
except
raise EIEControlExecError(
'Command execution failed. (Id: ' +
IntToStr(CmdId) + ')');
end
else
raise EIEControlExecError.Create(
'The function you requested is not ' +
'available. (Id: ' + IntToStr(CmdId) + ')');
end;
procedure TIEController.IEClose;
begin
if IsIEOpen then begin
FIE.Quit;
FIE := nil;
end;
end;
function TIEController.GetIEHeight: Integer;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Height;
end;
function TIEController.GetIELeft: Integer;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Left;
end;
function TIEController.GetIEWindowHandle: HWND;
begin
try
if Assigned(FIE) then
Result := FIE.Get_HWND
else
Result := 0;
except
Result := 0;
end;
end;
function TIEController.GetIEURL: string;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_LocationURL;
end;
function TIEController.GetIEAddressBarVisible: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_AddressBar;
end;
function TIEController.IsIEBusy: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Busy;
end;

10 January 2001 Delphi Informant Magazine

procedure TIEController.IEOpen;
begin
FIE := CoInternetExplorer.Create;
end;
procedure TIEController.SetIEURL(const Value: string);
var
Flags:
OleVariant;
TargetFrameName: OleVariant;
PostData:
OleVariant;
Headers:
OleVariant;
begin
if not IsIEOpen then IEShow;
FIE.Navigate(Value, Flags, TargetFrameName,
PostData, Headers);
end;
procedure TIEController.SetIEAddressBarVisible(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_AddressBar(Value);
end;
procedure TIEController.SetIEVisible(const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Visible := Value;
end;
function TIEController.GetIEOffLine: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Offline;
end;
function TIEController.GetIEVisible: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Visible;
end;
procedure TIEController.SetIEMenuBarVisible(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_MenuBar(Value);
end;
procedure TIEController.SetIEHeight(const Value: Integer);
begin
if not IsIEOpen then IEShow;
FIE.Set_Height(Value);
end;
procedure TIEController.SetIELeft(const Value: Integer);
begin
if not IsIEOpen then IEShow;
FIE.Set_Left(Value);
end;
procedure TIEController.SetIEOffline(const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_Offline(Value);
end;
function TIEController.GetIETop: Integer;
begin

On the Net
if not IsIEOpen then IEShow;
Result := FIE.Get_Top;
end;

if not IsIEOpen then IEShow;


FIE.Set_StatusText(Value);
end;

procedure TIEController.SetIETop(const Value: Integer);


begin
if not IsIEOpen then IEShow;
FIE.Set_Top(Value);
end;

function TIEController.GetIEStatusText: string;


begin
if not IsIEOpen then IEShow;
Result := FIE.Get_StatusText;
end;

function TIEController.GetIEWidth: Integer;


begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Width;
end;

function TIEController.GetIETheaterMode: Boolean;


begin
if not IsIEOpen then IEShow;
Result := FIE.Get_TheaterMode;
end;

procedure TIEController.SetIEWidth(const Value: Integer);


begin
if not IsIEOpen then IEShow;
FIE.Set_Width(Value);
end;

procedure TIEController.SetIETheaterMode(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_TheaterMode(Value);
end;

function TIEController.GetIEFullScreen: Boolean;


begin
if not IsIEOpen then IEShow;
Result := FIE.Get_FullScreen;
end;
procedure TIEController.SetIEFullScreen(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_FullScreen(Value);
end;
function TIEController.GetIEResizable: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Resizable;
end;
procedure TIEController.SetIEResizable(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_Resizable(Value);
end;
function TIEController.GetIESilent: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Silent;
end;
procedure TIEController.SetIESilent(const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_Silent(Value);
end;
function TIEController.GetIEStatusBarVisible: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_StatusBar;
end;
procedure TIEController.SetIEStatusBarVisible(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_StatusBar(Value);
end;
procedure TIEController.SetIEStatusText(
const Value: string);
begin

11 January 2001 Delphi Informant Magazine

function TIEController.GetIEToolbarVisible: Boolean;


var
Visible: Integer;
begin
if not IsIEOpen then IEShow;
Visible := FIE.Get_Toolbar;
if Visible = 0 then
Result := False
else
Result := True;
end;
procedure TIEController.SetIEToolbarVisible(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
if Value then
FIE.Set_ToolBar(1)
else
FIE.Set_ToolBar(0);
end;
procedure TIEController.IEGoBack;
begin
if not IsIEOpen then IEShow;
try
FIE.GoBack;
except
end;
end;
procedure TIEController.IEGoForward;
begin
if not IsIEOpen then IEShow;
try
FIE.GoForward;
except
end;
end;
procedure TIEController.IEGoHome;
begin
if not IsIEOpen then IEShow;
FIE.GoHome;
end;
procedure TIEController.IEGoSearch;
begin
if not IsIEOpen then IEShow;
FIE.GoSearch;
end;
procedure TIEController.IERefresh;

On the Net
begin
if not IsIEOpen then IEShow;
FIE.Refresh;
end;
procedure TIEController.IEShow;
begin
IEOpen;
IEVisible := True;
end;
function TIEController.IsIEOpen: Boolean;
begin
if GetIEWindowHandle = 0 then
Result := False
else
Result := True;
end;
procedure TIEController.IEPrint(ShowDlg: Boolean);
var
V: OleVariant;
begin
if ShowDlg then
IEExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DODefault, V, V)
else
IEExecWB(OLECMDID_PRINT,
OLECMDEXECOPT_DONTPROMPTUSER, V, V);
end;
procedure TIEController.IEPrintPreview;
var
V: OleVariant;
begin
IEExecWB(OLECMDID_PRINTPREVIEW,
OLECMDEXECOPT_DODEFAULT, V, V);
end;
procedure TIEController.IESave;
var
V: OleVariant;
begin
IEExecWB(OLECMDID_SAVE, OLECMDEXECOPT_DODEFAULT, V, V);
end;
procedure TIEController.IESaveAs(FileName: string);
var
VarIn,
VarOut: OleVariant;
begin
VarIn := FileName;
IEExecWB(OLECMDID_SAVEAS, OLECMDEXECOPT_DODEFAULT,
VarIn, VarOut);
end;
procedure TIEController.IEStop;
var
V: OleVariant;
begin
IEExecWB(OLECMDID_STOP, OLECMDEXECOPT_DODEFAULT, V, V);
end;
end.

End Listing One

12 January 2001 Delphi Informant Magazine

OP Tech

PGP / Secure Messaging / Delphi 4, 5

By Mike Riley

Pretty Good Privacy


Implementing Secure Messaging with PGP

hen asked about privacy in todays world, Sun Microsystems CEO Scott McNealy
remarked, You have zero privacy. Get over it. Of course, if this were really
the case, then Mr McNealy should have no qualms about posting his companys
confidential internal correspondence on public Internet sites. I havent seen this
happen yet. And thanks to the hard work of people like Phil Zimmerman, privacy
and data security can still exist in todays highly digitized world.

Phil Zimmerman to the Rescue


Pretty Good Privacy (PGP) is a popular, highquality cryptographic tool that is freely available
for personal use from MIT, and commercially
available for company use from Network Associates. The programs author, Phil Zimmerman, faced
everything from nancial ruin, government agency
intimidation, and patent infringement allegations
in his effort to release a product to protect basic
e-mail communication from prying eyes. Chronicled in Simson Garnkels book PGP: Pretty Good
Privacy [OReilly, 1994], Phils story is a triumph
in the ongoing battle of democratic freedom versus
technological Big Brother advances.
Most people unfamiliar with the degree their
personal e-mail accounts are exposed are often
shocked to learn how public their private e-mails
really are. The analogy of standard, unencrypted
e-mail communications owing across the public
Internet compared to a paper postcard in the US
Mail isnt that far off. Freely available tools exist
on the Internet today that provide packet-snifng
capabilities intended for just such snooping.
Even companies operating sophisticated commercial e-mail platforms that provide secure internal
communication may lose all that expensive protection if a message is sent to the Internet without any
form of encryption.
Because PGP has been freely available for personal
and educational use since the early 1990s, its
popularity compared to other cryptographic
messaging solutions has soared. And with
13 January 2001 Delphi Informant Magazine

the September 2000 expiration of RSAs biprime


asymmetric cryptography patent, many other
freely available public key software technologies are
sure to circulate on the Net. Until a better public
key solution comes along, however, PGP will most
likely be the leader in the eld of cross-platform
message encryption for some time to come.

Obtaining and Installing PGP


Depending on PGPs intended personal or commercial use, visit the appropriate Web site listed in
the Resources section at the end of this article
for details on how to obtain a copy of PGP for
Microsoft Windows 9x/NT/NT2000. Due to a
recently discovered vulnerability, its recommended
that version 6.5.8 or higher is used.
During the installation procedure, the user is
asked to create a key pair. It is at this time a
public and private key are created. The public
key is a bit of data that may be posted on a
public certicate server. The most popular of these
is http://pgpkeys.mit.edu:11371 for personal use,
and ldap://certserver.pgp.com for commercial use.
However, under no circumstances should the private key counterpart ever be shared. If the private
key should ever be compromised in any way, immediately revoke the key pair and create a new one. In
fact, its a good security practice to have key pairs
expire every 90 days to reduce the threat of tampering. To learn more about public and private keys,
including their generation and proper use, read the
well-written Introduction to Cryptography PDF
that accompanies the programs installation.

OP Tech

Figure 1: A rudimentary data-flow diagram of the PGP


encryption/decryption process.

Before moving on with the Delphi application requirements, make


sure you can successfully send and receive a signed, encrypted e-mail
with your newfound secure messaging tool. Just like OOP, learning
how public key cryptography works takes a bit of getting used to.
Follow the PGP Encryption/Decryption ow diagram in Figure 1 to
review a basic understanding of the process. Its only after using it for
a while that its value proposition really sinks in. And like OOP, once
you get this technologys religion, youre hooked.

Delphi PGP Program Requirements


Thanks to the dedicated work of Steve S.R. Heller, who
authored the SPGP.DLL that programmatically extends PGP to
Windows application development environments like Delphi, and
Michael in der Wiesche, who authored a very slick PGP component set, leveraging the power of PGP in Delphi applications is
a piece of cake. Check out the Resources section of this article
for the Internet location of these works. Once the SPGP.DLL
has been placed in the Windows system directory, and the PGP
components have been installed via the usual Delphi component
installation procedure, the power of commercial-grade cryptographic messaging is available to any Delphi developer.
To show just how simple it is to harness this power, I created an
application that illustrates the potential of this robust combination.

The PGP Demonstration Application


The PGP Demonstration application (see Figure 2) was constructed
to show the step-by-step processes of capturing Web-based content,
encrypting and signing that content, and sending it through a public
Internet e-mail service. The GUI is straightforward, and quite
honestly unnecessary for anything except to graphically display the
steps and outcomes of each phase of the application being processed.
To test this application using the key pair you established during
the PGP installation, change the CryptKeyIDs and SignKeyID values
to your key pair e-mail address. Remember to bracket the e-mail
address, and dont allow an ill-formed e-mail address with any spaces
or other illegal characters.
14 January 2001 Delphi Informant Magazine

Figure 2: The PGP Demonstration application in action.

Next, change the SignKeyPass value in the PGPEncodeGetSignKeyPass


event to the passphrase used for the selected key pair. A word of warning:
As noted in the source code listing, its a very bad idea to place passwords
and passphrases in source code. Thus, if automated message encryption
is required, consider removing the signature process or investigate an
effective way to obfuscate the source to limit passphrase visibility.
Lastly, if you are using the NetMaster SMTP control bundled
with Delphi Pro or Enterprise 4.x or higher, change the SMTP
Host and UserID values to those appropriate to your environment. Assuming all program dependencies have been met, and all
parameters have been correctly updated for your conguration,
build and run the application.
Clicking the globe toolbar icon will fetch the DelphiZine.com
home page and read it into the top memo eld. This URL could
just as easily be an internal corporate intranet address, an XML
data feed, or a CSV le. If the target URL is unavailable for any
reason, you can also simply type a message into the top memo eld
as well. Upon doing so, the key toolbar icon will become enabled,
allowing you to sign and encrypt the memo elds contents. The
PGP component suite supports methods for both the encryption
of physical les as well as strings in memory. For demonstration
purposes, the contents of the top memo eld are written out to a
pgpdemo.txt le. This practice is not recommended in a production
environment, however, as it adds yet another potential security
vulnerability to an otherwise secure process.
The bottom memo eld then retrieves the contents of the pgpdemo.txt
le, signs and encrypts the le, displays the results of the process in
the bottom memo eld, and saves the results to another le called
pgpdemo.pgp. The contents of this le can also be opened with a text
editor such as Notepad for review. Additionally, double-clicking any le
with a .asc or .pgp extension from within the Windows shell will invoke
the PGPTools application and automatically attempt to decrypt the le.
The nal step is transmitting the le via an SMTP gateway. If the
contents of the top memo eld were successfully encrypted and

OP Tech
signed, the mail toolbar icon will become active. Clicking this icon
will transmit the read-only contents of the bottom memo eld to
the e-mail address you specied earlier. To validate the success of this
transmission, simply retrieve the e-mail message from the intended
recipient, decrypt, and verify.

Conclusion
To be a useful tool in a production environment, the PGP demonstration application could be automated to kick off a batch operation
via a Timer event, or Windows Task Scheduler operation. Better yet,
an NT Service could be created to ensure the automated operation
of an encrypted message transmission from within the Windows NT
security and remote management framework.
So what type of production applications can be written using this
combination of technologies? Plenty. Consider an extranet nancial or inventory reporting application that can be inexpensively
and securely transmitted from one companys Intranet to another
in an asynchronous fashion using an Internet gateway. How about
a condential project status report system that updates contractors
and external consultants, regardless of the type of e-mail platform
their respective companies utilize? And for a virtually distributed,
decentralized organization on a tight budget, secure communications can be sent to employees with free public e-mail service
accounts. These messages can be safely decrypted well after the
recipient has copied and pasted the contents from their browser
for decryption, regardless of whether SSL is used during the message retrieval process. Visiting a Web page that res off a Delphiauthored ISAPI DLL or COM object that fullls the entire request
can trigger this type of process as well.
So the next time someone says, You have no privacy, smile and tell
them Get with it.

Resources
 PGP (Commercial Version) v6.5.8 or higher available from





Network Associates at http://www.pgp.com


PGP (Freeware Version) v6.5.8 or higher available from the MIT
Web site at http://web.mit.edu/network/pgp.html
Steve S.R. Hellers excellent SPGP.DLL v2.5.5 or higher,
available at http://www.oz.net/~srheller/spgp/
Michael in der Wiesches superb PGP components v1.3.1
or higher, available at http://home.t-online.de/home/
idw.doc/PGPcomp.htm
PGP: Pretty Good Privacy. Simson Garnkels remarkable story
of Phil Zimmermans trials and travails of bringing PGP to
the public, as well as a laymans introduction to cryptography.
Also includes detailed (albeit dated) descriptions of PGP command line options. Published by OReilly and available at
http://www.oreilly.com/catalog/pgp/

The projects referenced in this article are available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\JAN\
DI200101MR.

Mike Riley is a Chief Scientist for RR Donnelley & Sons, one of North Americas
largest printers. He actively participates in the companys I-net and eMerging
Technology strategies using a wide variety of distributed network technologies,
including Delphi 5.0. Mike can be reached via his public e-mail address,
mike_riley_@hotmail.com.
15 January 2001 Delphi Informant Magazine

Greater Delphi

PivotTable Component / Microsoft OWC / Excel / Automation / Delphi 5

By Alex Fedorov and Natalia Elmanova

Easy Pivot Tables


Using the Microsoft OWC PivotTable Component

ast month we demonstrated how to use two of the Microsoft Office Web Components,
ChartSpace and Spreadsheet, in Delphi applications. The other Office Web Component
of interest for Delphi developers is the PivotTable component thats used to pivot, filter, and
summarize information from various data sources and present it in meaningful ways.

In this article, well rst briey discuss the PivotTable services implemented in Microsoft Excel, so
you can get an idea of how and when the PivotTable component should be used. Then well provide you with several examples of how to use the
PivotTable component in Delphi applications.

Excel PivotTable Services


To be able to use the PivotTable component, we
need to understand at least the basics of the PivotTable services implemented in Microsoft Excel.
The Excel pivot table is an interactive table usually used to summarize or statistically analyze
data. The data can come from a variety of sources,
including an Excel range or database query. Cells
of the pivot table contain summaries for one of
the numeric columns of the data. These summaries are based on table rows in which the row
name and the column name for this particular
pivot table cell are the values of the corresponding
data table elds. Such summary tables are also
called crosstabs.
Filter Fields

Row Fields

Column Fields

Totals or Details Fields

Figure 1: Excel PivotTable layout.

16 January 2001 Delphi Informant Magazine

In Excel, the pivot table elds can be rotated, and


the summarized data itself can be ltered to present
different summaries or details for areas of interest,
as shown in Figure 1. This allows us to create
different crosstabs for the same data.
Lets consider, for instance, that we have a table
that contains sales data, as shown in Figure 2.
We can create an Excel pivot table using the PivotTable Wizard that can be activated with the Data
| PivotTable and PivotChart Report menu item. The
rows of this pivot table can, for example, contain
the Product eld of a table as its row eld, the
Salesperson eld of the table as its column eld,
and the City eld of the table as its lter eld
(in Excel, such elds are also called page elds), as
shown in Figure 3.
To create another pivot table view instead of the
one shown, a user can drag-and-drop elds to and
from the PivotTable toolbar that becomes extended
with an appropriate eld list where the pivot table
layout is edited. Note that a pivot table can have
no row elds, or no column elds, as well as no
lter elds.
Excel PivotTable services may be used with any
ODBC and OLE DB data sources, including
the multidimensional OLAP cubes accessible
through an appropriate OLE DB provider. Also
note that Excel allows using more than one eld
for rows, columns, or lters (for example, we
can use the Country, Region, and City elds
together), and this allows creating crosstabs with
hierarchical metadata.

Greater Delphi
It should be noted, however, that the PivotTable
component behavior is not the same as the Excel
PivotTable services. While the latter hides all data,
the former allows them to be modied interactively
at run time.

The TOleContainer Component


We werent able to use the PivotTable component
directly with Delphi 5. We havent found the
reason for this. Perhaps it expects some extra interfaces not implemented in the Delphi TForm class.
Maybe there are some problems in Delphi COM
support. Maybe there are some other reasons.
However, theres another way to use the PivotTable component in Delphi. We can place it
inside the TOleContainer component from the
System page of the Delphi Component palette.
We believe that this component adds some interfaces that are necessary for the PivotTable component to work properly, but are absent in the
current implementation of the Delphi TForm
class. Evidence for this is the fact that placing
the TOleContainer component on a form that
contains a PivotTable ActiveX control also makes
the latter work properly.

Order Date

City

Product

Extended Price Salesperson

January 5
January 5
January 5
January 6
January 6
January 6
January 6
January 6
January 7
January 7
January 7
January 7
January 7
January 7
January 7

Lyon
Lyon
Lyon
Marseille
Lyon
Strasbourg
Marseille
Lyon
Marseille
Lyon
Marseille
Strasbourg
Strasbourg
Marseille
Marseille

Ikura
Ipoh Coffee
Chocolade
Chang
Ipoh Coffee
Ipoh Coffee
Ikura
Chang
Chang
Ikura
Chocolade
Chocolade
Chang
Chocolade
Ipoh Coffee

$193.00
$782.00
$86.70
$1,061.82
$7,905.00
$938.40
$1,047.62
$190.00
$800.00
$95.00
$714.00
$437.50
$292.50
$465.00
$378.00

Michael Suyama
Janet Leverling
Michael Suyama
Janet Leverling
Nancy Davolio
Janet Leverling
Janet Leverling
Nancy Davolio
Michael Suyama
Nancy Davolio
Nancy Davolio
Michael Suyama
Michael Suyama
Nancy Davolio
Nancy Davolio

Figure 2: Sample sales data.


Salesperson

Lyon

Sum of
Extended Price
Product

Salesperson
Janet
Leverling

Michael
Suyama

Chang
Chocolade
Ikura
Ipoh Coffee
Grand Total

Nancy
Davolio

Grand Total

190
86.7
193

190
86.7
288
8,687
9,251.7

95

The TOleContainer component is used in


782
7,905
Delphi applications primarily as a visual con782
279.7
8190
tainer for OLE documents. However, we can
Figure 3: Summarized sales.
also use it with some ActiveX controls. The only
problem in this case is that we cannot use the
InsertObjectDialog method of the TOleContainer component. For
methods. The PivotTable component exposes the ActiveView
obvious reasons, ActiveX components dont appear on the list of
property that gives us access to the PivotView object that corOLE document servers. Instead, well use the CreateObject method responds to the current pivot table view (layout) presented in
of this component:
the PivotTable component. The PivotView object exposes several
collections that present elds used to build row, column, and
lter axes, as well as each data axis. The PivotTable component
var
PT : Variant;
also exposes the PivotData property that gives us access to the
...
pivot table cells, and the pivot table members that are the values
// Create an embedded OLE Object.
along the axes. A simplied PivotTable component object model
OleContainer1.CreateObject('OWC.PivotTable.9', False);
is shown in Figure 4.
// Access OLE object.
PT := OleContainer1.OleObject;

Next we must activate this component inside the OLE container.


This can be done using the DoVerb method of the OLE container.
Containers for OLE documents usually support several default
actions, such as displaying or activating an OLE object, as well as
custom actions dened in the specic OLE server (a list of commands
can be obtained using the ObjectVerbs property of the TOleContainer
component). Here we use one of these default actions to make the
PivotTable component visible and ready to use:
// Specify that the OLE Object will be
// activated manually and activate it.
with OleContainer1 do begin
AutoActivate := aaManual;
DoVerb(ovShow);
end;

Using the PivotTable Component


After weve placed the PivotTable component on our form within
the TOleContainer component, we can use its properties and
17 January 2001 Delphi Informant Magazine

In our rst example, well use the sample Northwind database


(Northwind.mdb) that comes with Microsoft Access. We will start
with dening the ConnectionString property:

PivotTable

PivotView

PivotDataAxis

PivotGroupAxis

PivotData

PivotFilterAxis

PivotTotals
(PivotTotal)

PivotCell
PivotMember

PivotFieldSets
(PivotFieldSet)

PivotFieldSets
(PivotFieldSet)

PivotFieldSets
(PivotFieldSet)

PivotFields
(PivotField)

PivotFields
(PivotField)

PivotFields
(PivotField)

PivotTotals
(PivotTotal)

PivotFieldSets
(PivotFieldSet)

PivotMembers
(PivotMember)

Objects
Collections

Figure 4: Simplified object model of the PivotTable component.

Greater Delphi
const
DataSourcePath = 'c:\data\northwind.mdb';
...
PT.ConnectionString :=
'PROVIDER=MICROSOFT.JET.OLEDB.4.0;' +
'DATA SOURCE=' + DataSourcePath;

Next, we need to specify what kind of data we will use in our pivot
table. This can be done either through the DataMember property,
or through the CommandText property. In our example, well use
the Product Sales for 1997 view (this view returns the information
about the product sales from January 1, 1997 to December 31,
1997), that can be specied like this:
PT.DataMember := '[Product Sales for 1997]';

var
View : Variant;
...
View := PT.ActiveView;
// Move fields to the row, column,
// and filter axes for grouping.
View.RowAxis.InsertFieldSet(
View.FieldSets['CategoryName']);
View.ColumnAxis.InsertFieldSet(
View.FieldSets['ShippedQuarter']);
View.DataAxis.InsertFieldset(
View.FieldSets['ProductName']);
View.DataAxis.InsertFieldset(
View.FieldSets['ProductSales']);

Figure 6: Specifying data for all parts of a pivot table.

or like this:
PT.CommandText :=
'SELECT

* FROM [Product Sales for 1997]';

After that, we have two options. We can show the Fields list box
and allow users to drag and drop elds into the pivot table manually,
or we can do the same thing programmatically. Lets see how the
latter can be done.
The elds in
PivotTable Fields PivotTable Collection
the pivot table
Row fields
RowAxis collection
discussed earColumn fields
ColumnAxis collection
lier in this artiFilter fields
FilterAxis collection
cle have the
Totals or Details fields DataAxis collection
appropriate
Figure 5: PivotTable fields and their collections.
collections
within the PivotTable components object model. This is shown in the table in
Figure 5.
The rst three collections support the InsertFieldSet method thats used
to specify the data for this set of elds. To specify the data for totals or
detail elds, we can either use the InsertTotal method, or InsertFieldSet
method. The code snippet shown in Figure 6 species data for all parts
of the pivot table.
Here weve dened that the CategoryName eld is used to create
the row axis data, the ShippedQuarter eld is used to create the
column axis data, and the ProductName and ProductSales elds
are used to create details to summarize them interactively.

Calculating Totals and Using Formulas


At this step, we have specied the data for Row, Column, and Detail
elds of the pivot table. Now we can add total values to it. Heres
the code that does the job:
// Add a total.
Total := View.AddTotal('Sales Total',
View.FieldSets['ProductSales'].Fields[0], plFunctionSum);
View.DataAxis.InsertTotal(Total);
View.Totals['Sales Total'].NumberFormat :='Currency';

Figure 7: The PivotTable component with calculated totals.

Specifying Colors
Now we can make our pivot table more attractive by applying
different colors to its various sections. To do this, we will use the
TotalBackColor, FieldLabelBackColor, MemberBackColor, FieldLabelFont,
and SubTotalBackColor properties of the PivotView object:
// Apply various color settings to
// different pivot table components.
View.TotalBackColor := 'CornSilk';
View.FieldLabelBackColor := 'DarkBlue';
View.FieldLabelFont.Color := 'White';
View.ColumnAxis.FieldSets[0].Fields[0].SubTotalBackColor :=
'LightSteelBlue';
View.RowAxis.FieldSets[0].Fields[0].SubTotalBackColor :=
'LightSteelBlue';
View.MemberBackColor := 'LightGrey';

Next, lets limit possible user manipulations of the pivot table:


// Remove the title bar.
View.TitleBar.Visible := False;
// Set various properties of the Pivot Table
// to limit user interaction.
PT.DisplayToolbar := False;
PT.AllowPropertyToolbox := False;

The result of calculating summaries and applying colors is shown in


Figure 7.
Weve added totals for the ProductSales eld for any quarter and any
product category, as well as grand totals for all product categories
and all quarters. In this case, weve calculated a sum of the detail
values. Instead, we could also calculate the maximal or the minimal
values, as well as the count of the detail data.
18 January 2001 Delphi Informant Magazine

In our rst example, we saw how to set the data source, dene
a pivot table layout, calculate totals and use formulas, and apply
various colors to the different parts of a pivot table. Lets now
move on to a discussion of lters.

Greater Delphi
Using Filters
To lter the data that appears in the pivot table, we need to use the
FilterAxis property of the PivotView object, for example:
View.FilterAxis.InsertFieldSet(
View.FieldSets['CategoryName']);

This allows users to lter the data appearing in the pivot table.
In other words, lter columns allow us to bring the third dimension to the at pivot table. Please note that instead of applying
lters programmatically, we can drag and drop an appropriate
column name to the lter eld area of the PivotTable component.

Using OLAP Cubes


Instead of data from a relational database, we can use data from the
local OLAP cube that can be created with Excel, Microsoft SQL
Server, or other tools. To use the local cube, we should specify the
Microsoft OLAP OLE DB provider and the location of the cube.
Following is an example of the ConnectionString property for this case:
PT.ConnectionString :=
'PROVIDER = MSOLAP; DATA SOURCE = C:\DATA\CUBE.CUB';

In the case of Microsoft SQL Server 7.0 OLAP cubes, the


ConnectionString property looks like the following:
PT.ConnectionString :=
'PROVIDER = MSOLAP; DATA SOURCE = maindesk;' +
'INITIAL CATALOG=FoodMart';

For Microsoft SQL Server 2000 OLAP cubes, the ConnectionString


property looks like the following:

var
View : Variant;
// PivotTable view
Total : Variant;
// Totals
R, C : Integer;
// Row and column counters
Cell : Variant;
// One cell
Agg
: Variant;
// Aggregate value
LI
: TListItem; // ListView item
CI
: TListColumn; // ListView column
...
// Specify column headers.
CI := ListView1.Columns.Add;
CI.Caption := 'Category';
CI.AutoSize := True;
for C := 0 to PT.ActiveData.ColumnMembers.Count-1 do begin
CI := ListView1.Columns.Add;
CI.Caption := PT.ActiveData.ColumnMembers[C].Caption;
CI.AutoSize := True;
end;
// For each row...
for R := 0 to PT.ActiveData.RowMembers.Count-1 do begin
LI := ListView1.Items.Add;
LI.Caption := PT.ActiveData.RowMembers[R].Caption;
// For each column in a row...
for C := 0 to PT.ActiveData.ColumnMembers.Count-1 do
begin
// Get cell value.
Cell := PT.ActiveData.Cells[
PT.ActiveData.RowMembers[R],
PT.ActiveData.ColumnMembers[C]];
// Get aggregate value.
Agg := Cell.Aggregates.Item['Sales Total'].Value;
// Show it.
LI.SubItems.Add(IntToStr(Agg));
end;
end;

Figure 8: Presenting PivotTable data in a Delphi ListView component.

PT.ConnectionString :=
'PROVIDER = MSOLAP.2; DATA SOURCE = maindesk;' +
'INITIAL CATALOG=FoodMart 2000';

In this case, the PivotTable component cannot display the source


data, because it isnt stored in OLAP cubes. However, you can
provide users with different levels of detail when the pivot table axes
are based on dimensions with several hierarchy levels.
Okay; weve demonstrated several ways to get data into a PivotTable.
Now lets look at how to get the data out of the component in
useful ways.

Getting Data from the PivotTable


We can use the PivotData object and PivotRowMember and
PivotColumnMember collections to extract data from the PivotTable component. Lets extend our example by adding the table
with aggregate values for each products sales by quarter. We will
present this data in the ListView component from the Win32
page of the Delphi Component palette. The code necessary to
implement this is shown in Figure 8.
The result of extracting the values from the pivot table is shown in
Figure 9. In this example, we have extracted the values along the row
and column axes, as well as summaries calculated by the PivotTable
component, and then put them into the ListView component.

Exporting and Saving Pivot Tables


Using the Export method of the PivotTable component, we can save
our pivot table to a le, and optionally open it in Microsoft Excel.
19 January 2001 Delphi Informant Magazine

Figure 9: The data obtained from the PivotTable component.

The Export method takes two arguments. The rst species the name
of the le; the second species the actions to be taken. We can specify
either plExportActionNone (its value is 0, indicating no action; just
save the le), or plExportActionOpenInExcel (its value is 1, save the
le and open it in Excel). If the le name isnt specied, a temporary
le named PivotTablexxxx will be created in the \Windows\Temp
directory. The pivot table is saved in XML format.
Another possibility is to save a static picture of a pivot table as a
GIF le. To do this, we use the ExportPicture method. All four of its
arguments are optional:
 the name of the le to store the image in. If this argument isnt
specied, the pivot table image will be saved to a le named
Pivot.GIF.

Greater Delphi
// Copy the PivotTable data into the Clipboard.
PT.Copy(PT.ActiveView);
// Create an instance of Excel for Automation.
Excel := CreateOLEObject('Excel.Application');
// Add a new workbook to the Excel instance.
WBook := Excel.Workbooks.Add;
// Get a reference to the first worksheet.
WSheet := WBook.WorkSheets[1];
// Paste the contents of the Clipboard into that sheet.
WSheet.Paste(WSheet.Range['A1', 'A1']);
// Make Columns AutoFit; everything will be visible.
WSheet.UsedRange.Columns.AutoFit;

Figure 10: Placing PivotTable component data into an instance of


Excel via the Clipboard.
// Get the PageSetup object.
PageSetup := WSheet.PageSetup;
// Orientation: 1 - Landscape, 2 - Portrait.
PageSetup.Orientation := 2;
// Specify data for header/footer.
PageSetup.CenterHeader := PT.ActiveView.TitleBar.Caption;
PageSetup.CenterFooter := 'Page &P of &N';
// We want to print gridlines.
PageSetup.PrintGridlines := True;
// Show an instance of Excel.
Excel.Visible := True;
// Let user interact with Excel.
Excel.UserControl := True;
// Call print preview function.
WSheet.PrintPreview;

Figure 11: Using Excel as an Automation server to print the data.





the name of the lter. Currently only GIF is supported.


the width of the image in pixels (required for server-side charts).
the height of the image in pixels (required for server-side charts).

The following code saves the pivot table into the Customers.GIF le
in the c:\Data folder:
PT.ExportPicture('c:\Data\Customers.GIF');

Implementing Printing Functionality


The PivotTable component has no native printing capability. However, we can easily implement this functionality by moving the pivot
table data into Excel, and then using Excels printing capability.
Of course, all of this is done via Automation. First, lets declare some
variables:
var
Excel
WBook
WSheet
PageSetup

:
:
:
:

Variant;
Variant;
Variant;
Variant;

//
//
//
//

Figure 12: The data obtained from the PivotTable component,


shown as an Excel print preview.

gridlines. After that, we make the Excel instance visible, and let users
interact with it (see Figure 11). The Print Preview window that
results from executing this code is shown in Figure 12. To actually
print, we can call the PrintOut function, in which case we dont need
to make Excel visible.

Conclusion
As far as licensing is concerned its okay to use Microsoft Ofce Web
Components on computers where any edition of Microsoft Ofce
or Microsoft Access 2000 is installed. For more information on the
deployment issues of such applications, refer to the documents on the
Microsoft Web site at http://www.microsoft.com/ofce.
Weve seen how to use the PivotTable component that is part of Microsoft Ofce Web Components. We know how to dene the data source
for this component, how to set the row, column, and lter elds, how to
dene the data elds, how to create summaries, and how to apply colors
to the different parts of the pivot table. We have also studied how to
receive values from the pivot table cells and axes, as well as to make this
component more or less interactive in our application. Finally, we also
saw how to print the data from a PivotTable component.
The three demonstration projects referenced in this article are available
on the Delphi Informant Magazine Complete Works CD located in
INFORM\2001\JAN\DI200101AF.

An instance of Excel.
Workbook.
Worksheet.
PageSetup object.

The main sequence of actions is straightforward. First, we will copy


the contents of the PivotTable to the Clipboard. Then, well create an
instance of Excel, add a new workbook, and get the rst worksheet
within it. After that, we can paste the contents of the Clipboard into
this worksheet, and adjust it to make its contents visible. This is
implemented by the code shown in Figure 10.
Now we have our pivot table data inside the spreadsheet, so we can
invoke the printing functions of Excel. First, we need the PageSetup
object. In this example, we will specify portrait orientation, but
this can be changed later. Then, we specify the header, footer, and
20 January 2001 Delphi Informant Magazine

Alex Fedorov is a Chief Technology Officer for Netface SA, based in Lausanne, Switzerland
(http://www.netface.ch). Hes one of the co-authors of Professional Active Server Pages
2.0 [Wrox Press, 1998] and ASP 2.0 Programmers Reference [Wrox Press, 1999].
Natalia Elmanova, Ph.D. is an executive editor for ComputerPress magazine published
in Moscow (http://www.compress.ru), and was a speaker at the 10th and 11th Annual
Inprise/Borland Conferences. Natalia and Alex are authors of Advanced Delphi Developers Guide to ADO [Wordware Publishing, 2000], and several Russian programming
books. You can visit their Web site at http://d5ado.homepage.com.

Quick CLX

CLX / Customs Controls / Cross-platform / Kylix

By Robert Kozak

The Life and Death of TButton


An Under-the-Hood Comparison of the VCL and CLX

hen I wrote my previous article (Cross-platform Controls in the August 2000 issue
of Delphi Informant Magazine) I was overwhelmed by the sheer volume of things
to write about. There was so much, in fact, that I knew an ongoing column was the only
way to properly do CLX any justice. So naturally, I was excited when my proposal to do a
column on CLX was accepted. With this column, we are going to explore the internals of
CLX and discuss some of the issues of Kylix development. Its my hope that by reading this
column, you will walk away with a better understanding of the internals of CLX.
As I write this, Kylix is still being developed. And
its possible that it has been released by the time
you read this. This means that there might be differences between the specics Ill provide here and
the nal product. You take that risk when you write
about the latest and greatest.
One of the questions I bet you have is: What is
different about CLX? Specically, how does CLX
procedure TWinControl.UpdateShowing;
var
ShowControl: Boolean;
I: Integer;
begin
ShowControl := (FVisible or
(csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
not (csReadingState in ControlState);
if ShowControl then begin
if FHandle = 0 then CreateHandle; { <------- }
if FWinControls <> nil then
for I := 0 to FWinControls.Count - 1 do
TWinControl(FWinControls[I]).UpdateShowing;
end;
if FHandle <> 0 then
if FShowing <> ShowControl then begin
FShowing := ShowControl;
try
Perform(CM_SHOWINGCHANGED, 0, 0);
except
FShowing := not ShowControl;
raise;
end;
end;
end;

Figure 1: The VCL TWinControl.UpdateShowing method.

21 January 2001 Delphi Informant Magazine

differ from the VCL? To answer this, Ill take a


common control, TButton, and show what happens to it as it is created, used, and destroyed with
CLX. This way we can delve into CLX and see
whats going on under the covers. For comparison,
I will do the same for a VCL TButton, since some
of you may not be that familiar with the internals
of the VCL.
This is going to be a high-level overview. Although
I will get into some of the lower-level code, Im
not going to mention every property that gets set
or every method that gets called during the life of a
button. This would just bog us down with details,
when what I really want to show are the highlights.
Take a look at Listing One (beginning on page
25). Heres a project that introduces the main star
the Button component. This code will compile
in Kylix and in Delphi. The uses clause has an
{ $IFDEF } block around it, so it will compile
using either CLX or the VCL. The code that creates a Button, uses it, and then destroys it, is the
same for a CLX project or a VCL project.

Creating the Button


Not much here to talk about. In CLX, and in the
VCL, a button is created the same way:
Button := TButton.Create(nil);

The constructor for both sets default property values


of the button. In CLX, the constructor also sets a
property named InputKeys. InputKeys is a mechanism

Quick CLX
procedure TWinControl.CreateHandle;
var
I: Integer;
begin
if FHandle = 0 then begin
CreateWnd; { <------- }
SetProp(FHandle, MakeIntAtom(ControlAtom),
THandle(Self));
SetProp(FHandle, MakeIntAtom(WindowAtom),
THandle(Self));
if Parent <> nil then
SetWindowPos(FHandle, Parent.PrecedingWindow(Self),
0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE +
SWP_NOACTIVATE);
for I := 0 to ControlCount - 1 do
Controls[I].UpdateAnchorRules;
end;
end;

Figure 2: The VCL TWinControl.CreateHandle method.

we introduced that groups keys together so your component can get a


chance to handle special keys before the underlying widget is set. Ill go
into more depth on this topic in a future article.
Note that I set the Buttons owner to nil. I did this because I want
to explicitly destroy the button myself, and not have the owner do it
when its destroyed. If youre very observant, or well versed in Delphi,
youll notice that it doesnt matter, because I am freeing the Button in
the OnDestroy event on the form. I did it this way so you could set a
breakpoint in the OnDestroy event and follow along.

Setting the Parent


This innocent assignment sets off a whole chain of events:
Button.Parent := Self;

Into the VCL


First, lets look at what happens with a VCL Button. Setting the Parent
will call the UpdateShowing method (shown in Figure 1). This method
determines whether the button should be shown based on the Visible
property and its state, i.e. its not being read from the stream. For the
button to be visible, it needs to have a window handle, so CreateHandle
(shown in Figure 2) calls CreateWnd and sets the z-order of the Button.
The CreateWnd method (shown in Figure 3) exists to simplify the
creation of a window. It sets up the necessary bookkeeping needed
by CreateWindowEx in the Windows API. To do this, it calls another
helper method named CreateParams.
As its name suggests, the CreateParams procedure sets up all the
parameters that describe the control (see Figure 4). CreateParams of
TButton also calls CreateSubClass, passing it the ControlClassName of
BUTTON. After all of this, CreateWnd calls the CreateWindowHandle
method (see Figure 5) which creates the window handle of the Button
by calling CreateWindowEx.
Theres a lot going on in the previous three paragraphs, so heres
a quick recap of the methods called in order to create a Button
component in the VCL:
 UpdateShowing
 CreateHandle
 CreateWnd
 CreateParams
 CreateSubClass
 CreateWindowHandle
 CreateWindowEx
22 January 2001 Delphi Informant Magazine

procedure TWinControl.CreateWnd;
var
Params: TCreateParams;
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
CreateParams(Params); { <------- }
with Params do begin
if (WndParent = 0) and (Style and WS_CHILD <> 0) then
if (Owner <> nil) and
(csReading in Owner.ComponentState) and
(Owner is TWinControl) then
WndParent := TWinControl(Owner).Handle
else
raise EInvalidOperation.CreateFmt(
SParentRequired, [Name]);
FDefWndProc := WindowClass.lpfnWndProc;
ClassRegistered := GetClassInfo(WindowClass.hInstance,
WinClassName, TempClass);
if not ClassRegistered or
(TempClass.lpfnWndProc <> @InitWndProc) then begin
if ClassRegistered then
Windows.UnregisterClass(WinClassName,
WindowClass.hInstance);
WindowClass.lpfnWndProc := @InitWndProc;
WindowClass.lpszClassName := WinClassName;
if Windows.RegisterClass(WindowClass) = 0 then
RaiseLastWin32Error;
end;
CreationControl := Self;
CreateWindowHandle(Params); { <------- }
if FHandle = 0 then
RaiseLastWin32Error;
end;
StrDispose(FText);
FText := nil;
UpdateBounds;
Perform(WM_SETFONT, FFont.Handle, 1);
if AutoSize then
AdjustSize;
end;

Figure 3: The VCL TWinControl.CreateWnd method.


procedure TButton.CreateParams(var Params: TCreateParams);
const
ButtonStyles: array[Boolean] of
DWORD = (BS_PUSHBUTTON, BS_DEFPUSHBUTTON);
begin
inherited CreateParams(Params);
CreateSubClass(Params, 'BUTTON');
Params.Style := Params.Style or ButtonStyles[FDefault];
end;

Figure 4: The VCL TButton.CreateParams method.

procedure TWinControl.CreateWindowHandle(
const Params: TCreateParams);
begin
with Params do
FHandle := CreateWindowEx(ExStyle, WinClassName,
Caption, Style, X, Y, Width, Height, WndParent, 0,
WindowClass.hInstance, Param);
end;

Figure 5: The VCL TWinControl.CreateWindowHandle method.

Im not going into further detail, because I want to concentrate on


CLX. I just wanted to give you an overview of how it works in the
VCL so you have some basis for comparison.

Into CLX
The process is much simpler in CLX, and easier to follow. It starts the

Quick CLX
procedure TWidgetControl.UpdateShowing;
var
ShowControl: Boolean;
I: Integer;
begin
ShowControl := (FVisible or
(csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
not (csReadingState in ControlState);
if ShowControl then begin
HandleNeeded; { <------- }
if FWidgets <> nil then
for I := 0 to FWidgets.Count - 1 do
TWidgetControl(FWidgets[I]).UpdateShowing;
end;
if HandleAllocated then
if FShowing <> ShowControl then begin
FShowing := ShowControl;
try
ShowingChanged;
except
FShowing := not ShowControl;
raise;
end;
end;
end;

Figure 6: The CLX TWidgetControl.UpdateShowing method.

same way; setting the parent causes the UpdateShowing procedure (see
Figure 6) to be called. In CLX (just as in the VCL) you need a handle
to display a control, but its not the same as a Windows handle.
In CLX, a handle is an opaque reference to the underlying widget
control. To get this handle, UpdateShowing calls HandleNeeded (see
Figure 7), and then calls CreateHandle. As you can see from Figure 8,
CreateHandle rst calls CreateWidget, which will create the widget.
The next method I want you to notice is InitWidget. Because
InitWidget gets called after the widget is created, you can be sure
the handle is valid. Its at this point that you can set properties on
the widget. For example, say you have a property named Color. In
SetColor you can check with HandleAllocated to see if you have a valid
handle. If the handle is allocated, you can make the proper call to set
the color. If not, you can store the value into a private eld variable,
and in InitWidget you make the call to set the color.
Now our Button is created, parented, and ready to rock-and-roll.
Heres the list of methods called in order to create a Button
component in CLX:
 UpdateShowing
 HandleNeeded
 CreateHandle
 CreateWidget
 InitWidget

Setting the Caption


This statement looks straightforward, but its implementation in the
VCL and CLX are very different:

procedure TWidgetControl.HandleNeeded;
begin
if FHandle = nil then begin
if Parent <> nil then
Parent.HandleNeeded;
CreateHandle; { <------- }
end;
end;

Figure 7: The CLX TWidgetControl.HandleNeeded method.


procedure TWidgetControl.CreateHandle;
begin
if FHandle = nil then begin
CreateWidget; { <------- }
QClxObjectMap_add(FHandle, Integer(Self));
SetInitialSize;
InitWidget; { <------- }
HookEvents; { <------- }
if (FHandle = nil) or (FHooks = nil) then
raise Exception.CreateResFmt(
@SInvalidCreateWidget, [ClassName]);
SetInitialBounds;
UpdateWidgetShowing; { Force the visible state. }
EnabledChanged;
{ Force enabled state. }
QWidget_setCursor(FHandle, Screen.Cursors[FCursor]);
if csRecreating in ControlState then
RestoreWidgetState;
end;
end;

Figure 8: CLX TWidgetControl.CreateHandle method.


procedure TButtonControl.SetText(const Value: TCaption);
begin
if Caption <> Value then begin
QButton_setText(Handle, @Value);
FAccelChar := Char(QButton_accel(Handle));
QButton_setAccel(Handle, 0);
TextChanged;
end;
end;

Figure 9: The CLX TButtonControl.SetText method.

that we call the setText method of the widget control. Notice that
the handle is the rst parameter. Next, we store the accelerator, then
clear it from the QButton control. We do this because were going to
handle all of the accelerators in the CLX framework.
Next we call a dynamic method named TextChanged as a notication.
This equates to the CM_TEXTCHANGED notication message in
the VCL. In fact, most of the component messages from the VCL
arent a part of CLX, and are replaced with dynamic methods. The only
time we kept a CM_ type message was when it had to be broadcast to
multiple controls. Most of the time, however, the CM_ messages from
the VCL only need to notify a descendant that something happened.

Handling the Event


The user event handler assignment is the same in the VCL and CLX.
Its just a simple property assignment:
Button.OnClick := ButtonClick;

Button.Caption := 'Hello, World!';

In the VCL, setting the Caption is handled by a method named


SetText in the TControl class. SetText calls SetTextBuf, which in turn
issues WM_SETTEXT and CM_TEXTCHANGED messages.

The real fun begins when you press the button and the event
res. If you put a breakpoint on the ShowMessage call, and press
the button, you will stop in the middle of the user event handler
named ButtonClick.

In CLX, SetText also handles setting the Caption, but in this case its
overridden in TButtonControl. Take a look at Figure 9; you can see

Bring up the Call Stack debug window, and lets follow this back about
seven calls. At the top youll see you are in ButtonClick. Going backward

23 January 2001 Delphi Informant Magazine

Quick CLX
TForm1.ButtonClick($BF30AC)
TControl.Click
TButton.Click
TButton.CNCommand((48401, 796, 0, 6226716, 0))
TControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TWinControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TButtonControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TControl.Perform(48401,796,6226716)
DoControlMsg(6226716,(no value))
TWinControl.WMCommand((273, 796, 0, 6226716, 0))
TCustomForm.WMCommand((273, 796, 0, 6226716, 0))
TControl.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TWinControl.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TCustomForm.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TWinControl.MainWndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
StdWndProc(10093556,273,796,6226716)
TWinControl.DefaultHandler((no value))
TControl.WMLButtonUp((514, 0, 45, 6, (45, 6), 0))
TControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
TWinControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
TButtonControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
TWinControl.MainWndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
StdWndProc(6226716,514,0,393261)
TApplication.HandleMessage
TApplication.Run
Project1

Figure 10: VCL call stack in response to clicking Button.


TForm1.ButtonClick($15E2994)
TControl.Click
TButton.Click
MouseEvent(???)
TWidgetControl.EventFilter($11EFB74,$12FC5C)
TButton.EventFilter(???,???)
TWidgetControl.MainEventHandler($11EFB74,$12FC5C)
Qt_hook::eventFilter(this=:011F072C, sender=:011EFB74, event=:0012FC5C)
QObject::activate_filters
QWidget::event
QApplication::notify
QETWidget::translateMouseEvent
QtWndProc
C:\WINDOWS\system32\user32.dll
C:\WINDOWS\system32\user32.dll
C:\WINDOWS\system32\user32.dll
QApplication::enter_loop
QApplication::exec
QApplication_exec(handle=:011E3764)
TApplication.Run
Project1.exe

Figure 11: CLX call stack in response to clicking Button.

in time, you see that it was called by TControl.Click, which in turn was
called by TButton.Click. This could be VCL or CLX code; its the same
mechanism, the same code. You can see the output of the call stack for
the VCL in Figure 10, and for CLX in Figure 11.

program Figures;
destructor TWidgetControl.Destroy;
var
I: Integer;
Instance: TControl;
begin
Destroying;
if Parent <> nil then begin
RemoveFocus(True);
SetParent(nil);
end;
if FHandle <> nil then begin
DestroyWidget;
FHandle := nil;
end;
I := ControlCount;
while I <> 0 do begin
Instance := Controls[I - 1];
Remove(Instance);
Instance.Destroy;
I := ControlCount;
end;
FBrush.Free;
FPalette.Free;
inherited Destroy;
end;

Figure 12: The CLX TWidgetControl.Destroy destructor.


Button.Free;

As in the VCL, the Free method checks to see if the object is not
nil, and then calls the Destroy method. The TWidgetControl.Destroy
method is shown in Figure 12. Youll notice that it calls DestroyWidget,
which as I bet youve guessed destroys the underlying widget.

Conclusion
There you have it: the life and death of TButton. By following the
creation, use, and destruction of a control like TButton, weve seen the
differences in Borlands two component frameworks. CLX is a wonderful component framework. Its well designed and elegant. You can tell
by the code that the VCL is harder to read at this level. CLX makes
this much easier by separating the component notications from the
event-processing code. By calling dynamic methods, instead of passing
messages, the code path is easier to follow.
I would like to thank Adam Sparky Markowitz for his comments
and suggestions.
The projects referenced in this article are available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\JAN\
DI200101RK.

In CLX, the MainEventFilter is analogous to the MainWndProc in


the VCL. This sets up a try..except block, squelches some events
at design time, and calls the virtual EventFilter. In the example,
TButton.EventFilter is called. It handles the DoubleClick event before
calling the inherited EventFilter.
This EventFilter, belonging to TWidgetControl, is like the
TWinControl.WndProc. This method is a large case statement that handles the basic event types such as mouse events, keyboard events, painting
events, drag-and-drop events, and mouse-wheel events, among others. In
the example, you have a mouse event (QEventType_MouseButtonRelease)
which is processed into a direct call to TButton.Click.

Destroying the Button


Finally, if you close the form, the Button components Free
method is called:
24 January 2001 Delphi Informant Magazine

Involved as a user of Delphi since the initial beta, Robert Kozak is a member of the
Kylix R&D team and has been with Borland since the latter half of 1999. Since he
joined Borland, he has been involved in the development of C++Builder 5 and
Kylix. Robert was involved with the start of TaDDA! (Toronto Area Delphi Developers
Association), which later merged with TDUG (Toronto Delphi Users Group). Robert
continues to stay active in the user community, and on the Borland newsgroups.

Quick CLX
Begin Listing One Demonstration project

procedure FormDestroy(Sender: TObject);


private
{ Private declarations }
public
{ Public declarations }
Button: TButton;
procedure ButtonClick(Sender: TObject);
end;

{ ******************************************************* }
{
Project source
}
{ ******************************************************* }
program ButtonProject;
uses
{ $IFDEF LINUX }
QForms,
{ $ENDIF }
{ $IFDEF MSWINDOWS }
Forms,
{ $ENDIF }
Unit1 in 'Unit1.pas' { Form1 };
{ $R *.res }
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
{ ******************************************************* }
{
Unit source
}
{ ******************************************************* }
unit Unit1;

var
Form1: TForm1;
implementation
{
{
{
{
{
{

$IFDEF LINUX }
$R *.xfm }
$ENDIF }
$IFDEF MSWINDOWS }
$R *.dfm }
$ENDIF }

procedure TForm1.FormCreate(Sender: TObject);


begin
Button := TButton.Create(nil);
Button.Parent := Self;
Button.Caption := 'Hello, World!';
Button.OnClick := ButtonClick;
end;

interface
uses
{ $IFDEF LINUX }
QT, Variants, Classes, QGraphics, QControls, QForms,
QDialogs, QStdCtrls;
{ $IFDEF MSWINDOWS }
{ $ENDIF }
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
{ $ENDIF }

procedure TForm1.FormDestroy(Sender: TObject);


begin
Button.Free;
end;
procedure TForm1.ButtonClick(Sender: TObject);
begin
ShowMessage('Welcome to my life.');
end;
end.

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);

25 January 2001 Delphi Informant Magazine

End Listing One

At Your Fingertips

DBGrid Images, Taskbar Tips, Environment Variables / Delphi 2-5

By Bruno Sonnino

DBGrid Images, etc.


Taskbar Tips and Environment Variables

f you need to place images in the cells of a DBGrid component, you must accomplish
what is known as custom drawing. To do this, you must write an event handler for the
components OnDrawColumnCell event.
Its declared as shown here:
TDrawColumnCellEvent = procedure(Sender:

a bitmap and draw it on the DBGrids canvas. The


Object Pascal implementation is shown in Figure 1,
and the run-time result is shown in Figure 2.

TObject; const Rect: TRect; DataCol:


Integer; Column: TColumn;
State: TGridDrawState) of object;

where Rect is the rectangle where the data


should be drawn, DataCol is the index of the
column to be drawn, Column is the TColumn
object relative to the data, and State is the current state of the cell.
You must also test if the cell is of type
TGraphicField. If it is, you then assign the eld to
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
var
Bitmap : TBitmap;
begin
// Test if the field is a TGraphicField.
if Column.Field is TGraphicField then
with DbGrid1.Canvas do begin
// Clear previous information.
FillRect(Rect);
// Create the bitmap.
Bitmap := TBitmap.Create;
try
// Assign the field contents to the bitmap.
Bitmap.Assign(Column.Field);
// Draw the bitmap in the grid's canvas.
Draw(Rect.Left, Rect.Top, Bitmap);
finally
Bitmap.Free;
end;
end;
end;

Figure 1: Drawing a bitmap in a DBGrid.


26 January 2001 Delphi Informant Magazine

Its important to understand how DBGrids


DefaultDrawing property works. If it is set
to True (the default), each cell is drawn before
OnDrawColumnCell is called. This means that
your custom drawing will take place after the
default drawing has occurred. This is exactly
what you want in this case, because the rows also
contain text elds. If you set DefaultDrawing to
False, then you would also have to handle the
drawing of the text elds.
Instead of going to all that trouble, its perfectly
acceptable to let the DBGrid go through its
default drawing rst, before calling your custom
OnDrawColumnCell event handler. This means
that the text, (GRAPHIC), is initially written to the
cell, but the user never sees it. In fact, you can
take advantage of this behavior. In this example,
the font size of the graphic cell has been set to
48, so that the cell is the proper size for the
graphic that will replace the text.

Changing the Application Title


in the Taskbar
The title of the application thats shown
in the Windows taskbar is provided by the
Application.Title property. If it isnt explicitly set,
Delphi uses the caption of the main form.
Therefore, to change the title shown in the taskbar, you must change the Application.Title property to the text you want. You can be as fancy and
creative as you like.

At Your Fingertips
To temporarily
store the icon to
Figure 5: Animated hourglass icon.
be shown, a
variable named
FCurrIcon is declared in the private section of the form. Each
time the OnTimer event handler is red, you retrieve the current
icon from the ImageList and set Application.Icon to it. Then, you
increment (or reset, if the value has reached the maximum value)
the current icon index. The result is shown in Figure 5.

Accessing Environment Variables


To access environment variables, you must use the Windows API
function GetEnvironmentStrings. The GetEnvironmentStrings function returns a single PChar that contains all of the environment
variables. Each variable takes the form Variable=Value, separated
from the next Variable/Value pair by a single null character (#0).
At the end of the variables, there is a double #0.
Figure 2: The custom-drawn DBGrid at run time.

Figure 3: Taskbar title with the current time.


procedure TForm1.Timer1Timer(Sender: TObject);
begin
// Get icon from the ImageList and
// assign it to Application.Icon.
ImageList1.GetIcon(FCurrIcon, Application.Icon);
// Increment icon index.
if FCurrIcon >= 9 then
FCurrIcon := 0
else
Inc(FCurrIcon);
end;
end;

Figure 4: Code to animate an icon in the taskbar.

For example, the title could be a clock. Simply drop a Timer


component on the main form, and then place the following statement in its OnTimer event handler:
Application.Title := TimeToStr(Time);

The statement will be executed every time the OnTimer event handler
is red (the default is every second), changing the title in the taskbar
and displaying the current time, as demonstrated in Figure 3.

Placing an Animated Icon in the Taskbar


To change an applications icon in the Windows taskbar, you
must assign a new icon to the Application.Icon property. Animated
icons arent a feature of the Windows taskbar, so you must simulate one by creating a sequence of icons and presenting them at
set intervals.

After using GetEnvironmentStrings, you must parse the resulting


PChar, until you nd a double #0. One way to parse the PChar is
to rst cast it as a Pascal string, thus: string(Env). This converts a
PChar to a string, but stops when it nds a #0 character. When the
cast is made youre assured there is only one environment variable
in the string, so you can assign it to a StringList, ListBox, etc.
After parsing the PChar, you increment the pointer, so it points to
the next string. If there is a double #0, then the value of the rst
element of the PChar will be a #0, and you can stop the loop. The
code in Figure 6 parses the environment strings and adds them to
a ListBox component.
As a bonus, we can parse the items in the ListBox, using the
Names and Values properties, as shown in Figure 7. When the
strings in the list are in the form Variable=Value, the Names
property retrieves the names of the variables, and the property
Values retrieves their values.

Justifying Text
One frequently asked question is how to justify text between margins.
The Label, Memo, and RichEdit components can align text on the
right or left, or center it, but theres no way to align text between
margins, as word processors do. The solution lies in a Windows API
function named SetTextJustication.
Heres its declaration:
function SetTextJustification(
DC: HDC;
// Handle of device context.
BreakExtra, // Length of extra space, in logical units.
BreakCount: Integer // Count of spaces in line of text.
): Integer; stdcall;

Use a Timer component and modify its OnTimer event handler to


present the icons one after the other. The best way to store the
icons is in an ImageList component. Its easy to retrieve the icons
in sequence using the ImageLists index, and ImageList has the
GetIcon method for retrieving the image as an icon.

It isnt, as many people think, an easy task to justify text; you


can see from the declaration that there isnt a Text parameter to
simply pass in to be justied. To use it, you must pass the handle
of the device context (usually Canvas.Handle), the count of breaks
(blank spaces) in the line, and the extra space to be added in these
blanks. Sound difcult? Well it is.

Once youve interactively placed the icons in sequence in the ImageList, the code shown in Figure 4 animates the icon. You should
set the Timers Interval property to a value that provides a smooth
animation effect (500, for half a second, was used in this example).

There is another trick to this function: rounding errors persist until


the next time its called. So, before calling the function for the next
line, you must clean the rounding errors, passing 0 as the second and
third parameters.

27 January 2001 Delphi Informant Magazine

At Your Fingertips
Env := GetEnvironmentStrings;
with ListBox1 do begin
// If it's a double #0, Env[0] will be a #0;
// stop the loop.
while Env[0] <> #0 do begin
// Assign parsed string to ListBox Items.
Items.Add(string(Env));
// Make Env point to the next string.
Inc(Env, StrLen(Env)+1);
end;
end;

Figure 6: Separating individual environment strings and adding

with StringGrid1, Listbox1 do begin


// Set grid row count to number of items in ListBox.
RowCount := Items.Count;
for i := 0 to Pred(Items.Count) do begin
// The first cell has the name of the variable.
Cells[0,i] := Items.Names[i];
// The second cell has the value of the variable.
Cells[1,i] := Items.Values[Cells[0,i]];
end;

Figure 7: Separating the environment variables names and


values.

Figure 8: This sample application parses and displays environment variables.

For example:
SetTextJustification(Canvas.Handle, 0, 0);

The steps to justify text are:


1) Get each word of the text, one by one.
2) For each word, verify if the line has reached the maximum
length; if it hasnt, increment the break count.
3) If the line length is greater than the maximum length, it must be
printed without the last word, justifying it.
4) If its the end of a paragraph, the line must be printed with no
justication.
28 January 2001 Delphi Informant Magazine

procedure JustPrint(Canvas: TCanvas; Text: string;


MaxWidth: Integer);
var
i : Integer;
BegWord : Integer;
Breaks : Integer;
SizeY, PosY : Integer;
LineToPrint : string;
AWord : string;
begin
i := 1;
PosY := 0;
// Get height of the line.
SizeY := Canvas.TextHeight('Wy');
LineToPrint := '';
Breaks := 0;
// Set transparent drawing mode.
SetBkMode(Canvas.Handle, Transparent);
while i <= Length(Text) do begin
// Remove initial spaces and line breaks.
while (Text[i] in [' ', #10, #13]) and
(i <= Length(Text)) do
Inc(i);
// Search word.
BegWord := i;
while (i <= Length(Text)) and
not (Text[i] in [' ', #10, #13]) do
Inc(i);
// Clear rounding errors.
SetTextJustification(Canvas.Handle, 0, 0);
// Aword has current word.
AWord := Copy(Text, BegWord, i-BegWord);
// Verify if line has reached maximum length.
if Canvas.TextWidth(
LineToPrint + ' ' + AWord) > MaxWidth then
begin
// Add extra spaces.
SetTextJustification(Canvas.Handle,
MaxWidth-Canvas.TextWidth(LineToPrint), Breaks);
Canvas.TextOut(0, PosY, LineToPrint);
LineToPrint := AWord;
Breaks := 0;
// Increment drawing position.
Inc(PosY, SizeY);
end
else
begin
// Add word to the line.
if LineToPrint <> '' then
begin
LineToPrint := LineToPrint + ' ' + AWord;
Inc(Breaks);
end
else
LineToPrint := AWord;
// End of text or paragraph, doesn't justify.
if (i > Length(Text)) or
(Text[i] in [#10, #13]) then
begin
Canvas.TextOut(0, PosY, LineToPrint);
Inc(PosY, SizeY);
LineToPrint := '';
Breaks := 0;
end;
end; // else...
end; // while i <= Length(Text) do...
end;

Figure 9: The custom JustPrint procedure.

To get each word of text, you can process the whole text as a big
string, character-by-character, searching for break characters (e.g.
space, carriage return, line feed). As shown here:

At Your Fingertips
would exceed the line length. That word becomes the rst word of
the next line. To print the line, you must call SetTextJustication,
passing the break count found and the extra space to add. This is
determined by subtracting the actual width of the line from the
maximum width of the line.
Heres one way to implement it:
// Add extra space.
SetTextJustification(Canvas.Handle,
ClientWidth-Canvas.TextWidth(LineToPrint, Breaks);
// PosY has the current Y position to print the text.
Canvas.TextOut(0, PosY, LineToPrint);
// The new line to print begins with the word found.
LineToPrint := AWord;
// Restart break count.
Breaks := 0;

Figure 10: The sample application justifies text on a form.


// BegWord has the beginning of the word.
BegWord := i;
// Search until text is finished or a break is found.
while (i <= Length(Text)) and
not (Text[i] in [' ', #10, #13]) do
Inc(i); // i is position of end of text or next break.

The second step is to determine if the line length, with the new word
included, exceeds the maximum line length. As the words t in the
line, they are included in a variable named LineToPrint.
This verication can be accomplished like this:
// Place current word in AWord variable.
AWord := Copy(Text, BegWord, i-BegWord);
// Verify if it's a line break.
if Canvas.TextWidth(
LineToPrint + ' ' + AWord) > ClientWidth then
...

If the line has reached the maximum length, it must be printed in


a justied manner. The last word must not be printed, because that

29 January 2001 Delphi Informant Magazine

To print the line if the end of the text or the end of the paragraph
has been reached, there is no special procedure; a simple call
to TextOut will accomplish the task. Figure 9 demonstrates my
JustPrint procedure, which prints justied text. You must pass
three parameters to it:
1) a canvas,
2) the text to print, and
3) the maximum width of the line.
Figure 10 shows the accompanying sample application with text
justied on a form.

Conclusion
This month I have placed ve programming pointers at your ngertips to assist you in your Delphi development efforts. Its now up to
you to put them to good use.
The projects referenced in this article are available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\JAN\
DI200101BS.

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

New & Used


By Alan C. Moore, Ph.D.

Sleuth QA Suite 2
Powerful, Flexible Debugging and Profiling Tools

urboPower is best known for its award-winning component/routine libraries such as


Async Professional, Orpheus, and Systools. But TurboPower also produces excellent
code-analysis tools to help a developer ensure the quality of a finished application. The
newest of these tools is Sleuth QA Suite 2 (with QA standing for quality assurance).
Version 1 of the suite has two tools: CodeWatch
is a debugger for nding memory leaks, resource
leaks, problems with API calls, and more. StopWatch is a powerful proler. New tools are being
added to Suite 2, which should be shipping by
the time you read this article (see Whats New in
Suite 2 later in this review).

to monitor calls to API functions. In the rst


category it can identify memory leaks, resource
leaks, and memory overruns. It includes a database of known errors for all Delphi 32-bit compilers so you can exclude them from your analysis
and concentrate on leaks in your code. You can
also add known leaks to the database.

Each tool comes with the outstanding documentation thats become a TurboPower trademark.
That documentation includes a manual with a
thorough tutorial for each application to get you
up and running fast. You can run either application from within the Delphi IDE, or as a standalone executable.

In the API category, this tool can identify invalid


parameters or return values for Win32 API function calls. You can have it create a log for all
Win32 API calls, listing parameter values and
return values. As you may be aware, not every
API function works in every Windows version;
some can be used only on the NT family. Therefore, CodeWatch allows you to test platform
compliance for Win32 API function calls.

Debugging with CodeWatch


CodeWatch is based on TurboPowers popular
memory debugger, MemorySleuth. It signicantly extends the memory debugging capabilities of the older product, and adds new features

Figure 1: Default CodeWatch view with project loaded.


30 January 2001 Delphi Informant Magazine

This tool provides a great deal of exibility giving


you control over the errors to report. If your
application uses units for which you dont have
the source code, you can still include them in
your testing. You can also analyze multiple modules at the same time, including dynamically
loaded DLLs.
Best of all, you dont have to make changes to
your code to use Sleuth CodeWatch. The only
requirement is that you compile your application
with debug information. Figure 1 shows the
main screen after an application has been loaded.
Note the Shortcut Bar on the left that provides
access to three folders (General, Memory, and
Resources) and various views within each. The
default view is the Modules view of the General
folder. That view shows information about the
units used, including time called, name, version,

New & Used


Page

Description

Module

Shows information about the project modules used by the application being analyzed,
including name, when it was loaded, version
stamp, code, data size, etc.
Active only if one of the API function or
parameter options is selected. Provides a
report on errors resulting from API calls or
improper parameter values. You can also log
API calls receiving helpful information.
Shows debug information generated by an
application calling the Windows API function,
OutputDebugString. You can also show internal Sleuth QA audit messages.
Provides comprehensive view of the results
of an application run, summarizing the information displayed in other views. You can
print this report or select other options.

Parameters
& Failures

Debug Output

Report

Figure 4: StopWatchs user interface.

Chart window at the lower left, which explains some of the resource
types being monitored. CodeWatch can conveniently take you right
to the line of code where un-released memory is allocated.

Figure 2: CodeWatchs four pages (views).

Profiling with StopWatch


StopWatch is the long-awaited proler, a completely new tool from
TurboPower. Ive tried a number of prolers, but in my opinion
none of them quite measures up to StopWatch. Most of the others
use an approach I would call invasive; they perform their proling
by inserting lines of code into your source code units to perform the
actual proling. StopWatch uses a technique called dynamic adaptive
instrumentation, which modies the unit being proled at run time.
This approach is fast and leaves your source code untouched.
This tools user interface is similar to that of CodeWatch, as shown
in Figure 4. Here we see the Routines view after loading an application. The pages available Routines, Profile, and Comparison
provide data related to a proling operation. These different views
provide a convenient means to view just the data in which youre
interested, or examine all of the results in report form. The various
views are explained in Figure 5.
Figure 3: CodeWatch Chart showing real-time memory use.

memory image base, etc. Figure 2 explains the General folders


four views.
I was impressed with the many options available, both in the kinds
of analysis performed, and the manner in which the results of the
analysis can be viewed. You can choose to analyze memory leaks
alone, and/or add resource leak analysis, API function call analysis,
and/or API function validation.
A particularly nice feature is the collection of graphical display
views. One such view Types, Stats, and Charts consists
of three re-sizable windows. The Event Browser provides statistics
about the memory used, and a chart showing peak usage throughout the duration of the project (see Figure 3). If you select this
view before beginning analysis of a project, you can watch the
chart create a real-time visual representation of your projects
memory use as the application executes.
The screen shot in Figure 3 was taken at the conclusion of a
program run and includes the Session Summary dialog box, optionally displayed on program termination. It shows a summary of the
various leaks and errors. Youll note that (most of ) the memory is
released at the end, as shown in the graph. Note the Customize
31 January 2001 Delphi Informant Magazine

These three views provide a lot of information, and a great deal


of exibility. StopWatchs Settings Wizard gives you considerable
control over how the proler operates. You can set the Proling
Mode to Always On so that proling will be active as soon as the
application starts executing, or Trigger Activated so that proling
will not start until a specic routine triggers it. You can have
StopWatch check those units with source code, without source
code, or both. You have further options regarding leaf routines
(which you rarely need to include), threads, and external functions.
Now thats exibility!
As with CodeWatch, StopWatch also includes an excellent tutorial,
a Maze that is begging to be optimized. The most time-consuming
routine, Walk, is listed at the top. If youre wondering about the
difference between Net Time and Gross Time, its quite simple.
Net Time is the amount of time spent in the routine, excluding
any time spent in routines called from within it. Gross Time is
the amount of time spent in the routine, including the time spent
in routines called from within it. This helps to determine if a
bottleneck is in the routine itself, or in one of the routines it calls.
Like CodeWatch, StopWatch includes charts that let you view
various aspects of a single or multiple proling runs. Seeing the
most time-consuming routines in graphical format makes it easy to
see where you need to perform optimizations. You can also choose

New & Used


View

Data Shown

Routines

Information about the executable routines


from which you can select the routines to
analyze during a profiling run. A list of routines can be displayed in various views with
different groupings and levels of detail.
Results of a profiling session shown after
the application terminates. Includes three
related views. The Main Routine view shows
results sorted in descending order by Net
Time with the top routine as the best candidate for optimization. There is also a CallsTo-Current-Routine view showing the names
of the routines that called the routine in the
Main Routine view, and a Calls-FromCurrent-Routine view.
Lets you compare the results from different
profiling sessions. Ideal for determining if
optimization changes are having desired
results.

Profile

Comparison

Sleuth QA Suite is a powerful and flexible collection of debugging and


profiling tools for Delphi and C++Builder. The first version consists of
CodeWatch and StopWatch. Suite 2 improves these and adds three
additional tools: a coverage analyzer; a line timer; and a test-script
recorder that can be used with all of the other tools.
TurboPower Software Company
PO Box 49009
Colorado Springs, CO 80949-9009
Phone: (800) 333-4160
E-Mail: info@turbopower.com
Web Site: http://www.turbopower.com
Price: List price: US$399; upgrade from first version, US$149;
upgrade from Memory Sleuth, US$279. Competitive upgrades from
other debugging tools may apply.

Figure 5: StopWatchs three views.

from the following, related to the current prole:


 Net Times shows the 10 most time-consuming routines.
 Gross Times is similar to Net Times, but includes time spent
in called routines.
 Call Count shows the 10 most frequently called routines.
 Averages gives the 10 routines with the highest net average
execution time.
You can see the differences in two proling runs in either text form
or graphical form, enabling you to determine if your attempts at
optimization have been successful or not. Theres a Comparison
Chart that lets you graphically compare proles loaded into the
Comparison view.

Outstanding Documentation
If youve used other TurboPower products, youre aware of the excellent documentation that comes with each of its products. Sleuth QA
Suite is no exception. As mentioned, the manual includes a tutorial
for both tools (with all the les you need on disk). The manual
and online Help provide all the information you need to begin to
use these tools quickly. Sleuth QA Suite goes further by providing
a cogent introduction to dealing with memory issues and performing code optimization. Once the Suite has shown you where the
problems are, the manual gives you important clues about what to
do to solve those problems. The material is so good that Ive encouraged the folks at TurboPower to expand it in future versions. Even
without the extensive improvements in Suite 2, the rst version of
Sleuth QA Suite is an essential tool for every Delphi and C++Builder
developer. Before we nish, however, lets see what Suite 2 brings us.

matter if these modules are statically or dynamically loaded. Like


StopWatch, CoverageAnalyst uses dynamic adaptive instrumentation to modify the module being analyzed at run time. According
to TurboPower, coverage analysis was the most popular feature
request for the rst version of Sleuth QA Suite.
LineProler, another result of frequent feature requests, allows
timing at the source line level. It also uses dynamic adaptive instrumentation technology. It times each source line for the routines you
select, keeping track of times executed, total time spent, maximum
time, minimum time, and the average time for each line.
The nal new tool, ActionRecorder, lets you build QA test scripts
by recording keyboard and mouse actions for an application and
for playing them back later. You can initiate playback directly
from the ActionRecorder tool. You can also initiate playback automatically from within any of the four analysis tools after youve
launched an application.
In addition, Sleuth QA Suite 2 now supports 32-bit compilers
from both Borland and Microsoft. The new version supports
Visual Basic 6 and Visual C++ 6, giving Delphi developers who
work with those tools an added bonus.

Whats New in Suite 2


In addition to considerable enhancements to the two tools weve
been discussing, Sleuth QA Suite 2 adds three new tools: CoverageAnalyst, a coverage analyzer; LineProler, a line timer; and
ActionRecorder, a test-script recorder that can be used with all of
the other tools.
CoverageAnalyst enables you to determine how many times a line
of source has been executed in a program run. You can use it for
various types of modules, e.g. EXE, DLL, OCX, etc. It doesnt
32 January 2001 Delphi Informant Magazine

Alan Moore is a Professor of Music at Kentucky State University, specializing


in music composition and music theory. He has been developing educationrelated applications with the Borland languages for more than 15 years. He
has published a number of articles in various technical journals. Using Delphi,
he specializes in writing custom components and implementing multimedia
capabilities in applications, particularly sound and music. You can reach Alan
at acmdoc@aol.com.

New & Used


By Ron Loewy

ExpressQuantumGrid
and ExpressInspector
T

he database grid component included with Delphi is an extraordinarily useful


user interface component for dealing with database information. It can display
multiple records of data in an efficient format. However, Ive always believed that
the grid handles editing inefficiently, at best. Other common tasks Ive found hard
or impossible to implement with the standard DBGrid component include sorting by
column and grouping. In addition, there are no special in cell editors that allow
masked edit, drop-down list, or memo editing.
Faced with a large enterprise application using
MIDAS, I resumed my quest for the ultimate grid
that would provide these capabilities. Enthusiastic
praise for the ExpressQuantumGrid on Borlands
newsgroups led me to look at the product.

ExpressQuantumGrid
ExpressQuantumGrid includes two main components: a TreeList view combination named

TdxTreeList, and a DBGrid replacement named


TdxDBGrid. A new version of the suite now
offers a data-aware TreeList component, but for
my needs its of lesser interest. The most important thing is that the grid control is based on
the same common ancestor, so it can display
hierarchical information.
In a nutshell, TdxDBGrid combines traditional
grid capabilities (tabular view of multiple rows
and columns) with the abilities of a run-time
data/report viewer with features such as grouping,
sorting, and summarization.
When it comes to data editing, the grid features
18 in-place cell editors (see Figure 1) that make
it easy to create professional data entry applications. Some of the different editors are checkbox, memo, BLOB, currency, drop-down date,
drop-down list, masked edit, time, and spin
editor (a numeric editor with spin buttons).
Theres even a drop-down grid where you can
choose a cell value from a grid that displays
multiple rows and columns.

Figure 1: ExpressQuantumGrids multi-row bands and in-place cell editors.

33 January 2001 Delphi Informant Magazine

The run-time data/report viewer supports multilevel hierarchy grouping based on key columns,
automatic sorting, and automatic summary at the
footer or group level (see Figure 2). Summary isnt
limited to the simple addition function, but supports functions like minimum, maximum, aver-

New & Used


age, and count. The grids horizontal layout can be
divided into multiple data bands, allowing one data
record to span more than one physical display row
in the grid. Grid rows can be created that combine
simple, single-line text items (e.g. a customers name,
address, and phone number) in multiple display lines
for the data record, while a memo eld (e.g. notes
about the customer) can span multiple display rows
all representing the same data record.

ExpressInspector
While a capable grid provides the ability to display
and edit multiple columns in more than one record of
data, sometimes its better to provide a single record
data form for editing. In this form you can display
more columns of data omitted from the grid view
because of screen real estate considerations, or provide
bigger areas for editing memo or graphic columns.
If there is something I hate about developing database applications, its creating single-record data-editing forms. These forms usually include numerous
data-aware controls, each with its unique editing
needs and layout requirements. As much as I like to
create visually interesting applications that provide
the end user with easy data-entering capabilities,
these forms seem to be nothing more than an exercise in using the IDE. Of course, when the application needs to be updated to support a new database
column, or a different visual representation or validation of another, the process is repeated.
ExpressInspector is a component set that mimics a
Delphi or Visual Basic object inspector (see Figure 3).
It allows you to connect a data source to a component,
and use a well-dened property editor to identify the
columns that you want displayed in the inspector,
including the type of editor to use. (Of the 18 in-place
cell editors in ExpressQuantumGrid, only the dropdown grid appears to be missing from ExpressInspector.) You can also create categories and hierarchies of
properties. Developer Express provides both a dataaware version of ExpressInspector, or a standard version where you can generate your categories and cells
programmatically.

Figure 2: Fixed bands, grouping by year, and group summary information.

Figure 3: The in-place cell editors in the object inspector.

Unless your data-editing-form requirements force you to create


multiple control forms manually, a single ExpressInspector can be
used and populated easily with the columns of available data.

Developing with the Controls and Documentation


Once dropped on a form, both components provide a dizzying
array of properties from which to choose. Luckily, both have component editors that help with the denition of data columns and
their types, allow editing of individual column components, and in
the case of the grid, provide the mechanism to create bands.
The philosophy behind the components is that many common
actions can be accomplished without code just by setting property values. This approach is great once youve studied the documentation and checked the sample programs and experimented, but
it can be frustrating without the background. Mistakes are easily
made by forgetting to set an option or provide a property value.
34 January 2001 Delphi Informant Magazine

In my application, I used a MIDAS client dataset as the source


of the data. After reading the documentation, I noticed that
if I wanted to provide grouping in the grid, I had to set the
egoLoadAllRecs member of the Options property. Unfortunately, the
Options property helps describe no less than 30 options (some of
them completely unrelated), so I failed to recognize the need to set
the KeyField property to enable the egoLoadAllRecs option. Without
it, the grid always displays empty a condition that was xed once
an e-mail sent to Developer Express technical support claried the
situation. Another error was made when the current cursor in the
underlying dataset did not change after I selected a node in the grid.
This time I knew enough to read the documentation thoroughly,
and found that egoCanNavigate must be set (and a set of other
options required by it) to avoid this problem.
The products come with a comprehensive Help le, many samples,
and a subscription to a Web-based FAQ system. The capabilities are
wide and the customization options are almost endless, so a close
examination of the references and sample applications is a must.

New & Used

After you use ExpressQuantumGrid and ExpressInspector, you wont


want to develop another Delphi database application without these
products. In-place cell editing and sorting by column and grouping
are easily accomplished in ExpressQuantumGrids VCL grid, and singlerecord editing is effortless in ExpressInspector. Both tools have wide
capabilities and customization options.
Developer Express
6340 McLeod Dr., Ste. 1
Las Vegas, NV 89120
Phone: (702) 262-0609
Web Site: http://www.devexpress.com
Price: ExpressQuantumGrid Suite: Standard, US$299; Professional,
US$349. ExpressInspector Suite, US$99.

I have to admit that Im impressed with the quality of Developer


Express technical support. Any question I submitted was immediately answered via e-mail and solutions were provided when the
problems were in my code or understanding. The consensus among
Developer Express users on the Borland forums mirrors my experience even when the issues are in the Developer Express code.

35 January 2001 Delphi Informant Magazine

In addition to the source, documentation, and samples, both products come with two additional libraries of code: a memory-based
dataset, and a collection of visual components that can be used in
your application. You should visit their Web site to get a full idea of
all the products available from Developer Express.

Conclusion
After my experience with ExpressQuantumGrid and ExpressInspector, I cant imagine developing a Delphi database application without these tools. The grid is the most capable VCL grid Ive found.
Just by using a small part of its capabilities and customization
options, my applications seem more professional, and the users
enjoy the editing capabilities.
In my opinion, a grid is the most important database UI control,
and ExpressQuantumGrid is a must-have tool in my database components arsenal. The ExpressInspector component is not as essential
for every application, but if your application can use an object
inspector-like interface for single-record editing, this component
is easy to use and capable. I dont hesitate to recommend it. Its
well built and provides the same exceptional technical support from
Developer Express.

Ron is a software developer specializing in Business Intelligence applications.


He can be reached at rloewy@transport.com.

Best Practices
Directions / Commentary

Whats on Your Bookshelf?

ike physicians, programmers cannot rest on their laurels. The industry advances far too quickly for anyone to pull
a Rip Van Winkle and snooze through the implementation of popular technologies such as XML, SOAP, COM+, ad
infinitum. You snooze, you lose is a maxim that applies very well to the programming profession.
It is just as necessary to lay a strong foundation on which to
fasten that knowledge. You need to acquire general principles of
programming before you can effectively apply the specics related to
new technologies and techniques. The principles are best acquired
through books, where you can commune with those older and wiser.

hairy enough to challenge even Wendls powers of concentration.


One afternoon, I was bent over a program listing while Wendl
was staring into space, his feet propped up on the desk. Our boss
came in and asked, Wendl! What are you doing? Wendl said, Im
thinking. And the boss said, Cant you do that at home?

A list of books which every programmer should read and periodically re-read follows. Theyre separated into two categories:
old classics and new classics. The old classics certainly arent as
old as Stendhals The Red and the Black or even Steinbecks Grapes
of Wrath, but as far as programming books go, theyre not spring
chickens either. Although the original year of publication is shown,
many have enjoyed recent revisions. Why no Delphi classics you
ask? That topic was addressed recently by Alan Moore in his
November 2000 File | New column.

The Psychology of Computer Programming by Gerald M. Weinberg


[Dorset House, 1971] is about programming and software project
management, but most of all its about programmers and what
makes us tick. Perhaps as important, and even more surprising to
non-programmers (including many managers) is what doesnt make
us tick. This book not only helps you understand yourself and your
colleagues better (and even non-programmers, as their motives and
world view are contrasted with those of programmers), but is also
an enjoyable read.

Old Classics

The Secrets of Consulting by Gerald M. Weinberg [Dorset House,


1986] is complementary to Weinbergs The Psychology of Computer
Programming. Its described as ... an irreverent, funny, provocative,
satirical but true look at those thousands of professionals, as well
as con men, who call themselves consultants. The book includes
numerous rules and laws, such as The Credit Rule: Youll never
accomplish anything if you care who gets the credit, The Titanic
Effect: The thought that a disaster is impossible often leads to an
unthinkable disaster, and The Weinberg Test: Would you place
your own life in the hands of this system?

The Mythical Man Month by Frederick P. Brooks, Jr. [AddisonWesley, 1975] contains a series of seminal essays on software project management, and introduces Brooks Law: Adding manpower
to a late project makes it later. Also noteworthy is Brooks No
Silver Bullet essay, which is reprinted in the 1995 edition.
Programming Pearls by Jon Louis Bentley [Addison-Wesley, 1985]
is a series of essays on programming techniques and computer
science theory. The preface explains the reasoning for the books
name: Just as natural pearls grow from grains of sand that have
irritated oysters, these programming pearls have grown from real
problems that have irritated real programmers.
Peopleware by Tom DeMarco and Timothy Lister [Dorset House,
1987] is a people-centric, common-sense guide to software project
management. Besides being a practical and insightful book, its also
a downright entertaining one. It even relates some Dilbertesque
episodes, such as this: In my years at Bell Labs, we worked in twoperson ofces. They were spacious, quiet, and the phones could be
diverted. I shared my ofce with Wendl Thomis who went on to
build a small empire as an electronic toy maker. In those days, he
was working on the ESS fault dictionary. The dictionary scheme
relied upon the notion of n-space proximity, a concept that was
36 January 2001 Delphi Informant Magazine

In 201 Principles of Software Development by Alan M. Davis


[McGraw-Hill, 1995], most of the principles are explained in a
concise, yet approachable way. In fact, they often t on a single
page. Unfortunately, this little gem is very hard to come by. I read
it as I was on the bench while working for a former employer.
Ive tried to order it from Amazon, Barnes & Noble, Fatbrain, etc.,
all to no avail. If anybody knows where I can get a copy of this
book, please let me know.

New Classics
Code Complete by Steve McConnell [Microsoft Press, 1993]. If
you only read one general programming book, this should be it.
McConnell shows what to do and why, teaching good program-

Best Practices
ming practices throughout. Example code is in C, Basic, Pascal,
and other languages, but is always explained well enough that it
can be followed regardless of your level of multilingualism. If you
are serious about programming, you should read and apply the
information in this book. (By the way, I did not remember
consciously, anyway that the name of McConnells column in
IEEE Software was Best Practices at the time I suggested that title
for this column.)
Rapid Development by Steve McConnell [Microsoft Press, 1996]
elucidates how to responsibly speed up the development process,
why RAD often goes bad, and what to do to prevent it.
Software Project Survival Guide by Steve McConnell [Microsoft
Press, 1998] makes specic suggestions on how to give yourself
the best chance of success on your software projects. It shows
that there is another way; you dont have to bump your head on
every step each time you come down the stairs (read the book
for the full analogy). Why does the author go to such lengths to
tell readers how to avoid death march projects? A quote from
the book proves illuminating: As Thomas Hobbes observed in the
17th century, life under mob rule is solitary, poor, nasty, brutish,
and short. Life on a poorly run software project is solitary, poor,
nasty, brutish, and hardly ever short enough.
After the Gold Rush by Steve McConnell [Microsoft Press, 1999]
makes the point that the software profession needs to grow up
if it wants to be considered a true engineering profession. He
also discusses the possible ramications for individual programmers who are not progressive and alert: Programmers who arent
paying attention could easily nd themselves working as twentyrst century software janitors. Read the book to nd out how to
prevent this from happening to you or someone you love.
The preface of The Pragmatic Programmer by Andrew Hunt and
David Thomas [Addison-Wesley, 2000] says, This book will
make you a better programmer. I agree. It introduces the broken
window theory, explains why and how you need to continue to
invest in your knowledge portfolio, and contains (not unlike
the 201 Principles book previously noted) a listing of 70 tips
and a checklist of things to keep in mind as you practice your
profession.
The following three books are not programming books per se,
but are nevertheless very helpful in understanding how best to
approach and solve challenges.
How to Solve It by G. Polya [Princeton University Press, 1971]
describes itself: This ... was written by an eminent mathematician,
but it is a book on how to think straight in any eld. The work
was a major inuence on the revival of heuristics or the study
of the methods and rules of discovery and invention. In lucid
and appealing prose, it shows how the mathematical method of
demonstrating a proof or dening an unknown can be of help in
attacking any problem that can be reasoned out from building
a bridge to winning a game of anagrams ... deft, indeed brilliant
instructions on stripping away irrelevancies and going straight to
the heart of a problem.
It is amazing at times how much this book seems as if it was written specically about solving software challenges. As an example
of that, there is a How to Solve It list comprised of the following
steps:
37 January 2001 Delphi Informant Magazine

1)
2)
3)
4)

You have to understand the problem.


Find the connection between the data and the unknown ...
You should obtain eventually a plan of the solution.
Carry out your plan.
Examine the solution obtained.

Sounds like analysis, design, coding, and testing to me.


Conceptual Blockbusting by James L. Adams [Perseus, 1990]. To
use a hackneyed term, this book teaches you to think out of
the box. Its described as a broad compilation of strategies and
techniques designed to liberate creative thinking and an excellent
do-it-yourself study guide to better ideas. This excerpt may whet
your appetite: ... the natural response to a problem seems to be
to try to get rid of it by nding an answer often taking the
rst answer that occurs and pursuing it because of ones reluctance
to spend the time and mental effort needed to conjure up a richer
storehouse of alternatives from which to choose... In engineering one
nds the Rube Goldberg solution, in which the problem is solved
by an inelegant and complicated collection of partial solutions.
The Design of Everyday Things by Donald A. Norman [Doubleday,
1990]. Isaac Asimov introduced this book well by saying: We
are all victimized by the natural perversity of inanimate objects.
Here is a book at last that strikes back both at the objects and
at the designers, manufacturers, and assorted human beings who
originate and maintain this perversity. It will do your heart good
and may even point the way to correcting matters. The following
quote applies especially well to our profession: Manuals tend to
be less helpful than they should be. They are often written hastily,
after the product is designed, under severe time pressure and with
insufcient resources, and by people who are overworked and
under appreciated. In the best of worlds, the manuals would be
written rst, then the design would follow the manual. Frederick
Brooks also makes this point (that the end-user documentation
should be written rst, and used as a guide in development) in The
Mythical Man Month.
In preparation for next years Delphi Hall of Fame inductions,
I would like to cordially invite all of you in Delphi-land to nominate a person (or people) who you think deserves to be in the
Hall. Please send your nominations, along with your reasons, to
bclayshannon@earthlink.net.
Clay Shannon

Clay Shannon is an independent Delphi consultant, author, trainer, and mentor


operating as Clay Shannon Custom Software. He is available for remote development, short-term on-site assignments, as well as select long-term assignments in the Spokane, WA/Coeur d Alene, ID area. To view his resume, go
to http://www.sysadminsrus.net/clayshannon/ClayShannon.doc. You can reach
him at bclayshannon@earthlink.net.

File | New
Directions / Commentary

Project JEDI: Knights Battling the Empire

elphi is more than an outstanding development tool. Its a community. Nowhere is that more apparent than in
Project JEDI, the Joint Endeavor of Delphi Innovators. JEDI is a project on the move.

A lot has changed since I last wrote about the Project in the June
1999 Delphi Informant Magazine. If youve visited the JEDI Web site
(http://www.delphi-jedi.org) lately, you know what Im talking about.
The site won About.coms Best of the Net award in March 2000.
In listing the award, the Projects goal was to extend Delphis native
access to the Windows API by translating C/C++ headers of popular,
but as yet unsupported, technologies into Delphi interface units.
That was JEDIs original goal, but the Project now encompasses
much more. I would describe it as an impressive organization of
international volunteers donating their efforts to improve Delphi and
help their fellow developers.
That spirit of community is accentuated by the third-party vendors
who have generously contributed their tools and resources, including:
Nevrona Designs, who donated licenses for ReportPrinterPro/Rave;
Digital Logikk AS, who donated copies of Time2Help for the Help
Team; and HREF Corporation, for hosting the Web site and providing their outstanding WebHub technology for the Web Team to use.
These are just three of many donations. Before discussing the exciting
new developments, I should go back and trace the Projects beginnings
for the benet of readers who may be just nding out about it.

The Power of the Internet


One thing is quite clear: Without the Internet there would be no
Project JEDI. In doing research for this column I found Brian
Dupras 9/25/97 message that started it all, one that was posted to
the former COBB Group DDJ list that I approved as a moderator of
that list. It began, Ive been using Delphi since its beta days, and Ive
grown to truly love the product. Theres one thing that concerns me,
however. Microsoft comes up with new SDKs all the time, most of
which could benet countless Delphi developers if only we had the
proper translations and code examples.
Brian went on to explain how frustrating it was to put a lot of effort
into translating a header le, only to have Borland release its own
version six months later. He also pointed out how quickly some of
the technologies were evolving and how out-of-date some of Borlands
translations were at that time. To make a long story short, the DDJ
list was ooded with messages that evening, and the notion of getting
together and taking on the task ourselves quickly emerged. A handful
of us participated in an Internet chat session the next day, resulting in
the beginnings of an organization.
38 January 2001 Delphi Informant Magazine

One of those people was Helen Borrie (Australia), who has continued to be one of the most active and hard-working Jedians. On
the following Monday she wrote to the DDJ list, letting folks
know what had occurred over the weekend. She began: Ive been
astonished - amazed - heartened - all of that - to see how people
have rallied around a perceived problem in the Borland support
machine and come up so willingly with the beginnings of a strategy
to solve it. What a great weekend! Then she expressed one of our
initial dilemmas: We here (a team of six Delphi developers with a
variety of Windows and DOS backgrounds) are just having a little
bit of trouble understanding what the project is aimed at. Over the
days and weeks that followed the Project was dened and named,
a president and other administrators selected, and work began on
the rst translations.
Ups and downs, reorgs, Borland, and growth. The project had many
ups and downs in its rst year. At times members wondered if the
Project would survive. Several early members deserve special recognition for managing those challenging times of the rst year. Tim
Hayes, the rst president of the Project, was one of the main driving
forces. Keith Anderson, another founding member, made Internet
support services available to the project. Robert Love organized Birdsof-a-Feather sessions at the 1998 and 1999 Borland Conferences.
The project had one especially low period. In early 1999 it seemed
all but dead, with almost no discussions on the Internet and little
activity. Our current president, Thomas Guarino, took the initiative of
inviting three of us who were still active in Internet JEDI discussions
(Michael Beck, Helen Borrie, and me) to form a new ADMIN Team.
Our goal was simply to get the project moving again, and we were
remarkably successful.
Soon we learned that some folks at Borland had been watching the
Project from a distance, wondering if it would survive and whether
they should get involved. We soon entered into a discussion with
members of the Delphi R&D Team about ways we could work
together. (I covered much of this in my June column, so I wont go
into details here.) When Delphi 5 was released, the companion CD
included one of our conversions. There was also a JEDI Easter egg
included in Delphi 5 (type AJEDI in the About box). Charlie
Calvert was one of the most important people at Borland who helped
foster the new relationship in so many ways.

File | New
A new team emerges. For any organization to remain successful,
it must be willing to change. Project JEDI is no exception. In
August 1999 Tom announced the formation of a Steering Group to
enhance communication among the many new JEDI leaders who had
emerged. This new group was soon making more and more decisions.
A few months ago it became clear to Tom that it was time to dissolve
the ADMIN Team. That small group had served its purpose of resurrecting the project with its members already active in the new Steering
Group. The story does not end here. With the many new JEDI
Knights from all over the world, and an expanded leadership group,
the Project has delved into new areas to serve the Delphi community.
Not just header conversions. Header conversions remain a major
focus of the Project, and that area has improved considerably with
many new translations. The core activity of the Project was enhanced
further by the clean-up and grading of the API library, accomplished
by Rudy Velthuis (Netherlands), Phil Shrimpton (UK), and Marcel
van Brakel (Netherlands). Thanks to them, its now a lot easier for
developers to locate the translations they need. However, there are so
many new exciting JEDI projects that I can only scratch the surface. A
major new area of emphasis is education, with several projects providing a plethora of programming information in a variety of formats.
JEDI Voyager provides an online journey through a series of articles
such as one on programming and testing by Robert Marquardt
(Germany), a Font Users Resource tool by Alan Lloyd (USA), and
a series of articles entitled Making Forms Work and on creating
Help Files by Kevin Gallagher (USA). In addition to this online
educational initiative, there is also an Education Team whose goal is
to provide Delphi educational material in a variety of formats. We
have received a lot of donations, with the biggest one coming from
Greg Lief (almost 8MB of training materials). To make it easy to
read such material when ofine, Michael Beck redesigned the JEDI
Dolphin, an application that reads articles and tutorials ofine. Be
sure to download it and check it out. If you are an aspiring Delphi
writer and/or have a particularly cogent way of presenting a technical
topic, please join us and add to the knowledge base.
Early in the project, we had discussions about creating code examples,
classes, components, and sample units. Today that goal is becoming
a reality. The JEDI Code Library (JCL) is a set of thoroughly tested

39 January 2001 Delphi Informant Magazine

and fully documented utility functions and non-visual classes built


upon code donated by the JEDI community. One of the most active
Jedians, Marcel van Brakel (Netherlands) is its main architect and
founding father, with help from Robert Marquardt (Germany)
and Matthias Thoma (Germany). A similar project, Project JEDIs
Visual Component Library (JVCL), is independent of any third-party
developer or Borland. It is intended to ll in the holes in Borland
Delphis standard VCL library. There are a number of international
contributors, including Petr Vones (Czech Republic), Anthony Steele
(South Africa), and Anders Melander (Denmark), to mention just
few. You can join them.
Finally, there are projects in their inception. Take a look at the
Proposal for A Project JEDI Software Engineering Tool Suite Project by Nicole Boivin. This provocative essay is much more than a
proposal and includes some interesting insights about approaches to
programming. You may be wondering if Project JEDI will have any
involvement with the Kylix project. The Jedix Page provides news and
snippets about Kylix, including a message from Michael Bonner, the
Coordinator of the JEDI Kylix Beta group.
As you can tell, theres a lot going on at Project JEDI these days,
and its only going to get bigger in the future. Why dont you join
us? Were proving every day that although the Delphi community is
much smaller than the Visual Basic Empire, we nevertheless can boast
of the best Windows developers using the best Windows development
tool. Nowhere is that more apparent than at Project JEDI. Now were
embarking on an exciting journey into the Linux world. Do you want
to be a part of the cutting edge?
Alan C. Moore, Ph.D.

Alan Moore is a Professor of Music at Kentucky State University, specializing in music


composition and music theory. He has been developing education-related applications
with the Borland languages for more than 15 years. He is the author of The Tomes
of Delphi: Win32 Multimedia API [Wordware Publishing, 2000] and co-author (with
John Penman) of an upcoming book in the Tomes series on Communications APIs.
He has also published a number of articles in various technical journals. Using
Delphi, he specializes in writing custom components and implementing multimedia
capabilities in applications, particularly sound and music. You can reach Alan at
acmdoc@aol.com.

Das könnte Ihnen auch gefallen