On The Cover: January 2001, Volume 7, Number 1

Download as pdf or txt
Download as pdf or txt
You are on page 1of 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.
REVIEWS
16 Greater Delphi 30 Sleuth QA Suite 2
Easy Pivot Tables — Alex Fedorov and Natalia Elmanova Product Review by Alan C. Moore, Ph.D.
Alex Fedorov and Natalia Elmanova demonstrate the power and flex-
ibility of PivotTable, one of the Microsoft Office Web Components, when
used from Delphi. 33 ExpressQuantumGrid and ExpressInspector
Product Review by Ron Loewy
21 Quick CLX
The Life and Death of TButton — Robert Kozak
Borland’s Robert Kozak begins his regular Kylix column. Focusing on CLX
(pronounced “clicks”), the cross-platform component library, he begins
DEPARTMENTS
with the humble TButton. 2 Delphi Tools
5 Newsline
26 At Your Fingertips 36 Best Practices by Clay Shannon
DBGrid Images, etc. — Bruno Sonnino
38 File | New by Alan C. Moore, Ph.D.
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


Delphi Starbase Announces StarEstimator
Starbase Corp. announced
T O O L S the release of StarEstimator,
an automated project-planning,
New Products risk-management, and cost-esti-
and Solutions mation solution. StarEstimator
is designed to ensure the deliv-
ery of software and Web devel-
opment projects on time and
within budget. StarEstimator
automates the planning pro-
cess, improving the probability
of aligning management expec-
tations with actual project costs
and completion dates.
StarEstimator provides the
structure for defining a proj-
Book Picks ect, analyzing the size of the
application, specifying the
languages to be used, exam- type of software or Web project Starbase Corp.
ining the team’s capability, regardless of language or plat- Price: US$2,499 for a single user.
Learn Object Pascal with Delphi addressing management con- form. Once the project model Phone: (888) 782-7700
Warren Rachele
Wordware Publishing, Inc.
straints, and tabulating the has been reviewed, refined, and Web Site: http://www.starbase.com
results and testing the method- accepted by the project team, it
ology employed. Every project can be imported into Microsoft
is divided into eight phases: Project and then into StarTeam, NAG Releases
analysis, design, coding, test- the company’s integrated tech- NAG Components
ing, validation, configuration nical collaboration and software
management and quality assur- development management tool.
for Delphi 1.4
ance, management, and doc- StarEstimator leverages seven NAG Software Solutions
umentation. Exact percentages estimation methods and draws announced the release of NAG
of the project estimation are from a database of more than Components for Delphi 1.4,
ISBN: 1-55622-719-1 pre-allocated, but can be cus- 20,000 successful development fixing minor bugs, improving
Cover Price: US$49.95
tomized to meet individual projects across a variety of proj- overall functionality, and
(358 pages, CD-ROM)
program requirements. ects and types. including new features and
StarEstimator creates a StarEstimator allows users components, such as the
complete estimation of cost, to make environmental adjust- TNAGSpinEdit component and
PGP: Pretty Good Privacy
schedule, tasks, deliverables, ments specific to their orga- TNAGSpeedButton compo-
Simson Garfinkel maintenance, and support nization and run “what-if ” nents, and a new TNAGButton
O’Reilly requirements for virtually any scenarios. that is glyph-capable.
All registered users of NAG
Xceed Software Releases FTP Library Components for Delphi v1.3
Xceed Software, Inc. released FTP standards. The library also are eligible for a free upgrade.
Xceed FTP Library 1.0, a includes many advanced fea- NAG Components for Delphi
new COM/ActiveX library that tures, including silent back- is an easy-to-use, one-stop suite
allows developers to add flexi- ground processing, support for of 100 percent native, VCL-
ble FTP file transfer capabilities both single-threaded (STA) based components for Borland
to their Windows or Web appli- and multi-threaded apartment Delphi. The entire package
cations. The library is designed (MTA) threading models, features over 40 components,
ISBN: 1-56592-098-8 to take advantage of the Win- custom interface advising, including visual controls for
Cover Price: US$34.95
Sock 2 networking API. sending and receiving files enhancing the user interface
(393 pages)
The Xceed FTP Library to/from memory, and the abil- of applications and non-visual
includes an easy-to-use program- ity to manually parse server components that allow devel-
ming interface, documentation, directory listings. opers to interact with the
prompt technical support, opti- The product ships with end-user’s system without any
mized code, flexibility, and reli- documentation, technical sup- knowledge about the low-level
able operation. port, and a 60-day money-back Windows API.
The Xceed FTP Library sup- guarantee.
ports the complete FTP com- NAG Software Solutions
mand set and complies with Xceed Software, Inc. Price: US$69.95
RFC 959, 1123, and 1579 Price: US$149.95 E-Mail: sales@nagsoftware.com
standards, making it firewall- Phone: (800) 865-2626 Web Site: http://www.nagsoftware.com/
friendly and compliant with Web Site: http://www.xceedsoft.com products/nagcomponents/

2 January 2001 Delphi Informant Magazine


ShazamWare Releases Shazam Power Query and Report Wizard 4.0
Delphi ShazamWare Data Systems, Inc.
T O O L S released Shazam Power Query 4.0
and Shazam Report Wizard 4.0.
New Products Shazam Power Query (SPQ) is the
and Solutions advanced visual SQL builder com-
ponent for Borland Delphi and
C++Builder. SPQ seamlessly inte-
grates with the Borland Database
Engine (BDE) and other BDE
alternatives that support SQL. It
also supports all TDataset-com-
patible controls, charting compo-
nents, and report writers.
SPQ includes a new Query-By-
Example (QBE) interface, which
supports visual table joins, outer
Book Picks joins, drag-and-drop field selec-
tion, custom field expressions,
aggregate calculations, and more.
Programming Microsoft
Developers can create their own
Internet Explorer 5 query metaphor with SPQ’s open
Scott Roberts architecture.
Microsoft Press The Shazam Data Model
component stores database infor-
mation, allowing developers to
change or alias table and field
names without affecting the data-
base. Developers can also auto-
mate table joins, eliminating the like interface. Reports are saved to are supported in Delphi and
need for end-users to join tables a local or client/server table with- C++Builder 3.0 through 5.0. Both
themselves. out special coding. products include all of their
ISBN: 0-7356-0781-8 ShazamWare also released SRW includes a preview Delphi source code, user and
Cover Price: US$49.99 Shazam Report Wizard (SRW) component with 10:1 compres- developer help files, and a royalty-
(511 pages, CD-ROM)
4.0, which expands SPQ’s query sion for distributing reports via free executable distribution license.
capabilities with visual layout and e-mail and the Web which does
presentation tools. not require DLLs. ShazamWare Data Systems, Inc.
Linux in a Nutshell, Third Edition Shazam Report Wizard 4.0 Shazam Power Query 4.0 and Price: Shazam Power Query 4.0, US$249;
Ellen Siever, Stephen Spainhour,
Stephen Figgins, Jessica P. Heckman
also includes Reports Explorer, a Shazam Report Wizard 4.0 sup- Shazam Report Wizard 4.0, US$349.
O’Reilly tool that manages a library of port Delphi and C++Builder 1.0 E-Mail: shazam@shazamware.com
reports with a Windows Explorer- through 5.0. BDE alternatives Web Site: http://www.shazamware.com

Zero G Ships InstallAnywhere 3.5


Zero G Software, Inc. InstallAnywhere builds pro- Anywhere to create universal
announced InstallAnywhere 3.5, fessional, multi-platform installers installers that can deploy software
which includes such enhance- capable of installing and configur- to multiple platforms via the
ments as full Java 2, version 1.3 ing software on virtually any client Internet or CD-ROM; build
compatibility, and Linux sup- or server platform. Each installer installers for platforms including
port. InstallAnywhere 3.5 pro- created by InstallAnywhere recog- Windows 95, 98, ME, 2000 and
ISBN: 0-596-00025-1 vides software producers with nizes the platform under which it NT, Mac OS, Solaris, Linux,
Cover Price: US$34.95 powerful solutions for installing is operating and then tailors its AIX, HP-UX, and any other
(797 pages)
and configuring software across installation to the target system, Java-enabled platform; install a
multiple platforms. whether by creating shortcuts in Java virtual machine as part of an
The InstallAnywhere 3.5 family the Windows Start menu, setting application’s install; and automat-
— the Enterprise Edition, Stan- custom icons on the Mac OS, ically build Web Installer HTML
dard Edition, and Now! — pro- starting Windows NT/2000 ser- pages for deploying software via
vides complicated multi-system vices, setting Unix environment intranets and the Internet.
deployment with an easy-to-use variables, or modifying file per-
interface. InstallAnywhere Enter- missions on Unix systems. Soft- Zero G Software, Inc.
prise Edition 3.5 solves multi-plat- ware producers no longer need to Price: Enterprise Edition, US$1,995; Stan-
form deployment problems and build separate installers for each dard Edition, US$995; Now! is free.
provides customization with inter- platform. E-Mail: sales@ZeroG.com
national support for 29 languages. Developers can rely on Install- Web Site: http://www.ZeroG.com

3 January 2001 Delphi Informant Magazine


Delphi WinA&D Delivers Enhanced Data Models
Excel Software extended the data information systems. Model edi- map design elements to specific
T O O L S modeling and SQL code genera- tors are complimented with verifi- SQL schemas.
tion capabilities in its WinA&D cation and balancing reports, code Generated SQL can be cus-
New Products modeling tool. Database designers generation, reengineering capa- tomized to include primary, for-
and Solutions can create logical and physical bilities and scriptable HTML eign, and alternate key constraints,
data models and generate the reporting features. and check constraints in either
SQL schema code for RDBMS Logical data models often use CREATE TABLE or ALTER
products, including Oracle, SQL expressive names and various pre- TABLE statements. Other SQL
Server, DB2, Sybase, Informix, sentation options to communicate elements can selectively generate
and InterBase. Rich data models information between developers. DROP or CREATE statements
can represent tables, views, The names of database objects like and RDBMS specific triggers, pro-
constraints, assertions, triggers, tables, views, and columns are typ- cedures, functions, and methods.
indexes, procedures, and other ically more constrained and cryp- WinA&D runs on Windows 95,
SQL elements. Other general tic in physical data models due 98, NT, or 2000. Data models
enhancements include diagram to the RDBMS limitations and created in earlier versions are auto-
scaling from 25 percent to 200 the legacy systems that they repre- matically converted to use the
Book Picks percent, increased dictionary
capacity, and a convenient Notes
sent. WinA&D allows designers
to easily toggle between logical
enhanced data modeling features
in the Desktop, Educational, and
window for attaching notes to dia- and physical models and show Developer editions.
Linux System Administrator’s gram objects in any type of model. entities (tables) with full attribute
Survival Guide, Second Edition WinA&D is a tool for lists, primary and foreign keys Excel Software
Tim Parker system analysis, requirement spec- only, or customized to show Price: Standard, US$495; Educational,
SAMS
ifications, software design, and specific attributes. US$845; Desktop, US$1,295; and Devel-
code generation. Popular mod- WinA&D’s namespace concept oper, US$1,995.
eling notations and supported now supports data modeling, SQL E-Mail: info@excelsoftware.com
methods include object-oriented code generation, and reengineer- Web Site: http://www.excelsoftware.com
analysis and design with UML ing of different database schemas.
or structured analysis and design Namespaces simplify team devel- Quasidata Announces
using Yourdon/DeMarco. Infor- opment, enable powerful orga-
mation Engineering (Martin) style nizational and reporting tools, DbAltGrid Suite 1.2
data models are used to develop partition complex systems, and Quasidata announced the
ISBN: 0-672-31793-1 availability of DbAltGrid Suite
Cover Price: US$49.99 Fractal Releases NNWFax 1.2, the latest release of their
(740 pages)
Fractal Software data-aware grid for Delphi and
announced the C++Builder.
release of NNWFax, New features include RTF field
Learn Red Hat Linux Security a Delphi/ support, auto-scaling of column
George M. Doss C++Builder com- widths, in-place editing of memo
Wordware Publishing, Inc. ponent for adding fields within a cell, the display of
fax functionality to text in a hint window, and the
your applications. indication of a URL in a cell.
Using COM DbAltGrid allows free-form
interfacing, layout (i.e. multiple lines of cells
NNWFax connects within a row), hierarchical col-
directly to WinFax umns structure, alternate row
Pro. Drop an coloring, and comfortable edit-
NNWFax compo- ing of numeric, Boolean, date,
nent on your form, time, memo, rich-formatted
ISBN: 1-55622-773-6 set the properties, memo, and graphic fields. It
Cover Price: US$39.95 and call the SendFax method. It’s NNWFax compiles directly into also provides the possibility of
(314 pages, CD-ROM)
that simple to send faxes. No your application without DLLs or displaying record summaries as
need to bother about com ports extra printer drivers; it includes RTF text.
or ISDN settings. It is guaranteed full source code, an extensive DbAltGrid is a DBGrid
to work on any system that runs help file, and an instructive dem- descendant, and is therefore
WinFax Pro. onstration application; can fax compatible. Converts to DbAlt-
This is an advantage over in silent mode without showing Grid will find no code conflict or
other communication toolboxes WinFax Pro; and more. loss in grid functionality.
which still need hardware pro-
gramming and debugging. Using Fractal Software Quasidata
WinFax Pro’s client/host func- Price: US$65 Price: US$145
tionality faxing over the network E-Mail: info@fractal.nl E-Mail: info@dbaltgrid.com
is just as easy. Web Site: http://www.fractal.nl Web Site: http://www.dbaltgrid.com

4 January 2001 Delphi Informant Magazine


Inprise/Borland Expands Application Server ADUG Provides Job
News Management Offering with AppCenter 4 Advertising Service
L I N E San Mateo, CA — Inprise/ cation performance; integration Collingwood, Australia —
Borland announced the availabil- with other major management The Australian Delphi User
ity of Inprise AppCenter 4, a platforms through SNMP MIB Group (ADUG) has recently
January 2001 new management platform that compatibility; wizards and auto- created an e-mail job advertis-
aims to maximize the uptime of discovery to simplify modeling ing service designed especially
Web-based applications, includ- CORBA infrastructures based on for programmers and employ-
ing those based on CORBA VisiBroker; and the ability to ers in Australia.
and Enterprise JavaBeans (EJB). import and export XML for For those who would like
Through continuous application easy sharing of Inprise AppCenter to keep an eye on the Delphi
component monitoring, load configurations. job market in Australia, visit
balancing, and fault recovery, More information is available at http://www.adug.org.au/jobs/
AppCenter 4 manages, maintains, http://www.borland.com. programmers.htm.
and corrects problems when no
technician is available. Inprise/Borland, Computer Sciences, and CiTR Offer
Inprise AppCenter 4 allows cus- Rapid Delivery of Customer Service Portals
tomers to define a logical map
of the application, along with Sydney and Canberra, Australia government organizations because
the relationships and dependen- — Inprise/Borland, Computer it ensures easy location of the
cies between each element. This Sciences Corp. (CSC), and CiTR various services and the vast
enables the distributed applica- announced an Australian alliance amounts of information govern-
tion components to be main- designed to speed the adoption ments manage, connecting them
tained across the network on any and deployment of Internet-based seamlessly across all layers and
type of machine. Once defined, federated portals. levels of government.
Inprise AppCenter 4 monitors Focusing primarily on the needs At the heart of DECADE
each component to ensure that of Australian federal, regional, and AccessPoint is Borland Appli-
the overall application remains local governments, the alliance cation Server technology from
available. If any one object fails, aims to develop, implement, host, Inprise/Borland, providing the
Inprise AppCenter will automati- and maintain a minimum of 10 platform on which a scalable,
cally start up a new object on a government portals by mid-2001 highly-reliable Web-based archi-
separate hardware platform if nec- to become the major supplier tecture is built.
essary. of federated portals to Australian
The main new features of government. Cast Your Vote for
Inprise AppCenter 4 include the CSC will provide the alliance’s the Delphi Informant
ability to model and manage all customers with a complete Web Magazine Readers
aspects of EJB-based applications portal hosting and services deliv-
deployed using Inprise Applica- ery capability.
Choice Awards
tion Server 4, including EJB con- CiTR’s DECADE AccessPoint Elk Grove, CA — Delphi
tainers and Web servers; dynamic speeds up the process of portal Informant Magazine announced
load balancing to ensure appli- development and is suited for the official ballot for the 2001
Delphi Readers Choice Awards
Delphi Informant Magazine Readers Offered Special is now available online at http://
Extension on Discounts from Monarch Bay Software www.DelphiZine.com.
Houston, TX — Monarch 7.0 or HelpTrac LITE through Each year, Delphi Informant
Bay Software announced special January 31, 2001, with a 20 per- Magazine recognizes outstand-
discounts for HelpTrac 7.0 and cent discount on all licenses. In ing products and vendors in
HelpTrac LITE, help desk soft- addition, the Monarch Bay Soft- the Delphi add-on market.
ware programs that enable busi- ware support and enhancement Please take a moment to select
nesses to manage customer agreement will be free from the your favorite Delphi tools.
service desks. HelpTrac 7.0 and purchase date through March 31, Simply complete the online
HelpTrac LITE are full-featured 2001. This special offer includes ballot on the Delphi Informant
programs for help desk and all HelpTrac 7.0 and HelpTrac Magazine Web site at http://
customer support management, LITE updates and new releases. www.DelphiZine.com.
allowing for management of help Pricing for HelpTrac 7.0 for This is your chance to voice
desk problems and solutions, one license is US$960. HelpTrac your opinions regarding the tools
tracking of calls, producing man- LITE for one license is US$320. and products you use in your
agement information, and build- There is a price reduction for everyday development efforts.
ing problem/solution histories. multiple licenses. For more infor- Voting ends January 31, 2001.
Readers of Delphi Informant mation, visit the Monarch Bay The results of this survey will be
Magazine will receive a special dis- Software Web site at http:// published in the April 2001 issue
count on purchases of HelpTrac www.helptrac.com/index.htm. of Delphi Informant Magazine.

5 January 2001 Delphi Informant Magazine


On the ’Net
Internet Explorer / Delphi

By Bill Todd

Surfing from Delphi


Part I: Using Internet Explorer as an Automation Server

I 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. You’ll use a custom component to wrap the
IWebBrowser2 interface. In part two, you’ll look at embedding a browser in your applica-
tion using the TWebBrowser control.

Using ShellExecute Controlling Internet Explorer


Before diving into Internet Explorer, let’s look at Microsoft provides the IWebBrowser2 interface
a simple solution. If you only need to display a to control Internet Explorer or the Microsoft
Web page in a browser, the Windows ShellExecute Web Browser ActiveX control. Borland makes
API function will do the job. Figure 1 shows a IWebBrowser2 a snap to use because the interface
simple example of using ShellExecute to display a and all of its methods are declared in the ShDocVw
Web page using the user’s default browser. To use unit. For documentation on IWebBrowser2 go
ShellExecute you have to add the ShellAPI unit to http://msdn.microsoft.com/workshop/browser/
to your uses clause. The straightforward result is webbrowser/reference/IFaces/IWebBrowser2/
shown in Figure 2. IWebBrowser2.asp. However, note that the meth-
ods whose names begin with put_ in in the docu-
procedure TForm1.Button1Click(Sender: TObject);
mentation are named set_ in in the ShDocVw unit.
var
FileName: string;
begin To make working with Internet Explorer easier,
FileName := 'www.borland.com'; I wrote a TIEController component that serves
ShellExecute(Application.MainForm.Handle, nil,
PChar(FileName), nil, nil, SW_SHOW);
as a wrapper for many of the methods in the
end; IWebBrowser2 interface (all of the code is shown
in Listing One beginning on page 9). Figure 3
Figure 1: Showing a Web page using ShellExecute. 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;

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 inter-
Figure 2: The result of the code in Figure 1. Looks familiar. face. FIE is a private member variable of the

6 January 2001 Delphi Informant Magazine


On the ’Net
Method/Property Description procedure TIEController.IERefresh;
begin
GetIEWindowHandle Returns the window handle of the Internet
if not IsIEOpen then IEShow;
Explorer window. FIE.Refresh;
IEClose Closes Internet Explorer. end;
IEGoBack Goes to the previous page.
IEGoForward Goes to the next page.
IEGoHome Goes to the home page. The first line of code requires some explanation. One of the problems
IEGoSearch Goes to the search page. with writing a component that provides an interface to an Automa-
IEOpen Opens Internet Explorer. tion server is deciding what to do if a programmer calls one of
IEPrint Prints the current page. the methods or accesses one of the properties without first opening
IEPrintPreview Previews the current page. the server. There are two choices: The component can either create
IERefresh Refreshes the current page. an instance of the server if one does not exist, or it can raise an
IESave Saves the current page. exception. In this case, I decided to open the server and make it
IESaveAs Saves the current page under a new name visible by calling the IEShow method.
or type.
IEShow Opens Internet Explorer and sets its visible There is a second problem with Internet Explorer: how to determine
property to True. if an instance of Internet Explorer has already been created. Setting
IEStop Stops the current operation. the interface reference variable, FIE, to nil when the IEClose method
IsIEBusy Returns True if Internet Explorer is busy is called will not work because the user can close Internet Explorer.
downloading a page. This means the TIEController component must check if Internet
IsIEOpen Returns True if Internet Explorer is open. Explorer is still open before calling its methods or accessing its
properties. The IsIEOpen and GetIEWindowHandle methods shown
Figure 3: TIEController’s public methods. in Figure 5 meet this need.
Property Description
The IsIEOpen method simply calls GetIEWindowHandle and
IEAddressBarVisible Whether the address bar is visible. returns True if the window handle is not zero. GetIEWindowHandle
IEFullScreen Whether the Internet Explorer window first checks to see if the interface reference, FIE, is nil. If it is
occupies the full screen. nil, the method returns zero. If not, the IWebBrowser2
IEHeight The height of the Internet Explorer Get_HWND method is called to get the window handle. If
window. Internet Explorer is not open, calling Get_HWND will cause an
IELeft The position of the left edge of the Internet EOleException, which is handled by the try..except block by set-
Explorer window. ting the result to zero.
IEMenuBarVisible Whether the menu bar is visible.
IEOffline Whether Internet Explorer is offline. There are some other surprising cases where error handling is required.
IEResizable Whether the Internet Explorer window is One would assume that the IWebBrowser2 GoForward and GoBack meth-
resizable. ods would simply do nothing if there was no page in the history list to
IESilent If True, no dialog boxes will be shown. which to move. Instead, they raise an exception. Therefore, the IEGo...
IEStatusBarVisible Whether the status bar is visible. methods of TIEController use a try..except block, as shown in Figure 6, to
IEStatusText The text shown in the Internet Explorer swallow the exception if you try to go to a page that doesn’t exist.
status bar.
IETheaterMode If True, Internet Explorer is in Theater One more method deserves special mention: IsIEBusy. IsIEBusy returns
mode. In Theater mode, no menu, toolbar, True if Internet Explorer is busy downloading a page, or engaged in
address bar, or status bar are shown. some other activity. You must check if Internet Explorer is busy before
IEToolbarVisible Whether the toolbar is visible. you try to do anything to the current page, such as print it.
IETop The position of the top edge of the Internet
Explorer window. Many of the methods of the IWebBrowser2 interface are “getter” and
IEVisible Whether the Internet Explorer window is “setter” methods for properties. TIEController implements these as
visible. Delphi properties whose getter and setter methods call the correspond-
IEWidth The width of the Internet Explorer window. ing IWebBrowser2 methods. A typical example is the IEAddressBarVisible
IEURL The URL of the current page. property with its getter and setter methods, as shown in Figure 7.
Figure 4: TIEController‘s public properties.
Understanding ExecWB
If you look at the methods of the IWebBrowser2 interface, you will
TIEController component whose type is IWebBrowser2. If you want notice that a lot of methods you would expect to find are missing.
to open an instance of Internet Explorer on another machine using For example, there is no Print or SaveAs method. The reason is
DCOM, call CoInternetExplorer.CreateRemote and pass the machine that there are no corresponding functions in Internet Explorer
name as the parameter. itself. Internet Explorer is just a simple application that hosts
the Microsoft Web Browser ActiveX control. It is the browser
Once Internet Explorer is open, you can use the FIE interface refer- control inside Internet Explorer that actually provides many of the
ence variable to call any of the methods of the IWebBrowser2 inter- functions you find on Internet Explorer menus and toolbars. To
face. A simple example is the IERefresh method. This method tells allow Internet Explorer’s Automation clients to access the methods
Internet Explorer to refresh the current page just as if the user had of the embedded Web browser control, the IWebBrowser2 interface
clicked the Refresh button: includes a method called ExecWB.

7 January 2001 Delphi Informant Magazine


On the ’Net
function TIEController.IsIEOpen: Boolean; property IEAddressBarVisible: Boolean
begin read GetIEAddressBarVisible
if GetIEWindowHandle = 0 then write SetIEAddressBarVisible;
Result := False
else function TIEController.GetIEAddressBarVisible: Boolean;
Result := True; begin
end;
if not IsIEOpen then IEShow;
Result := FIE.Get_AddressBar;
function TIEController.GetIEWindowHandle: HWND;
end;
begin
try
procedure TIEController.SetIEAddressBarVisible(
if Assigned(FIE) then
const Value: Boolean);
Result := FIE.Get_HWND
begin
else
if not IsIEOpen then IEShow;
Result := 0;
except FIE.Set_AddressBar(Value);
Result := 0; end;
end;
Figure 7: The IEAddressBarVisible property and its methods.
end;

Figure 5: Determining if Internet Explorer is open. function TIEController.IEExecWB(CmdId, CmdOption: Integer;


VarIn, VarOut: OleVariant): Boolean;
procedure TIEController.IEGoBack; begin
begin if FIE.QueryStatusWB(CmdId) =
if not IsIEOpen then IEShow; OLECMDF_ENABLED + OLECMDF_SUPPORTED then
try try
FIE.GoBack; FIE.ExecWB(CmdId, CmdOption, VarIn, VarOut)
except except
end; raise EIEControlExecError(
end; 'Command execution failed. (Id: ' +
IntToStr(CmdId) + ')');
Figure 6: Trapping the exception in the IEGo... methods. end
else
raise EIEControlExecError.Create(
ExecWB takes four parameters. The first is an integer that identifies 'The function you requested is not ' +
'available. (Id: ' + IntToStr(CmdId) + ')');
the command you want the browser to execute. The constants for
end;
these commands are declared in the ShDocVw unit, and all begin
with OLECMDID_. The second parameter, which is also an integer, Figure 8: The IEExecWB method.
specifies a command option. Again, the constants for these options
are declared in ShDocVw, but the command option constants begin procedure TIEController.IEPrint(ShowDlg: Boolean);
var
with OLECMDEXECOPT_. The third and fourth parameters con-
V: OleVariant;
tain any input or output parameters. begin
if ShowDlg then
The problem with using ExecWB is that the version of the browser you IEExecWB(OLECMDID_PRINT,
are controlling may not implement all of the OLECMDID constants OLECMDEXECOPT_DODEFAULT, V, V)
else
defined in ShDocVw. To solve this problem Microsoft provides the
IEExecWB(OLECMDID_PRINT,
QueryStatusWB method. QueryStatusWB takes an OLECMDID as a OLECMDEXECOPT_DONTPROMPTUSER, V, V);
parameter and returns a value that indicates the status of the com- end;
mand. The possible return values are also defined as constants in
Figure 9: The IEPrint method.
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 defined at the begin-
ning of the TIEController type declaration section. If the command
Figure 10: This test application demonstrates how to control IE
status is correct, ExecWB is called. If ExecWB raises an EOleException, as an Automation server.
an EIEControlExecError exception is raised.

Figure 9 shows the IEPrint method which calls IEExecWB with the calls IEExecWB with a command ID of OLECMDID_PRINT,
appropriate command ID. In this example, the method takes a and an option of OLECMDEXECOPT_DODEFAULT. If ShowDlg
Boolean parameter that controls whether the printer selection dialog is False, the command option constant is changed to
box is displayed. If the ShowDlg parameter is True, the method OLECMDEXECOPT_DONTPROMPTUSER. The methods of

8 January 2001 Delphi Informant Magazine


On the ’Net
TIEController do not implement all of the available command IDs, but procedure SetIELeft(const Value: Integer);
do implement the ones you’re most likely to need. You can easily add procedure SetIEMenuBarVisible(const Value: Boolean);
any others you want to use. procedure SetIEOffline(const Value: Boolean);
procedure SetIEResizable(const Value: Boolean);
procedure SetIESilent(const Value: Boolean);
Conclusion procedure SetIEStatusBarVisible(const Value: Boolean);
If you need to give users the ability to view information on the Web, procedure SetIEStatusText(const Value: string);
controlling Internet Explorer through Automation provides an easy procedure SetIETheaterMode(const Value: Boolean);
solution. It lets you open the browser and take the user anywhere, procedure SetIEToolbarVisible(const Value: Boolean);
procedure SetIETop(const Value: Integer);
and then turn control over to the user to work as they wish. You can
procedure SetIEVisible(const Value: Boolean);
also go to a page on the Web and save or print it without the user procedure SetIEURL(const Value: string);
ever seeing Internet Explorer. The sample software that accompanies procedure SetIEWidth(const Value: Integer);
this article includes both the TIEController component, and a test protected
application that shows how to use it (see Figure 10). ∆ { Protected declarations }
function IEExecWB(CmdId, CmdOption: Integer;
VarIn, VarOut: OleVariant): Boolean;
The files referenced in this article are available on the Delphi public
Informant Magazine Complete Works CD located in INFORM\01\ { Public declarations }
JAN\DI200101BT. procedure IEClose;
function GetIEWindowHandle: HWND;
procedure IEGoBack;
procedure IEGoForward;
procedure IEGoHome;
Bill Todd is president of The Database Group, Inc., a database consulting and procedure IEGoSearch;
function IsIEBusy: Boolean;
development firm based near Phoenix. He is co-author of four database program- function IsIEOpen: Boolean;
ming books, author of more than 60 articles, a contributing editor to Delphi procedure IEOpen;
Informant Magazine, and a member of Team Borland, providing technical support procedure IEPrint(ShowDlg: Boolean);
on Borland Internet newsgroups. He is a frequent speaker at Borland Developer procedure IEPrintPreview;
procedure IERefresh;
Conferences in the US and Europe. Bill is also a nationally-known trainer and
procedure IESave;
has taught Delphi programming classes across the country and overseas. He is procedure IESaveAs(FileName: string);
currently a speaker on the Delphi Development Seminars Kylix World Tour. Bill can procedure IEShow;
be reached at bill@dbginc.com. For more information on the Kylix World Tour, procedure IEStop;
visit http://www.DelphiDevelopmentSeminars.com. property IEAddressBarVisible: Boolean
read GetIEAddressBarVisible
write SetIEAddressBarVisible;
property IEFullScreen: Boolean
read GetIEFullScreen write SetIEFullScreen;
property IEHeight: Integer
Begin Listing One — The TIEController component read GetIEHeight write SetIEHeight;
unit IEController; property IELeft: Integer
read GetIELeft write SetIELeft;
interface property IEMenuBarVisible: Boolean
read GetIEMenuBarVisible write SetIEMenuBarVisible;
uses property IEOffline: Boolean
Windows, Messages, SysUtils, Classes, Graphics, Controls, read GetIEOffline write SetIEOffline;
Forms, Dialogs, shdocvw; property IEResizable: Boolean
read GetIEResizable write SetIEResizable;
type property IESilent: Boolean
EIEControlError = class(Exception); read GetIESilent write SetIESilent;
EIEControlExecError = class(EIEControlError); property IEStatusBarVisible: Boolean
TIEController = class(TComponent) read GetIEStatusBarVisible
private write SetIEStatusBarVisible;
{ Private declarations } property IEStatusText: string
FIE: IWebBrowser2; read GetIEStatusText write SetIEStatusText;
function GetIEAddressBarVisible: Boolean; property IETheaterMode: Boolean
function GetIEFullScreen: Boolean; read GetIETheaterMode write SetIETheaterMode;
function GetIEHeight: Integer; property IEToolbarVisible: Boolean
function GetIELeft: Integer; read GetIEToolbarVisible write SetIEToolbarVisible;
function GetIEMenuBarVisible: Boolean; property IETop: Integer read GetIETop write SetIETop;
function GetIEOffLine: Boolean; property IEVisible: Boolean
function GetIEResizable: Boolean; read GetIEVisible write SetIEVisible;
function GetIESilent: Boolean; property IEWidth: Integer
function GetIEStatusBarVisible: Boolean; read GetIEWidth write SetIEWidth;
function GetIEStatusText: string; property IEURL: string read GetIEURL write SetIEURL;
function GetIETheaterMode: Boolean; published
function GetIEToolbarVisible: Boolean; { Published declarations }
function GetIETop: Integer; end;
function GetIEVisible: Boolean;
function GetIEWidth: Integer; procedure Register;
function GetIEURL: string;
procedure SetIEAddressBarVisible(const Value: Boolean); implementation
procedure SetIEFullScreen(const Value: Boolean);
procedure SetIEHeight(const Value: Integer); uses ComObj;

9 January 2001 Delphi Informant Magazine


On the ’Net

procedure Register; function TIEController.GetIEMenuBarVisible: Boolean;


begin begin
RegisterComponents('DGI', [TIEController]); if not IsIEOpen then IEShow;
end; Result := FIE.Get_MenuBar;
end;
{ TIEController }
procedure TIEController.IEOpen;
function TIEController.IEExecWB(CmdId, CmdOption: Integer; begin
VarIn, VarOut: OleVariant): Boolean; FIE := CoInternetExplorer.Create;
var end;
q: Integer;
begin procedure TIEController.SetIEURL(const Value: string);
if FIE.QueryStatusWB(CmdId) = OLECMDF_ENABLED + var
OLECMDF_SUPPORTED then Flags: OleVariant;
try TargetFrameName: OleVariant;
FIE.ExecWB(CmdId, CmdOption, VarIn, VarOut) PostData: OleVariant;
except Headers: OleVariant;
raise EIEControlExecError( begin
'Command execution failed. (Id: ' + if not IsIEOpen then IEShow;
IntToStr(CmdId) + ')'); FIE.Navigate(Value, Flags, TargetFrameName,
end PostData, Headers);
else end;
raise EIEControlExecError.Create(
'The function you requested is not ' + procedure TIEController.SetIEAddressBarVisible(
'available. (Id: ' + IntToStr(CmdId) + ')'); const Value: Boolean);
end; begin
if not IsIEOpen then IEShow;
procedure TIEController.IEClose; FIE.Set_AddressBar(Value);
begin end;
if IsIEOpen then begin
FIE.Quit; procedure TIEController.SetIEVisible(const Value: Boolean);
FIE := nil; begin
end; if not IsIEOpen then IEShow;
end; FIE.Visible := Value;
end;
function TIEController.GetIEHeight: Integer;
begin function TIEController.GetIEOffLine: Boolean;
if not IsIEOpen then IEShow; begin
Result := FIE.Get_Height; if not IsIEOpen then IEShow;
end; Result := FIE.Get_Offline;
end;
function TIEController.GetIELeft: Integer;
begin function TIEController.GetIEVisible: Boolean;
if not IsIEOpen then IEShow; begin
Result := FIE.Get_Left; if not IsIEOpen then IEShow;
end; Result := FIE.Visible;
end;
function TIEController.GetIEWindowHandle: HWND;
begin procedure TIEController.SetIEMenuBarVisible(
try const Value: Boolean);
if Assigned(FIE) then begin
Result := FIE.Get_HWND if not IsIEOpen then IEShow;
else FIE.Set_MenuBar(Value);
Result := 0; end;
except
Result := 0; procedure TIEController.SetIEHeight(const Value: Integer);
end; begin
end; if not IsIEOpen then IEShow;
FIE.Set_Height(Value);
function TIEController.GetIEURL: string; end;
begin
if not IsIEOpen then IEShow; procedure TIEController.SetIELeft(const Value: Integer);
Result := FIE.Get_LocationURL; begin
end; if not IsIEOpen then IEShow;
FIE.Set_Left(Value);
function TIEController.GetIEAddressBarVisible: Boolean; end;
begin
if not IsIEOpen then IEShow; procedure TIEController.SetIEOffline(const Value: Boolean);
Result := FIE.Get_AddressBar; begin
end; if not IsIEOpen then IEShow;
FIE.Set_Offline(Value);
function TIEController.IsIEBusy: Boolean; end;
begin
if not IsIEOpen then IEShow; function TIEController.GetIETop: Integer;
Result := FIE.Get_Busy; begin
end;

10 January 2001 Delphi Informant Magazine


On the ’Net

if not IsIEOpen then IEShow; if not IsIEOpen then IEShow;


Result := FIE.Get_Top; FIE.Set_StatusText(Value);
end; end;

procedure TIEController.SetIETop(const Value: Integer); function TIEController.GetIEStatusText: string;


begin begin
if not IsIEOpen then IEShow; if not IsIEOpen then IEShow;
FIE.Set_Top(Value); Result := FIE.Get_StatusText;
end; end;

function TIEController.GetIEWidth: Integer; function TIEController.GetIETheaterMode: Boolean;


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

procedure TIEController.SetIEWidth(const Value: Integer); procedure TIEController.SetIETheaterMode(


begin const Value: Boolean);
if not IsIEOpen then IEShow; begin
FIE.Set_Width(Value); if not IsIEOpen then IEShow;
end; FIE.Set_TheaterMode(Value);
end;
function TIEController.GetIEFullScreen: Boolean;
begin function TIEController.GetIEToolbarVisible: Boolean;
if not IsIEOpen then IEShow; var
Result := FIE.Get_FullScreen; Visible: Integer;
end; begin
if not IsIEOpen then IEShow;
procedure TIEController.SetIEFullScreen( Visible := FIE.Get_Toolbar;
const Value: Boolean); if Visible = 0 then
begin Result := False
if not IsIEOpen then IEShow; else
FIE.Set_FullScreen(Value); Result := True;
end; end;

function TIEController.GetIEResizable: Boolean; procedure TIEController.SetIEToolbarVisible(


begin const Value: Boolean);
if not IsIEOpen then IEShow; begin
Result := FIE.Get_Resizable; if not IsIEOpen then IEShow;
end; if Value then
FIE.Set_ToolBar(1)
procedure TIEController.SetIEResizable( else
const Value: Boolean); FIE.Set_ToolBar(0);
begin end;
if not IsIEOpen then IEShow;
FIE.Set_Resizable(Value); procedure TIEController.IEGoBack;
end; begin
if not IsIEOpen then IEShow;
function TIEController.GetIESilent: Boolean; try
begin FIE.GoBack;
if not IsIEOpen then IEShow; except
Result := FIE.Get_Silent; end;
end; end;

procedure TIEController.SetIESilent(const Value: Boolean); procedure TIEController.IEGoForward;


begin begin
if not IsIEOpen then IEShow; if not IsIEOpen then IEShow;
FIE.Set_Silent(Value); try
end; FIE.GoForward;
except
function TIEController.GetIEStatusBarVisible: Boolean; end;
begin end;
if not IsIEOpen then IEShow;
Result := FIE.Get_StatusBar; procedure TIEController.IEGoHome;
end; begin
if not IsIEOpen then IEShow;
procedure TIEController.SetIEStatusBarVisible( FIE.GoHome;
const Value: Boolean); end;
begin
if not IsIEOpen then IEShow; procedure TIEController.IEGoSearch;
FIE.Set_StatusBar(Value); begin
end; if not IsIEOpen then IEShow;
FIE.GoSearch;
procedure TIEController.SetIEStatusText( end;
const Value: string);
begin procedure TIEController.IERefresh;

11 January 2001 Delphi Informant Magazine


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

W hen asked about privacy in today’s 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 company’s
confidential internal correspondence on public Internet sites. I haven’t seen this
happen yet. And thanks to the hard work of people like Phil Zimmerman, privacy
and data security can still exist in today’s highly digitized world.

Phil Zimmerman to the Rescue the September 2000 expiration of RSA’s “biprime
Pretty Good Privacy (PGP) is a popular, high- asymmetric cryptography” patent, many other
quality cryptographic tool that is freely available freely available public key software technologies are
for personal use from MIT, and commercially sure to circulate on the ’Net. Until a better public
available for company use from Network Associ- key solution comes along, however, PGP will most
ates. The program’s author, Phil Zimmerman, faced likely be the leader in the field of cross-platform
everything from financial ruin, government agency message encryption for some time to come.
intimidation, and patent infringement allegations
in his effort to release a product to protect basic Obtaining and Installing PGP
e-mail communication from prying eyes. Chroni- Depending on PGP’s intended personal or com-
cled in Simson Garfinkel’s book PGP: Pretty Good mercial use, visit the appropriate Web site listed in
Privacy [O’Reilly, 1994], Phil’s story is a triumph the “Resources” section at the end of this article
in the ongoing battle of democratic freedom versus for details on how to obtain a copy of PGP for
technological Big Brother advances. Microsoft Windows 9x/NT/NT2000. Due to a
recently discovered vulnerability, it’s recommended
Most people unfamiliar with the degree their that version 6.5.8 or higher is used.
personal e-mail accounts are exposed are often
shocked to learn how public their “private” e-mails During the installation procedure, the user is
really are. The analogy of standard, unencrypted asked to create a key pair. It is at this time a
e-mail communications flowing across the public public and private key are created. The public
Internet compared to a paper postcard in the US key is a bit of data that may be posted on a
Mail isn’t that far off. Freely available tools exist public certificate server. The most popular of these
on the Internet today that provide packet-sniffing is http://pgpkeys.mit.edu:11371 for personal use,
capabilities intended for just such snooping. and ldap://certserver.pgp.com for commercial use.
Even companies operating sophisticated commer- However, under no circumstances should the pri-
cial e-mail platforms that provide secure internal vate key counterpart ever be shared. If the private
communication may lose all that expensive protec- key should ever be compromised in any way, imme-
tion if a message is sent to the Internet without any diately revoke the key pair and create a new one. In
form of encryption. fact, it’s a good security practice to have key pairs
expire every 90 days to reduce the threat of tamper-
Because PGP has been freely available for personal ing. To learn more about public and private keys,
and educational use since the early 1990s, its including their generation and proper use, read the
popularity — compared to other cryptographic well-written “Introduction to Cryptography PDF”
messaging solutions — has soared. And with that accompanies the program’s installation.

13 January 2001 Delphi Informant Magazine


OP Tech

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


encryption/decryption process. Figure 2: The PGP Demonstration application in action.

Before moving on with the Delphi application requirements, make Next, change the SignKeyPass value in the PGPEncodeGetSignKeyPass
sure you can successfully send and receive a signed, encrypted e-mail event to the passphrase used for the selected key pair. A word of warning:
with your newfound secure messaging tool. Just like OOP, learning As noted in the source code listing, it’s a very bad idea to place passwords
how public key cryptography works takes a bit of getting used to. and passphrases in source code. Thus, if automated message encryption
Follow the PGP Encryption/Decryption flow diagram in Figure 1 to is required, consider removing the signature process or investigate an
review a basic understanding of the process. It’s only after using it for effective way to obfuscate the source to limit passphrase visibility.
a while that its value proposition really sinks in. And like OOP, once
you get this technology’s religion, you’re hooked. Lastly, if you are using the NetMaster SMTP control bundled
with Delphi Pro or Enterprise 4.x or higher, change the SMTP
Delphi PGP Program Requirements Host and UserID values to those appropriate to your environ-
Thanks to the dedicated work of Steve “S.R.” Heller, who ment. Assuming all program dependencies have been met, and all
authored the SPGP.DLL that programmatically extends PGP to parameters have been correctly updated for your configuration,
Windows application development environments like Delphi, and build and run the application.
Michael in der Wiesche, who authored a very slick PGP compo-
nent set, leveraging the power of PGP in Delphi applications is Clicking the globe toolbar icon will fetch the DelphiZine.com
a piece of cake. Check out the “Resources” section of this article home page and read it into the top memo field. This URL could
for the Internet location of these works. Once the SPGP.DLL just as easily be an internal corporate intranet address, an XML
has been placed in the Windows system directory, and the PGP data feed, or a CSV file. If the target URL is unavailable for any
components have been installed via the usual Delphi component reason, you can also simply type a message into the top memo field
installation procedure, the power of commercial-grade crypto- as well. Upon doing so, the key toolbar icon will become enabled,
graphic messaging is available to any Delphi developer. allowing you to sign and encrypt the memo field’s contents. The
PGP component suite supports methods for both the encryption
To show just how simple it is to harness this power, I created an of physical files as well as strings in memory. For demonstration
application that illustrates the potential of this robust combination. purposes, the contents of the top memo field are written out to a
pgpdemo.txt file. This practice is not recommended in a production
The PGP Demonstration Application environment, however, as it adds yet another potential security
The PGP Demonstration application (see Figure 2) was constructed vulnerability to an otherwise secure process.
to show the step-by-step processes of capturing Web-based content,
encrypting and signing that content, and sending it through a public The bottom memo field then retrieves the contents of the pgpdemo.txt
Internet e-mail service. The GUI is straightforward, and — quite file, signs and encrypts the file, displays the results of the process in
honestly — unnecessary for anything except to graphically display the the bottom memo field, and saves the results to another file called
steps and outcomes of each phase of the application being processed. pgpdemo.pgp. The contents of this file can also be opened with a text
editor such as Notepad for review. Additionally, double-clicking any file
To test this application using the key pair you established during with a .asc or .pgp extension from within the Windows shell will invoke
the PGP installation, change the CryptKeyIDs and SignKeyID values the PGPTools application and automatically attempt to decrypt the file.
to your key pair e-mail address. Remember to bracket the e-mail
address, and don’t allow an ill-formed e-mail address with any spaces The final step is transmitting the file via an SMTP gateway. If the
or other illegal characters. contents of the top memo field were successfully encrypted and

14 January 2001 Delphi Informant Magazine


OP Tech
signed, the mail toolbar icon will become active. Clicking this icon
will transmit the read-only contents of the bottom memo field to
the e-mail address you specified 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 demon-
stration 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 finan-
cial or inventory reporting application that can be inexpensively
and securely transmitted from one company’s Intranet to another
in an asynchronous fashion using an Internet gateway. How about
a confidential 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 communica-
tions 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 mes-
sage retrieval process. Visiting a Web page that fires off a Delphi-
authored ISAPI DLL or COM object that fulfills 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.” Heller’s excellent SPGP.DLL v2.5.5 or higher,
available at http://www.oz.net/~srheller/spgp/
 Michael in der Wiesche’s 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 Garfinkel’s remarkable story
of Phil Zimmerman’s trials and travails of bringing PGP to
the public, as well as a layman’s introduction to cryptography.
Also includes detailed (albeit dated) descriptions of PGP com-
mand line options. Published by O’Reilly and available at
http://www.oreilly.com/catalog/pgp/

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

Mike Riley is a Chief Scientist for RR Donnelley & Sons, one of North America’s
largest printers. He actively participates in the company’s 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

L 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 that’s used to pivot, filter, and
summarize information from various data sources and present it in meaningful ways.

In this article, we’ll first briefly discuss the Pivot- In Excel, the pivot table fields can be rotated, and
Table services implemented in Microsoft Excel, so the summarized data itself can be filtered to present
you can get an idea of how and when the Pivot- different summaries or details for areas of interest,
Table component should be used. Then we’ll pro- as shown in Figure 1. This allows us to create
vide you with several examples of how to use the different crosstabs for the same data.
PivotTable component in Delphi applications.
Let’s consider, for instance, that we have a table
Excel PivotTable Services that contains sales data, as shown in Figure 2.
To be able to use the PivotTable component, we
need to understand at least the basics of the Pivot- We can create an Excel pivot table using the Pivot-
Table services implemented in Microsoft Excel. Table Wizard that can be activated with the Data
The Excel pivot table is an interactive table usu- | PivotTable and PivotChart Report menu item. The
ally used to summarize or statistically analyze rows of this pivot table can, for example, contain
data. The data can come from a variety of sources, the Product field of a table as its row field, the
including an Excel range or database query. Cells Salesperson field of the table as its column field,
of the pivot table contain summaries for one of and the City field of the table as its filter field
the numeric columns of the data. These sum- (in Excel, such fields are also called page fields), as
maries are based on table rows in which the row shown in Figure 3.
name and the column name for this particular
pivot table cell are the values of the corresponding To create another pivot table view instead of the
data table fields. Such summary tables are also one shown, a user can drag-and-drop fields to and
called crosstabs. from the PivotTable toolbar that becomes extended
with an appropriate field list where the pivot table
layout is edited. Note that a pivot table can have
Filter Fields
no row fields, or no column fields, as well as no
Column Fields filter fields.

Excel PivotTable services may be used with any


ODBC and OLE DB data sources, including
Row Fields

the multidimensional OLAP cubes accessible


Totals or Details Fields through an appropriate OLE DB provider. Also
note that Excel allows using more than one field
for rows, columns, or filters (for example, we
can use the Country, Region, and City fields
together), and this allows creating crosstabs with
Figure 1: Excel PivotTable layout. hierarchical “metadata.”

16 January 2001 Delphi Informant Magazine


Greater Delphi
It should be noted, however, that the PivotTable Order Date City Product Extended Price Salesperson
component behavior is not the same as the Excel January 5 Lyon Ikura $193.00 Michael Suyama
PivotTable services. While the latter hides all data, January 5 Lyon Ipoh Coffee $782.00 Janet Leverling
the former allows them to be modified interactively January 5 Lyon Chocolade $86.70 Michael Suyama
at run time. January 6 Marseille Chang $1,061.82 Janet Leverling
January 6 Lyon Ipoh Coffee $7,905.00 Nancy Davolio
The TOleContainer Component January 6 Strasbourg Ipoh Coffee $938.40 Janet Leverling
We weren’t able to use the PivotTable component January 6 Marseille Ikura $1,047.62 Janet Leverling
directly with Delphi 5. We haven’t found the January 6 Lyon Chang $190.00 Nancy Davolio
reason for this. Perhaps it expects some extra inter- January 7 Marseille Chang $800.00 Michael Suyama
faces not implemented in the Delphi TForm class. January 7 Lyon Ikura $95.00 Nancy Davolio
Maybe there are some problems in Delphi COM January 7 Marseille Chocolade $714.00 Nancy Davolio
support. Maybe there are some other reasons. January 7 Strasbourg Chocolade $437.50 Michael Suyama
January 7 Strasbourg Chang $292.50 Michael Suyama
However, there’s another way to use the Pivot- January 7 Marseille Chocolade $465.00 Nancy Davolio
Table component in Delphi. We can place it January 7 Marseille Ipoh Coffee $378.00 Nancy Davolio
inside the TOleContainer component from the
Figure 2: Sample sales data.
System page of the Delphi Component palette.
We believe that this component adds some inter- Salesperson Lyon
faces that are necessary for the PivotTable com-
ponent to work properly, but are absent in the Sum of
current implementation of the Delphi TForm Extended Price Salesperson
class. Evidence for this is the fact that placing
Product Janet Michael Nancy Grand Total
the TOleContainer component on a form that Leverling Suyama Davolio
contains a PivotTable ActiveX control also makes Chang 190 190
the latter work properly. Chocolade 86.7 86.7
Ikura 193 95 288
The TOleContainer component is used in Ipoh Coffee 782 7,905 8,687
Delphi applications primarily as a visual con- Grand Total 782 279.7 8190 9,251.7
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 don’t appear on the list of property that gives us access to the PivotView object that cor-
OLE document servers. Instead, we’ll 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 fields used to build row, column, and
var filter axes, as well as each data axis. The PivotTable component
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.
OleContainer1.CreateObject('OWC.PivotTable.9', False); along the axes. A simplified PivotTable component object model
// Access OLE object. is shown in Figure 4.
PT := OleContainer1.OleObject;
In our first example, we’ll use the sample Northwind database
Next we must activate this component inside the OLE container. (Northwind.mdb) that comes with Microsoft Access. We will start
This can be done using the DoVerb method of the OLE container. with defining the ConnectionString property:
Containers for OLE documents usually support several default
actions, such as displaying or activating an OLE object, as well as
custom actions defined in the specific OLE server (a list of commands
PivotTable
can be obtained using the ObjectVerbs property of the TOleContainer
component). Here we use one of these default actions to make the
PivotView PivotData
PivotTable component visible and ready to use:

// Specify that the OLE Object will be PivotDataAxis PivotGroupAxis PivotFilterAxis PivotCell
PivotTotals
// activated manually and activate it. (PivotTotal)
with OleContainer1 do begin PivotMember
PivotFieldSets PivotFieldSets PivotFieldSets PivotFieldSets
AutoActivate := aaManual; (PivotFieldSet) (PivotFieldSet) (PivotFieldSet) (PivotFieldSet)
DoVerb(ovShow); PivotMembers
end; (PivotMember)
PivotFields PivotFields PivotFields
(PivotField) (PivotField) (PivotField)
Objects
Collections
Using the PivotTable Component PivotTotals
(PivotTotal)
After we’ve placed the PivotTable component on our form within
the TOleContainer component, we can use its properties and Figure 4: Simplified object model of the PivotTable component.

17 January 2001 Delphi Informant Magazine


Greater Delphi
const var
DataSourcePath = 'c:\data\northwind.mdb'; View : Variant;
... ...
PT.ConnectionString := View := PT.ActiveView;
'PROVIDER=MICROSOFT.JET.OLEDB.4.0;' + // Move fields to the row, column,
'DATA SOURCE=' + DataSourcePath; // and filter axes for grouping.
View.RowAxis.InsertFieldSet(
View.FieldSets['CategoryName']);
Next, we need to specify what kind of data we will use in our pivot
View.ColumnAxis.InsertFieldSet(
table. This can be done either through the DataMember property, View.FieldSets['ShippedQuarter']);
or through the CommandText property. In our example, we’ll use View.DataAxis.InsertFieldset(
the “Product Sales for 1997” view (this view returns the information View.FieldSets['ProductName']);
View.DataAxis.InsertFieldset(
about the product sales from January 1, 1997 to December 31,
View.FieldSets['ProductSales']);
1997), that can be specified like this:
Figure 6: Specifying data for all parts of a pivot table.
PT.DataMember := '[Product Sales for 1997]';

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 fields into the pivot table manually,
or we can do the same thing programmatically. Let’s see how the
latter can be done.

The fields in
PivotTable Fields PivotTable Collection
the pivot table
Row fields RowAxis collection
discussed ear-
Column fields ColumnAxis collection
lier in this arti-
Filter fields FilterAxis collection
cle have the
Totals or Details fields DataAxis collection
appropriate Figure 7: The PivotTable component with calculated totals.
collections Figure 5: PivotTable fields and their collections.
within the Piv- Specifying Colors
otTable component’s object model. This is shown in the table in Now we can make our pivot table more attractive by applying
Figure 5. different colors to its various sections. To do this, we will use the
TotalBackColor, FieldLabelBackColor, MemberBackColor, FieldLabelFont,
The first three collections support the InsertFieldSet method that’s used and SubTotalBackColor properties of the PivotView object:
to specify the data for this set of fields. To specify the data for totals or
detail fields, we can either use the InsertTotal method, or InsertFieldSet // Apply various color settings to
// different pivot table components.
method. The code snippet shown in Figure 6 specifies data for all parts
View.TotalBackColor := 'CornSilk';
of the pivot table. View.FieldLabelBackColor := 'DarkBlue';
View.FieldLabelFont.Color := 'White';
Here we’ve defined that the CategoryName field is used to create View.ColumnAxis.FieldSets[0].Fields[0].SubTotalBackColor :=
the row axis data, the ShippedQuarter field is used to create the 'LightSteelBlue';
View.RowAxis.FieldSets[0].Fields[0].SubTotalBackColor :=
column axis data, and the ProductName and ProductSales fields 'LightSteelBlue';
are used to create details to summarize them interactively. View.MemberBackColor := 'LightGrey';

Calculating Totals and Using Formulas Next, let’s limit possible user manipulations of the pivot table:
At this step, we have specified the data for Row, Column, and Detail
fields of the pivot table. Now we can add total values to it. Here’s // Remove the title bar.
the code that does the job: View.TitleBar.Visible := False;
// Set various properties of the Pivot Table
// to limit user interaction.
// Add a total.
PT.DisplayToolbar := False;
Total := View.AddTotal('Sales Total',
View.FieldSets['ProductSales'].Fields[0], plFunctionSum); PT.AllowPropertyToolbox := False;
View.DataAxis.InsertTotal(Total);
View.Totals['Sales Total'].NumberFormat :='Currency';
The result of calculating summaries and applying colors is shown in
Figure 7.
We’ve added totals for the ProductSales field for any quarter and any
product category, as well as grand totals for all product categories In our first example, we saw how to set the data source, define
and all quarters. In this case, we’ve calculated a sum of the detail a pivot table layout, calculate totals and use formulas, and apply
values. Instead, we could also calculate the maximal or the minimal various colors to the different parts of a pivot table. Let’s now
values, as well as the count of the detail data. move on to a discussion of filters.

18 January 2001 Delphi Informant Magazine


Greater Delphi
Using Filters var
View : Variant; // PivotTable view
To filter the data that appears in the pivot table, we need to use the
Total : Variant; // Totals
FilterAxis property of the PivotView object, for example: R, C : Integer; // Row and column counters
Cell : Variant; // One cell
View.FilterAxis.InsertFieldSet( Agg : Variant; // Aggregate value
View.FieldSets['CategoryName']); LI : TListItem; // ListView item
CI : TListColumn; // ListView column
...
This allows users to filter the data appearing in the pivot table. // Specify column headers.
In other words, filter columns allow us to bring the “third dimen- CI := ListView1.Columns.Add;
sion” to the flat pivot table. Please note that instead of applying CI.Caption := 'Category';
CI.AutoSize := True;
filters programmatically, we can drag and drop an appropriate
for C := 0 to PT.ActiveData.ColumnMembers.Count-1 do begin
column name to the filter field area of the PivotTable component. CI := ListView1.Columns.Add;
CI.Caption := PT.ActiveData.ColumnMembers[C].Caption;
Using OLAP Cubes CI.AutoSize := True;
Instead of data from a relational database, we can use data from the end;
// For each row...
local OLAP cube that can be created with Excel, Microsoft SQL for R := 0 to PT.ActiveData.RowMembers.Count-1 do begin
Server, or other tools. To use the local cube, we should specify the LI := ListView1.Items.Add;
Microsoft OLAP OLE DB provider and the location of the cube. LI.Caption := PT.ActiveData.RowMembers[R].Caption;
Following is an example of the ConnectionString property for this case: // For each column in a row...
for C := 0 to PT.ActiveData.ColumnMembers.Count-1 do
begin
PT.ConnectionString :=
// Get cell value.
'PROVIDER = MSOLAP; DATA SOURCE = C:\DATA\CUBE.CUB';
Cell := PT.ActiveData.Cells[
PT.ActiveData.RowMembers[R],
In the case of Microsoft SQL Server 7.0 OLAP cubes, the PT.ActiveData.ColumnMembers[C]];
// Get aggregate value.
ConnectionString property looks like the following: Agg := Cell.Aggregates.Item['Sales Total'].Value;
// Show it.
PT.ConnectionString := LI.SubItems.Add(IntToStr(Agg));
'PROVIDER = MSOLAP; DATA SOURCE = maindesk;' + end;
'INITIAL CATALOG=FoodMart'; end;

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


For Microsoft SQL Server 2000 OLAP cubes, the ConnectionString
property looks like the following:

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 isn’t 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; we’ve demonstrated several ways to get data into a PivotTable.


Now let’s 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 Figure 9: The data obtained from the PivotTable component.
PivotColumnMember collections to extract data from the Pivot-
Table component. Let’s extend our example by adding the table The Export method takes two arguments. The first specifies the name
with aggregate values for each product’s sales by quarter. We will of the file; the second specifies the actions to be taken. We can specify
present this data in the ListView component from the Win32 either plExportActionNone (its value is 0, indicating no action; just
page of the Delphi Component palette. The code necessary to save the file), or plExportActionOpenInExcel (its value is 1, save the
implement this is shown in Figure 8. file and open it in Excel). If the file name isn’t specified, a temporary
file named PivotTablexxxx will be created in the \Windows\Temp
The result of extracting the values from the pivot table is shown in directory. The pivot table is saved in XML format.
Figure 9. In this example, we have extracted the values along the row
and column axes, as well as summaries calculated by the PivotTable Another possibility is to save a static picture of a pivot table as a
component, and then put them into the ListView component. GIF file. To do this, we use the ExportPicture method. All four of its
arguments are optional:
Exporting and Saving Pivot Tables  the name of the file to store the image in. If this argument isn’t
Using the Export method of the PivotTable component, we can save specified, the pivot table image will be saved to a file named
our pivot table to a file, and optionally open it in Microsoft Excel. Pivot.GIF.

19 January 2001 Delphi Informant Magazine


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. Figure 12: The data obtained from the PivotTable component,
PageSetup.CenterHeader := PT.ActiveView.TitleBar.Caption;
shown as an Excel print preview.
PageSetup.CenterFooter := 'Page &P of &N';
// We want to print gridlines.
PageSetup.PrintGridlines := True; gridlines. After that, we make the Excel instance visible, and let users
// Show an instance of Excel. interact with it (see Figure 11). The Print Preview window that
Excel.Visible := True;
// Let user interact with Excel.
results from executing this code is shown in Figure 12. To actually
Excel.UserControl := True; print, we can call the PrintOut function, in which case we don’t need
// Call print preview function. to make Excel visible.
WSheet.PrintPreview;

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


Conclusion
As far as licensing is concerned it’s okay to use Microsoft Office Web
Components on computers where any edition of Microsoft Office
 the name of the filter. Currently only GIF is supported. or Microsoft Access 2000 is installed. For more information on the
 the width of the image in pixels (required for server-side charts). deployment issues of such applications, refer to the documents on the
 the height of the image in pixels (required for server-side charts). Microsoft Web site at http://www.microsoft.com/office.

The following code saves the pivot table into the Customers.GIF file We’ve seen how to use the PivotTable component that is part of Micro-
in the c:\Data folder: soft Office Web Components. We know how to define the data source
for this component, how to set the row, column, and filter fields, how to
PT.ExportPicture('c:\Data\Customers.GIF'); define the data fields, how to create summaries, and how to apply colors
to the different parts of the pivot table. We have also studied how to
Implementing Printing Functionality receive values from the pivot table cells and axes, as well as to make this
The PivotTable component has no native printing capability. How- component more or less interactive in our application. Finally, we also
ever, we can easily implement this functionality by moving the pivot saw how to print the data from a PivotTable component. ∆
table data into Excel, and then using Excel’s printing capability.
The three demonstration projects referenced in this article are available
Of course, all of this is done via Automation. First, let’s declare some on the Delphi Informant Magazine Complete Works CD located in
variables: INFORM\2001\JAN\DI200101AF.

var
Excel : Variant; // An instance of Excel.
WBook : Variant; // Workbook.
WSheet : Variant; // Worksheet.
PageSetup : Variant; // PageSetup object.

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


the contents of the PivotTable to the Clipboard. Then, we’ll create an
instance of Excel, add a new workbook, and get the first worksheet
Alex Fedorov is a Chief Technology Officer for Netface SA, based in Lausanne, Switzerland
within it. After that, we can paste the contents of the Clipboard into
(http://www.netface.ch). He’s one of the co-authors of Professional Active Server Pages
this worksheet, and adjust it to make its contents visible. This is
2.0 [Wrox Press, 1998] and ASP 2.0 Programmer’s Reference [Wrox Press, 1999].
implemented by the code shown in Figure 10.
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
Now we have our pivot table data inside the spreadsheet, so we can
Inprise/Borland Conferences. Natalia and Alex are authors of Advanced Delphi Develop-
invoke the printing functions of Excel. First, we need the PageSetup
er’s Guide to ADO [Wordware Publishing, 2000], and several Russian programming
object. In this example, we will specify portrait orientation, but
books. You can visit their Web site at http://d5ado.homepage.com.
this can be changed later. Then, we specify the header, footer, and

20 January 2001 Delphi Informant Magazine


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

W 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. It’s 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 differ from the VCL? To answer this, I’ll take a
it’s possible that it has been released by the time common control, TButton, and show what hap-
you read this. This means that there might be dif- pens to it as it is created, used, and destroyed with
ferences between the specifics I’ll provide here and CLX. This way we can delve into CLX and see
the final product. You take that risk when you write what’s going on under the covers. For comparison,
about the latest and greatest. I will do the same for a VCL TButton, since some
of you may not be that familiar with the internals
One of the questions I bet you have is: What is of the VCL.
different about CLX? Specifically, how does CLX
This is going to be a high-level overview. Although
I will get into some of the lower-level code, I’m
procedure TWinControl.UpdateShowing;
not going to mention every property that gets set
var
ShowControl: Boolean; or every method that gets called during the life of a
I: Integer; button. This would just bog us down with details,
begin when what I really want to show are the highlights.
ShowControl := (FVisible or
(csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
Take a look at Listing One (beginning on page
not (csReadingState in ControlState); 25). Here’s a project that introduces the main star
if ShowControl then begin — the Button component. This code will compile
if FHandle = 0 then CreateHandle; { <------- } in Kylix and in Delphi. The uses clause has an
if FWinControls <> nil then
{ $IFDEF } block around it, so it will compile
for I := 0 to FWinControls.Count - 1 do
TWinControl(FWinControls[I]).UpdateShowing; using either CLX or the VCL. The code that cre-
end; ates a Button, uses it, and then destroys it, is the
if FHandle <> 0 then same for a CLX project or a VCL project.
if FShowing <> ShowControl then begin
FShowing := ShowControl;
try
Creating the Button
Perform(CM_SHOWINGCHANGED, 0, 0); Not much here to talk about. In CLX, and in the
except VCL, a button is created the same way:
FShowing := not ShowControl;
raise;
Button := TButton.Create(nil);
end;
end;
end; The constructor for both sets default property values
of the button. In CLX, the constructor also sets a
Figure 1: The VCL TWinControl.UpdateShowing method. property named InputKeys. InputKeys is a mechanism

21 January 2001 Delphi Informant Magazine


Quick CLX

procedure TWinControl.CreateHandle; procedure TWinControl.CreateWnd;


var var
I: Integer; Params: TCreateParams;
begin TempClass: TWndClass;
if FHandle = 0 then begin ClassRegistered: Boolean;
CreateWnd; { <------- } begin
SetProp(FHandle, MakeIntAtom(ControlAtom), CreateParams(Params); { <------- }
THandle(Self)); with Params do begin
SetProp(FHandle, MakeIntAtom(WindowAtom), if (WndParent = 0) and (Style and WS_CHILD <> 0) then
THandle(Self)); if (Owner <> nil) and
if Parent <> nil then (csReading in Owner.ComponentState) and
SetWindowPos(FHandle, Parent.PrecedingWindow(Self), (Owner is TWinControl) then
0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE + WndParent := TWinControl(Owner).Handle
SWP_NOACTIVATE); else
for I := 0 to ControlCount - 1 do raise EInvalidOperation.CreateFmt(
Controls[I].UpdateAnchorRules; SParentRequired, [Name]);
end; FDefWndProc := WindowClass.lpfnWndProc;
end; ClassRegistered := GetClassInfo(WindowClass.hInstance,
WinClassName, TempClass);
if not ClassRegistered or
Figure 2: The VCL TWinControl.CreateHandle method.
(TempClass.lpfnWndProc <> @InitWndProc) then begin
if ClassRegistered then
we introduced that groups keys together so your component can get a Windows.UnregisterClass(WinClassName,
chance to handle special keys before the underlying widget is set. I’ll go WindowClass.hInstance);
into more depth on this topic in a future article. WindowClass.lpfnWndProc := @InitWndProc;
WindowClass.lpszClassName := WinClassName;
if Windows.RegisterClass(WindowClass) = 0 then
Note that I set the Button’s owner to nil. I did this because I want RaiseLastWin32Error;
to explicitly destroy the button myself, and not have the owner do it end;
when it’s destroyed. If you’re very observant, or well versed in Delphi, CreationControl := Self;
CreateWindowHandle(Params); { <------- }
you’ll notice that it doesn’t matter, because I am freeing the Button in if FHandle = 0 then
the OnDestroy event on the form. I did it this way so you could set a RaiseLastWin32Error;
breakpoint in the OnDestroy event and follow along. end;
StrDispose(FText);
FText := nil;
Setting the Parent UpdateBounds;
This innocent assignment sets off a whole chain of events: Perform(WM_SETFONT, FFont.Handle, 1);
if AutoSize then
AdjustSize;
Button.Parent := Self; end;

Into the VCL Figure 3: The VCL TWinControl.CreateWnd method.


First, let’s look at what happens with a VCL Button. Setting the Parent
will call the UpdateShowing method (shown in Figure 1). This method
procedure TButton.CreateParams(var Params: TCreateParams);
determines whether the button should be shown based on the Visible
const
property and its state, i.e. it’s not being read from the stream. For the ButtonStyles: array[Boolean] of
button to be visible, it needs to have a window handle, so CreateHandle DWORD = (BS_PUSHBUTTON, BS_DEFPUSHBUTTON);
(shown in Figure 2) calls CreateWnd and sets the z-order of the Button. begin
inherited CreateParams(Params);
CreateSubClass(Params, 'BUTTON');
The CreateWnd method (shown in Figure 3) exists to simplify the Params.Style := Params.Style or ButtonStyles[FDefault];
creation of a window. It sets up the necessary bookkeeping needed end;
by CreateWindowEx in the Windows API. To do this, it calls another
helper method named CreateParams. Figure 4: The VCL TButton.CreateParams method.

As its name suggests, the CreateParams procedure sets up all the


procedure TWinControl.CreateWindowHandle(
parameters that describe the control (see Figure 4). CreateParams of
const Params: TCreateParams);
TButton also calls CreateSubClass, passing it the ControlClassName of begin
BUTTON. After all of this, CreateWnd calls the CreateWindowHandle with Params do
method (see Figure 5) which creates the window handle of the Button FHandle := CreateWindowEx(ExStyle, WinClassName,
by calling CreateWindowEx. Caption, Style, X, Y, Width, Height, WndParent, 0,
WindowClass.hInstance, Param);
end;
There’s a lot going on in the previous three paragraphs, so here’s
a quick recap of the methods called in order to create a Button
component in the VCL: Figure 5: The VCL TWinControl.CreateWindowHandle method.
 UpdateShowing
 CreateHandle I’m not going into further detail, because I want to concentrate on
 CreateWnd CLX. I just wanted to give you an overview of how it works in the
 CreateParams VCL so you have some basis for comparison.
 CreateSubClass
 CreateWindowHandle Into CLX
 CreateWindowEx The process is much simpler in CLX, and easier to follow. It starts the

22 January 2001 Delphi Informant Magazine


Quick CLX
procedure TWidgetControl.HandleNeeded;
procedure TWidgetControl.UpdateShowing;
begin
var
if FHandle = nil then begin
ShowControl: Boolean;
if Parent <> nil then
I: Integer;
Parent.HandleNeeded;
begin
ShowControl := (FVisible or CreateHandle; { <------- }
(csDesigning in ComponentState) and end;
not (csNoDesignVisible in ControlStyle)) and end;
not (csReadingState in ControlState); Figure 7: The CLX TWidgetControl.HandleNeeded method.
if ShowControl then begin
HandleNeeded; { <------- }
if FWidgets <> nil then procedure TWidgetControl.CreateHandle;
for I := 0 to FWidgets.Count - 1 do begin
TWidgetControl(FWidgets[I]).UpdateShowing; if FHandle = nil then begin
end;
CreateWidget; { <------- }
if HandleAllocated then
QClxObjectMap_add(FHandle, Integer(Self));
if FShowing <> ShowControl then begin
SetInitialSize;
FShowing := ShowControl;
InitWidget; { <------- }
try
ShowingChanged; HookEvents; { <------- }
except if (FHandle = nil) or (FHooks = nil) then
FShowing := not ShowControl; raise Exception.CreateResFmt(
raise; @SInvalidCreateWidget, [ClassName]);
end; SetInitialBounds;
end; UpdateWidgetShowing; { Force the visible state. }
end; EnabledChanged; { Force enabled state. }
QWidget_setCursor(FHandle, Screen.Cursors[FCursor]);
if csRecreating in ControlState then
Figure 6: The CLX TWidgetControl.UpdateShowing method. RestoreWidgetState;
end;
same way; setting the parent causes the UpdateShowing procedure (see end;

Figure 6) to be called. In CLX (just as in the VCL) you need a handle Figure 8: CLX TWidgetControl.CreateHandle method.
to display a control, but it’s not the same as a Windows handle.
procedure TButtonControl.SetText(const Value: TCaption);
In CLX, a handle is an opaque reference to the underlying widget begin
control. To get this handle, UpdateShowing calls HandleNeeded (see if Caption <> Value then begin
Figure 7), and then calls CreateHandle. As you can see from Figure 8, QButton_setText(Handle, @Value);
FAccelChar := Char(QButton_accel(Handle));
CreateHandle first calls CreateWidget, which will create the widget.
QButton_setAccel(Handle, 0);
TextChanged;
The next method I want you to notice is InitWidget. Because end;
InitWidget gets called after the widget is created, you can be sure end;
the handle is valid. It’s at this point that you can set properties on Figure 9: The CLX TButtonControl.SetText method.
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 that we call the setText method of the widget control. Notice that
handle. If the handle is allocated, you can make the proper call to set the handle is the first parameter. Next, we store the accelerator, then
the color. If not, you can store the value into a private field variable, clear it from the QButton control. We do this because we’re going to
and in InitWidget you make the call to set the color. handle all of the accelerators in the CLX framework.

Now our Button is created, parented, and ready to rock-and-roll. Next we call a dynamic method named TextChanged as a notification.
Here’s the list of methods called in order to create a Button This equates to the CM_TEXTCHANGED notification message in
component in CLX: the VCL. In fact, most of the component messages from the VCL
 UpdateShowing aren’t a part of CLX, and are replaced with dynamic methods. The only
 HandleNeeded time we kept a CM_ type message was when it had to be broadcast to
 CreateHandle multiple controls. Most of the time, however, the CM_ messages from
 CreateWidget the VCL only need to notify a descendant that something happened.
 InitWidget
Handling the Event
Setting the Caption The user event handler assignment is the same in the VCL and CLX.
This statement looks straightforward, but its implementation in the It’s just a simple property assignment:
VCL and CLX are very different:
Button.OnClick := ButtonClick;
Button.Caption := 'Hello, World!';

The real fun begins when you press the button and the event
In the VCL, setting the Caption is handled by a method named fires. If you put a breakpoint on the ShowMessage call, and press
SetText in the TControl class. SetText calls SetTextBuf, which in turn the button, you will stop in the middle of the user event handler
issues WM_SETTEXT and CM_TEXTCHANGED messages. named ButtonClick.

In CLX, SetText also handles setting the Caption, but in this case it’s Bring up the Call Stack debug window, and let’s follow this back about
overridden in TButtonControl. Take a look at Figure 9; you can see seven calls. At the top you’ll see you are in ButtonClick. Going backward

23 January 2001 Delphi Informant Magazine


Quick CLX
TForm1.ButtonClick($BF30AC) program Figures;
TControl.Click
TButton.Click destructor TWidgetControl.Destroy;
TButton.CNCommand((48401, 796, 0, 6226716, 0)) var
TControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0)) I: Integer;
TWinControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0)) Instance: TControl;
TButtonControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0)) begin
TControl.Perform(48401,796,6226716) Destroying;
DoControlMsg(6226716,(no value)) if Parent <> nil then begin
TWinControl.WMCommand((273, 796, 0, 6226716, 0)) RemoveFocus(True);
TCustomForm.WMCommand((273, 796, 0, 6226716, 0)) SetParent(nil);
TControl.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0)) end;
TWinControl.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0)) if FHandle <> nil then begin
TCustomForm.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0)) DestroyWidget;
TWinControl.MainWndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0)) FHandle := nil;
StdWndProc(10093556,273,796,6226716) end;
TWinControl.DefaultHandler((no value)) I := ControlCount;
TControl.WMLButtonUp((514, 0, 45, 6, (45, 6), 0)) while I <> 0 do begin
TControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0)) Instance := Controls[I - 1];
TWinControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0)) Remove(Instance);
TButtonControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0)) Instance.Destroy;
TWinControl.MainWndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0)) I := ControlCount;
StdWndProc(6226716,514,0,393261) end;
TApplication.HandleMessage FBrush.Free;
TApplication.Run FPalette.Free;
Project1 inherited Destroy;
end;

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


Figure 12: The CLX TWidgetControl.Destroy destructor.
TForm1.ButtonClick($15E2994)
TControl.Click Button.Free;
TButton.Click
MouseEvent(???)
As in the VCL, the Free method checks to see if the object is not
TWidgetControl.EventFilter($11EFB74,$12FC5C)
TButton.EventFilter(???,???) nil, and then calls the Destroy method. The TWidgetControl.Destroy
TWidgetControl.MainEventHandler($11EFB74,$12FC5C) method is shown in Figure 12. You’ll notice that it calls DestroyWidget,
Qt_hook::eventFilter(this=:011F072C, sender=:011EFB74, event=:0012FC5C) which — as I bet you’ve guessed — destroys the underlying widget.
QObject::activate_filters
QWidget::event
QApplication::notify Conclusion
QETWidget::translateMouseEvent There you have it: the life and death of TButton. By following the
QtWndProc
C:\WINDOWS\system32\user32.dll
creation, use, and destruction of a control like TButton, we’ve seen the
C:\WINDOWS\system32\user32.dll differences in Borland’s two component frameworks. CLX is a wonder-
C:\WINDOWS\system32\user32.dll ful component framework. It’s well designed and elegant. You can tell
QApplication::enter_loop
by the code that the VCL is harder to read at this level. CLX makes
QApplication::exec
QApplication_exec(handle=:011E3764) this much easier by separating the component notifications from the
TApplication.Run event-processing code. By calling dynamic methods, instead of passing
Project1.exe messages, the code path is easier to follow.
Figure 11: CLX call stack in response to clicking Button.
I would like to thank Adam “Sparky” Markowitz for his comments
in time, you see that it was called by TControl.Click, which in turn was and suggestions. ∆
called by TButton.Click. This could be VCL or CLX code; it’s the same
mechanism, the same code. You can see the output of the call stack for The projects referenced in this article are available on the Delphi Infor-
the VCL in Figure 10, and for CLX in Figure 11. mant 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 han-
dles the basic event types such as mouse events, keyboard events, painting
events, drag-and-drop events, and mouse-wheel events, among others. In Involved as a user of Delphi since the initial beta, Robert Kozak is a member of the
the example, you have a mouse event (QEventType_MouseButtonRelease) Kylix R&D team and has been with Borland since the latter half of 1999. Since he
which is processed into a direct call to TButton.Click. 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
Destroying the Button Association), which later merged with TDUG (Toronto Delphi Users Group). Robert
Finally, if you close the form, the Button component’s Free continues to stay active in the user community, and on the Borland newsgroups.
method is called:

24 January 2001 Delphi Informant Magazine


Quick CLX

Begin Listing One — Demonstration project procedure FormDestroy(Sender: TObject);


private
{ ******************************************************* }
{ Private declarations }
{ Project source }
public
{ ******************************************************* }
program ButtonProject; { Public declarations }
Button: TButton;
uses procedure ButtonClick(Sender: TObject);
{ $IFDEF LINUX } end;
QForms,
{ $ENDIF } var
{ $IFDEF MSWINDOWS } Form1: TForm1;
Forms,
{ $ENDIF } implementation
Unit1 in 'Unit1.pas' { Form1 };
{ $IFDEF LINUX }
{ $R *.res } { $R *.xfm }
{ $ENDIF }
begin { $IFDEF MSWINDOWS }
Application.Initialize; { $R *.dfm }
Application.CreateForm(TForm1, Form1); { $ENDIF }
Application.Run;
end. procedure TForm1.FormCreate(Sender: TObject);
begin
{ ******************************************************* } Button := TButton.Create(nil);
{ Unit source } Button.Parent := Self;
{ ******************************************************* } Button.Caption := 'Hello, World!';
unit Unit1; Button.OnClick := ButtonClick;
end;
interface
procedure TForm1.FormDestroy(Sender: TObject);
uses begin
{ $IFDEF LINUX } Button.Free;
QT, Variants, Classes, QGraphics, QControls, QForms, end;
QDialogs, QStdCtrls;
{ $IFDEF MSWINDOWS } procedure TForm1.ButtonClick(Sender: TObject);
{ $ENDIF } begin
Windows, Messages, SysUtils, Variants, Classes, Graphics, ShowMessage('Welcome to my life.');
Controls, Forms, Dialogs, StdCtrls; end;
{ $ENDIF }
end.
type
TForm1 = class(TForm)
End Listing One
procedure FormCreate(Sender: TObject);

25 January 2001 Delphi Informant Magazine


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

By Bruno Sonnino

DBGrid Images, etc.


Taskbar Tips and Environment Variables

I 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
component’s OnDrawColumnCell event.
It’s declared as shown here: a bitmap and draw it on the DBGrid’s canvas. The
Object Pascal implementation is shown in Figure 1,
TDrawColumnCellEvent = procedure(Sender: and the run-time result is shown in Figure 2.
TObject; const Rect: TRect; DataCol:
Integer; Column: TColumn; It’s important to understand how DBGrid’s
State: TGridDrawState) of object; DefaultDrawing property works. If it is set
to True (the default), each cell is drawn before
where Rect is the rectangle where the data OnDrawColumnCell is called. This means that
should be drawn, DataCol is the index of the your custom drawing will take place after the
column to be drawn, Column is the TColumn default drawing has occurred. This is exactly
object relative to the data, and State is the cur- what you want in this case, because the rows also
rent state of the cell. contain text fields. If you set DefaultDrawing to
False, then you would also have to handle the
You must also test if the cell is of type drawing of the text fields.
TGraphicField. If it is, you then assign the field to
Instead of going to all that trouble, it’s perfectly
acceptable to let the DBGrid go through its
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
default drawing first, before calling your custom
State: TGridDrawState); OnDrawColumnCell event handler. This means
var that the text, (GRAPHIC), is initially written to the
Bitmap : TBitmap; cell, but the user never sees it. In fact, you can
begin
take advantage of this behavior. In this example,
// Test if the field is a TGraphicField.
if Column.Field is TGraphicField then the font size of the graphic cell has been set to
with DbGrid1.Canvas do begin 48, so that the cell is the proper size for the
// Clear previous information. graphic that will replace the text.
FillRect(Rect);
// Create the bitmap.
Bitmap := TBitmap.Create; Changing the Application Title
try
// Assign the field contents to the bitmap.
in the Taskbar
Bitmap.Assign(Column.Field); The title of the application that’s shown
// Draw the bitmap in the grid's canvas. in the Windows taskbar is provided by the
Draw(Rect.Left, Rect.Top, Bitmap); Application.Title property. If it isn’t explicitly set,
finally Delphi uses the caption of the main form.
Bitmap.Free;
end; Therefore, to change the title shown in the task-
end; bar, you must change the Application.Title prop-
end;
erty to the text you want. You can be as fancy and
Figure 1: Drawing a bitmap in a DBGrid. creative as you like.

26 January 2001 Delphi Informant Magazine


At Your Fingertips
To temporarily
store the icon to
be shown, a Figure 5: Animated “hourglass” icon.
variable named
FCurrIcon is declared in the private section of the form. Each
time the OnTimer event handler is fired, 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 func-
tion 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.
After using GetEnvironmentStrings, you must parse the resulting
PChar, until you find a double #0. One way to parse the PChar is
to first cast it as a Pascal string, thus: string(Env). This converts a
PChar to a string, but stops when it finds a #0 character. When the
Figure 3: Taskbar title with the current time.
cast is made you’re assured there is only one environment variable
in the string, so you can assign it to a StringList, ListBox, etc.
procedure TForm1.Timer1Timer(Sender: TObject);
begin
// Get icon from the ImageList and After parsing the PChar, you increment the pointer, so it points to
// assign it to Application.Icon. the next string. If there is a double #0, then the value of the first
ImageList1.GetIcon(FCurrIcon, Application.Icon); element of the PChar will be a #0, and you can stop the loop. The
// Increment icon index.
if FCurrIcon >= 9 then
code in Figure 6 parses the environment strings and adds them to
FCurrIcon := 0 a ListBox component.
else
Inc(FCurrIcon); As a bonus, we can parse the items in the ListBox, using the
end;
Names and Values properties, as shown in Figure 7. When the
end;
strings in the list are in the form Variable=Value, the Names
Figure 4: Code to animate an icon in the taskbar. property retrieves the names of the variables, and the property
Values retrieves their values.
For example, the title could be a clock. Simply drop a Timer
component on the main form, and then place the following state- Justifying Text
ment in its OnTimer event handler: One frequently asked question is how to justify text between margins.
The Label, Memo, and RichEdit components can align text on the
Application.Title := TimeToStr(Time); right or left, or center it, but there’s no way to align text between
margins, as word processors do. The solution lies in a Windows API
The statement will be executed every time the OnTimer event handler function named SetTextJustification.
is fired (the default is every second), changing the title in the taskbar
and displaying the current time, as demonstrated in Figure 3. Here’s its declaration:

Placing an Animated Icon in the Taskbar function SetTextJustification(


To change an application’s icon in the Windows taskbar, you DC: HDC; // Handle of device context.
BreakExtra, // Length of extra space, in logical units.
must assign a new icon to the Application.Icon property. Animated
BreakCount: Integer // Count of spaces in line of text.
icons aren’t a feature of the Windows taskbar, so you must simu-
): Integer; stdcall;
late one by creating a sequence of icons and presenting them at
set intervals.
It isn’t, as many people think, an easy task to justify text; you
Use a Timer component and modify its OnTimer event handler to can see from the declaration that there isn’t a Text parameter to
present the icons one after the other. The best way to store the simply pass in to be justified. To use it, you must pass the handle
icons is in an ImageList component. It’s easy to retrieve the icons of the device context (usually Canvas.Handle), the count of breaks
in sequence using the ImageList’s index, and ImageList has the (blank spaces) in the line, and the extra space to be added in these
GetIcon method for retrieving the image as an icon. blanks. Sound difficult? Well — it is.

Once you’ve interactively placed the icons in sequence in the Image- There is another trick to this function: rounding errors persist until
List, the code shown in Figure 4 “animates” the icon. You should the next time it’s called. So, before calling the function for the next
set the Timer’s Interval property to a value that provides a smooth line, you must clean the rounding errors, passing 0 as the second and
animation effect (500, for half a second, was used in this example). third parameters.

27 January 2001 Delphi Informant Magazine


At Your Fingertips
Env := GetEnvironmentStrings;
with ListBox1 do begin procedure JustPrint(Canvas: TCanvas; Text: string;
// If it's a double #0, Env[0] will be a #0; MaxWidth: Integer);
// stop the loop. var
while Env[0] <> #0 do begin i : Integer;
// Assign parsed string to ListBox Items. BegWord : Integer;
Items.Add(string(Env)); Breaks : Integer;
// Make Env point to the next string. SizeY, PosY : Integer;
Inc(Env, StrLen(Env)+1); LineToPrint : string;
end; AWord : string;
end; begin
i := 1;
Figure 6: Separating individual environment strings and adding PosY := 0;
// Get height of the line.
SizeY := Canvas.TextHeight('Wy');
with StringGrid1, Listbox1 do begin LineToPrint := '';
// Set grid row count to number of items in ListBox. Breaks := 0;
RowCount := Items.Count; // Set transparent drawing mode.
for i := 0 to Pred(Items.Count) do begin SetBkMode(Canvas.Handle, Transparent);
// The first cell has the name of the variable. while i <= Length(Text) do begin
Cells[0,i] := Items.Names[i]; // Remove initial spaces and line breaks.
// The second cell has the value of the variable. while (Text[i] in [' ', #10, #13]) and
Cells[1,i] := Items.Values[Cells[0,i]]; (i <= Length(Text)) do
end; Inc(i);
// Search word.
Figure 7: Separating the environment variables’ names and BegWord := i;
values. 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
Figure 8: This sample application parses and displays environ- (Text[i] in [#10, #13]) then
ment variables. begin
Canvas.TextOut(0, PosY, LineToPrint);
Inc(PosY, SizeY);
LineToPrint := '';
For example: Breaks := 0;
end;
SetTextJustification(Canvas.Handle, 0, 0); end; // else...
end; // while i <= Length(Text) do...
end;
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 Figure 9: The custom JustPrint procedure.
length; if it hasn’t, increment the break count.
3) If the line length is greater than the maximum length, it must be To get each word of text, you can process the whole text as a big
printed without the last word, justifying it. string, character-by-character, searching for break characters (e.g.
4) If it’s the end of a paragraph, the line must be printed with no space, carriage return, line feed). As shown here:
justification.

28 January 2001 Delphi Informant Magazine


At Your Fingertips
would exceed the line length. That word becomes the first word of
the next line. To print the line, you must call SetTextJustification,
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.

Here’s 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;

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 justified text. You must pass
three parameters to it:
1) a canvas,
Figure 10: The sample application justifies text on a form. 2) the text to print, and
3) the maximum width of the line.
// BegWord has the beginning of the word.
BegWord := i; Figure 10 shows the accompanying sample application with text
// Search until text is finished or a break is found.
justified on a form.
while (i <= Length(Text)) and
not (Text[i] in [' ', #10, #13]) do
Inc(i); // i is position of end of text or next break. Conclusion
This month I have placed five programming pointers at your finger-
tips to assist you in your Delphi development efforts. It’s now up to
The second step is to determine if the line length, with the new word you to put them to good use. ∆
included, exceeds the maximum line length. As the words fit in the
line, they are included in a variable named LineToPrint. The projects referenced in this article are available on the Delphi Infor-
mant Magazine Complete Works CD located in INFORM\2001\JAN\
This verification can be accomplished like this: DI200101BS.

// 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
... 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
If the line has reached the maximum length, it must be printed in Applications in Delphi 5, published in Portuguese. He can be reached at
a justified manner. The last word must not be printed, because that sonnino@netmogi.com.br.

29 January 2001 Delphi Informant Magazine


New & Used

By Alan C. Moore, Ph.D.

Sleuth QA Suite 2
Powerful, Flexible Debugging and Profiling Tools

T 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 to monitor calls to API functions. In the first
is a debugger for finding memory leaks, resource category it can identify memory leaks, resource
leaks, problems with API calls, and more. Stop- leaks, and memory overruns. It includes a data-
Watch is a powerful profiler. New tools are being base of known errors for all Delphi 32-bit com-
added to Suite 2, which should be shipping by pilers so you can exclude them from your analysis
the time you read this article (see “What’s New in and concentrate on leaks in your code. You can
Suite 2” later in this review). also add known leaks to the database.

Each tool comes with the outstanding documen- In the API category, this tool can identify invalid
tation that’s become a TurboPower trademark. parameters or return values for Win32 API func-
That documentation includes a manual with a tion calls. You can have it create a log for all
thorough tutorial for each application to get you Win32 API calls, listing parameter values and
up and running fast. You can run either applica- return values. As you may be aware, not every
tion from within the Delphi IDE, or as a stand- API function works in every Windows version;
alone executable. some can be used only on the NT family. There-
fore, CodeWatch allows you to test platform
Debugging with CodeWatch compliance for Win32 API function calls.
CodeWatch is based on TurboPower’s popular
memory debugger, MemorySleuth. It signifi- This tool provides a great deal of flexibility giving
cantly extends the memory debugging capabili- you control over the errors to report. If your
ties of the older product, and adds new features application uses units for which you don’t have
the source code, you can still include them in
your testing. You can also analyze multiple mod-
ules at the same time, including dynamically
loaded DLLs.

Best of all, you don’t 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
Figure 1: Default CodeWatch view with project loaded. units used, including time called, name, version,

30 January 2001 Delphi Informant Magazine


New & Used

Page Description
Module Shows information about the project mod-
ules used by the application being analyzed,
including name, when it was loaded, version
stamp, code, data size, etc.
Parameters Active only if one of the API function or
& Failures 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.
Debug Output Shows debug information generated by an
application calling the Windows API function,
OutputDebugString. You can also show inter-
nal Sleuth QA audit messages.
Figure 4: StopWatch’s user interface.
Report Provides comprehensive view of the results
of an application run, summarizing the infor-
Chart window at the lower left, which explains some of the resource
mation displayed in other views. You can
types being monitored. CodeWatch can conveniently take you right
print this report or select other options.
to the line of code where un-released memory is allocated.
Figure 2: CodeWatch’s four pages (views).
Profiling with StopWatch
StopWatch is the long-awaited profiler, a completely new tool from
TurboPower. I’ve tried a number of profilers, 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 profiling
by inserting lines of code into your source code units to perform the
actual profiling. StopWatch uses a technique called dynamic adaptive
instrumentation, which modifies the unit being profiled at run time.
This approach is fast and leaves your source code untouched.

This tool’s user interface is similar to that of CodeWatch, as shown


in Figure 4. Here we see the Routines view after loading an applica-
tion. The pages available — Routines, Profile, and Comparison —
provide data related to a profiling operation. These different views
provide a convenient means to view just the data in which you’re
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.
These three views provide a lot of information, and a great deal
of flexibility. StopWatch’s Settings Wizard gives you considerable
memory image base, etc. Figure 2 explains the General folder’s control over how the profiler operates. You can set the Profiling
four views. Mode to Always On so that profiling will be active as soon as the
application starts executing, or Trigger Activated so that profiling
I was impressed with the many options available, both in the kinds will not start until a specific routine triggers it. You can have
of analysis performed, and the manner in which the results of the StopWatch check those units with source code, without source
analysis can be viewed. You can choose to analyze memory leaks code, or both. You have further options regarding leaf routines
alone, and/or add resource leak analysis, API function call analysis, (which you rarely need to include), threads, and external functions.
and/or API function validation. Now that’s flexibility!

A particularly nice feature is the collection of graphical display As with CodeWatch, StopWatch also includes an excellent tutorial,
views. One such view — Types, Stats, and Charts — consists a Maze that is begging to be optimized. The most time-consuming
of three re-sizable windows. The Event Browser provides statistics routine, Walk, is listed at the top. If you’re wondering about the
about the memory used, and a chart showing peak usage through- difference between Net Time and Gross Time, it’s quite simple.
out the duration of the project (see Figure 3). If you select this Net Time is the amount of time spent in the routine, excluding
view before beginning analysis of a project, you can watch the any time spent in routines called from within it. Gross Time is
chart create a real-time visual representation of your project’s the amount of time spent in the routine, including the time spent
memory use as the application executes. 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.
The screen shot in Figure 3 was taken at the conclusion of a
program run and includes the Session Summary dialog box, option- Like CodeWatch, StopWatch includes charts that let you view
ally displayed on program termination. It shows a summary of the various aspects of a single or multiple profiling runs. Seeing the
various leaks and errors. You’ll note that (most of ) the memory is most time-consuming routines in graphical format makes it easy to
released at the end, as shown in the graph. Note the Customize see where you need to perform optimizations. You can also choose

31 January 2001 Delphi Informant Magazine


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 rou- Sleuth QA Suite is a powerful and flexible collection of debugging and
tines can be displayed in various views with profiling tools for Delphi and C++Builder. The first version consists of
different groupings and levels of detail. CodeWatch and StopWatch. Suite 2 improves these and adds three
Profile Results of a profiling session shown after additional tools: a coverage analyzer; a line timer; and a test-script
the application terminates. Includes three recorder that can be used with all of the other tools.
related views. The Main Routine view shows
results sorted in descending order by Net TurboPower Software Company
Time with the top routine as the best candi- PO Box 49009
date for optimization. There is also a Calls- Colorado Springs, CO 80949-9009
To-Current-Routine view showing the names
of the routines that called the routine in the Phone: (800) 333-4160
Main Routine view, and a Calls-From- E-Mail: info@turbopower.com
Current-Routine view. Web Site: http://www.turbopower.com
Comparison Lets you compare the results from different Price: List price: US$399; upgrade from first version, US$149;
profiling sessions. Ideal for determining if upgrade from Memory Sleuth, US$279. Competitive upgrades from
optimization changes are having desired other debugging tools may apply.
results.
Figure 5: StopWatch’s three views.

from the following, related to the current profile: matter if these modules are statically or dynamically loaded. Like
 Net Times shows the 10 most time-consuming routines. StopWatch, CoverageAnalyst uses dynamic adaptive instrumenta-
 Gross Times is similar to Net Times, but includes time spent tion to modify the module being analyzed at run time. According
in called routines. to TurboPower, coverage analysis was the most popular feature
 Call Count shows the 10 most frequently called routines. request for the first version of Sleuth QA Suite.
 Averages gives the 10 routines with the highest net average
execution time. LineProfiler, another result of frequent feature requests, allows
timing at the source line level. It also uses dynamic adaptive instru-
You can see the differences in two profiling runs in either text form mentation technology. It times each source line for the routines you
or graphical form, enabling you to determine if your attempts at select, keeping track of times executed, total time spent, maximum
optimization have been successful or not. There’s a Comparison time, minimum time, and the average time for each line.
Chart that lets you graphically compare profiles loaded into the
Comparison view. The final new tool, ActionRecorder, lets you build QA test scripts
by recording keyboard and mouse actions for an application and
Outstanding Documentation for playing them back later. You can initiate playback directly
If you’ve used other TurboPower products, you’re aware of the excel- from the ActionRecorder tool. You can also initiate playback auto-
lent documentation that comes with each of its products. Sleuth QA matically from within any of the four analysis tools after you’ve
Suite is no exception. As mentioned, the manual includes a tutorial launched an application.
for both tools (with all the files you need on disk). The manual
and online Help provide all the information you need to begin to In addition, Sleuth QA Suite 2 now supports 32-bit compilers
use these tools quickly. Sleuth QA Suite goes further by providing from both Borland and Microsoft. The new version supports
a cogent introduction to dealing with memory issues and perform- Visual Basic 6 and Visual C++ 6, giving Delphi developers who
ing code optimization. Once the Suite has shown you where the work with those tools an added bonus. ∆
problems are, the manual gives you important clues about what to
do to solve those problems. The material is so good that I’ve encour-
aged the folks at TurboPower to expand it in future versions. Even
without the extensive improvements in Suite 2, the first version of
Sleuth QA Suite is an essential tool for every Delphi and C++Builder
developer. Before we finish, however, let’s see what Suite 2 brings us.

What’s New in Suite 2


In addition to considerable enhancements to the two tools we’ve
been discussing, Sleuth QA Suite 2 adds three new tools: Cover- Alan Moore is a Professor of Music at Kentucky State University, specializing
ageAnalyst, a coverage analyzer; LineProfiler, a line timer; and in music composition and music theory. He has been developing education-
ActionRecorder, a test-script recorder that can be used with all of related applications with the Borland languages for more than 15 years. He
the other tools. has published a number of articles in various technical journals. Using Delphi,
he specializes in writing custom components and implementing multimedia
CoverageAnalyst enables you to determine how many times a line capabilities in applications, particularly sound and music. You can reach Alan
of source has been executed in a program run. You can use it for at acmdoc@aol.com.
various types of modules, e.g. EXE, DLL, OCX, etc. It doesn’t

32 January 2001 Delphi Informant Magazine


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, I’ve always believed that
the grid handles editing inefficiently, at best. Other common tasks I’ve 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 TdxTreeList, and a DBGrid replacement named
MIDAS, I resumed my quest for the ultimate grid TdxDBGrid. A new version of the suite now
that would provide these capabilities. Enthusiastic offers a data-aware TreeList component, but for
praise for the ExpressQuantumGrid on Borland’s my needs it’s of lesser interest. The most impor-
newsgroups led me to look at the product. tant thing is that the grid control is based on
the same common ancestor, so it can display
ExpressQuantumGrid hierarchical information.
ExpressQuantumGrid includes two main com-
ponents: a TreeList view combination named 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 applica-
tions. Some of the different editors are check-
box, memo, BLOB, currency, drop-down date,
drop-down list, masked edit, time, and spin
editor (a numeric editor with spin buttons).
There’s even a drop-down grid where you can
choose a cell value from a grid that displays
multiple rows and columns.

The run-time data/report viewer supports multi-


level hierarchy grouping based on key columns,
automatic sorting, and automatic summary at the
footer or group level (see Figure 2). Summary isn’t
limited to the simple addition function, but sup-
Figure 1: ExpressQuantumGrid’s multi-row bands and in-place cell editors. ports functions like minimum, maximum, aver-

33 January 2001 Delphi Informant Magazine


New & Used
age, and count. The grid’s 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 customer’s name,
address, and phone number) in multiple display lines
for the data record, while a memo field (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 it’s 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 data-


base applications, it’s creating single-record data-edit- Figure 2: Fixed bands, grouping by year, and group summary information.
ing 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 exer-
cise in using the IDE. Of course, when the applica-
tion needs to be updated to support a new database
column, or a different visual representation or valida-
tion 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-defined 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 drop-
down grid appears to be missing from ExpressInspec-
tor.) You can also create categories and hierarchies of
properties. Developer Express provides both a data- Figure 3: The in-place cell editors in the object inspector.
aware version of ExpressInspector, or a standard ver-
sion where you can generate your categories and cells
programmatically. In my application, I used a MIDAS client dataset as the source
of the data. After reading the documentation, I noticed that
Unless your data-editing-form requirements force you to create if I wanted to provide grouping in the grid, I had to set the
multiple control forms manually, a single ExpressInspector can be egoLoadAllRecs member of the Options property. Unfortunately, the
used and populated easily with the columns of available data. Options property helps describe no less than 30 options (some of
them completely unrelated), so I failed to recognize the need to set
Developing with the Controls and Documentation the KeyField property to enable the egoLoadAllRecs option. Without
Once dropped on a form, both components provide a dizzying it, the grid always displays empty — a condition that was fixed once
array of properties from which to choose. Luckily, both have com- an e-mail sent to Developer Express technical support clarified the
ponent editors that help with the definition of data columns and situation. Another error was made when the current cursor in the
their types, allow editing of individual column components, and in underlying dataset did not change after I selected a node in the grid.
the case of the grid, provide the mechanism to create bands. This time I knew enough to read the documentation thoroughly,
and found that egoCanNavigate must be set (and a set of other
The philosophy behind the components is that many common options required by it) to avoid this problem.
actions can be accomplished without code — just by setting prop-
erty values. This approach is great once you’ve studied the docu- The products come with a comprehensive Help file, many samples,
mentation and checked the sample programs and experimented, but and a subscription to a Web-based FAQ system. The capabilities are
it can be frustrating without the background. Mistakes are easily wide and the customization options are almost endless, so a close
made by forgetting to set an option or provide a property value. examination of the references and sample applications is a must.

34 January 2001 Delphi Informant Magazine


New & Used
In addition to the source, documentation, and samples, both prod-
ucts come with two additional libraries of code: a memory-based
dataset, and a collection of visual components that can be used in
After you use ExpressQuantumGrid and ExpressInspector, you won’t your application. You should visit their Web site to get a full idea of
want to develop another Delphi database application without these all the products available from Developer Express.
products. In-place cell editing and sorting by column and grouping
are easily accomplished in ExpressQuantumGrid’s VCL grid, and single- Conclusion
record editing is effortless in ExpressInspector. Both tools have wide After my experience with ExpressQuantumGrid and ExpressInspec-
capabilities and customization options. tor, I can’t imagine developing a Delphi database application with-
out these tools. The grid is the most capable VCL grid I’ve found.
Developer Express Just by using a small part of its capabilities and customization
6340 McLeod Dr., Ste. 1 options, my applications seem more professional, and the users
Las Vegas, NV 89120 enjoy the editing capabilities.

Phone: (702) 262-0609 In my opinion, a grid is the most important database UI control,
Web Site: http://www.devexpress.com and ExpressQuantumGrid is a must-have tool in my database com-
Price: ExpressQuantumGrid Suite: Standard, US$299; Professional, ponents arsenal. The ExpressInspector component is not as essential
US$349. ExpressInspector Suite, US$99. 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 don’t hesitate to recommend it. It’s
I have to admit that I’m impressed with the quality of Developer well built and provides the same exceptional technical support from
Express’ technical support. Any question I submitted was immedi- Developer Express. ∆
ately 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 experi- Ron is a software developer specializing in Business Intelligence applications.
ence — even when the issues are in the Developer Express code. He can be reached at rloewy@transport.com.

35 January 2001 Delphi Informant Magazine


Best Practices
Directions / Commentary

What’s on Your Bookshelf?

L 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 hairy enough to challenge even Wendl’s powers of concentration.
fasten that knowledge. You need to acquire general principles of One afternoon, I was bent over a program listing while Wendl
programming before you can effectively apply the specifics related to was staring into space, his feet propped up on the desk. Our boss
new technologies and techniques. The principles are best acquired came in and asked, ‘Wendl! What are you doing?’ Wendl said, ‘I’m
through books, where you can commune with those older and wiser. thinking.’ And the boss said, ‘Can’t you do that at home?’”

A list of books which every programmer should read — and peri- The Psychology of Computer Programming by Gerald M. Weinberg
odically re-read — follows. They’re separated into two categories: [Dorset House, 1971] is about programming and software project
old classics and new classics. The old classics certainly aren’t as management, but most of all it’s about programmers and what
old as Stendhal’s The Red and the Black or even Steinbeck’s Grapes makes us tick. Perhaps as important, and even more surprising to
of Wrath, but as far as programming books go, they’re not spring non-programmers (including many managers) is what doesn’t make
chickens either. Although the original year of publication is shown, us tick. This book not only helps you understand yourself and your
many have enjoyed recent revisions. Why no Delphi classics you colleagues better (and even non-programmers, as their motives and
ask? That topic was addressed recently by Alan Moore in his world view are contrasted with those of programmers), but is also
November 2000 “File | New” column. an enjoyable read.

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


The Mythical Man Month by Frederick P. Brooks, Jr. [Addison- 1986] is complementary to Weinberg’s The Psychology of Computer
Wesley, 1975] contains a series of seminal essays on software proj- Programming. It’s described as “... an irreverent, funny, provocative,
ect management, and introduces Brooks’ Law: “Adding manpower satirical but true look at those thousands of professionals, as well
to a late project makes it later.” Also noteworthy is Brooks’ “No as con men, who call themselves consultants.” The book includes
Silver Bullet” essay, which is reprinted in the 1995 edition. numerous rules and laws, such as “The Credit Rule: You’ll never
accomplish anything if you care who gets the credit,” “The Titanic
Programming Pearls by Jon Louis Bentley [Addison-Wesley, 1985] Effect: The thought that a disaster is impossible often leads to an
is a series of essays on programming techniques and computer unthinkable disaster,” and “The Weinberg Test: Would you place
science theory. The preface explains the reasoning for the book’s your own life in the hands of this system?”
name: “Just as natural pearls grow from grains of sand that have
irritated oysters, these programming pearls have grown from real In 201 Principles of Software Development by Alan M. Davis
problems that have irritated real programmers.” [McGraw-Hill, 1995], most of the principles are explained in a
concise, yet approachable way. In fact, they often fit on a single
Peopleware by Tom DeMarco and Timothy Lister [Dorset House, page. Unfortunately, this little gem is very hard to come by. I read
1987] is a people-centric, common-sense guide to software project it as I was “on the bench” while working for a former employer.
management. Besides being a practical and insightful book, it’s also I’ve tried to order it from Amazon, Barnes & Noble, Fatbrain, etc.,
a downright entertaining one. It even relates some Dilbertesque all to no avail. If anybody knows where I can get a copy of this
episodes, such as this: “In my years at Bell Labs, we worked in two- book, please let me know.
person offices. They were spacious, quiet, and the phones could be
diverted. I shared my office with Wendl Thomis who went on to New Classics
build a small empire as an electronic toy maker. In those days, he Code Complete by Steve McConnell [Microsoft Press, 1993]. If
was working on the ESS fault dictionary. The dictionary scheme you only read one general programming book, this should be it.
relied upon the notion of n-space proximity, a concept that was McConnell shows what to do and why, teaching good program-

36 January 2001 Delphi Informant Magazine


Best Practices
ming practices throughout. Example code is in C, Basic, Pascal, 1) You have to understand the problem.
and other languages, but is always explained well enough that it 2) Find the connection between the data and the unknown ...
can be followed regardless of your level of multilingualism. If you You should obtain eventually a plan of the solution.
are serious about programming, you should read and apply the 3) Carry out your plan.
information in this book. (By the way, I did not remember — 4) Examine the solution obtained.
consciously, anyway — that the name of McConnell’s column in
IEEE Software was “Best Practices” at the time I suggested that title Sounds like analysis, design, coding, and testing to me.
for this column.)
Conceptual Blockbusting by James L. Adams [Perseus, 1990]. To
Rapid Development by Steve McConnell [Microsoft Press, 1996] use a hackneyed term, this book teaches you to think “out of
elucidates how to responsibly speed up the development process, the box.” It’s described as “a broad compilation of strategies and
why RAD often goes bad, and what to do to prevent it. techniques designed to liberate creative thinking” and “an excellent
do-it-yourself study guide to better ideas.” This excerpt may whet
Software Project Survival Guide by Steve McConnell [Microsoft your appetite: “... the natural response to a problem seems to be
Press, 1998] makes specific suggestions on how to give yourself to try to get rid of it by finding an answer — often taking the
the best chance of success on your software projects. It shows first answer that occurs and pursuing it because of one’s reluctance
that there is another way; you don’t have to bump your head on to spend the time and mental effort needed to conjure up a richer
every step each time you come down the stairs (read the book storehouse of alternatives from which to choose... In engineering one
for the full analogy). Why does the author go to such lengths to finds the “Rube Goldberg” solution, in which the problem is solved
tell readers how to avoid “death march” projects? A quote from by an inelegant and complicated collection of partial solutions.”
the book proves illuminating: “As Thomas Hobbes observed in the
17th century, life under mob rule is solitary, poor, nasty, brutish, The Design of Everyday Things by Donald A. Norman [Doubleday,
and short. Life on a poorly run software project is solitary, poor, 1990]. Isaac Asimov introduced this book well by saying: “We
nasty, brutish, and hardly ever short enough.” are all victimized by the natural perversity of inanimate objects.
Here is a book at last that strikes back both at the objects and
After the Gold Rush by Steve McConnell [Microsoft Press, 1999] at the designers, manufacturers, and assorted human beings who
makes the point that the software profession needs to “grow up” originate and maintain this perversity. It will do your heart good
if it wants to be considered a true engineering profession. He and may even point the way to correcting matters.” The following
also discusses the possible ramifications for individual program- quote applies especially well to our profession: “Manuals tend to
mers who are not progressive and alert: “Programmers who aren’t be less helpful than they should be. They are often written hastily,
paying attention could easily find themselves working as twenty- after the product is designed, under severe time pressure and with
first century software janitors.” Read the book to find out how to insufficient resources, and by people who are overworked and
prevent this from happening to you or someone you love. under appreciated. In the best of worlds, the manuals would be
written first, then the design would follow the manual.” Frederick
The preface of The Pragmatic Programmer by Andrew Hunt and Brooks also makes this point (that the end-user documentation
David Thomas [Addison-Wesley, 2000] says, “This book will should be written first, and used as a guide in development) in The
make you a better programmer.” I agree. It introduces the broken Mythical Man Month.
window theory, explains why and how you need to continue to
invest in your “knowledge portfolio,” and contains (not unlike In preparation for next year’s Delphi Hall of Fame inductions,
the 201 Principles book previously noted) a listing of 70 tips I would like to cordially invite all of you in Delphi-land to nomi-
and a checklist of things to keep in mind as you practice your nate a person (or people) who you think deserves to be in the
profession. Hall. Please send your nominations, along with your reasons, to
bclayshannon@earthlink.net. ∆
The following three books are not programming books per se,
but are nevertheless very helpful in understanding how best to — Clay Shannon
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 field. The work
was a major influence 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 defining 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 Clay Shannon is an independent Delphi consultant, author, trainer, and mentor
the heart of a problem.” operating as Clay Shannon Custom Software. He is available for remote devel-
opment, short-term on-site assignments, as well as select long-term assign-
It is amazing at times how much this book seems as if it was writ- ments in the Spokane, WA/Coeur d’ Alene, ID area. To view his resume, go
ten specifically about solving software challenges. As an example to http://www.sysadminsrus.net/clayshannon/ClayShannon.doc. You can reach
of that, there is a “How to Solve It” list comprised of the following him at bclayshannon@earthlink.net.
steps:

37 January 2001 Delphi Informant Magazine


File | New
Directions / Commentary

Project JEDI: Knights Battling the Empire

D elphi is more than an outstanding development tool. It’s 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 One of those people was Helen Borrie (Australia), who has contin-
1999 Delphi Informant Magazine. If you’ve visited the JEDI Web site ued to be one of the most active and hard-working Jedians. On
(http://www.delphi-jedi.org) lately, you know what I’m talking about. the following Monday she wrote to the DDJ list, letting folks
The site won About.com’s “Best of the Net” award in March 2000. know what had occurred over the weekend. She began: “I’ve been
In listing the award, the Project’s goal was “to extend Delphi’s native astonished - amazed - heartened - all of that - to see how people
access to the Windows API by translating C/C++ headers of popular, have rallied around a perceived problem in the Borland support
but as yet unsupported, technologies into Delphi interface units.” machine and come up so willingly with the beginnings of a strategy
That was JEDI’s original goal, but the Project now encompasses to solve it. What a great weekend!” Then she expressed one of our
much more. I would describe it as “an impressive organization of initial dilemmas: “We here (a team of six Delphi developers with a
international volunteers donating their efforts to improve Delphi and variety of Windows and DOS backgrounds) are just having a little
help their fellow developers.” bit of trouble understanding what the project is aimed at.” Over the
days and weeks that followed the Project was defined and named,
That spirit of community is accentuated by the third-party vendors a president and other administrators selected, and work began on
who have generously contributed their tools and resources, including: the first translations.
Nevrona Designs, who donated licenses for ReportPrinterPro/Rave;
Digital Logikk AS, who donated copies of Time2Help for the Help Ups and downs, reorgs, Borland, and growth. The project had many
Team; and HREF Corporation, for hosting the Web site and provid- ups and downs in its first year. At times members wondered if the
ing their outstanding WebHub technology for the Web Team to use. Project would survive. Several early members deserve special recogni-
These are just three of many donations. Before discussing the exciting tion for managing those challenging times of the first year. Tim
new developments, I should go back and trace the Project’s beginnings Hayes, the first president of the Project, was one of the main driving
for the benefit of readers who may be just finding out about it. forces. Keith Anderson, another founding member, made Internet
support services available to the project. Robert Love organized Birds-
The Power of the Internet of-a-Feather sessions at the 1998 and 1999 Borland Conferences.
One thing is quite clear: Without the Internet there would be no
Project JEDI. In doing research for this column I found Brian The project had one especially low period. In early 1999 it seemed
Dupras’ 9/25/97 message that started it all, one that was posted to all but dead, with almost no discussions on the Internet and little
the former COBB Group DDJ list that I approved as a moderator of activity. Our current president, Thomas Guarino, took the initiative of
that list. It began, “I’ve been using Delphi since its beta days, and I’ve inviting three of us who were still active in Internet JEDI discussions
grown to truly love the product. There’s one thing that concerns me, (Michael Beck, Helen Borrie, and me) to form a new ADMIN Team.
however. Microsoft comes up with new SDKs all the time, most of Our goal was simply to get the project moving again, and we were
which could benefit countless Delphi developers if only we had the remarkably successful.
proper translations and code examples.”
Soon we learned that some folks at Borland had been watching the
Brian went on to explain how frustrating it was to put a lot of effort Project from a distance, wondering if it would survive and whether
into translating a header file, only to have Borland release its own they should get involved. We soon entered into a discussion with
version six months later. He also pointed out how quickly some of members of the Delphi R&D Team about ways we could work
the technologies were evolving and how out-of-date some of Borland’s together. (I covered much of this in my June column, so I won’t go
translations were at that time. To make a long story short, the DDJ into details here.) When Delphi 5 was released, the companion CD
list was flooded with messages that evening, and the notion of getting included one of our conversions. There was also a JEDI Easter egg
together and taking on the task ourselves quickly emerged. A handful included in Delphi 5 (type AJEDI in the About box). Charlie
of us participated in an Internet chat session the next day, resulting in Calvert was one of the most important people at Borland who helped
the beginnings of an organization. foster the new relationship in so many ways.

38 January 2001 Delphi Informant Magazine


File | New
A new team emerges. For any organization to remain successful, and fully documented utility functions and non-visual classes built
it must be willing to change. Project JEDI is no exception. In upon code donated by the JEDI community. One of the most active
August 1999 Tom announced the formation of a Steering Group to Jedians, Marcel van Brakel (Netherlands) is its main architect and
enhance communication among the many new JEDI leaders who had “founding father,” with help from Robert Marquardt (Germany)
emerged. This new group was soon making more and more decisions. and Matthias Thoma (Germany). A similar project, Project JEDI’s
Visual Component Library (JVCL), is independent of any third-party
A few months ago it became clear to Tom that it was time to dissolve developer or Borland. It is intended to “fill in the holes in Borland
the ADMIN Team. That small group had served its purpose of resur- Delphi’s standard VCL library.” There are a number of international
recting the project with its members already active in the new Steering contributors, including Petr Vones (Czech Republic), Anthony Steele
Group. The story does not end here. With the many new JEDI (South Africa), and Anders Melander (Denmark), to mention just
Knights from all over the world, and an expanded leadership group, few. You can join them.
the Project has delved into new areas to serve the Delphi community.
Finally, there are projects in their inception. Take a look at the
Not just header conversions. Header conversions remain a major “Proposal for A Project JEDI Software Engineering Tool Suite Proj-
focus of the Project, and that area has improved considerably with ect” by Nicole Boivin. This provocative essay is much more than a
many new translations. The core activity of the Project was enhanced proposal and includes some interesting insights about approaches to
further by the clean-up and grading of the API library, accomplished programming. You may be wondering if Project JEDI will have any
by Rudy Velthuis (Netherlands), Phil Shrimpton (UK), and Marcel involvement with the Kylix project. The Jedix Page provides news and
van Brakel (Netherlands). Thanks to them, it’s now a lot easier for snippets about Kylix, including a message from Michael Bonner, the
developers to locate the translations they need. However, there are so Coordinator of the JEDI Kylix Beta group.
many new exciting JEDI projects that I can only scratch the surface. A
major new area of emphasis is education, with several projects provid- As you can tell, there’s a lot going on at Project JEDI these days,
ing a plethora of programming information in a variety of formats. and it’s only going to get bigger in the future. Why don’t you join
us? We’re proving every day that although the Delphi community is
JEDI Voyager provides an online journey through a series of articles much smaller than the Visual Basic Empire, we nevertheless can boast
such as one on programming and testing by Robert Marquardt of the best Windows developers using the best Windows development
(Germany), a Font User’s Resource tool by Alan Lloyd (USA), and tool. Nowhere is that more apparent than at Project JEDI. Now we’re
a series of articles entitled “Making Forms Work” and on creating embarking on an exciting journey into the Linux world. Do you want
Help Files by Kevin Gallagher (USA). In addition to this online to be a part of the cutting edge? ∆
educational initiative, there is also an Education Team whose goal is
to provide Delphi educational material in a variety of formats. We — Alan C. Moore, Ph.D.
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 offline, Michael Beck redesigned the JEDI Alan Moore is a Professor of Music at Kentucky State University, specializing in music
Dolphin, an application that reads articles and tutorials offline. Be composition and music theory. He has been developing education-related applications
sure to download it and check it out. If you are an aspiring Delphi with the Borland languages for more than 15 years. He is the author of The Tomes
writer and/or have a particularly cogent way of presenting a technical of Delphi: Win32 Multimedia API [Wordware Publishing, 2000] and co-author (with
topic, please join us and add to the knowledge base. 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
Early in the project, we had discussions about creating code examples, Delphi, he specializes in writing custom components and implementing multimedia
classes, components, and sample units. Today that goal is becoming capabilities in applications, particularly sound and music. You can reach Alan at
a reality. The JEDI Code Library (JCL) is a set of thoroughly tested acmdoc@aol.com.

39 January 2001 Delphi Informant Magazine

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy