You are on page 1of 34

February 2002, Volume 8 Number 2

Creating Lively Regions

Cover Art By: Arthur A. Dugoni Jr.

ON THE COVER

REVIEWS

21

DeployMaster

23

InfoPower 3000

27

Addict 3.0

30

LockBox 2

32

Delphi Developers Guide to XML

Sound+Vision

Hot Spots: Part I Victor Hornback


Victor Hornback shows you how to create hot spots, Windows regions
that detect MouseOver events, to make graphic images behave like Web
page hot spots or hot links. Its the first of a two-part series.

FEATURES
8

On the Net

A WebSnap Online Survey: Part II Corbin Dunn


In this second of a two-part series, Borland QA Engineer Corbin
Dunn completes his WebSnap survey by adding server-side scripting to the main page, and getting into the implementation details
of creating the survey pages.

13

The API Calls

16

Columns & Rows

Windows API Error Handling: Part II Marcel van Brakel


Marcel van Brakel concludes his two-part Windows API Error Handling series
with a look at retrieving messages from a specific message DLL, setting the
last-error code, error modes, and Delphis approach to error handling.
InterBase 6.5 Bill Todd
Bill Todd gives us the skinny on InterBase 6.5, which offers 64-bit file
I/O, improved cache management, metadata security, the new ROWS
clause, and XML export for easy integration with Web applications.
1 February 2002 Delphi Informant Magazine

Product Review by Mike Riley


Product Review by Bill Todd
Product Review by Alan C. Moore, Ph.D.
Product Review by Alan C. Moore, Ph.D.
Book Review by Ron Loewy

DEPARTMENTS
2
33

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

Delphi
T O O L S

New Products
and Solutions

Book Picks
XSLT Programmers Reference,
2nd Edition
Michael Kay
Wrox Press

ISBN: 1-861005-06-7
Cover Price: US$34.99
(938 pages)
www.wrox.com

Component-based Product Line


Engineering with UML
Colin Atkinson, et al.
Addison-Wesley

ISBN: 0-201-73791-4
Cover Price: US$49.99
(503 pages)
www.aw.com/cseng

2 February 2002 Delphi Informant Magazine

IntraWeb 3.1 Available


Phoenix Business Enterprises
released version 3.1 of its newly
acquired IntraWeb product.
IntraWeb 3.1 delivers a means for
creating Internet, intranet, and
extranet applications in a quick
and maintainable manner. This
product allows the creation of
scalable Web applications simply
by creating your forms in the
Delphi IDE at design time and
using standard Delphi code; no
HTML or JavaScript is required.
IntraWeb applications only
require an HTML 4.0-compli-

ant browser, and dont require


ActiveX, Java, or special plug-ins.
Other Web-based development tools make you deal with
issues such as state tracking,
CGI scripting, or complex client
configurations. This isnt the case
with IntraWeb 3.1. You simply
create your application using
the IntraWeb component suite
within Delphi, install it on a
server, and then your clients can
access it using a compatible Web
browser. For added flexibility, the
application can also be run locally

as a normal executable, and you


can deploy it as a stand-alone
service or ISAPI DLL.
IntraWeb is currently available
for Delphi 5 and 6 in a Developer or Enterprise version. A
free, fully-functional evaluation
copy is available for download at
http://www.pbe.com.

and documentation tools.


JBuilder supports the full spectrum of development methodologies, offers productivity features
to support extreme programming,
and includes tight integration
with Borland Enterprise Server,
BEA WebLogic, IBM WebSphere, and iPlanet Application
Server. It allows for application
development and deployment on
the Windows, Linux, Solaris, and
Mac platforms.
In addition, JBuilder supports

Java 2, Java 2 Swing/JFC, XML,


Java2D, Java collections, message queue, accessibility APIs,
JavaBeans, JDBC, Enterprise
JavaBeans, JSP/Servlets, serialization, inner classes, remote method
invocation, Java native interface,
Java archives, and more.

Phoenix Business Enterprises


Price: Developer, US$499; Enterprise,
US$599
Contact: pbe@pbe.com
Web Site: http://www.pbe.com

Borland Ships JBuilder 6


Borland Software
Corp. announced
Borland JBuilder 6, the
latest version of their Java
development environment. JBuilder simplifies
Java development and
deployment by increasing developer productivity, allowing customers to
bring their applications
to market faster. Built
on open standards such
as Java 2 Platform, Enterprise Edition (J2EE),
JBuilder allows organizations to adopt the latest
technology innovations
to move their development projects forward
without abandoning
existing investments. JBuilder also
offers optimal flexibility for development and deployment with
support for multiple operating
systems, version control systems,
and application servers.
With JBuilder, developers can
use new, two-way visual Enterprise
JavaBean (EJB) designers to easily
create reusable EJBs. Enterprises
can leverage existing projects with
JBuilders new UML code visualization, refactoring, unit testing,

Borland Software Corp.


Price: Personal, US$54.95; Professional,
US$999; and Enterprise, US$2,999.
Contact: customer-service@borland.com
Web Site: http://www.borland.com

Delphi
T O O L S

New Products
and Solutions

Book Picks
XSLT
Johan Hjelm and Peter Stark
John Wiley & Sons

ISBN: 0-471-40603-1
Cover Price: US$49.99
(311 pages, CD-ROM)
www.wiley.com/compbooks

Applied XML
Alex Ceponkus and Faraz Hoodbhoy
John Wiley & Sons

Peganza Software Announces Pascal Analyzer 1.1


Peganza Software released
version 1.1 of Pascal Analyzer, a Windows program
that helps Delphi and
Pascal developers measure,
check, and document their
source code. Pascal Analyzer
(PAL) is a development
tool that makes software
projects of any size easier
to understand and enables
developers to produce more
flawless and reliable code.
PAL contains numerous
optimization, validation,
and documentation features, which help fine-tune
and manage the development process. It provides
easier maintenance, fewer
errors, improved code quality,
and simpler migration of projects
between programmers. Reports
generated by PAL contain a
wealth of important information
about the source code. This data
will help you better understand
your source code and assist you
in producing code of a higher
quality and reliability.
PAL spots many types of programming bugs and anomalies,
such as unused variables, name
conflicts, and incorrect scope.
In addition to common crossreference reports, PAL produces
class hierarchy lists, lists of side

calls between subprograms), and


Cross-reference Report (where
identifiers are set and referenced).
Reports are output as HTML or
ordinary ASCII. HTML reports
are suitable for Web publishing,
and you may customize the
HTML through style sheets and
optionally use frames.
A free evaluation version is
available for download at http://
www.peganza.com/download.htm.
Peganza Software
Price: US$89 to US$119
Contact: info@peganza.com
Web Site: http://www.peganza.com

DROSTE Software, Inc. Releases SQLWrite V2.6.2


DROSTE Software, Inc.
announced DROSTE SQLWrite
V2.6.2, a productivity tool that
allows you to build and test SQL
statements without having to com-

ISBN: 0-471-34402-8
Cover Price: US$49.99
(474 pages, CD-ROM)
www.wiley.com/compbooks

effects, and warnings about


unused identifiers.
Currently, there are 30 different
types of reports. Alone or combined with each other, these
reports will help you retain control
over your programs not only
during their development, but
throughout their entire lifetime.
Key reports include the Warnings Report (lists possible errors
and anomalies), Code Reduction Report (trims the fat from
your code), Complexity Report
(measures your code), Uses Report
(finds unnecessary units in the
uses lists), Call Tree Report (tracks

pile and rerun your applications.


This product was designed so users
can prototype their SQL queries.
DROSTE SQLWrite V2.6.2 has
an easy to use SQL Editor with

syntax highlighting. Using the


Table Explorer, you can view the
table structure, select fields to
include in the query, and then
paste them into the SQL Editor.
Additional features include
saving and printing SQL scripts,
editing multiple SQL scripts all
at once, making local table and
ODBC connections to any database, exporting query results to
local tables, ASCII, Excel, or
HTML, and drag-and-drop for
opening and editing multiple
SQL scripts quickly. DROSTE
SQLWrite V2.6.2. also supports
updating queries.
DROSTE Software, Inc.
Price: Single user, US$29.95; 10 users,
US$249.95.
Contact: (978) 686-5775
Web Site: http://www.drostesoftware.com

3 February 2002 Delphi Informant Magazine

Sound+Vision
Hot Spots / Component Development / Delphi 4-6

By Victor Hornback

Hot Spots
Part I: Building an Editor; Creating Events

hen you think of hot spots, whats the first thing that comes to mind? Spring Break
in Cancun, Mardi Gras in New Orleans, or maybe BorCon 2002 in Anaheim?
In Web pages, hot spots are regions mapped to an image that detect MouseMove and
MouseClick events, and that usually provide a link to another Web page. In a Delphi
application, you can use hot spots to give your application a more graphical means of
navigation or input. For example, you could use pictures as a type of selection menu, a
way to link the user to Web pages or online help, or a way to launch another form or
application. Once you understand what hot spots are and how they work, youll find it
easy to implement them in Delphi.
This article, and another next month, will take you
through implementing a custom hot-spot component. In Part I, youll learn how to create a hot-spot
editor. In Part II, Ill show you how to create and
capture the events necessary to make a hot spot. Youll
also learn some advanced component-writing tech-

Figure 1: A simple hot spot using an Image component on a form.


4 February 2002 Delphi Informant Magazine

niques, such as registering a custom property editor,


and streaming non-published component data.

What Are Graphic Hot Spots?


As you move your mouse cursor around the screen,
you get various visual signals when youve landed
on something you can click. For example, you
know by convention that the desktop usually has
icons on it and that they launch applications. Similarly, buttons and menu items always look like
you expect them to look, so youre accustomed to
using them no matter what application youre in.
Sometimes, if you hover the mouse over a button,
you see additional hints in the form of a pop-up
text message. However, graphic hot spots and hot
links (textual hot spots) do something a little different: they typically change the mouse cursor to a
symbol of a hand with the index finger extended.
This is the accepted convention for showing that
if you click with the finger cursor, youll probably
jump to a Web page or online help page that relates
to the text or picture you just clicked.
How does the finger know when to appear and
what to do when you click it? The hot spot does all
the work. It simply detects that the mouse is over it
(a MouseMove event) and changes the mouse cursor
temporarily. Then, when you press the mouse
button, the hot spot detects a MouseClick and

Sound+Vision
procedure TForm1.Image1Click(Sender: TObject);
begin
ShellExecute(Handle, // Handle to parent window.
// Pointer to string that specifies operation.
'Open',
// Pointer to file or folder name.
'http://www.borland.com/delphi',
// Pointer to string that specifies
// executable-file parameters.
'',
// Pointer to string that specifies
// default directory.
'',
// Whether file is shown when opened.
SW_SHOWNORMAL);
end;

Figure 2: The Image components OnClick event handler.

executes some code. If youre wondering which Delphi components


can detect MouseMove and MouseClick events, theyre introduced in
TControl, and all visual components descend from TControl.
Armed with that information, you can make a simple hot spot. Open
a new Delphi project and drop an Image component on the form.
Use the Object Inspector to load the Image components Picture
property. (You can find a picture of every Delphi programmers favorite goddess [Athena.bmp] in the \Borland Shared\Images\Splash\
16Color directory; see Figure 1.)
Next, set the Image components Cursor property to crHandPoint. This
is what gives you the finger when you move the mouse cursor over
the image. Finally, define an OnClick event for the Image component
and fill in the code to launch your Web browser to Borlands Delphi
home page. Figure 2 shows the Image components OnClick event
handler that will launch your browser.

Figure 3: Hot-spot editor form.

Before compiling this application, youll also have to add ShellAPI


to your units uses clause in order to use the ShellExecute function.
ShellExecute is a Windows API call that opens a specified file. In this
case, by specifying a URL for the file name, Windows automatically
launches your default Web browser to the specified Web address. Run
your application, move the mouse around, and click on the image. Wow!
It looks like a hot spot. It acts like a hot spot. It must be a hot spot!

To create your hot-spot editor, start with a new Delphi project.


Save the project as HotSpots.dpr, and name the form unit
HotSpotEditForm.pas. Place two Panel components on the form,
and align one to alTop and the other to alBottom. Place a ScrollBox
component in the middle of the form and align it to alClient. Note:
Youre using a ScrollBox so that if the image on which you are
drawing the hot spot is bigger than our form, the ScrollBox will allow
us to view the entire image.

Okay, that was a neat little trick. But what if you want just Athenas
head to be the hot spot? (A more practical example might be a map
of the world, where each country on the map is a different hot spot.)
How do you do that? Obviously, these shapes are highly irregular, and
all your Delphi components are rectangular. Its not a very good fit,
but there is a solution.
Fortunately, the Windows API provides something named regions.
At its base level, a region is simply a dynamically allocated data structure that represents a shape definition. Regions can be mapped to
anywhere on the screen and can even be rendered. More importantly,
regions can define any shape imaginable and can detect the mouse
cursor being over them. So, all you have to do is map out the points
that define your shape (for instance, the outline of Athenas head) and
create one of these Windows regions.
Then, as you move the mouse around and click the mouse buttons,
youll ask the region if the cursor is over it, right? Yes, thats pretty
much it, but mapping Athenas head could be a bit of trick. Have you
ever tried counting pixels? Instead, create an editor that allows you to
5 February 2002 Delphi Informant Magazine

draw the hot spot by using the mouse. Then, you can use that editor
in a custom component that allows you to create a hot spot, and code
what happens when the mouse moves over it.

Creating the Hot-spot Editor Form

Next, place an Image component on the ScrollBox and change the


Cursor property of the Image to crCross. Finally, add a Button to the
upper panel and name it ClearBtn with the label Clear. Then, add a
BitButton to the lower panel, name it CancelBtn, and change its Kind
property to bkCancel. Your editor form should look like Figure 3.

Adding the Necessary Data Structures


Now, you need to add the necessary elements for creating a
Windows region. To create a region, you may choose from several
different functions for creating regions of various shapes. Well
be using CreatePolygonRgn in this example, because a polygon is
the most versatile shape for approximating other shapes. Youll
find this definition for CreatePolygonRgn in the Microsoft Win32
Developers Reference Library:
HRGN CreatePolygonRgn(
CONST POINT *lppt, // Pointer to array of points.
int cPoints,
// Number of points in array.
int fnPolyFillMode // Polygon-filling mode.
);

Sound+Vision
FCurrPt: TPoint;
//
FDrawing: Boolean;
//
FHotSpot: THotSpotRegion;//
FPicture: TPicture;
//
FPoints: THotSpotPoints; //
FPointCount: Integer;
//
FPrevPt: TPoint;
//
FRect: TRect;
//

Location point of the mouse.


Indicates we're done drawing.
Pointer to a Windows region.
Picture to draw a HotSpot on.
Outline as array of points.
# of points added to array.
Last point the mouse was at.
Dimensions of the PaintBox.

Figure 4: Hot-spot editor field definitions.


procedure THotSpotEditFrm.FormCreate(Sender: TObject);
begin
FDrawing := True; // Indicate the mode we are in.
// Procedure that clears all points in FPoints array.
ClearPoints;
// Draw shape transparent in the center.
PaintBox.Canvas.Pen.Mode := pmNotXor;
FPicture := TPicture.Create;
FPicture.LoadFromFile('athena.bmp');
// FRect holds the dimensions of the PaintBox for
// drawing FPicture on it.
FRect.TopLeft.x := 0;
FRect.TopLeft.y := 0;
FRect.BottomRight.x := PaintBox.Width;
FRect.BottomRight.y := PaintBox.Height;
end;
procedure THotSpotEditFrm.FormDestroy(Sender: TObject);
begin
// Clean up dynamically created objects.
DeleteObject(FHotSpot);
FPicture.Free;
end;

Figure 5: Form OnCreate and OnDestroy event code.

procedure THotSpotEditFrm.AddPoint(NewPt: TPoint);


begin
if FPointCount <= 99 then
begin
FPoints[FPointCount].X := NewPt.X;
FPoints[FPointCount].Y := NewPt.Y;
Inc(FPointCount);
end
else
ShowMessage('Maximum number of points reached.');
end;
procedure THotSpotEditFrm.ClearPoints;
begin
FPointCount := 0;
end;
procedure THotSpotEditFrm.RemoveLastPoint;
begin
Dec(FPointCount);
end;

Figure 6: Functions to help manage your array of points.

From this definition, you can deduce that you will need to fill an
array of points with your editor, and keep track of the number of
points youve added. Then, using your array of points, you will be
creating an HRGN, or handle to a region. Add the following type
definitions to your HotSpotEditForm unit:
type
THotSpotRegion = HRGN;
THotSpotPoints = Array[0..99] of TPoint;

6 February 2002 Delphi Informant Magazine

procedure THotSpotEditFrm.ClearBtnClick(Sender: TObject);


begin
if MessageDlg('Do you want to cancel current drawing?',
mtWarning, [mbYes, mbNo], 0) = mrYes then
begin
ClearPoints;
DeleteObject(FHotSpot); // Clear any HotSpot.
FDrawing := True;
// Allow drawing again.
PaintBox.OnPaint(nil); // Clear the PaintBox.
end;
end;
procedure THotSpotEditFrm.CancelBtnClick(Sender: TObject);
begin
Close;
end;

Figure 7: OnClick event handlers for the buttons.


procedure THotSpotEditFrm.PaintBoxMouseMove(
Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
// The current position of the mouse is used to draw
// the shape as the cursor is moved and to add a point
// when the mouse is clicked.
FCurrPt.x := X;
FCurrPt.Y := Y;
if FDrawing then
// Force PaintBox to paint itself.
PaintBox.OnPaint(nil)
else
// Done drawing, so mimic how HotSpot will behave.
begin
if PtInRegion(FHotSpot, X, Y) then
// The mouse is over the HotSpot.
PaintBox.Cursor := crHandPoint
else
PaintBox.Cursor := crCross;
end;
end;
procedure THotSpotEditFrm.PaintBoxMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
// Allow adding points only if we're drawing.
if FDrawing then
begin
// Add points using the left mouse button;
// a right mouse click finishes the drawing.
if Button = mbLeft then
AddPoint(FCurrPt)
else
if (Button = mbRight) and (FPointCount > 2) then
// Finish drawing and create HotSpot region.
begin
FDrawing := False;
FHotSpot := CreatePolygonRgn(FPoints,
FPointCount, WINDING);
// Paint the final HotSpot shape.
PaintBox.OnPaint(nil);
end;
end;
end;

Figure 8: TImage MouseMove and MouseUp event handler code.

Next, in the private declarations section, add the field variables


shown in Figure 4. The use of these field variables will become
apparent once you start coding. For the time being, notice that you
have an FHotSpot of type THotSpotRegion. This is how you will
reference the HRGN created by CreatePolygonRgn. Also, you can see
that you have your array of points (FPoints) and the count of how

Sound+Vision
Its pretty simple stuff so far. Now, make use of those buttons you
added earlier. Define an OnClick event handler for each of the buttons, and code them as shown in Figure 7.
Now for the fun stuff: Draw a hot spot using the mouse. Naturally, you
will need to use the OnMouseMove and OnMouseUp events. You probably only want to draw on the Image component of your form, because
thats where your picture is displayed. Define MouseMove and MouseUp
event handlers for the Image component as shown in Figure 8.
Thats it! Youre done. Now compile it and take it for a test drive.
Make Athenas head into a hot spot (see Figure 9). (Its what every
Delphi programmer has been dying to do.)
Of course, you lose your hot spot as soon as you close the form.
You also havent provided any functionality for clicking on the hot
spot. What you need is a persistent component that can stream
your hot spot from design time to run time, and allow you to
define MouseOver and MouseClick events. The second article in
this two-part series will show you how to do just that. Youll be
entering the brave world of advanced component-writing techniques. The good news is that youve already done the hard part
in creating the hot-spot editor.

Conclusion

Figure 9: Editor with Athenas head outlined as a hot spot.

many points you added to that array (FPointCount). Note: Youre


using a static array of 100 points, for simplicity and because 100
points is sufficient to describe most complex shapes; however, you
can use more or fewer points in your array.

Putting It All Together

Hot spots are simply Windows regions that detect MouseOver


events. You can use them to make graphic images behave like Web
page hot spots or hot links. These regions can represent almost
any shape imaginable. You can create regions easily by using Windows API functions in Delphi. Part II article will show you how
to use the hot-spot editor you created in this article in a custom
Delphi hot-spot component.
The hot-spot editor and example project referenced in this article are
available on the Delphi Informant Complete Works CD located in
INFORM\2002\FEB\DI200202VH.

Now, lets get some housekeeping out of the way. Define OnCreate
and OnDestroy event handlers for your form and add the code shown
in Figure 5.
Now, youre ready to get down to the business of drawing a hot
spot. First, you can take care of the grunt work by creating three
procedures to help manage your array of points. Add the procedures shown in Figure 6 to the private declarations section.

7 February 2002 Delphi Informant Magazine

Victor Hornback lives in Colorado Springs, CO, where he does Delphi consulting
work. Readers may contact him at victorhornback@yahoo.com.

On the Net
WebSnap / JScript / HTML / Delphi 6 Enterprise

By Corbin Dunn

A WebSnap Online Survey


Part II: Implementation Details

his is the second of a two-part article series that demonstrates how to create an
online survey using the new WebSnap capabilities of Delphi 6 Enterprise edition. Part I article introduced WebSnap by comparing it to the Web Broker technology
available in previous versions of Delphi. It also described how to build the InterBase
database used by the sample application and the construction of the survey application itself.
<html>
<head>
<title>
<%= Page.Title %>
</title>
</head>
<body bgcolor="#FFFFFF">
<h1><%= Application.Title %></h1>
<h2><%= Page.Title %></h2>
<table cellspacing="0" cellpadding="0">
<td>
<% // This code was copied from the default
// script generated by WebSnap.
e = new Enumerator(Pages)
s = ''
c = 0
for (; !e.atEnd(); e.moveNext())
{
// Don't show the results page.
if (e.item().Name == 'ResultsPage')
continue;
if (e.item().Published)
{
if (c>0) s += '&nbsp;|&nbsp;'
if (Page.Name != e.item().Name)
s += '<a href="' + e.item().HREF +
'">' + e.item().Title + '</a>'
else
s += e.item().Title
c++
}
}
if (c>1) Response.Write(s)
%>
</td>
</table>

Figure 1: Header.html, which will be included by all pages in


this application.
8 February 2002 Delphi Informant Magazine

This month well finish building the sample


application. First, well add server-side scripting to
the main page, then get into the implementation
details of creating the survey pages.

Adding Script to MainPage


One of the greatest powers of WebSnap is the ability to write server-side scripting. This gives the Web
developer complete control of exactly how the user
interface is presented through a browser. For ease
of use, well write one header file that all the pages
in this application will include. Create a new text
file with the contents of the listing in Figure 1 and
name it header.html.
By default, WebSnap applications use JScript as
the underlying script engine, and the association is
made on a per-page basis. Each Web page module
has a PageProducer property, and the corresponding
PageProducer has a ScriptEngine property. Selecting
the ScriptEngine property should give you a list
of currently installed script engines. At this time,
WebSnap fully supports only the JScript engine.
Developers are working on VBScript and PerlScript
support.
To differentiate HTML from script, WebSnap
takes the ASP approach and encloses all script
in <% %> tags. Inside of header.html, the first
script you see is <%= Page.Title %>. Having an
equals sign immediately after the % is a shortcut
for <% Response.Write(Page.Title) %>. This
immediately begs questions about where all these

On the Net
variables (such as Page and Response) come from, and what
methods and properties they have. Jim Tierney, a WebSnap architect at Borland, has created a comprehensive listing of everything
that you can do with server-side scripting. Its available at
http://community.borland.com/article/0,1410,27467,00.html.
Response and Page are Global Objects, accessible in any script. The
most important thing to remember is that Response.Write allows you
to output results to the generated HTML page from inside of script.
Next, create another text file named footer.html with the following content:
<P><i>WebVotes - Built with WebSnap and Delphi 6</i>
</body>
</html>

The purpose of using a footer and header is so you only have to change
one file to have your entire Web site show the results.
After you have created the header and footer, open the MainPg.pas unit
and select the MainPg.html tab from the bottom of the code editor. Delete
the default, generated script and add the contents from Figure 2.
First, notice the reference variables created, such as:
var survey = Modules.WebData.SurveyAdapter

These give us access to the adapters on the Web data module named
WebData. A key property on all adapters is Errors. You may recall the
following code, added to the OnExecute event handler of AddVoteAction:
except
on E: Exception do
AddVoteAdapter.Errors.AddError(E);

You create an Enumerator to access the Errors from script. An Enumerator


is a standard JScript class that allows iteration through an object. From
the JScript reference, available at http://msdn.microsoft.com/scripting,
we find Enumerator has the following methods:
atEnd returns a Boolean value indicating if the Enumerator is at the
end of the collection;
moveFirst resets the first item in the collection as the current item;
moveNext makes the next item the current item in the collection;
item returns the current item in the collection.
Enumerating through all the errors is as simple as:
var e = new Enumerator(survey.Errors)
for (; !e.atEnd(); e.moveNext())
Response.Write('<li>' + e.item().Message)

Its key to realize that what the item function returns depends on
what the Enumerator is enumerating through. In this example,
we are enumerating through survey.Errors, so e.item will return
AdapterErrorsType, which has a Message property.
Next, another enumerator is created with which to walk through all the
master records in the table SURVEY:
var surveyEnum = new Enumerator(survey.Records);
for (; !surveyEnum.atEnd(); surveyEnum.moveNext())
{
%>

9 February 2002 Delphi Informant Magazine

<!--#include file="header.html"-->
<P>
Welcome! Feel free to vote on any of these surveys.
<%
// Create some shortcuts to commonly used adapters.
// This greatly improves performance.
var survey = Modules.WebData.SurveyAdapter;
var surveyData = Modules.WebData.SurveyDataAdapter;
var addVote = Modules.WebData.AddVoteAdapter;
// Display any errors that may have happened.
var e = new Enumerator(survey.Errors)
for (; !e.atEnd(); e.moveNext())
Response.Write('<li>' + e.item().Message)
// Enumerate through all the surveys.
var surveyEnum = new Enumerator(survey.Records);
for (; !surveyEnum.atEnd(); surveyEnum.moveNext())
{
%>
<form method="post">
<a href="<%=Pages.ResultsPage.HREF%>?SURVEY_ID=
<%=survey.SURVEY_ID.Value %>">
<b><%=survey.DESCRIPTION.DisplayText%></b></a><br>
<input type="hidden"
name="__act"
value="<%=addVote.AddVoteAction.AsFieldValue%>">
<input type="hidden"
name="<%=addVote.SurveyId.InputName%>"
value="<%=survey.SURVEY_ID.Value%>">
<%
// Add all the details in another sub-table.
var dataRecords = new Enumerator(surveyData.Records);
for (; !dataRecords.atEnd(); dataRecords.moveNext())
{
%>
<input
type="radio"
name="<%=addVote.SurveyDataId.InputName%>"
value="<%=surveyData.SURVEY_DATA_ID.Value%>">
<%=surveyData.DESCRIPTION.DisplayText%>
<%
}
%>
<input type="Submit" value="Vote">
</form>
<%
} // for
%>
<!--#include file="footer.html"-->

Figure 2: The script and HTML inside of MainPg.html.

Notice the closing of the script tag allows us to easily embed HTML
inside the enumeration. The first thing to do is add an HTML form tag
to allow posting of a new vote. In order for us to tell WebSnap what we
are updating, there is the special hidden __act (action) input:
<input type="hidden"
name="__act"
value="<%=addVote.AddVoteAction.AsFieldValue%>">

Setting __act to a value of addVote.AddVoteAction.AsFieldValue links


the form to the OnExecute code of the WebData.AddVotesAdapters
action AddVoteAction. AsFieldValue is a special function that was made
to encode an AdapterActionType into a hidden input field. On the
MainPage, there is an AdapterDispatcher component, which decodes
the __act value sent from a Web site, dispatches the request to the
correct AdapterAction, and calls its corresponding OnExecute event.
The first thing our AddVoteActions OnExecute code does is check the
value of SurveyId.ActionValue. To pass this information back to the
application, we use another hidden field:

On the Net
ing data fields, such as SURVEY_ID. Inside script, all the fields
are of AdapterFieldType. Value is a procedure that gives the underlying value of a given AdapterFieldType.
Other useful things you can do with AdapterFieldTypes include
limiting who can view the field, such as:
<% if (survey.SURVEY_ID.CanView) { %>
<!-- Actually write out the SURVEY_ID,
since the user can view it -->
<% } // end if %>

See the online Help files, or the aforementioned server-side scripting link, for more information about what properties are available
for this type.

Figure 3: The ModifySurveyPage as seen from a Web browser.


<!-- Add the ability to add another survey -->
<form method="post">
<%
// Go back to the first record to be able to insert.
surveyEnum.moveFirst();
survey.Mode="Insert";
// Writing the hidden fields tells the adapter
// in which mode it should run.
if (survey.HiddenFields != null)
survey.HiddenFields.WriteFields(Response)
if (survey.HiddenRecordFields != null)
survey.HiddenRecordFields.WriteFields(Response)
%>

Figure 4: The last form, which adds a new survey.

The next thing in the source code is a nested enumeration on


surveyData.Records to go through the detail records. For each
record, a radio-button input is created:
<input
type="radio"
name="<%=addVote.SurveyDataId.InputName%>"
value="<%=surveyData.SURVEY_DATA_ID.Value%>">
<%=surveyData.DESCRIPTION.DisplayText%>

The name thats going to be associated with this input is SurveyDataId


in the Web data module. The value it needs to contain is the
nested DataSets SURVEY_DATA_ID key. Again, this information
is used in the OnExecute method of the AddVoteAction, with the
SurveyDataId.ActionValue. This code is executed when the form is
submitted by clicking the submit button:
<input type="Submit" value="Vote">.

Creating the ModifySurveyPage


We now have the ability to vote for a particular survey, but no
way to add new surveys. We need a way to add additional surveys,
as seen in Figure 3.
Select File | New | Other, and select WebSnap Page Module from the
WebSnap tab. In the New WebSnap Page Module dialog box, set
the HTML Template to be Blank, and set the Page Name to be
ModifySurveyPage. Leave everything else as the defaults and click
OK. Save the generated file as ModifySurveyPg.pas, and click on
the ModifySurveyPg.html button at the bottom of the code editor.
The source for ModifySurveyPg.html is shown in Listing One (on
page 12). It has some similar enumerations to display the current
surveys in the database. The additional changes deal with using a
DataSetAdapter components built-in actions. Take a closer look at
the last form, which adds a new survey (see Figure 4).
Figure 5: ResultsPage as seen in a Web browser.
<input type="hidden"
name="<%=addVote.SurveyId.InputName%>"
value="<%=survey.SURVEY_ID.Value%>">

When the form is submitted, SurveyId.ActionValue will contain


the SURVEY_IDs Value. The InputName function encodes
SurveyId so it can be used as an HTML input elements name.
Because survey is a link to Modules.WebData.SurveyAdapter, and
SurveyAdapter is a DataSetAdapter, we can access all the underly10 February 2002 Delphi Informant Magazine

The first thing to do is to move back to the first record with the
Enumerator. The DataSetAdapter components Mode is then set to
be Insert, preparing it for a new record. Next, the HiddenFields and
HiddenRecordFields are written. All Web applications are stateless;
however, by writing out HiddenFields, WebSnap is able to maintain
state, such as the current adapter mode.
As with all forms, there needs to be an __act input to tell the application what action to perform. As mentioned earlier, all DataSetAdapter
components have some default actions, including Edit, New, Cancel,
and Apply. In this case, we want to apply the changes (a new insert)
to the database and use the AsFieldValue property to set the value of

On the Net
the __act. All actions are of AdapterActionType, and AsFieldValue is the
key for WebSnap applications to determine which adapter action
to call:
<input type="hidden" name="__act"
value="<%=survey.Apply.AsFieldValue%>'">

Finally, in order to set the field values in the database, use the
survey.DESCRIPTION AdapterFieldType:
Create a New Survey:
<input type="text"
size="60"
name="<%=survey.DESCRIPTION.InputName%>">
<input type="Submit" value="Add">
</form>

As mentioned before, InputName provides a name for an HTML


input element, and links it to the underlying AdapterField when
the page is submitted.

Creating the ResultsPage


The last page demonstrates the use of server-side scripting to
create HTML dynamically, and to view the survey results, as
shown in Figure 5.
Select File | New | Other, and select WebSnap Page Module from
the WebSnap tab. In the New WebSnap Page Module dialog box,
set the HTML Template to be Blank, and set the Page Name to be
ResultsPage. Then save the generated file as ResultsPg.pas.
We need to determine the total votes for a given survey in order
to find percentages for each survey item. Add WebDataMod to the
lower uses list, drop an IBQuery component from the InterBase
tab to the ResultsPage module, and make the following changes:
Set the Database property to be WebData.MainDatabase.
Set the DataSource property to be WebData.dtsrcSurvey.
Set the Name property to be qryTotalVotes.
Then set the SQL property to:
SELECT SUM(Votes) Total_Votes
FROM Survey_Data
WHERE Survey_ID = :Survey_ID

Add a DataSetAdapter component to ResultsPage and make the


following changes:
Set its Name property to be TotalVotesAdapter.
Set its DataSet property to be qryTotalVotes.
The ResultsPage module needs to display results for one particular
record in the database. We need a way to locate a given record,
such as using a traditional query parameter passed in the URL.
The source for MainPg.html contains a link to ResultsPage with:
<a href="<%=Pages.ResultsPage.HREF%>?SURVEY_ID=
<%=survey.SURVEY_ID.Value%>">...</a>

In any Web application, query parameters are the name=value


pairs passed at the end of a URL following a question mark. In
this case, SURVEY_ID is set to the current SURVEY_ID field
while enumerating through all the records. When ResultsPage is
invoked, we need to locate this record. The perfect place to do this
is in the OnBeforeDispatchPage event handler, as seen in Figure 6.
11 February 2002 Delphi Informant Magazine

procedure TResultsPage.WebPageModuleBeforeDispatchPage(
Sender: TObject; const PageName: string;
var Handled: Boolean);
begin
// Go to the correct record.
if Request.QueryFields.Values['SURVEY_ID'] <> '' then
with WebData do begin
tblSurvey.Open;
tblSurveyData.Open;
qryTotalVotes.Open;
if not tblSurvey.Locate('SURVEY_ID',
Request.QueryFields.Values['SURVEY_ID'], []) then
raise Exception.CreateFmt(
'Failed to locate survey id %s',
[Request.QueryFields.Values['SURVEY_ID']]);
end
else
raise Exception.Create(
'No Survey ID passed to the results page');
end;

Figure 6: Using the OnBeforeDispatchPage event to locate the


current master record.
<!--#include file="header.html"-->
<%
var survey = Modules.WebData.SurveyAdapter;
var surveyData = Modules.WebData.SurveyDataAdapter;
var totalVotes = TotalVotesAdapter;
// WriteResult is a helper function to generate an
// HTML table with a row representing the percentage
// of votes for a given option.
function WriteResult(votes, total)
{
var percent = 100 * votes/total;
var rest = 100 - percent;
Response.Write(
'<table width="80%">' +
'<tr><td width="' + percent +
'%" bgcolor="Red">&nbsp;</td>' +
'<td width="' + rest + '%">' +
'(' + votes + '/' + total + ')</td>' +
'</tr>' + '</table>');
}
%>
<b><%=survey.DESCRIPTION.DisplayText%></b><br>
<%
var dataRecords = new Enumerator(surveyData.Records);
for (; !dataRecords.atEnd(); dataRecords.moveNext())
{
Response.Write(surveyData.DESCRIPTION.DisplayText);
WriteResult(surveyData.VOTES.Value,
totalVotes.TOTAL_VOTES.Value);
}
%>
<!--#include file="footer.html"-->

Figure 7: Source for ResultsPg.html.

Select the ResultsPg.html tab from the bottom of the code editor,
and set the HTML to what is shown in Figure 7. Notice the use of
a custom JScript function, WriteResult, to create an HTML table
with a width based on a percentage of the total votes.
The beauty of this is that the HTML designer can easily modify
the HTML script to change the appearance of the application
instantly, with no recompilation needed.
The last thing to do is uncomment the Response.SendRedirect
code in WebDataMod.pas (if you commented it out earlier). Add

On the Net
ResultsPg to the uses list of WebDataMod to get the application
to compile. Run the application to properly register the Web
App Debugger executable, and select Tools | Web App Debugger to
start the test server. You should now be able to browse to http:
//localhost:1024/WebVote.WebVote and view the results of your
labor.
Of course, by default, there will be no surveys in the database, so
select the ModifySurveyPage link and add a survey and some options.
Then, you can go back to the main page and do some voting.
The project and database referenced in this article are available on the
Delphi Informant Complete Works CD located in INFORM\2002\FEB\
DI200202CD.

<td><b>Description</b></td>
<td><b>Votes</b></td>
</tr>
<%
}
for (; !dataRecords.atEnd(); dataRecords.moveNext())
{
%>
<tr>
<td><%=surveyData.SURVEY_DATA_ID.DisplayText%></td>
<td><%=surveyData.DESCRIPTION.DisplayText%></td>
<td><%=surveyData.VOTES.DisplayText%></td>
</tr>
<%
} // for
%>
<!-- Add the ability to add another survey data item -->
<tr>
<td colspan="3">
<form method="post">
<%
dataRecords.moveFirst();
surveyData.Mode="Insert";
if (surveyData.HiddenFields != null)
surveyData.HiddenFields.WriteFields(Response)
if (surveyData.HiddenRecordFields != null)
surveyData.HiddenRecordFields.WriteFields(
Response)
%>
<input type="hidden" name="__act"
value="<%=surveyData.Apply.AsFieldValue%>'">
<input type="hidden"
name="<%=surveyData.SURVEY_ID.InputName%>"
value="<%=survey.SURVEY_ID.DisplayText%>">
<input type="hidden"
name="<%=surveyData.VOTES.InputName%>"
value="0">
New survey option:
<input type="text" size="60"
name="<%=surveyData.DESCRIPTION.InputName%>">
<input type="Submit" value="Add">
</td>
</form>
</tr>
</table>
</blockquote>

Corbin Dunn has worked for Borland Software Corporation for three years. He
started in Developer Support and is now a quality-assurance engineer for the
rapid-application-development product group, where hes currently working on
WebSnap. Readers may reach him at cdunn@borland.com.

Begin Listing One Source for ModifySurveyPg.html


<!--#include file="header.html"-->
<%
// Create some shortcuts to commonly used adapters.
// This greatly improves performance.
var survey = Modules.WebData.SurveyAdapter;
var surveyData = Modules.WebData.SurveyDataAdapter;
// Display any errors that may have happened.
var e = new Enumerator(survey.Errors)
for (; !e.atEnd(); e.moveNext())
Response.Write('<li>' + e.item().Message)
var e = new Enumerator(surveyData.Errors)
for (; !e.atEnd(); e.moveNext())
Response.Write('<li>' + e.item().Message)
%>
<br>Current Surveys in the database:
<%
// Enumerate through all the master records.
var surveyEnum = new Enumerator(survey.Records);
for (; !surveyEnum.atEnd(); surveyEnum.moveNext())
{
%>
Survey ID: <b><%=survey.SURVEY_ID.DisplayText%></b>,
Name: <b><%=survey.DESCRIPTION.DisplayText%></b>
<br>
<blockquote>
<table border="1">
<%
// Add all the details in another, sub-table.
var dataRecords = new Enumerator(surveyData.Records);
// Add a header, if not at the end of the records.
if (!dataRecords.atEnd())
{
%>
<i>Options in the survey:</i>
<tr>
<td><b>Survey Data Id</b></td>

12 February 2002 Delphi Informant Magazine

<%
} // for
%>
<!-- Add the ability to add another survey -->
<form method="post">
<%
// Go back to the first record to be able to insert.
surveyEnum.moveFirst();
survey.Mode="Insert";
// Writing the hidden fields tells the adapter what
// mode it should be in.
if (survey.HiddenFields != null)
survey.HiddenFields.WriteFields(Response)
if (survey.HiddenRecordFields != null)
survey.HiddenRecordFields.WriteFields(Response)
%>
<input type="hidden" name="__act"
value="<%=survey.Apply.AsFieldValue%>'">
Create a New Survey:
<input type="text"
size="60"
name="<%=survey.DESCRIPTION.InputName%>">
<input type="Submit" value="Add">
</form>
<!--#include file="footer.html"-->

End Listing One

The API Calls


Windows API / Error Handling / Delphi 2-6

By Marcel van Brakel

Windows API Error Handling


Part II: Retrieving Messages from a Message DLL

rror detection and handling play an extremely important role when interfacing
with the Windows API. In Part I, we saw how to detect error conditions after calling a Windows API function, and how to retrieve a message text describing the error.
This month, we conclude the series with a look at retrieving messages from a specific
message DLL, setting the last-error code, error modes, and Delphis approach to error
handling.
const
// Definition of a Lan Manager error code.
NERR_BASE = 2100;
NERR_NetNotStarted = (NERR_BASE + 2);
procedure TForm1.Button3Click(Sender: TObject);
const
Flags = FORMAT_MESSAGE_FROM_HMODULE or
FORMAT_MESSAGE_ALLOCATE_BUFFER;
// Message for Lan Manager exist in netmsg.dll.
NetMsg = 'netmsg.dll';
var
DLL: HMODULE;
Msg: PChar;
begin
// Simulate a Lan Manager error, see next section
// for SetLastError.
SetLastError(NERR_NetNotStarted);
// Retrieve the message text associated with the error.
DLL := LoadLibraryEx(PChar(NetMsg), 0,
LOAD_LIBRARY_AS_DATAFILE);
if DLL <> 0 then
if FormatMessage(Flags, Pointer(DLL), GetLastError,
0, @Msg, 0, nil) > 0 then
begin
ShowMessage(Msg);
LocalFree(Cardinal(Msg));
FreeLibrary(DLL);
end
else
ShowMessage('FormatMessage failed');
end;

Figure 1: Retrieving messages from a specific message DLL.


13 February 2002 Delphi Informant Magazine

Retrieving Messages from a Specific


Message DLL

Its quite possible that the message text associated


with an error code wont be available in one of
the system DLLs that FormatMessage searches by
default. Examples of this are the Lan Manager
error codes under Windows 9x, and the WinSock
error codes under Windows NT. In this case, you
can still use FormatMessage, but youll have to tell
the function where it can find the message text by
supplying a handle to the correct message DLL as
the lpSource parameter.
Figure 1 demonstrates how to do this. First,
we need to use the LoadLibraryEx function to
obtain a handle to the DLL. Specifying the
LOAD_LIBRARY_AS_DATAFILE flag causes the
system to load the DLL as if it were a normal data file,
i.e. without attempting to call the DLLs initialization
entry point, or performing import fix-ups. Then, we
can supply this handle as the lpSource parameter of
FormatMessage. In addition, we need to replace the
FORMAT_MESSAGE_FROM_SYSTEM flag with
FORMAT_MESSAGE_FROM_HMODULE to tell
the function to use the supplied handle.
If youve looked at the declaration of FormatMessage
carefully, youve probably noticed the dwLanguageId
parameter. This parameter can be used to specify the

The API Calls


language of the message text that FormatMessage should return. In the
examples, Ive passed zero for this parameter, which causes the function
to return the message in the default language (see the Platform SDK
documentation, available online at http://msdn.microsoft.com, for
details). You can, however, pass a locale for this parameter (using the
MAKELANGID macro) to retrieve the message in a different language. For that to work, the message DLL must contain the message
text for the requested language. Otherwise, the function will fail with
the ERROR_RESOURCE_LANG_NOT_FOUND error.

Setting the Last-error Code


The last-error code is used throughout the Windows API to report error
information, but its not limited to the Windows API. You can use
this mechanism to report errors yourself. You can do so by using the
SetLastError and SetLastErrorEx functions, which are the same functions
the Windows API uses to set the last-error code:
procedure SetLastError(dwErrCode: DWORD); stdcall;
procedure SetLastErrorEx(dwErrCode, dwType: DWORD);
stdcall;

Because the second parameter for SetLastErrorEx is currently


unused, both functions are identical for now. You can use any
of the predefined error codes from WinError.pas, or, if none of
them suit your needs, you can create your own. Refer to Figure
2 in Part I for an explanation of how error codes should be
constructed. When you decide to construct your own error codes,
the important thing to remember is to set the Customer bit
(bit 29) to 1, so you can distinguish your error codes from
Windows API error codes. If you decide to roll your own error
codes, you will want to provide error messages your users can
retrieve by using FormatMessage, as shown in Figure 1. To do
so, you will need to build a message-table resource into your
executable. (Although building a message-table resource isnt
difficult, it is beyond the scope of this article.)
Besides using SetLastError for reporting custom errors, the function is useful in another common scenario. That is when youre
writing a utility routine that, at some point, detects an error but
has to perform some cleanup before it can exit. To preserve the
error code, you must either ensure that none of the functions you
call to perform the cleanup overwrites the last error, or you must
temporarily save the last error in a local variable and reset the
error code again after the cleanup. Figure 2 demonstrates the latter
approach, which is usually the only option. Because this function
preserves the last-error code, the caller can determine why the
routine failed by examining the result of GetLastError. Youd probably code this differently, possibly with a try..finally block, but the
concept remains the same.

Error Handling the Delphi Way


In Part I, I mentioned how exception handling is often the preferred
way of handling errors. It would be fairly easy to map the last-error
code paradigm used by the Windows API to exceptions. All you need
to do is check the return value of the API function and raise a custom
exception in case of failure. This exception class could call GetLastError automatically, followed by FormatMessage, and store the result of
both as properties of the exception object for later retrieval or display.
The designers of the Delphi RTL thought this would be useful as
well, so they encapsulated it for us.
The RTL defines an exception class specifically for Windows API
errors, EOSError. Although you could raise an exception of this
14 February 2002 Delphi Informant Magazine

function MyUtilityRoutine: Boolean;


var
LastError: DWORD;
F: THandle;
P: Pointer;
begin
Result := False;
F := CreateFile('myfile.txt', 0, 0, nil,
OPEN_EXISTING, 0, 0);
if F <> INVALID_HANDLE_VALUE then begin
P := VirtualAlloc(nil, 4096, MEM_COMMIT,
PAGE_READWRITE);
if P = nil then begin
// Before we can exit we must close the file to
// prevent a resource leak. However, doing so
// might overwrite the last-error code which we
// need to propogate to the caller, so it can
// determine the reason for the failure.
LastError := GetLastError;
CloseHandle(F);
SetLastError(LastError);
Exit;
end;
// Do something amazing with the file
// and memory buffer.
VirtualFree(P, 0, MEM_RELEASE);
CloseHandle(F);
Result := True;
end;
end;

Figure 2: Preserving the last-error code beyond cleanup.

class explicitly, its uncommon to do so. Instead, you will use the
helper routine RaiseLastOSError. This function, which takes no
parameters, raises an EOSError exception for you, and takes care
of initializing the Message and ErrorCode properties of the raised
EOSError exception object. Using this function, transforming
error codes into exceptions becomes a snap:
if not SomeWindowsApiFunction then
RaiseLastOSError;

If you refer back to Figure 1, you see there also is a class of functions that do not set the last-error code, but instead return the
error code directly as the function result. The trick in this case is
to call SetLastError first, then call RaiseLastOSError:
var
R: DWORD;
begin
R := SomeApiFunction;
if R <> 0 then begin
SetLastError(R);
RaiseLastOSError;
end;
end;

The Delphi engineers didnt stop with RaiseLastOSError, however.


Although the Windows API uses various types to indicate failure
(see Figure 1 in Part I), the most common type is a simple Boolean
return value. To relieve you from having to write those tedious ifnot-then-RaiseLastOSError statements, the Delphi RTL provides a
function that does this for you: Win32Check. Using this function,
you can replace the RaiseLastOSError code with the semantically
identical code:
Win32Check(SomeWindowsApiFunction);

The API Calls


function SetErrorMode(uMode: UINT): UINT; stdcall;
function DiskInDrive(Drive: Char): Boolean;
var
ErrorMode: Cardinal;
begin
Result := False;
if Drive in ['a'..'z'] then
Dec(Drive, $20);
{ Try to access the drive. It doesn't really matter how
we access the drive; calling DiskSize is more or less
a random choice. The call to SetErrorMode supresses
the system-provided error dialog box, if there is no
disk in the drive and causes the to DiskSize function
to fail instead. }
ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
Result := DiskSize(Ord(Drive) - $40) <> -1;
finally
SetErrorMode(ErrorMode);
end;
end;

Figure 3: Declaration and usage of SetErrorMode.


Value

Action

Use the system default, which


is to display all error dialog
boxes.
SEM_FAILCRITICALERRORS
The system doesnt display the
critical-error-handler message
box. Instead, the system sends
the error to the calling process.
SEM_NOALIGNMENTFAULTEXCEPT Itanium or RISC: The system
automatically fixes memory
alignment faults, and makes
them invisible to the application. It does this for the calling
process and any descendant
processes. This value has no
effect on x86 processors.
Windows XP: After this value is
set for a process, subsequent
attempts to clear the value are
ignored.
SEM_NOGPFAULTERRORBOX
The system doesnt display the
general-protection-fault
message box. This flag should
only be set by debugging
applications that handle
general protection faults
themselves with an exception
handler.
SEM_NOOPENFILEERRORBOX
The system does not display a
message box when it fails to
find a file. Instead, the error is
returned to the calling process.
Figure 4: Process error modes.

If SomeWindowsApiFunction returns False, Win32Check raises an


EOSError exception. If it returns True, so does Win32Check. Unfortunately, Borland didnt overload this function to work with other
common return types.
The RaiseLastOSError function and the EOSError exception class are the
Delphi 6 equivalents of RaiseLastWin32Error and EWin32Error, respec15 February 2002 Delphi Informant Magazine

tively, from previous Delphi versions. The use of RaiseLastWin32Error


and EWin32Error is marked deprecated by Borland, so you should start
using these new ones in your projects, especially if theres a possibility
youll ever need to port your code to another OS, such as Linux.

Error Modes
A series about error handling wouldnt be complete without at least
mentioning error modes. Each process has an error mode associated
with it that determines whether the system handles certain types of
errors by informing the user, or if the process itself is allowed to
handle these. Examples of such errors are unhandled exceptions, findfile errors, and data misalignment.
The most common of these is the find-file error. This error
occurs, for example, when you attempt to read from a floppy
disk, but theres no floppy in the drive. By default, the system will
display a dialog box informing the user of this fact and fail the
operation. Sometimes, however, this isnt the behavior youd like
to see in your applications. Instead of the default error dialog box,
you might prefer some other course of action, such as displaying
your own dialog box. To override the display of the error dialog
box by Windows, use the SetErrorMode function to set the error
mode for the process. Figure 3 shows how this is done.
Figure 4 lists the possible values of the uMode parameter and their
meanings. The function returns the previous error mode that is useful,
so you can restore it at a later point as demonstrated in Figure 3. Such a
feature is especially important for general-purpose library routines. One
word of caution, though: The error mode is a process-wide setting. This
means that when a thread changes the error mode, it changes for all other
threads in the same process. For this reason, youll want to implement
some kind of access policy when changing the error mode.

Conclusion
Error detection and handling play an extremely important role when
interfacing with the Windows API. In this two-part series, Ive shown
you how to detect error conditions after calling a Windows API
function, how to retrieve a message text describing the error, and the
provisions the Delphi RTL has to make error handling as painless as
possible. Ive also shown how you can control how some Windows
API functions handle error conditions. How you handle error conditions after youve detected them is highly application- and domainspecific. It could mean retrying a function call, falling back on the use
of other Windows API functions, or simply aborting your application
after informing the user. In any case, you now know how to detect
any error and get some useful information about it.
Accompanying this article are two projects: ErrorDemo and ErrorLookup. The first one contains all the code shown in this series;
the second contains an IDE wizard that allows you to look up the
message text associated with any Windows API error code. See the
included readme.txt for installation details.
The projects and files referenced in this article are available on the
Delphi Informant Complete Works CD located in INFORM\2002\
FEB\DI200202MV.

Marcel van Brakel is an independent contractor specializing in Windows systemlevel development using Delphi. Hes a contributing member of Project JEDI (http:
//www.delphi-jedi.org) and is the founder of the JEDI Code Library. You can write
to him at brakelm@chello.nl, or visit members.chello.nl/m.vanbrakel2.

Columns & Rows


InterBase 6.5 / SQL

By Bill Todd

InterBase 6.5
XML Support and a Whole Lot More

nterBase is on the move again with an impressive update that adds significant new
features. InterBase has always been able to support large databases, but in the
past the way it did this was a holdover from the days when hard disk partitions were
limited to a maximum size of either 2 or 4GB. Creating a large database required
spreading it across multiple files and specifying a maximum size for each file except
the last. While this wasnt a serious limitation, it required more work and planning
to create a large database. You had to anticipate the maximum size of the database
and create enough files to accommodate that size. It also made moving the database
to a different location on the server difficult, because the full path to each of the
secondary files was contained within the database. The only way to move a multi-file
database was to perform a backup and restore.
InterBase 6.5 makes managing large databases
much easier by implementing 64-bit file I/O
on the Windows, Linux, and Solaris platforms.
Now your InterBase databases can expand to
the full size of the hard drives on which they
reside. You still have the flexibility to create a
multi-file database if you need to spread the
database across multiple drives, but fewer files
will be required since each file can expand to
fill the drive that hosts it. Note that a 64-bit
file I/O is only supported by Linux version 2.4
kernels. If you install InterBase 6.5 on an earlier
version of Linux, it will behave exactly the same
as InterBase 6.
This is the first step in enabling InterBase to
easily handle larger databases and transaction
volumes. The second step will be the addition of
symmetrical multiprocessing (SMP) support in
InterBase 7.0.

Canceling Queries
One problem you can encounter with any database is a query that takes much longer than you
16 February 2002 Delphi Informant Magazine

expected. This can be a common problem in a


system where users are allowed to construct their
own queries against a large database. InterBase
6.5 provides a way to cancel a SQL statement
thats executing via a call to the InterBase API.
The thread thats executing the statement cannot
cancel it, so if you want to provide users with
the ability to cancel an executing statement, you
need to run it in a background thread. When
you cancel a statement, its as though the statement encountered an error and terminated. Anything done by that statement is undone. Note,
however, that canceling a statement has no effect
on its transaction. The transaction is still active,
and you can commit or roll back as you wish.
Figure 1 shows the code to cancel a running statement that was executed using the InterBase Express
IBQuery component. The unit that contains this
code must include IBHeader, IB, IBIntf, and
IBExternals in its uses clause. The first statement
in the StopQuery method retrieves the StatementHandle property from the IBQuery component
thats running the statement you want to stop. The

Columns & Rows


procedure TfrmMain.StopQuery;
var
StatementHandle: TISC_STMT_HANDLE;
ISC_Result: ISC_STATUS;
begin
StatementHandle := dmMain.ibqTest.StmtHandle;
ISC_Result := isc_dsql_free_statement(StatusVector,
@StatementHandle, DSQL_CANCEL);
if ISC_Result > 0 then
IBDatabaseError;
end;

Figure 1: Canceling a query.

second statement in this method calls the InterBase API function


isc_dsql_free_statement, passing the StatusVector variable provided by
InterBase Express and the address of the statement handle variable.
The third parameter is a constant that specifies that the statement
should be stopped. If the value returned by isc_dsql_free_statement is
greater than zero, the call to the InterBase Express IBDatabaseError
procedure retrieves the InterBase error code and message, and raises
an exception. Note that you cannot cancel a statement thats executed
using the dbExpress or BDE components, because these components
dont provide access to the statement handle.
You can also cancel a statement that youre executing in ISQL.
After starting the statement, just press CC to cancel it.

Migrating to InterBase 6.5


If you revoke all access to the system tables using SQL REVOKE
or the BLINDMETA.SQL script, users running earlier versions of
InterBase wont be able to access the database at all. The InterBase
6.5 engine has been modified to allow it to access the system tables
even though the user doesnt have rights to these tables. This capability doesnt exist in earlier versions of the InterBase engine. If you
want to send a database to someone using InterBase 6, youll have to
run READMETA.SQL against it first and be aware that if the user
does a backup and restore all, the metadata security will be removed.
When InterBase 6.5 opens an InterBase 6.0 ODS 10 database, it
will automatically convert it to ODS 10.1 to indicate that metadata
security is supported and revoke all rights except SELECT from
PUBLIC for all of the system tables. If you need to allow all users to
write to the system tables, youll have to run WRITEMETA.SQL on
the database after opening it in InterBase 6.5. InterBase 6.5 will still
be able to open ODS 9 databases to provide a migration path from
InterBase 5.x and it wont modify an ODS 9 database in any way.

The ROWS Clause


The ROWS clause is an extension to standard SQL that lets you
control the number of rows returned by SELECT or the number of
rows affected by UPDATE and DELETE. The general syntax is:
ROWS <lower_value> [TO <upper_value>]
[ BY <step_value>] [PERCENT] [WITH TIES]

Improved Metadata Security


InterBase 6.5 implements a new on-disk structure, version 10.1, to
support security for system tables. When you create a new database
with InterBase 6.5, the only access that all users have to the system
tables is SELECT. Only SYSDBA can update the system tables.
This ensures that users (other than SYSDBA or the database owner)
cannot corrupt the database by modifying the metadata.
InterBase 6.5 includes three scripts that allow you to easily change
access to the system tables for any database. WRITEMETA.SQL
grants all access on each system table to PUBLIC. After running
this script, all users will have full access to the system tables just as
they have in earlier versions of InterBase. READMETA.SQL first
revokes all rights on each system table from PUBLIC, and then
grants SELECT on each table to PUBLIC. This provides all users
with the same rights they would have by default on a database created in InterBase 6.5. BLINDMETA.SQL revokes all rights on all
system tables so that only SYSDBA can see or update the tables.
If you need more control over access to the system tables, you
can use GRANT and REVOKE to grant specific privileges to
individual users or roles on individual tables. Both gbak and the
InterBase backup service have been modified to preserve system
table security settings across a backup and restore.
Its important to note that revoking rights on system tables only
restricts what users can do. It will never interfere with InterBases
ability to modify the metadata to carry out an operation that
a user performs using SQL DDL against other tables in the
database, such as adding an index or dropping a table. Revoking all rights will prevent external utilities such as IBConsole,
gbak, and gstat from reading the metadata unless they are
being run by SYSDBA or the database owner. In addition, two
client-side InterBase API functions, isc_blob_lookup_desc and
isc_array_lookup_bounds, wont function unless theyre being run
by SYSDBA or the database owner.
17 February 2002 Delphi Informant Magazine

The location of the ROWS clause in the hierarchy of clauses in a


SQL statement is:
<FROM_clause>
<WHERE_clause>
<GROUP_clause>
<HAVING_clause>
<ORDER BY_clause>
<ROWS_clause>

The best way to understand what the ROWS clause can do for
you is to look at some examples. All of the examples in this article
use the sample Employee database that comes with InterBase.
The following code shows the use of ROWS in conjunction with
the ORDER BY clause to select the five records with the largest
discount:
SELECT * FROM Sales
ORDER BY Discount DESC
ROWS 1 TO 5

The ORDER BY clause sorts the returned records in descending


order by Discount, and the ROWS clause limits the number of
rows returned to the first five. Although you can use ROWS without ORDER BY, it would be unusual to do so because limiting
the number of rows infers that you want the first N rows based
on some order. As long as the first row you want returned is the
first row in the result set, you can shorten the query to:
SELECT * FROM Sales
ORDER BY Discount DESC
ROWS 5

If you want all of the rows that have the same values in the fields
in the ORDER BY clause as the first five rows, use the WITH
TIES option:

Columns & Rows


SELECT * FROM Sales
ORDER BY Discount DESC
ROWS 1 TO 5 WITH TIES

This query returns eight rows instead of five because some of the
values that appear in the Discount field in the first five rows also
appear in other rows. Note that you must use an ORDER BY
clause when you use WITH TIES.
Instead of a fixed number of rows, you can also request a percentage of the rows in the result set:
SELECT * FROM Sales
ORDER BY Total_Value DESC
ROWS 10 PERCENT

Property

Description

HeaderTag

A string that will be used as the header tag in


the XML stream. If HeaderTag is null, "<?xml
version="1.0">" will be used as the default
value.

DatabaseTag

A string that will be used as the database tag.


If this property is null, the string "Database"
will be used.
A string that will be used as the table tag. If
this property is null, the string "Table" will be
used.
A string that will be used as the row tag. If this
property is null, the string "Row" will be used.
A set property with three members:
xmlDisplayNull = includes null values in the
XML data; xmlAttribute = generates the XML
as attributes instead of tags; and
xmlNoHeader = means dont include a header
in the XML.
The stream that receives the XML.

TableTag

RowTag
Flags

This returns the top 10 percent of the rows in Sales by Total_Value.


Another interesting use of the PERCENT option is to select an
evenly distributed subset of rows. This query returns five rows
with each of those rows separated from its neighbor by 20 percent
of the rows in the table:
SELECT * FROM CUSTOMER
ROWS 100 BY 20 PERCENT

This query can contain an ORDER BY clause. For example, to


obtain an even distribution of rows geographically, you could
order the table by state or ZIP Code.
You can also use the ROWS clause with aggregates. The following
query returns the top three sales representatives based on the total
value of their orders:
SELECT Sales_Rep, SUM(Total_Value) AS Total
FROM Sales
GROUP BY Sales_Rep
ORDER BY 2 DESC
ROWS 3

This query returns the top 25 percent of sales representatives based


on the total value of their orders:
SELECT Sales_Rep, SUM(Total_Value) AS Total
FROM Sales
GROUP BY Sales_Rep
ORDER BY 2 DESC
ROWS 25 PERCENT

The ROWS clause can also be used with UPDATE to give the 10
highest-paid employees a raise:
UPDATE Employee
SET Salary = Salary * 1.01
ORDER BY Salary DESC
ROWS 10

You can also use ROWS with DELETE to delete the 10 highest-paid
employees (if you want to delete everyone whose salary is equal to one
of the 10 highest-paid employees, just add the WITH TIES option):
DELETE FROM Employee
ORDER BY Salary DESC
ROWS 10

18 February 2002 Delphi Informant Magazine

Stream

Figure 2: TIBOutputXML properties.


procedure TMainForm.ShowXMLBtnClick(Sender: TObject);
var
IBOutputXML: TIBOutputXML;
Stream: TStringStream;
begin
IBOutputXML := TIBOutputXML.Create;
Stream := TStringStream.Create('');
try
IBOutputXML.HeaderTag := '<?xml version = "1.0"?>' +
#10#13 + '<!-- Examples XML from IB (overcash) -->' +
#10#13;
IBOutputXML.DatabaseTag := DatabaseEdit.Text;
IBOutputXML.TableTag := TableEdit.Text;
IBOutputXML.RowTag := RowEdit.Text;
IBOutputXML.Stream := Stream;
IBOutputXML.Flags := [];
if AttributesCb.Checked then
IBOutputXML.Flags :=
IBOutputXML.Flags + [xmlAttribute];
if NullCb.Checked then
IBOutputXML.Flags :=
IBOutputXML.Flags + [xmlDisplayNull];
if NoHeaderCb.Checked then
IBOutputXML.Flags :=
IBOutputXML.Flags + [xmlNoHeader];
SelectData;
OutputXML(IBSQL1, IBOutputXML);
Stream.Position := 0;
Memo1.Lines.LoadFromStream(Stream);
finally
IBOutputXML.Free;
Stream.Free;
IBSQL1.Close;
EmployeeTran.Commit;
EmployeeDb.Connected := False;
end;
end;

Figure 3: The Show XML buttons OnClick event handler.

XML Support
InterBase Express now includes a new class, TIBOutputXML, that
lets you easily retrieve data in XML. Figure 2 shows the properties of TIBOutputXML. Figure 3 shows the code from the Show
XML buttons OnClick event handler in the sample application

Columns & Rows


(see end of article for download details), and Figure 4 shows the
applications main form.
The code in Figure 3 begins by creating an instance of the
TIBOutputXML class. To use this class, you must include the
IBXML unit in your uses clause. Next, a TStringStream instance is
created to receive the XML. The next nine statements assign values
to the IBOutputXML objects properties. The call to the SelectData
method, shown in Figure 5, sets the values of the properties of
the IBDatabase, IBTransaction, and IBSQL components used to
select the data. The call to the OutputXML procedure takes two
parameters. The first is the IBSQL component that provides the
data, and the second is the IBOutputXML object that will convert
the query result set to XML. The remainder of the code assigns the
contents of the StringStream to the Lines property of the Memo
component on the form, frees the StringStream and IBOutputXML
objects, closes the query, and commits the transaction.
Outputting the XML to a stream gives you great flexibility. If you
want to write it to a file, use a TFileStream instance in place of the
StringStream. This is demonstrated in the XML To File buttons OnClick
event handler shown in Figure 6.
The XML features of InterBase 6.5 are implemented in a new DLL,
IBXML.DLL. If youre installing InterBase yourself, youll need to
make sure that this DLL is in the /System or /System32 directory,
or in another directory on the path, so that Windows can find it.

Improved Cache Management


Setting the buffer size in InterBase 6 to more than 10,000 pages could
cause performance to actually decrease. Not only has this problem been
fixed, but the cache manager has been improved in other ways. First,
the overhead required for cache management has been reduced, e.g.
the total amount of memory required for a 65,000 page cache has been
reduced by almost 7MB. Allocation of large caches is also much faster
in InterBase 6.5. Other changes improve I/O efficiency and eliminate
the need for threads to wait for shared page latches.
Note that increasing the cache size is no guarantee of improved performance. It depends on what youre doing and the number of simultaneous users. However, the more simultaneous users you have, the
more likely it is that increasing the cache will improve performance.

Processor Affinity
Running prior versions of InterBase SuperServer under Windows
on multiprocessor machines can actually degrade performance.
Windows will switch the InterBase process from one CPU to
another, and this is an expensive operation.
The InterBase 6.5 ibconfig file for Windows installations will contain
the following line to attach the InterBase process to the first CPU:
#CPU_AFFINITY

If you wish to attach InterBase to a different CPU, change this directive to use the bit value that corresponds to the CPU, e.g. 2 for the
second CPU, 4 for the third CPU, and so on.

Other Changes
InterBase 6.5 also includes InterClient 2.5, and the latest version
of the InterBase JDBC driver has been updated to be consistent
with JRE 1.3. This means you must have Java 2 installed to use
InterClient 2.5.
19 February 2002 Delphi Informant Magazine

Figure 4: The XML Demo applications main form.


procedure TMainForm.SelectData;
begin
EmployeeDb.DatabaseName := DatabasePathEdit.Text;
EmployeeDb.Connected := True;
EmployeeTran.StartTransaction;
IBSQL1.SQL.Clear;
IBSQL1.SQL.Add(SQLEdit.Text);
end;

Figure 5: The SelectData method.

procedure TMainForm.SaveXmlBtnClick(Sender: TObject);


var
IBOutputXML: TIBOutputXML;
Stream: TFileStream;
begin
IBOutputXML := TIBOutputXML.Create;
Stream := TFileStream.Create(ChangeFileExt(
Application.ExeName, '.xml'), fmCreate);
try
IBOutputXML.HeaderTag := '<?xml version = "1.0"?>' +
#10#13 + '<!-- Examples XML from IB -->' + #10#13;
IBOutputXML.DatabaseTag := DatabaseEdit.Text;
IBOutputXML.TableTag := TableEdit.Text;
IBOutputXML.RowTag := RowEdit.Text;
IBOutputXML.Stream := Stream;
IBOutputXML.Flags := [];
if AttributesCb.Checked then
IBOutputXML.Flags :=
IBOutputXML.Flags + [xmlAttribute];
if NullCb.Checked then
IBOutputXML.Flags :=
IBOutputXML.Flags + [xmlDisplayNull];
if NoHeaderCb.Checked then
IBOutputXML.Flags :=
IBOutputXML.Flags + [xmlNoHeader];
SelectData;
OutputXML(IBSQL1, IBOutputXML);
ShowMessage('The XML file has been created.');
finally
IBOutputXML.Free;
Stream.Free;
IBSQL1.Close;
EmployeeTran.Commit;
EmployeeDb.Connected := False;
end;
end;

Figure 6: The XML To File buttons OnClick event handler.

Past versions of InterBase have transmitted VARCHAR fields from


the server to the client padded to their full length with spaces. This

Columns & Rows


Conclusion
InterBase 6.5 offers a wealth of new
features for a minor version upgrade.
With 64-bit file I/O to make
working with large databases easier,
improved cache management for
better performance, metadata security to protect your database design,
the ROWS clause to control the
number of records affected by DML
statements, and XML export for easy
integration with Web applications,
this is definitely an upgrade worth
getting.
The project referenced in this article
is available on the Delphi Informant
Complete Works CD located in
INFORM\2002\FEB\DI200202BT.

Figure 7: The IBConsole index view.

could cause performance problems on slow or heavily-loaded networks. This behavior has been changed so that only the actual data in
the VARCHAR field is transmitted.
IBConsole has also been enhanced to include a separate indexes node
for each database. Selecting this node displays information about all
of the indexes in the database grouped by table, as shown in Figure
7. Items shown include the index name, sort order, uniqueness, columns included in the index, column order, and whether the index is
active. For indexes that are part of a foreign key constraint, the name
of the primary index referenced by the foreign key is also noted.

Bill Todd is president of The Database Group, Inc., a database consulting


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

Informant is a registered trademark of Informant Communications Group, Inc., Elk Grove, California.
Delphi is a trademark of Borland Software Corporation.

20 February 2002 Delphi Informant Magazine

New & Used


By Mike Riley

DeployMaster
Low-cost Installation Tool for Simpler Apps

ouve just finished your latest application masterpiece, and its time to show it to the
rest of the world. Unfortunately, gone are the days of Windows application deployment when all that was necessary was an application directory, the programs executable,
and perhaps a few support DLLs. Now you have to consider everything, from shortcuts and
file associations to registry key entries and language localization. DeployMaster, however,
could make application installations simple again.
DeployMaster provides developers a tabbed, formsbased interface with which to enter installation
attributes. It walks developers through a simple,
10-step process that covers:
file-type associations, which users may select
during installation, thereby allowing them to deselect any associations they prefer not to modify;

speaking-language localization;
user registration validation via a separate
DLL;
creating program shortcuts on the Windows
Start menu and desktop; and
generating a single, self-executable installation file.

DeployMasters build process is also accessible


via a command-line interface to automate a
compile-make-build-deploy development cycle
in just a single step. DeployMaster can host
DLLs, such as the sample source code in C or
Delphi that comes with the program to authenticate a users registration number.

Figure 1: DeployMasters Project tab. Enter the program name, main executable, license, and readme file paths here.
21 February 2002 Delphi Informant Magazine

Unfortunately, DeployMaster doesnt support


advanced features that typically come with the
Enterprise versions of its competitors. From an
end users perspective, the most notable difference is that DeployMaster uses screens that
the developer cannot manipulate or modify
programmatically. Button captions, text labels,
and their layouts are fixed in DeployMasters own
proprietary design. As a result, the deployment
doesnt look standard, even though DeployMaster
performs all the necessary Windows-compliant
installation steps, even to the point of providing
uninstall-program capability.

New & Used

DeployMaster is an inexpensive, albeit limited, installation-building program. While DeployMaster gets the job done for relatively
straightforward deployments, developers seeking a Windows
Installer-capable tool will need to look elsewhere.
JGsoft
Lerrekensstraat 5
2220 Heist-op-den-Berg
Belgium
E-Mail: sales@jgsoft.com
Web Site: http://www.deploymaster.com
Price: single-user license, US$99; five-user license, US$399.
Figure 2: This sequence provides a detailed log of events and
errors in the installation-building process.

Another missing feature thats become popular in the Internet era


is a registration screen. Even though DeployMaster could launch
such a feature as a separate program or DLL after it completes the
applications installation process, the lack of this standard installation
capability means more work for the developer. Likewise, DeployMaster doesnt offer the install-from-the-Web, or update-from-the-Web
features that its more expensive competitors support. The program
also lacks any pre-bundled install configurations for software dependencies, such as Microsoft VB run time or Borland BDE files, and
offers no way to save popular dependency configurations for later
import with other project builds.

Figure 3: The registry requires manual entry of program registry values. Unfortunately, DeployMaster cannot import or
export registry files.

One of the most troubling aspects for DeployMaster and other older
setup-application builders is that theyre based on old installation
technology. While Windows 9x and NT clients will exist for some
time to come, Microsoft has proclaimed that new program installations should use Windows Installer (WI) technology to comply with
Windows. The first well-known commercial application that shipped
using WI technology was Microsoft Office 2000. Since that time,
almost every new professional-level application has offered the option
of installing using WI. It addresses a number of installation deficiencies that exist in programs like DeployMaster. WI also helps prevent
infamous DLL Hell installation conflicts that plague non-WI-based
setup utilities, including DeployMaster.
Still, for basic installations that require simple functionality of singlefile, stand-alone setup executables and shortcut icons placed on the
Start menu and desktop, DeployMaster fits the bill. Its certainly less
expensive than even its closest competitor.

Figure 4: The final, built product. The layout of this dialog screen
is identical for all DeployMaster-constructed installations.

Another deficiency is that developers must enter registry keys manually, instead of allowing the developer to import a registry file. While
not a big issue with small application deployments, this can be a
major headache with applications that include ActiveX controls and
DLLs that require numerous registry key entries.
22 February 2002 Delphi Informant Magazine

Download the fully functional demo at http://www.deploymaster.com


and see how well it supports your project needs. Buying the product
will make the nag screen that pops up on your DeployMaster-built
installations go away. However, for those developers seeking more
sophisticated deployments, featuring Internet updates, and the look
and feel of other programs, check out the Enterprise editions of
DeployMasters competitors.

Mike Riley is a chief scientist with RR Donnelley & Sons, one of North Americas
largest printers. He participates in the companys I-net and eMerging Technology strategies using a wide variety of distributed network technologies, including
Delphi 5. Readers may reach him at mike_riley_@hotmail.com.

New & Used


By Bill Todd

InfoPower 3000
The Grid Alone Is Worth the Upgrade

have been using InfoPower since the first version, and, with each release, I am amazed
at the power of its new features. InfoPower just keeps getting better.

InfoPower long has been renowned for its grid,


and it has a host of new features starting
with the ability to nest one grid inside another
to any depth. This provides a very intuitive and
space-efficient way to display one-to-many relationships. Figure 1 shows a grid displaying a oneto-many-to-many relationship. In this example,
the outer grid displays customer records. By
clicking the plus sign in the first column, the user
can open a detail grid showing the order records
for that customer. Clicking the plus sign in the
Items column of an order record opens another
nested grid that displays the item records. Notice
that when a detail grid opens, the master records
row expands to contain the detail grid so that no
master records are covered. This means moving
to any other visible master record and opening its
detail grid requires just one mouse click.

Figure 1: A one-to-many-to-many relationship.


23 February 2002 Delphi Informant Magazine

Moreover, you can now embed more types of


controls in the InfoPower grid than ever before,
including the data inspector, the new InfoPower
check box, a radio group, and non-InfoPower
controls such as the DBImage component. The
grid now lets each embedded control paint itself
within the grid so that any embedded control will
display correctly for every row in the grid without
any additional code.
You can also now export the data displayed
in the InfoPower grid in tab-delimited text,
CSV, fixed-length ASCII, HTML, and SYLK
formats. An option for XML also is provided,
but isnt implemented in the current release.
The ExportOptions property, which has several
sub-properties, controls the export format. The
Delimiter sub-property comes into play when
you set the ExportType property to wwgetTxt
to create an ASCII file. Setting Delimiter to ,
gives CSV output. Setting Delimiter to #9 at run
time produces tab-delimited text output, and
setting Delimiter to null generates fixed-length
ASCII output. The ability to export to SYLK
format is particularly useful when you want
users to load the data into a spreadsheet. (The
SYLK format preserves the formatting.)
The FileName property holds the file name and
extension for the output file. If you leave the
property blank, the output file will have the same
name as your EXE and will have the extension TXT.
When you export to HTML, the grid data is placed
in an HTML table. The HTMLBorderWidth property lets you set the border width for the table.

New & Used


The Options sub-property gives you even more control of the
export process. The members of the Options set let you determine:
if the file will include the column titles;
if footer cells will be exported;
whether the cell colors the OnCalcCellColors event handler generates
will be included in an HTML export;
whether fields will be enclosed in double quotes;
whether only the selected records will be exported when using
multiselect;
whether the file will include a record number; and
whether the formatted data will be saved to the Clipboard
instead of to a file.
If you are exporting to HTML, you can control:
whether the exported table will be transparent;
whether alternating rows will be displayed in different colors; and
whether URL columns in the data will be shown as active links
in the HTML file.

Figure 2: Grid colors blended with a background bitmap.

If you want a grid with a truly distinctive appearance, the new


background-texture-tiling capability will provide it. The best way to
understand this feature is to look at the grid in Figure 2. What you
see is a light-gray, textured background bitmap that blends automatically with the colors of the various areas of the grid.
If you look closely at Figure 2, you will see the new grouped-titles
feature in action. A Name title on the first line spans and groups the
First and Last fields, and a Billing Information title spans the Shipping Address and Payment Method fields. Using this technique lets
you save screen space because it does not repeat the same word in the
title of multiple fields. The technique also provides visual grouping
that makes the data easier to understand.
Have you ever had to display a calculated field in a grid, but didnt
want to sacrifice the screen real estate to display the columns that
underlie the calculation? Its no problem until the user needs to edit
the value of one or more underlying fields. Then you need to create
another form to provide a view in which to edit. InfoPower lets
you do this all within the grid. Figure 3 shows a grid that displays
two calculated fields, Name and ShippingAddress. In both cases, an
Expand/Collapse button appears in the field. When the user expands
the field, an embedded grid appears that displays the fields used in
the calculation and gives the user an easy, intuitive way to edit them.
One thing thats annoyed me for a long time is that the user couldnt
edit the data displayed in fixed columns in the grid. Its great to be
able to freeze one or more columns in the grid, so they are always
visible when the user scrolls horizontally. But, when doing this meant
the fixed columns were read-only, I rarely used the feature. Now,
users can edit the data in fixed columns and resize those columns.
New additions to the Options property control both capabilities individually, so you can turn these features on and off at will.
Another nice new feature is the ability to designate columns as URL
links. When you do, the grid will automatically display the field
contents as a live link and open the link if the user clicks it. You can
make the links more user-friendly by storing an optional description
with the link in the database. If you do, the description will appear in
the grid instead of the actual link.
The grid now supports alternating row colors, with no programming required, and you can choose the colors you want to use.
Proportional column sizing is another new feature that ensures all
24 February 2002 Delphi Informant Magazine

Figure 3: Using an Expand/Collapse button to edit a calculated field.

the columns are resized automatically to fit within the client area of
the grid. Users have always been able to resize columns at run time,
but now they can resize rows, as well, by dragging on the horizontal
line in the record-indicator column. The ability to control the color
of the lines in the grid is another nice touch. The enhancements to
the grid alone are worth the cost of upgrading to InfoPower 3000,
but many other components received new features too.
One of InfoPowers most unique controls is the data inspector. Shown
in Figure 4, the data inspector lets you display the data from one or
more tables in a tree view, with nodes that can expand and collapse to
let the user control what information is visible at any time. Now, you
can display multiple records simultaneously in adjacent columns. Like
the grid, the new data inspector supports a wider variety of embedded
controls, including non-InfoPower controls. The inspector also supports the same background-texture tiling described for the grid. Also
like the grid, you can now choose to have alternate rows displayed in
different colors. The tree view has been enhanced with lines that make
the hierarchical relationship between items stand out visually.
InfoPowers edit-mask feature always has been far more powerful
than the native Delphi edit masks, and this release increases that
power with automatic auto-filling of characters. If a field value
starts with the same character in every case, the edit mask will
automatically insert it when the next character is typed.
InfoPower 3000 introduces new check box and radio group controls,
shown in Figures 5 and 6 respectively. Both support custom glyphs for
the controls and the flexible framing and transparency of other InfoPower components. Additionally, both integrate with the grid, Record
View dialog, and data inspector. The Record View dialog has been
enhanced to support framing and transparency. New currency and
numeric-editing components eliminate the problem of users entering
formatting symbols with the data. Now, a user can enter an amount as
$12,344.27, and the component will remove the $ and comma automatically before converting the value from a string to a number. These
arent all the enhancements, but they are the most significant.

New & Used

Figure 5: The new check box component.

Figure 4: The data inspector.

Another InfoPower component that really stands out is the


RichEdit component. Its been enhanced with full text justification, better OLE support, and the ability to be embedded in the
InfoPower grid. This lets you display rich text within grid cells.
Even the InfoPower combo box has been improved with the
ability to remember the users previously entered values. The
next time the users program is executed, these previously entered
values appear in the combo boxs drop-down list automatically.
You can also specify a separate MRU list, so the most recent
entries appear at the top of the list.

The Rest of the Story


If you havent used InfoPower before, I cant really do it justice in this
brief review. There are too many components to describe in the space
available, so Ill just hit the highlights.
The TwwDBDateTimePicker provides a drop-down calendar to make
choosing a date easy. To enter the current date, just tap the spacebar. At
the end of the date, you can tap the spacebar again to enter the current
time. The control will display week numbers optionally, and you can
control whether the current date is circled in the calendar. InfoPower
provides an event to allow you to control which dates are in bold.
InfoPower provides a complete suite of controls for finding data led
by TwwFilterDialog. The Filter Dialog component works with tables,
queries, and even ClientDataSets to let users filter their view of data.
When filtering a query, you also have the option to let the database
server perform the filter instead of fetching all the data and performing the filter on the users workstation. The filter can encompass any
number of columns and supports matching on single values or a range
of values. Filters on columns can be logically connected using AND or
OR. When searching for a single value in a column, you can specify
an exact match or what the value starts with, or search for the value
anywhere in the columns text. You can control whether the match is
case sensitive, and you can give the user the ability to logically invert
the search and see all records that do not match the specified criteria.
Filter Dialog can filter the data using the OnFilterRecord event of the
dataset component, the dataset components Filter property, or by
building the WHERE clause of a query. By setting the right properties, you can make sure you get the best possible performance with the
back-end database you are using.
25 February 2002 Delphi Informant Magazine

Figure 6: The new radio group.

Other search components include TwwLocateDialog, TwwSearchDialog,


and TwwIncrementalSearch. TwwLocateDialog lets a user search easily
for a value in a column using exact match, starts with, or is contained in
options. The user can control the case sensitivity of the search and use
wild cards in the search string. Find First and Find Next buttons let a user
step easily through all the records that match the search criteria.
TwwIncrementalSearch looks like an edit box, but provides
incremental searching on a single field in a dataset. As the user
types each succeeding letter, the dataset is repositioned dynamically to the first record that matches the characters typed so far.
TwwSearchDialog also provides incremental searching but in a
dialog box that displays the dataset being searched in a grid. The
user can choose which field to search, and the dialog can contain a
user-defined button that lets you add custom features.
The TwwRecordViewDialog and its cousin, TwwRecordViewPanel,
are unique components. The biggest problem with grids is having
to scroll horizontally to see all the fields in the current record.
The Record View dialog lets you pop up a dialog box that shows
all fields from the current record in any dataset in a Delphi form
created when the dialog is displayed. The Record View dialog not
only creates the form on the fly but also picks the right edit control
based on each fields data type. By setting a property, you can control whether the form will have a horizontal or vertical layout. The
Record View Panel provides the same functionality in a panel that
you can drop on your own custom form.
InfoPower always has had great documentation, and version 3000
is no exception. In addition to complete online help, InfoPower
3000 comes with a 300-page printed manual. It has an excellent
index to make finding the information you want easy. All the
properties, methods, and events are clearly described, and code

New & Used


samples are provided to show you how to take maximum advantage of InfoPowers features.

Conclusion
If I could only have one add-in component suite for Delphi, it would be
InfoPower. InfoPower will reduce the amount of code you have to write
and provide your users with a much more attractive, powerful, and functional user interface than any you could create using the standard Delphi
components alone. InfoPower continues to be the must-have add-in for
Delphi developers.

Bill Todd is president of The Database Group, Inc., a database-consulting and


development firm based near Phoenix. He is co-author of four database programming books, author of more than 80 articles, a contributing editor to Delphi
Informant Magazine, and a member of Team B, providing technical support on
the Borland Internet newsgroups. Todd is a nationally known trainer, has taught
Delphi programming classes across the country and overseas, and is a frequent
speaker at Borland developer conferences in the United States and Europe. Readers can reach him at bill@dbginc.com.

26 February 2002 Delphi Informant Magazine

If I could only have one component suite for Delphi, it would be


InfoPower. It will reduce the amount of code you have to write,
and provide your users with a much more attractive, powerful,
and functional user interface than any you could create using the
standard Delphi components alone. InfoPower continues to be the
must-have add-in for Delphi developers.
Woll2Woll Software
2217 Rhone Drive
Livermore, CA 94550
Voice: (925) 371-1663
Fax: (925) 371-1664
E-Mail: sales@woll2woll.com
Web Site: http://www.woll2woll.com
Price: Standard (supports Delphi 5 and Delphi 6), US$249;
upgrade from InfoPower 2000 Standard, US$129. Professional
(includes C++Builder 5 support and complete source code for all
InfoPower components), US$329; upgrade from InfoPower 2000
Professional, US$149; upgrade from InfoPower 2000 Standard,
US$179. For additional pricing options, visit
http://www.woll2woll.com/prices_fo_new.html.

New & Used


By Alan C. Moore, Ph.D.

Addict 3.0
Modern Spelling-check Support for Delphi Apps

ts hard to imagine a word processor that doesnt include support for checking spelling.
Today, such support is part of many other kinds of applications, including Internet mail
applications. In fact, any application that includes a Memo, RichEdit, or similar component is
a good candidate for supporting a spelling checker.
One of the best-known Delphi (and C++Builder)
component libraries providing this kind of support
is Addict from Addictive Software. Currently in its
third major version, this excellent library provides the
full range of modern spelling-check and thesaurus
support. Addict provides these features the Delphi
way with native classes and components that
provide flexibility and power, and with well-conceived properties and methods.
Two versions are available, and there are two types
of libraries: Standard and Professional. Add-ons are
available in a special PlusPack. The Standard library
includes thesaurus and spelling-check components. The Professional version includes live-spelling RichEdit components, full source code, and

C++Builder support. The PlusPack includes a spelling-dictionary compiler, which requires Windows
NT or Windows 2000; a thesaurus file compiler; and
a utility to import and export custom dictionaries.
The product includes a 30-day, money-back guarantee, and you can download a trial version.
Addict offers considerable support for a variety of
languages. (See Addicts dictionaries page, http://
www.addictivesoftware.com/dicts.htm.) It also has
support in the user interface for Swedish, Brazilian
Portuguese, Afrikaans, German, Spanish, Russian,
Dutch, Danish, and French, among others. Theres
a good deal of flexibility in Addicts custom dictionary capabilities as well. Version 3 has a customdictionary editor that allows users to modify a list
of words they want the checker to ignore or autocorrect. You can set custom dictionaries to mark
a word as always misspelled, for instance if you frequently misspell a word in one context, but its correctly spelled as another word, such as typing their
when you mean there. Best of all, you can load any
combination of main and custom dictionaries simultaneously, a feature Ive not seen in other tools with
which Ive worked. Visit the Addictive Software Web
site to find out about the latest product updates.
For example, version 3.01 adds word counting and
Delphi 6 support, and version 3.2 adds suggestions
learning as well as additional third-party support.
The update is free for 3.0 users.

Components and Classes

Figure 1: Checking spelling in a demo using Orpheus


with Addict.
27 February 2002 Delphi Informant Magazine

Addict includes various components and classes


that enable you to add sophisticated spellingcheck and thesaurus support to your applications.
Version 3 adds support for a live spelling checker,
or check-as-you-type feature to standard RichEdit
components. Such support has been available
with third-party controls since version 2. Spelling-check options include all the familiar ones

New & Used


Class
TAddictSpell3

TAddictRichEdit

Figure 2: A live spelling check in a demo using WPTools with Addict.

for a seemingly misspelled word: Ignore, Ignore All, Change, Change


All, Add, Auto-Correct, Cancel, and Undo. The various options I just
described are available in the Spelling dialog box used when checking a document (see Figure 1).
From this dialog box, you may bring up another dialog box to configure
subtle options, such as ignoring abbreviations, words in uppercase, or
words containing numbers. With the new support for live checking,
your users can right-click on a word and see a number of suggested
replacements. Figure 2 shows live checking in action. Note the misspelled words underlined in red and Addicts context-sensitive help.
Note also that the spelling checker is ignoring hyperlinks.
Addict provides this support through its components and classes (see
Figure 3). Most of the components and classes are used in the demo
application that comes with this product (and which you can download
from the Internet site). The first supports the functionality Ive described
(and much more) through properties, including LiveSpelling and
ActiveCustomDictionary, and through methods such as AddIgnore. I
found it extremely easy to implement basic spelling-check and thesaurus
functionality. With the main spelling-check and thesaurus components
on a form, and with two buttons dedicated to executing their primary
methods, I was able to implement this functionality in my test program
with just two simple methods. Each contains only one line of code:

TAddictDBRichEdit

TThesaurus3

TConfigurationDialogCtrl

TThesaurusDialogCtrl

Description
Main support for checking spelling.
It adds a user interface to base class
TAddictSpell3Base. The latter provides
the ability to parse an edit control,
store multiple user configurations,
look up words in many dictionary
types, generate alternate word
suggestions, and run the user
interface needed to perform a
spelling check.
An extension of Delphis TRichEdit
wrapper class, TAddictRichEdit
provides context-sensitive spelling
features, such as automatic
underlining of misspelled words
and automatic replacement of
misspelled words with correct words
while the user types.
An extension of Delphis data-aware
TRichEdit wrapper class, it provides
context-sensitive spelling features,
such as those listed for
TAddictRichEdit.
Adds a default user interface
(dialog box) to the base class,
TThesaurus3Base. The latter provides
the ability to locate the current word
in an edit control, look up words in
a thesaurus file, and run the user
interface needed to look the word up.
Facilitates communication with
TAddictSpell3Base class, and
interacts with it directly.
TConfigurationDialogCtrl provides
all the capabilities of a custom
spelling-check and configuration
dialog box.
Facilitates communication with
TThesaurus3Base class to provide
the functionality of a custom
thesaurus dialog box.

Figure 3: Selected Addict classes.

procedure TForm1.btnThesaurusClick(Sender: TObject);


begin
Thesaurus31.LookupWinControl(AddictRichEdit1);
end;
procedure TForm1.btnSpellCheckClick(Sender: TObject);
begin
AddictSpell31.CheckWinControl(AddictRichEdit1, ctAll);
end;

Advanced Features and Third-party Support and Integration


So far, Ive emphasized the usual features you would expect to
find in a modern spelling-check library. Now, Ill introduce some
additional powerful features. Again, these are demonstrated in
the demo application available on Addictive Softwares Web site.
Figure 4 shows one of these features, the spelling checkers ability
to move from one control to another control, sometimes of a different type. Other portions of the demo application show default
and custom dialog boxes; special support, such as for repeated
words; string grid support; and access to low-level functions.
28 February 2002 Delphi Informant Magazine

Figure 4: Advanced features shown in Addicts demo application.

New & Used


Component
/Library

Vendor

Integration

DBISAM

Elevate Software

DreamMemo

Dream Company

InfoPowers
rich-edit control
LMD-Tools

Woll2Woll Software

Orpheus

TurboPower

PlusMemo

Electro-Concept
Mauricie
Sergey Tkachenko

Addicts DB-aware,
live-spelling RichEdit
can be used with
this database product.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Live-spelling,
spelling-check and
thesaurus support.

TRichView
WPTools

LMD Innovative

Julian Ziersch
Software

Figure 5: Supported third-party controls.

One particularly attractive feature of this library is its extensibility, made possible through partnerships with third-party component vendors (see Figure 5).
The creators of Addict have constructed a parsing architecture
to facilitate adding this spelling-check and thesaurus support to
nearly any edit control. On the Addictive Software Web site, you
can read about and download demo applications and components
that add spelling-check and thesaurus support to one or more of
their components. As you read above, some vendors also support
live checks of spelling. In addition to those listed in Figure 5
(and described in more detail on the Web site), Ive learned of
a recent addition: support for TMS Softwares TAdvStringGrid
component, including live spelling-check support.
This product, including documentation, is completely electronic
(that is, no physical products are involved). The support consists

Addict is a comprehensive library of Delphi components and tools


for adding spelling-check and thesaurus support to Delphi applications. The Standard library includes spelling-check and thesaurus
components. A PlusPack includes a spelling-dictionary compiler, which
requires Windows NT or Windows 2000; a thesaurus file compiler;
and a utility to perform custom dictionary imports and exports. The
Professional version includes the full source code and more. The
library includes excellent integration with many other Delphi thirdparty components and libraries, such as Orpheus and WPTools.
Addictive Software Company
7001 Old Redmond Road, #J337
Redmond, WA 98052
E-Mail: support@addictivesoftware.com
Web Site: http://www.addictivesoftware.com
Price: Addict v2 (Delphi 3 and earlier, source code included), US$150;
Addict v3 Standard (Delphi 4 and later, no source code), US$119;
Addict v3 PlusPack (compilers and import-export utilities), US$49; and
Addict v3 Professional (source code and more included), US$199.

29 February 2002 Delphi Informant Magazine

Figure 6: Addicts thesaurus in action.

mainly of the help file and the sample programs. While these are
excellent, I feel this product would benefit greatly from the addition of a manual and perhaps a tutorial to show newer developers
how to use some of the more subtle features. I dont regard the
lack of these items as a serious defect, however. I found the library
easy to use despite their absence. The company provides support
through e-mail (through which I always received a quick response)
and newsgroups. For more information, please see the fact file
that accompanies this article.
To conclude, I feel this is an excellent library with only minor
defects. Having used thesaurus features in commercial word processors, I was somewhat disappointed by the default thesaurus file.
The thesaurus response for the word shown in Figure 6 seems fairly
acceptable. But, for other words, the thesaurus file provided just one
context, such as a words use as an adjective, when the word clearly
had at least one more use, such as a noun or verb form.
Perhaps improving the default thesaurus file could become a joint
project for Addict developers. But, despite these minor issues, I
feel this is the best tool Ive seen for providing spelling-check
support for Delphi applications.

Alan C. Moore, Ph.D., is a professor at Kentucky State University, where he


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

New & Used


By Alan C. Moore, Ph.D.

LockBox 2
From Cyberspace to Cipherspace

lectronic communication has enhanced our lives. Unfortunately, cyberspace is hardly


the safest place in which to communicate sensitive information. But, with the routines and classes in TurboPowers latest version of LockBox, you can create applications
to store and retrieve data with a high level of security.

Designed for Delphi 3 or later, the tools in LockBox 2 include in-house algorithms that formed
the core of LockBox 1 (included here mainly for
Class

Description

TLbBaseComponent

Base class for all LockBox components. It


implements a Version property.
Base class for the encryption class and
component hierarchy. It manages the
basic functionality of a cipher engine:
encrypting and decrypting buffers of
data, files, streams, and strings.
Ancestor class for the symmetric
encryption components. It provides key
management functionality.
Provides encryption and decryption
facilities using the Blowfish cipher.
Provides encryption and decryption
facilities using the Data Encryption
Standard (DES) cipher.
Provides encryption and decryption
facilities using the triple-DES cipher. Its
slower but much more secure than DES.
Provides encryption and decryption
facilities using the Rijndael, or Advanced
Encryption Standard (AES), cipher.
Ancestor class for the asymmetric
encryption component. It provides key
management functionality.
Provides encryption and decryption
facilities using the RSA cipher.

TLbCipher

TLbSymmetricCipher

TLbBlowfish
TLbDES

TLb3DES

TLbRijndael*

TLbAsymmetricCipher*

TLbRSA*

Figure 1: The encryption classes in LockBox 2 support the most


respected algorithms (* indicates that its new).
30 February 2002 Delphi Informant Magazine

backwards compatibility), and the most respected


and tested encryption and hashing algorithms.
The inclusion of the latter justifies TurboPower
officials describing this library as an industrialstrength data-encryption toolbox.
As do other TurboPower libraries, this one
includes full source code. LockBox also provides
similar functionality in both high- and low-level
forms. For most users, its encryption and hashing components provide easy access to its cryptographic algorithms. But, for the experienced
programmer who needs greater control, LockBox
includes a whole series of low-level routines. It
also includes tools for creating and verifying digital signatures, for working with the public and
private keys on which the algorithms depend,
and more. Figure 1 shows all the encryption
classes, and Figure 2 shows the hashing classes.
All the classes are demonstrated in the example
programs that ship with LockBox 2.
All encryption classes support encrypting or
decrypting strings, streams, files, and buffers.
They also support the generation of random
keys. Similarly, hashing classes can operate on
strings, streams, files, and buffers. LockBox 2
adds an asymmetric hashing class, TLbSHA1.
Why so many algorithms? Some are faster, but
may sacrifice security. (Figure 3 shows a speed
comparison of all of them and their various
options.) Others are very secure, but may take

New & Used


Class

Description

TLbHash
TLbMD5

Base class for the hash component hierarchy.


Provides secure hashing facilities using the MD5
algorithm.
Provides secure hashing facilities using the
SHA-1 algorithm.

TLbSHA1*

Figure 2: Hashing classes and components (* indicates that its new).

longer to process their data. The triple-DES cipher is an example


of an algorithm that is noticeably slower, but much more secure
than the older DES on which its based.
An especially important reason for including all of these algorithms
is compatibility. If your client is communicating with someone
whos using an application that wasnt developed with LockBox,
you still may be able to exchange data, provided youre both using
the same encryption algorithm. Additionally, if youre using one
of the less secure algorithms (the manual is very clear on levels
of security), and some cracker comes up with a breakthrough that
reliably and quickly decodes the algorithm youre using, you can
quickly switch to another one that hasnt been broken.
With the addition of the popular Rijndael algorithm, the RSA
encryption algorithm, and the SHA-1 hashing algorithm, the
library now includes all the most popular ones.
Public and private keys play a crucial role in the encryption and
decryption of data. Note that they can be of varying lengths, often
depending on the algorithm. Generally, the longer the key the
more secure the data being encrypted. LockBox provides several
tools and example applications for managing keys.

Documentation and Support


As with other TurboPower libraries, LockBoxs documentation (the Help file and the nearly 200-page manual) is excellent. TurboPower has expanded the manual considerably, and it
now includes sufficient general background information on the
discipline of cryptology. Of course, the manual includes all the

LockBox 2 is a superb library of Delphi and Kylix classes that


provide high- and low-level support for all the most popular
cryptographic and hashing algorithms. All the classes work with
strings, streams, files, and buffers. LockBox includes example
programs and full source code. The manual is well organized, comprehensive, and very well written. LockBox 2 is the data-security
solution for Delphi, C++Builder, and Kylix development.
TurboPower Software Company
15 North Nevada Avenue
Colorado Springs, CO 80903-1708
Phone: Within United States and Canada, (800) 333-4160;
international, (719) 471-3898.
Fax: (719) 471-9091
E-Mail: info@turbopower.com
Web Site: http://www.turbopower.com
Price: US$349; upgrade, US$149; 20 percent discount available
for owners of another TurboPower product.

31 February 2002 Delphi Informant Magazine

Figure 3: An example program that allows you to test various


algorithms.

key properties and methods of the classes along with information


on using the low-level routines. The manual has many helpful
code fragments, and its excellent example projects demonstrate
features and capabilities. The company provides excellent support
by e-mail, in online newsgroups, and by phone. Questions posted
to the newsgroups are usually answered the same day. Users can
download minor upgrades for free.

Conclusion
LockBox 2 is a powerful and flexible encryption and decryption
library that provides a complete solution for adding data security
to your applications. LockBox supports all the most popular algorithms and provides both low-level routines and high-level classes
and components to access those algorithms. Best of all, you dont
have to be a master cryptologist to use LockBox. You can download a trial version from TurboPowers Web site. As TurboPower
does for its other products, it provides a generous 60-day, moneyback guarantee the most generous in the industry, as far as
Im aware.

Alan C. Moore, Ph.D., is a professor at Kentucky State University, where he


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

TextFile

Delphi Developers Guide to XML


When I was introduced to XML
about 3 years ago I could
see a lot of promise, but had no
immediate application for it. My
enthusiasm, however, lead me
to write an article that was published in the July 1999 issue of
this magazine. I moved to different projects since, and my interest in XML has been put on the
back burner. When my project
(not a Web-specific development
project) required reading and
writing data in XML format, it
was clear to me that the promise
of a text-based, standard-backed,
Internet protocol-friendly, meta
language has paid off.
A visit to Powells technical
bookstore revealed row after
row of XML this and XML
that books. Most, unfortunately,
seemed to be either reprints of
the specification, or step-by-step
texts aimed at HTML coders.
Fortunately, some books aimed
at developers the real consumers of XML technology
seemed more promising,
and Keith Woods book aimed
directly at Delphi developers
caught my eye. (Mr. Wood is
a frequent contributor to Delphi
Informant Magazine.)
The book is divided into 5
sections: The first section of
the book is an Introduction
to XML. It includes a chapter
about history, comparison to
HTML and basic applications,
a terse definition of the syntax
(what constitutes a well formed
and valid document, elements,
entities, namespaces, etc.),
32 February 2002 Delphi Informant Magazine

how to create Document Type


Definitions, Style sheets (XSL)
and transformations (XSLT),
document navigation (Xlink,
Xpath, Xpointer), and finally
XML schemas (an alternative
to DTSes). This section of the
book is a very good way to get
a general idea of what XML is,
and to find references to all the
details if you need more information about specific areas.

A SAX parser overcomes these


limits, but doesnt provide the
hierarchical information that a
DOM parser provides. If your
application needs to handle large
XML documents, this section
will provide information about
the SAX elements, classes and
interfaces, the Microsoft SAX
implementation, and a conversion of the original Java SAX
interfaces to Delphi.

The second section deals with


parsing XML documents using
the W3Cs Document Object
Model (DOM). The DOM
provides hierarchical information and illustrates the relations between elements in a
document. The book contains
a chapter describing the W3C
DOM, and three chapters discussing specific implementations: the Microsoft DOM that
ships with Internet Explorer
and the latest operating systems; the TurboPower XML
Partner VCL DOM implementation; and the Open XML
(XDOM) VCL implementation, an open-source parser.

The fourth section of the book


deals with serving XML. The
chapters describe simple XML
generation from a database, and
show how to use the DOM and
SAX interfaces to create XML
output. One chapter shows how
to apply XSL Transformations
to create HTML and RTF
output from XML documents.
Another chapter discusses the
XML Broker used by MIDAS
(DataSnap in Delphi 6) for
multi-tier applications.

The next section of the book


deals with SAX (Simple API
for XML), an event-driven
parser that fires events based
on elements found in an XML
document. A DOM parser needs
to read the entire XML document, parse it, and represent it
as a series of objects in memory.
In cases where memory and
processing speed resources are
scarce, this can lead to problems
if the source document is big.

The last section presents several


sample applications that use
XML (a mass mailer, a customized client showing how to work
with specific DTDs, an application for Exams), and a discussion of the Simple Object Access
Protocol (SOAP), the underlying
protocol of Web services. The
book comes with a CD-ROM
that includes copies of the official XML and related specifications, all the sample code from
the book, and several sample
third-party software packages.
If you need to understand
XML from a developers point

of view, this is the only Delphispecific book. I can also recommend it without reservation.
The book provides concise
information, and covers parsing and producing XML from
Delphi applications. The inclusion of sample applications
will help developers realize the
benefits of XML support. The
only problem I can see with
the book is that Delphi 6specific classes/technologies
arent addressed, but the
information presented is still
applicable. If you read the
book you will be able to take
advantage of new XML-related
technologies in Delphi 6 with
no problems.
Ron Loewy

Delphi Developers Guide to


XML by Keith Wood,
Wordware Publishing,
http://www.wordware.com.
ISBN: 1-55622-812-0
Cover Price: US$59.95
(526 pages, CD-ROM)

File | New
Directions / Commentary

Plug-in Culture

lug-ins dynamic link libraries that extend functionality are nothing new. Theyve been used for years in
diverse applications from Netscape Navigator to Macromedia Director. Plug-ins allow third parties and their
customers many advantages: application vendors can provide updates and fixes without re-distributing an entire
application; users can add custom features. How do third parties facilitate communicating with their applications?
Typically, a vendor that supports plug-ins will provide an API so developers can extend or add functionality.
In Delphi, we could regard IDE extensions, such as Experts, as plug-ins.
Borland supports these extensions
through Delphis Open Tools API.
One of the most powerful and
feature-rich IDE add-ons is Eagle
Softwares CodeRush. Interestingly, CodeRush includes its
own plug-ins, as well as the
Rush API that allows users
to extend both CodeRush
and Delphi. Now in version 6,
CodeRush includes a plethora of
powerful plug-ins. Theres even one
plug-in, AutoFill, that permits extensions to itself (as Ill discuss at the end
of this column).
While discussing plug-ins with me, Mark
Miller (chief architect of CodeRush) made the
following observations: Plug-ins allow us to get
new solutions to our customers immediately. Other
companies develop and save features for their next
major product release, resulting in a delay between
the moment the feature is developed and the time
when customers can actually employ that technology, e.g.
by upgrading to the next product version. With a plug-in
architecture theres no delay; customers are always empowered with the latest technology.
I also wanted to find out how CodeRush users and plug-in
developers felt about the availability of plug-ins. So I posted
a series of questions on the Eagle Software Newsgroup, eagle
.public.support.thirdparty. The first question, What has the
availability of plug-ins meant to you, elicited responses that
demonstrated the extreme loyalty of CodeRush users. One
33 February 2002 Delphi Informant Magazine

developer wrote that although he bought Delphi 6 when it first


came out, he postponed using it because he simply [could not]
face working without the facilities [offered by] CodeRush and the
many plug-ins. Another developer emphasized the increase in
productivity, stating Using the statistics generated by CodeRush and some empirical measurements I estimate I saved
between two and three months off an eighteen month
project using CodeRush.
What are some of the most popular CodeRush plug-ins
and what do they do? TokenMatch is at the top of many
developers lists, certainly mine. This plug-in enhances
the readability of source code in the editor by highlighting the beginning and ending keywords of many kinds
of blocks: begin..end, try..finally, and so on. It was written
originally as a collaborative effort between CodeRushs chief
architect Mark Miller and Team-B member, Thomas Huijer.
Its become a standard part of CodeRush. Thomas perspective on the importance of plug-ins mirrors that of many
other CodeRush users: Plug-ins are the most important
thing in CodeRush. Even CodeRush itself implements a
lot of its features in plug-ins. I look at CodeRush as an
API. CodeRush is everything that Delphis Open Tools
API should have been. With the new [plug-in] architecture introduced [a few years ago] in CodeRush 4, writing
plug-ins has become really easy, a lot more easier than using
[Delphis] Tools API and it offers a lot more functionality.
Another favorite is Whopper, which could be described as File |
Reopen on steroids. Delphis most-recently-used file capability
is limited to just 15 project and unit files. Whopper lets you set
the limit for each file type (*.pas, *.dpr, *.dpk, and so forth) as
high as you want. Other plug-ins, such as Hot Files, allow you
to organize those files into folders for quick and easy access.
The Instant Variable Declaration plug-in also appears on many
lists of CodeRush favorites. I find myself using this one a lot,

File | New
particularly since Ive been expanding a large TAPI interface unit
containing a huge class to which Im constantly adding new fields.
I also asked about developers experiences writing plug-ins. One
reported a bit of a learning curve, but a couple of others characterized the process as easy. In fact, one said it was no more
difficult than writing a Delphi application. The first developer
recommended I use his own UnitNavigator plug-in to navigate
the lengthy RushIntf.pas, the CodeRush unit that provides
(mostly abstract) methods that support CodeRush/Delphi extensions. Support for writing plug-ins has increased in the Professional version of CodeRush with this highly commented and
bookmarked RushIntf.pas file, a great online tutorial, and a very
active newsgroup especially dedicated to plug-in developers.
My own experience in writing plug-ins has also been satisfying.
When the first version of CodeRush came out I wrote a Project
Identifier plug-in that made variable and other project-specific
names easily available to be pasted into the editor (I even wrote an
article about the process of writing it, which is still on the Eagle
Software site: http://www.eagle-software.com). These identifiers
were saved in a file that could be automatically loaded when the
unit was being edited. However, there were a few keystrokes and
navigational issues involved, and I was not entirely satisfied with
the functionality. With the introduction of the AutoFill plug-in
in CodeRush 5, Mark Miller suggested I write an extension to
this plug-in so it could incorporate those lists of identifiers in the

34 February 2002 Delphi Informant Magazine

names it presents when typing in the editor. I hope to make this


extended plug-in available soon, and write another article for the
Eagle Software site so that other users can see how easy it is to
extend AutoFill in ways that match their development needs.
CodeRush isnt the only Delphi tool that supports user-extensions
through plug-ins. The Professional Edition of CodeSite 2 (from
Raize Software: http://www.raize.com) supports custom inspectors
through its extensible plug-in architecture. I suspect that there
may be other tools in the Delphi community that also support
this kind of extensibility through plug-ins. We need to see more
of this!
Alan C. Moore, Ph.D.

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


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

You might also like