You are on page 1of 45

August 2001, Volume 7, Number 8

Cover Art By: Arthur Dugoni

ON THE COVER
5 On the ’Net 30 Columns & Rows
Using SOAP — Keith Wood Using the XML Features of SQL Server 2000:
Keith Wood introduces SOAP, its uses and importance, then shares a Part I — Alex Fedorov
Delphi SOAPDispatcher component you can simply drop onto a data Alex Fedorov describes the XML features of SQL Server 2000 — the
module, connect to a SOAP parser, and compile. SELECT statement’s FOR XML clause in particular. It’s the start of a
series explaining how to leverage these capabilities using Delphi.

FEATURES REVIEWS
11 On Language 34 ModelMaker Code Explorer
Console Applications: Part I — Mike Edenfield Product Review by Robert Leahey
Console applications get no respect, but that’s just because they’re
misunderstood. Mike Edenfield kicks off a multi-part series to dispel 37 Protection PLUS
the myths and extol the virtues of these useful non-GUI apps. Product Review by Warren Rachele
40 Cryptography and e-Commerce
16 Inside OP Book Review by Mike Riley
Procedural Types — Rick Spence
Rick Spence shines a light on procedural types, “the most underused 41 The Official InstallShield for Windows Installer
type in Object Pascal.” Delphi itself uses them for event handlers. You Developer’s Guide
can use them to parameterize the logic of a subroutine. Book Review by Bill Todd
42 Red Hat Linux 7 Server
21 OP Tech Book Review by Bill Todd
A Little Help — Bill Todd
Delphi provides you with all the tools to easily add Windows Help 42 Designing Web Usability
to your applications, then fails to adequately document how to use Book Review by Mike Riley
them. Bill Todd steps into the breach with a help-full demonstration.
DEPARTMENTS
25 Kylix Tech 2 Delphi Tools
Shared Objects — Rick Ross 43 Best Practices by Clay Shannon
Delphi makes it easy to create Linux shared libraries (AKA shared 44 File | New by Alan C. Moore, Ph.D.
objects), but they can still be tricky. Rick Ross explains SO versioning,
compiler directives, calling conventions, exception handling, and more.

1 August 2001 Delphi Informant Magazine


Aivosto Oy Announces ActiveXplorer 2.1
Delphi Aivosto Oy
T O O L S launched ActiveX-
plorer 2.1, an
New Products ActiveX file viewer
and Solutions and analyzer.
ActiveXplorer 2.1
adds detection of
missing files and
file compare to
solve DLL hell.
ActiveXplorer 2.1
also analyzes con-
trols and generates
a technical docu-
ment of methods
and properties.
Book Picks ActiveXplorer
2.1 shows the
details of all
ActiveX files
Project Retrospectives:
A Handbook for Team Reviews
installed on your learn to use undocumented tems. A free evaluation copy is
Norman L. Kerth system to keep you informed controls. You can analyze any available for download.
Dorset House Publishing of file versions, CLSIDs, and ActiveX component to see
installation paths. what’s inside it and view
ActiveXplorer 2.1 finds file the properties and methods
registration problems and lets with descriptions on how they Aivosto Oy
you clean the system registry; should be called. Price: Single user, US$59; site license,
simply right-click any file to ActiveXplorer 2.1 is sup- US$590.
register it correctly. ported by Windows 95/98/ Contact: vbshop@aivosto.com
ActiveXplorer 2.1 lets you ME/NT/2000 operating sys- Web Site: http://www.aivosto.com

ISBN: 0-932633-44-7 Woll2Woll Software Releases InfoPower 3000


Cover Price: US$33.95 Woll2Woll Software, Inc. the grid and data inspector. to Microsoft Office, incremen-
(268 pages)
http://www.dorsethouse.com
released InfoPower 3000, a InfoPower 3000 validation lan- tal search performance, record
visual component library for guage has improved picture viewing, word processor with
building database applications mask support and many more full-justified text, support for
An Introduction to General for Borland’s Delphi and common mask templates, such dataset types in the filtering
Systems Thinking C++Builder. as e-mail addresses, IP dialog, and a stay-on-top mode
Gerald M. Weinberg
InfoPower 3000 includes addresses, etc. during searching in the locate
Dorset House Publishing
a data inspector to display A flexible checkbox control dialog box.
multiple records vertically for has been integrated into Info- InfoPower 3000 is available
an alternative to the left-right Power 3000. This control in Standard and Professional
editing of a traditional grid. supports custom bitmaps for editions, and is compatible
The inspector allows develop- checkbox glyphs, dynamic cap- with Delphi 5 and Delphi
ers to embed a variety of non- tions so the label can match 6. InfoPower 3000 Professional
InfoPower controls (such as the the database value, and custom includes source code and
Delphi DBImage). InfoPower framing and transparency. The C++Builder compatibility and
3000’s masterpiece grid can custom radio group control support for older versions of
ISBN: 0-932633-49-8
display and edit your master/ supports custom bitmaps for Delphi. InfoPower and
Cover Price: US$33.95
(279 pages) detail relationships, display and the radio-button glyphs, addi- 1stClass are available in a
http://www.dorsethouse.com manage titles in the grid tional glyphs for each radio bundle in both Standard and
hierarchically, and embed a item, custom framing and Professional editions.
whole series of new custom transparency, and integration
controls, such as the data with the InfoPower grid,
inspector, custom checkboxes record-view components, and Woll2Woll Software, Inc.
and radio-groups, formatted data inspector. InfoPower 3000 Pricing: Standard, US$249; Profes-
text (RichEdit) display, or comboboxes have automatic sional, US$329. InfoPower and 1stClass,
custom controls. history and MRU (most- bundle Standard, US$349; Professional,
InfoPower 3000 provides recently-used) lists. US$479.
integrated support for back- InfoPower 3000 has improved Contact: sales@woll2woll.com
ground texture tiling and custom framing with hot- Web Site: http://www.woll2woll.com/
flicker-free display for both tracking of border styles similar infopower

2 August 2001 Delphi Informant Magazine


Delphi Developer Express Announces ExpressPrinting System 2.0
Developer Express Inc.
T O O L S announced ExpressPrinting
System 2.0, a data presentation
New Products and visualization system for
and Solutions Delphi and C++Builder.
ExpressPrinting System 2.0 is a
component publishing system
that allows you to bring the
best features of your user inter-
face to the printed page.
Via its Report Link technol-
ogy, Printing System 2.0 is able
to render and print visual con-
trols from Developer Express,
such as the ExpressQuan-
tumGrid and ExpressMaster-
Book Picks View, as well as a number of
standard VCL controls such as
the StringGrid and RichEdit.
Designing Solutions with COM+ ExpressPrinting System 2.0
Technologies
Ray Brown, Wade Baron,
supports 20 visual controls, several presentation styles for you control virtually all aspects
and William D. Chadwick III including the ExpressMaster- each ReportLink. Using run- of printing and supports the
Microsoft Press View. Using the ExpressMas- time customization, users can selection and creation of styles,
terView in conjunction with create new styles on the fly and selection of paper types, header/
ExpressPrinting System 2.0 save them for later use, just as footer content and layout, mar-
gives you the ability to create Microsoft does in Outlook. gins, and scaling.
complex master-detail reports. ExpressPrinting System gives
For a green bar-like report, you control over the presenta-
simply use the custom draw tion and positioning of page
support to implement all types headers and footers. There is
of effects such as green bar, support for standard elements Developer Express Inc.
ISBN: 0-7356-1127-0 high-low threshold, and visual such as page numbers, dates, Price: US$179.99; upgrade from version
Cover Price: US$69.99 alerts using the rich custom and times and the Express- 1, US$79.99.
(891pages, CD-ROM)
draw event model of Express- Printing System includes run- Contact: info@devexpress.com,
http://mspress.microsoft.com
Printing System 2.0. The new time customization. (888) GO-DEVEX
Print Style components provide The page setup dialog box lets Web Site: http://www.devexpress.com
PKI: Implementing and
Managing E-Security Borland Unveils Industry’s First RAD Web Services Development Platform
Andrew Nash, William Duane,
Borland Software Corp. WebSnap, and DataSnap to applications or services to con-
Celia Joseph, and Derek Brink
Osborne/McGraw-Hill released Borland Delphi 6, its help users build server-side and nect with databases such as
rapid application development client-side applications that Oracle, MS-SQL Server, Infor-
(RAD) environment for Win- support Web Services specifica- mix, IBM DB2, Sybase, and
dows. Delphi 6 enables the tions through a fully integrated InterBase, through industry
rapid development and deploy- set of Web Services-enabled standard Web Services and
ment of applications using visual tools, advanced compiler XML, DCOM, or CORBA.
XML- and SOAP-based Web technology, and reusable soft- When paired with Borland
Services technologies. Delphi ware components. The BizSnap Kylix, Delphi 6 users can build
6 Web Services and XML Web Services RAD develop- single-source applications for
technologies allow companies ment platform simplifies busi- both Windows and Linux.
ISBN: 0-07-213123-3
Cover Price: US$49.99
to share and exchange applica- ness-to-business integration by Delphi 6 is available in
(513 pages) tion data, such as information creating XML/SOAP Web three editions: Enterprise, Pro-
http://www.osborne.com about a business, transaction, Services and connections. fessional, and Personal.
or supplier, over the Internet. WebSnap is a component-based
Delphi 6 supports major Web application development
emerging industry standards, framework that supports Borland Software Corp.
such as XML, SOAP, WSDL, leading Web Application Price: Enterprise, US$2,999; Enterprise
and XSL, which in turn Servers, including Apache, upgrade, US$2,399; Professional,
support Web Services-based Netscape, and Microsoft Inter- US$999; Professional upgrade, US$399;
vendor platforms such as net Information Services (IIS). Personal, US$99.
Microsoft’s .NET and BizTalk, DataSnap allows customers to Contact: (800) 632-2864
and Sun Microsystems’ ONE. build Web Services-enabled Web Site: http://www.borland.com/
Delphi 6 includes BizSnap, middleware to enable client delphi

3 August 2001 Delphi Informant Magazine


Delphi Excel Software Releases
QuickUML 1.0
T O O L S Excel Software released Quick-
UML 1.0, a QuickUML design
New Products tool. QuickUML 1.0 provides
and Solutions integration and ease-of-use for
a core set of UML models.
An entire project is accessible
through a tabbed window with
smooth integration between use
cases, class models, object models,
dictionary, and code. The project
is saved as an industry standard
XML file.
Use cases capture the essence of
user-visible functions the system projects, design namespaces, models from Delphi, C++, or
must perform. Each use case UML stereotype extensions, Java code.
Book Picks describes a named interaction in flexible color support, custom QuickUML runs on Windows
terms of actor inputs and system detail fields and automated gen- 95/98/NT/2000.
responses. Use case entries link eration of class models from the
RSA Security’s Official Guide to
to models, code, foreign files, or dictionary. An integrated code Excel Software
Cryptography other use case entries to provide manager enables the designer Price: Single-user license, US$495; five-user
Steve Burnett and Stephen Paine a solid foundation for develop- to navigate through code files site license, US$1,485; unlimited-user site
Osborne/McGraw-Hill ment. Designers enjoy easy nav- linked to use cases and diagram license, US$2,970. QuickUML Windows dem-
igation and two-way traceability objects. QuickUML works with onstration and manual, US$50.
between use cases, related models, the WinTranslator reengineer- Contact: info@excelsoftware.com
code files, and foreign docu- ing tool to generate class Web Site: http://www.excelsoftware.com
ments.
Class models describe the types Developer Express Releases ExpressBars Suite 4
of objects in the system and vari- Developer Express Inc. released on your specific needs. Toolbars
ous kinds of static relationships ExpressBars Suite version 4, a can wrap their items across mul-
that exist among them. Rich toolbar, menu, and sidebar system tiple lines, allowing your users
ISBN: 0-07-213139-X class models may contain classes, for Delphi and C++Builder devel- access to all application func-
Cover Price: US$59.99 interfaces, components, packages, opers that offers complete emula- tionality regardless of window
(419 pages, CD-ROM)
and other objects. Flexible pre- tion of the look and feel standards size. Status bars have been static
http://www.osborne.com
sentation options allow different defined by Microsoft — from and defined by developers at
instances of a class to be presented Office 97-2000 to Office XP. design time. With ExpressBars 4
Transactional COM+: Building with its full list of attributes and ExpressBars 4 removes limita- status bars, you can allow your
Scalable Applications operations, compressed to show tions with its new features: Use users to fully customize an appli-
Tim Ewald
just the class name or selectively the docking control to fine-tune cation’s status bar to meet their
Addison-Wesley
showing class members relevant to the placement of toolbars in specific needs.
the context of a specific diagram. container controls such as panels The ExpressBars 4 suite
Projects may contain hundreds and frames. The Container Con- includes ExpressBars Menu and
of diagrams accessible through a trol gives you the ability to Toolbar System, ExpressSideBar
contents view that provides easy embed any VCL control within Outlook Style Navigation
navigation and flexible diagram toolbars and menus; you can System, ExpressImage Controls,
management. even embed forms, panels, and InPlace Editors and Controls,
Sequence diagrams illustrate frames. With ExpressBars’ col- Online and PDF Help Files,
how objects interact with empha- lapsible menus, you can intro- Email Support, Express Support
ISBN: 0-201-61594-0 sis on the order in which duce an intuitive menu layout to Forum, Sample Project Files, and
Cover Price: US$39.95 things occur. Horizontal oper- your applications that eliminates optional Source Code (includes
(436 pages) ation connections are drawn sub-menu clutter and reduces versions for Delphi 3, 4, 5 and
http://www.awl.com/cseng between vertical lifelines under the time it takes users to get to C++Builder 3, 4, 5).
objects, modules, and packages. the core functions of your appli-
A project dictionary is automati- cation. Logical groups allow you Developer Express Inc.
cally produced as class and object to treat a group of controls as Price: ExpressBars Suite version 4, US$179.99
models are drawn, but designers a single unit for the purposes of (with source, single-developer license only) or
can also directly access dictionary showing, hiding, enabling, and US$129.99 (without source, single-developer
entries. Changes made to an disabling with a single line of license only). Upgrades and bundle discount
object in any model are instantly code. With the MRU support in packages available.
reflected throughout the project. ExpressBars 4, your applications Contact: info@devexpress.com,
QuickUML contains advanced can offer users a rich array of the (888) GO-DEVEX
features for multiple language most recently used items, based Web Site: http://www.devexpress.com

4 August 2001 Delphi Informant Magazine


On the ’Net
SOAP / XML / HTTP / Distributed Systems / Delphi 3-5

By Keith Wood

Using SOAP
Cleaner Remote Access with the
Simple Object Access Protocol

T he Simple Object Access Protocol (SOAP) is, according to the spec, a “lightweight
protocol for exchange of information in a decentralized, distributed environment.”
Based on XML, it defines a messaging framework for use in a distributed system.

The SOAP specification is currently a W3C Note messages can be combined to provide a request/
dated 8 May 2000. As such, it is intended for response pattern.
discussion only, although several organizations
— including Borland and Microsoft — are pro- The basic structure of a SOAP message is shown in
moting its use. It details the contents of the mes- Figure 1. An Envelope main element contains the
sages that are sent between distributed objects, message. Its namespace must be as shown, because
describes an encoding scheme for representing this value defines the version of SOAP in use, and
data within those messages, and sets out a con- a SOAP server must reject messages for versions
vention for using these to facilitate remote proce- it doesn’t recognize. Another optional attribute of
dure calls and responses. the Envelope may define the encoding scheme used
for data within the request. It should appear like
SOAP can be used with a variety of underlying the following, which denotes the standard SOAP
transport mechanisms; however, only HTTP is dis- encoding based on the XML Schema specification:
cussed in the specification. Using HTTP lets us
establish a SOAP server as a Web application and SOAP-ENV:encodingStyle=
communicate with it through the standard port. "http://schemas.xmlsoap.org/soap/encoding/"
This overcomes problems in opening other ports
for a more specialized connection. An optional Header element may appear as the
first child of the Envelope (and nowhere else). It
Introducing SOAP surrounds any number of elements that contain
SOAP messages are simple XML documents with a items of “global” interest to the main request.
defined structure and content that depends entirely For example, it may define a transaction to use
on the application. Although basically a one-way in database accesses. Each element in the header
transmission from one object to another, SOAP may have a SOAP-ENV:mustUnderstand attri-
bute that’s set to 0 (False, the default), or 1
(True). When True, the server must acknowledge
<?xml version="1.0"?>
and process this header element, because it’s
<SOAP-ENV:Envelope
xmlns:SOAP-ENV= assumed to affect the request in some fundamen-
"http://schemas.xmlsoap.org/soap/envelope/"> tal way. If it cannot be actioned, an error must
<SOAP-ENV:Body> be generated.
<findMovies>
<rating>PG-13</rating>
</findMovies>
The Body element contains the actual requests,
</SOAP-ENV:Body> formatted as child elements. Parameters for each
</SOAP-ENV:Envelope> request appear as children of that element.
Such elements may have arbitrary types (defined
Figure 1: A SOAP message (request). through an attribute) that conform to the encod-

5 August 2001 Delphi Informant Magazine


On the ’Net

<?xml version="1.0"?> <?xml version="1.0"?>


<SOAP-ENV:Envelope <SOAP-ENV:Envelope
xmlns:SOAP-ENV= xmlns:SOAP-ENV=
"http://schemas.xmlsoap.org/soap/envelope/"> "http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body> <SOAP-ENV:Body>
<findMoviesResponse> <SOAP-ENV:Fault>
<movie>Entrapment</movie> <faultcode>SOAP-ENV:Client</faultcode>
<movie>Life is Beautiful</movie> <faultstring>No movies found</faultstring>
</findMoviesResponse> <detail>No movies found for rating 'NR'</detail>
</SOAP-ENV:Body> </SOAP-ENV:Fault>
</SOAP-ENV:Envelope> </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Figure 2: The SOAP response.


Figure 4: Returning a SOAP error.

Code Meaning
VersionMismatch The namespace on the SOAP Envelope is { Event signature for SOAP headers. }
unrecognized. TSOAPHeaderEvent = procedure(Sender: TSOAPDispatcher;
Name, Value: string; Attrs: TStrings;
mustUnderstand A header element with its mustUnderstand
MustUnderstand: Boolean;
attribute set to 1 is not understood, or
var Understood: Boolean) of object;
cannot be processed.
Client The message body is badly formed, or { The customised Web dispatcher for SOAP requests. }
does not contain enough information to TSOAPDispatcher = class(TCustomWebDispatcher)
process it. Some change is required to the private
body before the message can be re-sent. FName: string;
Server The request could not be processed due to FOnHeader: TSOAPHeaderEvent;
problems in the server. The message itself is FResponses: TList;
FSOAPFault: ESOAPFault;
fine and may succeed if re-sent at a later time.
FSOAPActions: TSOAPActions;
Figure 3: SOAP fault codes. FSOAPParser: TCustomSOAPParser;
procedure ClearResponses;
protected
ing scheme established in the envelope. Additional elements may function GetContent: string; virtual;
appear following the Body. procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
procedure SetSOAPFault(Fault: ESOAPFault);
Once a SOAP application processes a request, it sends a response procedure SetSOAPParser(Parser: TCustomSOAPParser);
in the form of another SOAP message. Again we have the SOAP procedure SOAPAction(Sender: TObject;
Envelope and Body tags with appropriate namespace declarations. Request: TWebRequest; Response: TWebResponse;
Within the body we have an element that contains the result of var Handled: Boolean); virtual;
public
the request. By convention, this element is named the same as the
constructor Create(AOwner: TComponent); override;
request element with the string “Response” appended. It surrounds destructor Destroy; override;
the elements that provide the actual reply. Figure 2 shows a possible property Content: string read GetContent;
response to the message in Figure 1. property SOAPFault: ESOAPFault
read FSOAPFault write SetSOAPFault;
procedure DoHeader(Name, Value: string; Attrs: TStrings;
If some error condition arises, a response is still sent, but instead it MustUnderstand: Boolean; var Understood: Boolean);
contains a SOAP-ENV:Fault element in the body. Within this appear procedure SetResponse(Name: string; Responses: TList);
a number of pre-defined elements that describe the problem. The published
faultcode element is intended for automated use, and consists of one property AfterDispatch;
property BeforeDispatch;
of a small number of generic types. The SOAP specification defines
property OnHeader: TSOAPHeaderEvent
those shown in Figure 3. We may define other types as necessary. read FOnHeader write FOnHeader;
property SOAPActions: TSOAPActions
In the faultstring element we find a human-readable description read FSOAPActions write FSOAPActions;
of the problem. Both these two elements must always be present. property SOAPParser: TCustomSOAPParser
read FSOAPParser write SetSOAPParser;
The faultactor element identifies the actor that generated the end;
error and is only required when multiple actors are used. Finally,
the detail element must appear whenever the error results from
processing the body of the request, and must not be present Figure 5: A SOAP dispatcher.
otherwise. It contains further details about the problem, such as
internal error codes, a stack trace, etc. Figure 4 shows an example the supplied URI identifying the request. Specifying an empty string
of an error response. means that the HTTP URL itself denotes the call:

When transmitted via HTTP, any error response must be sent back SOAPAction: "http://www.movies.com/findMovies.dll"
with an HTTP status code of 500, indicating “Internal server error”.
For valid responses the return code is the usual 200, for “OK”.
Incoming SOAP requests over HTTP must also include a header Processing SOAP
field that indicates the purpose of the message. This allows firewalls Using SOAP over HTTP is the only combination described in the
to easily filter requests. The header looks like the following, with specification, although other transport mechanisms could be used.

6 August 2001 Delphi Informant Magazine


On the ’Net

{ Event signature for SOAP requests. } { Process a SOAP request. }


TSOAPInvokeEvent = procedure(Sender: TSOAPAction; procedure TSOAPDispatcher.SOAPAction(Sender: TObject;
Params: TStrings; Request: TWebRequest; Request: TWebRequest; Response: TWebResponse;
Response: TWebResponse; var Handled: Boolean) of object; var Handled: Boolean);
var
{ A single SOAP action. } Index: Integer;
TSOAPAction = class(TCollectionItem) SOAP: TSOAPRequest;
private SOAPQuery: string;
FName: string; begin
FOnInvoke: TSOAPInvokeEvent; SOAP := nil;
protected try
function GetDispatcher: TSOAPDispatcher; try
function GetDisplayName: string; override; if not Assigned(FSOAPParser) then
public
raise ESOAPFault.Create(SOAPFaultServer, NoParser);
procedure AssignTo(Dest: TPersistent); override;
if Request.Method = 'GET' then
procedure DoInvoke(Params: TStrings;
SOAPQuery := Request.Query
Request: TWebRequest; Response: TWebResponse;
else
var Handled: Boolean);
SOAPQuery := Request.Content;
procedure SetResponse(Name: string; Responses: TList);
{ Parse the incoming request. }
published
SOAP := FSOAPParser.ParseSOAP(
property Name: string read FName write FName;
Self, HTTPDecode(SOAPQuery));
property OnInvoke: TSOAPInvokeEvent
Handled := False;
read FOnInvoke write FOnInvoke;
{ Find a registered handler for it... }
end;
for Index := 0 to FSOAPActions.Count - 1 do
{ SOAP actions handled by a dispatcher. } if FSOAPActions[Index].Name = SOAP.Name then begin
TSOAPActions = class(TCollection) { ... and call it. }
private FSOAPActions[Index].DoInvoke(SOAP.Params,
FOwner: TSOAPDispatcher; Request, Response, Handled);
function GetItem(Index: Integer): TSOAPAction; Break;
procedure SetItem(Index: Integer; end;
const Value: TSOAPAction); if not Handled then
protected raise ESOAPFault.Create(SOAPFaultClient,
function GetOwner: TPersistent; override; Format(UnknownRequest, [SOAP.Name]));
public except
constructor Create(Owner: TSOAPDispatcher); on Error: Exception do begin
property Items[Index: Integer]: TSOAPAction { Trap errors and return to the caller. }
read GetItem write SetItem; default; if Error is ESOAPFault then
function Add: TSOAPAction; with ESOAPFault(Error) do
end; SOAPFault := ESOAPFault.Create(FaultCode,
FaultString, FaultActor, Detail)
else
Figure 6: SOAP actions defined. SOAPFault := ESOAPFault.Create(
SOAPFaultServer, Error.Message);
// Internal server error.
HTTP requests let us easily add SOAP functionality by extending Response.StatusCode := 500;
our Web server, and overcomes security problems in opening alter- end;
nate ports. Under this scheme, we write a Web application that end;
gets called for specific SOAP requests. The program knows how to finally
if Assigned(SOAP) then
extract the message, and to direct it to an appropriate processor for
SOAP.Free;
evaluation. It then packages up the response (or error) and sends it end;
back to the client. { Format the response. }
Response.Content := Content;
In true Delphi fashion, we can create components that encapsu- Response.ContentType := 'text/xml';
Handled := True;
late this functionality, and reduce writing a SOAP processor to
end;
dropping a couple of components on a form and coding an event
handler. In normal Web applications, the request comes into a
Web module that encapsulates a dispatcher for the query to one of Figure 7: Handling a SOAP request.
a number of actions. When we create a Web application through
the wizard, we have a Web module automatically generated for us. ties it to a customized handler. A SOAPParser property is added
An alternative is to replace that Web module with a standard data to connect with an XML parser that unpacks the message, while
module, and drop a WebDispatcher component on it to handle the OnHeader property lets us attach an event handler to respond
the forwarding. Since SOAP fundamentally alters the normal dis- to header elements. Figure 5 shows the declaration for the new
patch of requests, the easiest way to implement it is to build a dispatcher. This component and supporting code appear in the
replacement for the WebDispatcher component. SOAPDispatcher.pas unit. (The unit and entire example application
are available for download; see end of article for details.)
This new component, SOAPDispatcher, is derived from
TCustomWebDispatcher to pick up the basic request-handling mech- Instead of the Web actions of a standard dispatcher, the SOAP
anisms. However, it does not expose the Actions property since all one provides the SOAPActions property. This is a collection of
queries are processed in the same manner. Internally it registers a TSOAPAction objects, each of which matches a name with an
single action, setting it to deal with any incoming request, and event handler that knows how to deal with requests of that type.

7 August 2001 Delphi Informant Magazine


On the ’Net

{ A SOAP exception. } { Abstract base class for SOAP parsers. }


ESOAPFault = class(Exception) TCustomSOAPParser = class(TComponent)
private public
FDetail: string; constructor Create(AOwner: TComponent); override;
FFaultActor: string; function ParseSOAP(SOAPDispatcher: TSOAPDispatcher;
FFaultCode: string; SOAPRequest: string): TSOAPRequest; virtual; abstract;
function GetFaultString: string; end;
procedure SetFaultString(FaultString: string);
public
constructor Create(FaultCode, FaultString: string; Figure 9: SOAP parser class.
Detail: string = ''; FaultActor: string = ''); virtual;
property Detail: string read FDetail write FDetail;
{ Handle the "findMovies" SOAP request. }
property FaultActor: string procedure TdmdSOAP.SOAPFindMoviesInvoke(
read FFaultActor write FFaultActor; Sender: TSOAPAction; Params: TStrings;
property FaultCode: string Request: TWebRequest; Response: TWebResponse;
read FFaultCode write FFaultCode; var Handled: Boolean);
property FaultString: string var
read GetFaultString write SetFaultString; Movies: TList;
function XML: WideString; virtual; Rating: string;
end; begin
Movies := TList.Create;
try
Figure 8: The SOAP fault class. Rating := Params.Values['rating'];
with qryMovies do begin
if Rating <> '' then
When an incoming message matches one of these names, the cor- SQL[2] := 'WHERE rating = ''' + Rating + ''''
responding event is triggered, passing across the action object, any else
parameters extracted from the message, references to the HTTP SQL[2] := '';
request and response, and a flag to indicate that processing was Open;
while not EOF do begin
completed. Figure 6 shows the declarations for the collection and Movies.Add(TSOAPResponse.Create('movie',
its event handler. FieldByName('name').DisplayText));
Next;
end;
We should retrieve any required parameters within those event Close;
handlers, and process the request appropriately. A SOAP response if Movies.Count = 0 then
should be generated through the SetResponse method on the raise ESOAPFault.Create(SOAPFaultClient,
'No movies found',
action, passing in the name of the response element (usually
'No movies found for rating ''' + Rating + '''');
the action name with “Response” appended) and a list of Sender.SetResponse(Sender.Name, Movies);
TSOAPResponse objects, each of which has a name and associated end;
value. These are used to produce the elements and content of the finally
Movies.Free;
returned SOAP body. end;
Handled := True;
Processing the request to invoke an appropriate action is shown in end;
Figure 7. The entire incoming message is retrieved based on the
request method, before being passed to the XML parser connected Figure 10: The action handler in the SOAP server.
to the dispatcher. It returns a TSOAPRequest object that contains
the request name, and a list of parameters stored as name/value
pairs in a string list. We then iterate through the list of defined In a separate unit, SOAPOXParser.pas, we write a concrete imple-
SOAP actions to find a match and call its event handler. An error mentation of the SOAP parser, allowing us to only include the
is raised if no match is found. The HTTP response is formatted parser we want. In this case, the Open XML parser is used.
from the content established by the action handler (or an excep- After creating the XML parser itself, the DOM is built from the
tion), and returned to the client. incoming string. Thereafter the structure is traversed to extract the
various elements.
If an error occurs, we raise an ESOAPFault exception, as shown in
Figure 8. This has properties that map to those defined in the SOAP Numerous checks are made in accordance with the SOAP spec-
specification, letting us set their values during construction. The ification: no DOCTYPE declaration or processing instruction
XML function formats the values as a SOAP Fault element, ready for nodes may appear, the required Envelope and Body elements must
returning to the client. appear in the correct positions, the SOAP namespace (version)
must be correct, and headers that must be understood are indeed
Parsing a Request understood. Any violation generates an ESOAPFault exception
To avoid restricting SOAP processing through the SOAPDis- that is trapped by the dispatcher.
patcher component to a single XML parser, the TCustomSOAP-
Parser abstract class is defined (see Figure 9). It establishes the As header elements appear in the hierarchy, a call is made back
functionality required of a parser — namely to take a request as to the dispatcher in case an OnHeader event handler is available.
an XML string and return a TSOAPRequest object that embodies Upon return, the MustUnderstand and Understood flags are com-
that content. The assumption is that each call contains only a pared, producing an error if they are not compatible.
single request, and that its parameters are simple string values. A
subclass of this one must implement the ParseSOAP method using The contents of the Body element are compiled into the
the abilities of a particular parser. TSOAPRequest object constructed by this function. Each param-

8 August 2001 Delphi Informant Magazine


On the ’Net

Figure 11: The example SOAP server data module. Figure 12: The example SOAP client at run time.

eter is set as a simple string value against its element’s name in a { Compile the SOAP request and parse the results. }
string list. The action handler then retrieves these by name from procedure TfrmMovies.btnFindClick(Sender: TObject);
var
the Values property.
Element: IXMLDOMNode;
Request: string;
These components are compiled and made available to Delphi by
including them in a package. A new unit, SOAPReg.pas, contains { Extract the list of matching movies. }
procedure ListMovies(Node: IXMLDOMNode);
the registration calls for them, while an associated resource file, var
SOAPReg.dcr, supplies the icons for the Component palette. Index: Integer;
begin
SOAP Server for Index := 0 to Node.ChildNodes.Length - 1 do
with Node.ChildNodes.Item[Index] do
To demonstrate these components in action, we can build a Web lbxMovies.Items.Add(Text);
server application that responds to a “findMovies” request. It takes end;
one optional parameter, the rating, and returns a list of matching
begin
movies from those in the database table. Typical request and Screen.Cursor := crHourglass;
response documents appear in Figures 1 and 2. try
pgcSOAP.ActivePage := tshMovies;
lbxMovies.Items.Clear;
Before we begin creating the Web server application, download the memRequest.Lines.Clear;
latest Extended Document Object Model components by Dieter memResponse.Lines.Clear;
Köhler at http://www.philo.de/xml/dom. These are required by the Request := '<?xml version="1.0"?>' + '<' +
SOAPEnvPrefix + ':' + SOAPEnvelopeTag + ' ' +
TSOAPOpenXMLParser class discussed shortly.
NamespaceAttr + ':' + SOAPEnvPrefix + '="' +
SOAPEnvNamespace + '">' + '<' + SOAPEnvPrefix + ':' +
To build the server, first create a new Web server application based SOAPBodyTag + '>' + '<findMovies>';
on ISAPI. Begin by selecting File | New to display the New Items if cbxRating.Text <> '' then
Request := Request + '<rating>' + cbxRating.Text +
dialog box, and select Web Server Application from the New page. Then '</rating>';
remove the generated Web module from the project without saving Request := Request + '</findMovies>' + '</' +
its contents, and add a data module to the project by selecting Data SOAPEnvPrefix + ':' + SOAPBodyTag + '>' + '</' +
SOAPEnvPrefix + ':' + SOAPEnvelopeTag + '>';
Module from the New page.
memRequest.Lines.Text := Request;
htpSOAP.Header := SOAPActionHeader + ': findMovies';
Place SOAPDispatcher and SOAPOpenXMLParser components htpSOAP.Post(FApplication, Request);
on the data module (they’re automatically linked). Then drop memResponse.Lines.Text := htpSOAP.Body;
if not FXMLDoc.LoadXML(htpSOAP.Body) then begin
Session and Query components on the data module. Set the MessageDlg(FXMLDoc.ParseError.Reason,
Session’s AutoSessionName property to True, point the query at the mtError, [mbOK], 0);
Movie-watchers database, and enter its SQL statement. It must be Exit;
end;
entered on different lines as shown here, so the WHERE clause Element :=
can easily be replaced later: FXMLDoc.DocumentElement.FirstChild.FirstChild;
if Element.NodeName =
SOAPEnvPrefix + ':' + SOAPFaultTag then
SELECT * FROM Movie
MessageDlg('Error in request'#13 +
WHERE Rating = '?'
Element.ChildNodes.Item[1].Text,mtError,[mbOK],0)
else
Press the ellipsis button next to the SOAPActions property of the ListMovies(Element);
finally
dispatcher in the Object Inspector, add a new SOAP action, and Screen.Cursor := crDefault;
name it findMovies. Create an OnInvoke event handler for it, and end;
enter the code shown in Figure 10. This procedure retrieves the rating end;
value sent by the client (if there is one), and queries the database for
matching movies. Those found are placed in TSOAPResponse objects Figure 13: The OnClick event handler for the example applica-
for use in the response document. tion’s Find button.

9 August 2001 Delphi Informant Magazine


On the ’Net
The data module should look like the one shown in Figure 11. Conclusion
Finally, compile and deploy the application to the Web server. The Simple Object Access Protocol, being based on XML, provides
a platform- and operating system-independent way of communicating
SOAP Client between distributed objects. Part of its design goals are simplicity and
Next we need to create a client to talk to the server. Although we extensibility. It doesn’t take the place of more robust schemes, such as
could use a simple HTML page and have the returned XML show CORBA, because it provides no additional services, e.g. no distributed
up in the browser, a stand-alone client lets us more easily supply garbage collection, and no passing of objects by reference.
the variable rating parameter, and display the outgoing request
and incoming response. Furthermore, with IE we cannot see any The development of the SOAP components in this article lets us
error response, because the HTTP status of 500 overrides this and easily create a Web application that offers SOAP functionality. Just
displays its own message. drop the SOAPDispatcher component onto a data module, connect a
SOAP parser, hook up the actions, and compile.
The client application is very simple, as shown in Figure 12. It
contains a page control to allow for entry of the request, and then SOAP can provide a simple entry point into using remote procedure
displays both the SOAP request and its response. A combobox calls, letting us decouple the client and server, which may be using dif-
provides a selection of the standard movie ratings. Most of the ferent environments and programming languages. Instead of the Delphi
processing occurs behind the Find button; its event handler is shown client shown here, we could just as easily use Java, C++, or JavaScript.
in Figure 13. Watch for more development in this arena. ∆

The event handler first clears the listbox, request, and response Resources
memo controls. It then constructs the SOAP XML as a string value,  SOAP Specification: http://www.w3c.org/TR/SOAP
and copies it to the first memo field. An NMHTTP component  Open XML: http://www.philo.de/xml/
supplies the connectivity to the Web application. After setting the
required SOAP header, its Post method is called to send the SOAP This article is adapted from material for the forthcoming book Delphi
request. Any response appears in its Body property, which is copied Developer’s Guide to XML [Wordware, 2001].
into a memo field and loaded into a DOM for interpretation.
Microsoft’s DOM is used in this case. The project and database referenced in this article are available on the
Delphi Informant Magazine Complete Works CD located in INFORM\
If an error response is found, it produces a dialog box showing 2001\AUG\DI200108KW.
the reason. Otherwise, each entry in the response (a movie title) is
added to the main listbox. To generate a SOAP error, we just pick a Keith Wood is an Australian working his way around the United States. He’s
rating that has no matching movies, such as “NR”. a systems engineer with Active Health Management, based near Boston, and a
freelance technical writer. Keith started using Borland’s products with Turbo Pascal
By default the client looks for the local Web server, and an applica- on a CP/M machine. His forthcoming book, Delphi Developer’s Guide to XML
tion on it named cgi-bin/SOAPServer.dll. The two menu options [Wordware, 2001], covers many other aspects of XML. You can reach him via
on the form let us change these settings. e-mail at kbwood@CompuServe.com.

10 August 2001 Delphi Informant Magazine


On Language
Console Applications / Input/Output / Delphi 2-6

By Mike Edenfield

Console Applications
Part I: Exploding the Myths, Exploring the Basics

C onsole applications are applications that run in the Win32 character mode,
or the CUI (character user interface) subsystem. And yes, they look just like
applications used to look before the GUI took over the world: a limited character set
on a black background, as shown in Figure 1.

Despite its unassuming exterior, however, the console  Console applications run in a real-mode DOS
application is perfectly capable of performing myriad window. This is completely false. In fact, the
programming tasks, as this series will prove. More- reverse is true: DOS programs are run via a
over, it can often do the same job a GUI application console application named Winoldap, which
can, with an executable a tenth of the size. runs in a 32-bit console window. (Winoldap
is, however, a special type of console program
There are so many myths about console applications, that emulates a real mode by way of the x86
that I’d like to begin this series by debunking a few. “Virtual86” mode.)
Most of these misconceptions stem from the fact that  Console applications can’t use the Windows
console applications look and behave like DOS pro- API. Again, while it is possible to write a
grams. Here are some of the more common myths: console application without a single API call,
 Console applications aren’t really 32-bit. This when using only run-time library functions
myth arises because, in most Windows compil- the entire Windows API is available to your
ers, the standard “old” run-time input/output console application.
functions (e.g. Pascal’s Readln and Writeln) can  This one has three parts: console applica-
be used to read/write to the console. This is just a tions can’t display windows; GUI applica-
convenience, however: internally those functions tions can’t read/write to a console; console
use the same file input/output functions that any applications can’t use the VCL. Because con-
other 32-bit Windows application does. sole applications run without forms, many
people assume they can’t use any graphical
controls — or indeed, the entire VCL! This
isn’t true. A console is merely a special kind
of window that the OS allocates to a process,
which maps its standard input/output/error
file handles to read/write from that console.
Any application can allocate a console of its
very own, and a console application certainly
can create a window if it chooses.

Basic I/O
The first step to creating a console application
in Delphi is to make sure the compiler informs
Windows that your application should run in the
console subsystem. This is done via the:

Figure 1: A simple console application at run time. {$APPTYPE}

11 August 2001 Delphi Informant Magazine


On Language
compiler directive. (Note: Just as with any compiler directive, there {$APPTYPE CONSOLE}
can be no spaces between the braces and the directive itself.) Your program HelloWorld;
application can actually get a console other ways, but this is the
simplest. This directive causes several things to happen when your var
sName: string;
program starts:
 If the creating (parent) process has a console, the new (child) begin
process inherits it. Otherwise, the new process is allocated its Write('Who is this? ');
own console. Readln(sName);

 The child process’ standard file handles (Input and Output in Writeln('Hello, ', sName,
', welcome to the Console World!');
Delphi) are mapped to Read and Write from this new console Writeln('Hit <Enter> to exit');
window. Readln;
end.
The Pascal functions Readln and Writeln default to using Input
and Output as the files. In DOS, these file handles were mapped Figure 2: A simple “Hello World” application.
directly to the keyboard and screen buffers, respectively. In Win-
dows, they are mapped to the input and output buffers of the new As we touched on in “Basic I/O” earlier, when a new console is
console window. created, or a new process attaches to its parent process’ console, the
operating system automatically creates three “standard” file handles.
One important result of this close connection between Delphi’s read/ There are two API functions to manipulate these handles, which will
write functions and the Windows console is that source code from be used extensively in high-level console programming:
old DOS Pascal applications will generally compile with nothing
more than the addition of {$APPTYPE CONSOLE} somewhere in the function GetStdHandle(nStdHandle: DWORD): THandle; stdcall;
program file. The only caveats are that screen or graphical units such function SetStdHandle(nStdHandle: DWORD; hHandle: THandle):
BOOL; stdcall;
as CRT or BGI aren’t present (and, in fact, wouldn’t work on a
console), and that some of Pascal’s lower-level library functions (the
in/out functions, the direct register manipulation) are a no-no in These functions simply return a copy of the current standard file
protected-mode Win32. handle, or set the standard handle to a previously opened file handle
you supply. The three standard handles are:
In C, there is a difference between console and GUI processes, in  STD_INPUT_HANDLE — Corresponds to Delphi’s Input
that GUI processes name their entry point WinMain, while console variable.
processes simply call their entry point main. In Delphi, of course,  STD_OUTPUT_HANDLE — Corresponds to Delphi’s Output
the entry point is always the unnamed begin in the program’s main variable.
.dpr file. However, while typical Delphi applications simply call  STD_ERROR_HANDLE — Standard output (no Delphi
Application.Run; from this procedure, a console application may keep variable).
all of its code here. It’s possible (just as in Turbo Pascal, etc.) to use
other units, but the simplest console applications will contain most A very simple use of these functions can be found in Figure 3, which
or all of their code in the .dpr file. A short example of this can be uses a stream object to write to the standard output console window
seen in Figure 2 — the ever-popular “Hello World” application. (All instead of Writeln. (The run-time result of this code is shown in
of the sample applications presented in this article are available for Figure 1.) While this example is somewhat contrived, it does demon-
download; see end of article for details.) strate the typical usage of GetStdHandle, which is to get a handle that
can be passed to other Windows API calls that expect a file handle.
When working with small console applications, it’s often conve- SetStdHandle is generally used to redirect the standard file handles of a
nient to edit and compile them outside the IDE. Calling the Delphi child process (an advanced topic that will be discussed later in this series).
command-line compiler requires less memory than the full IDE,
is slightly faster, and doesn’t generate all the additional option and Advanced I/O: Reading Input Events
symbol files the IDE does when building and saving projects. While it’s possible to use basic I/O routines to read and write to the
console, there’s much more to it. Because a console is just a special
To compile applications from the command line, simply make sure type of window, there are API calls you can make to manipulate that
your {$DELPHI}\Bin directory is in your path. Windows makes this window and receive input from it, including mouse input. In this
rather simple, as it allows you to specify a batch file to run whenever section, we’ll discuss receiving keyboard and mouse events from the
a command-line shell is run. Right-click any “MS-DOS” shortcut console at a lower level than simple Readln library functions.
and enter a batch file name, then put the correct “PATH=” line in
there. Then call the DCC32 program passing it the name of your There are two levels of console input/output that Windows recognizes.
.dpr file, as in: We have already looked at the basics of what is termed “high-level”
console mode. The API calls used for high-level console input are:
C:\> DCC32 Listing1.dpr
function ReadFile(hFile: THandle; var Buffer;
and you will generate a Listing1.exe file, ready to be run. nNumberOfBytesToRead: DWORD;
var lpNumberOfBytesRead: DWORD;
lpOverlapped: POverlapped): BOOL; stdcall;
Advanced I/O: File Handles function ReadConsole(hConsoleInput: THandle;
Before discussing the more advanced I/O features that consoles offer, lpBuffer: Pointer; nNumberOfCharsToRead: DWORD;
a brief discussion of what console handles are, and how to use them, var lpNumberOfCharsRead: DWORD; lpReserved: Pointer):
is necessary. BOOL; stdcall;

12 August 2001 Delphi Informant Magazine


On Language

{$APPTYPE CONSOLE} {$APPTYPE CONSOLE}


program FileOutput; program ConsoleInput;

uses uses
Classes, Windows; Windows;

var var
strmOut: THandleStream; hInput: THandle;
hOutput: THandle; arrInputRecs: array[0..9] of TInputRecord;
dwCur, dwCount: DWORD;
begin cCur: Char;
{ Get the current output handle. } coorCur: TCoord;
hOutput := GetStdHandle(STD_OUTPUT_HANDLE);
{ Create a new handle stream with our output handle, begin
and use our stream object to write to it. } hInput := GetStdHandle(STD_INPUT_HANDLE);
strmOut := THandleStream.Create(hOutput); while True do begin
strmOut.Write('This is standard output.'+ #13#10, 26); ReadConsoleInput(hInput, arrInputRecs[0], 10, dwCount);
strmOut.Write('It shows up on the console screen!' + for dwCur := 0 to 10 - 1 do
#13#10, 36); case arrInputRecs[dwCur].EventType of
strmOut.Free; KEY_EVENT:
Writeln('Hit <Enter> to exit'); with arrInputRecs[dwCur].Event.KeyEvent do begin
Readln; cCur := AsciiChar;
end. if cCur = '' then
if bKeyDown then
Writeln('Unprintable key pressed.')
Figure 3: A simple example of the three standard functions. else
Writeln('Unprintable key released.')
These two functions, when used on a console window, behave almost else
identically. The differences are that ReadConsole can also read Unicode if bKeyDown then
character input from a console, while ReadFile can be used on non- Writeln('Pressed ', cCur, ' ',
console-handles, such as file and memory handles which may be redi- wRepeatCount, ' times.')
else
rected to standard input. (The console’s current code page determines
Writeln('Released ', cCur, ' key.');
if the input is ANSI or Unicode, a topic discussed later in this series.) end;
Unless you may need to read Unicode data, stick to Readln, which { Delphi has renamed MOUSE_EVENT to avoid a
maps to ReadFile and will handle redirected input handles properly. conflict with a Windows function mouse_event.
_MOUSE_EVENT:
with arrInputRecs[dwCur].Event.MouseEvent do
You’ll notice that both functions require a file handle as an argument. begin
This handle is extracted from the console via GetStdHandle, and in fact, coorCur := dwMousePosition;
nearly all of the API calls expect a console handle passed to them, either if dwEventFlags = 0 then
a handle to the input buffer or one of the output buffers. (Creating and Writeln('Button Pressed at ', coorCur.X,
',', coorCur.Y);
using multiple output buffers will be discussed later in this series.)
end;
end; // case
For even more control over the console input, there is a “low-level” end; // while
input method, which involves hooking into the console’s event end.
queue. This event queue behaves similarly to a window’s message
queue, but receives “input events” instead of messages. There are five Figure 4: Intercepting keyboard and mouse events.
key API functions used in low-level console input:

function ReadConsoleInput(hConsoleInput: THandle; is the Delphi alias for the INPUT_RECORD structure. The
var lpBuffer: TInputRecord; nLength: DWORD; structure is, in fact, a union of five types of input events. The first
var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
field, EventType, indicates which type of event record follows. A
function WriteConsoleInput(hConsoleInput: THandle;
const lpBuffer: TInputRecord; nLength: DWORD; complete discussion of each type of event is available on MSDN.
var lpNumberOfEventsWritten: DWORD): BOOL; stdcall; The two we’re most interested in are keyboard and mouse events.
function PeekConsoleInput(hConsoleInput: THandle; Briefly, the three less common types of events are:
var lpBuffer: TInputRecord; nLength: DWORD;
var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
 WINDOW_BUFFER_SIZE_EVENT — Sent when the screen
function GetNumberOfConsoleInputEvents(
size changes.
hConsoleInput: THandle; var lpNumberOfEvents: DWORD):  MENU_EVENT — Sent when the user uses the console’s menu
BOOL; stdcall; or toolbar.
function FlushConsoleInputBuffer(
hConsoleInput: THandle): BOOL; stdcall;
 FOCUS_EVENT — Sent when the console gets focus.
The WINDOW_BUFFER_SIZE event is pretty uncommon, as most
The hConsoleInput parameter, used by all five functions, is the result people leave their consoles at the fixed size given when they’re cre-
of calling: ated. The WINDOW_BUFFER_SIZE event only occurs in certain
console modes, and is generally used to repaint the screen if needed.
GetStdHandle(STD_INPUT_HANDLE) The other two events, MENU_EVENT and FOCUS_EVENT, are
used internally by Windows, and are mostly undocumented (they’re
The remaining parameters should all be fairly self-explanatory, not difficult to figure out). In fact, Microsoft’s documentation advises
except for the newly introduced TInputRecord type. TInputRecord that they “should be ignored.”

13 August 2001 Delphi Informant Magazine


On Language
people don’t have a 32-button mouse, only the first five have pre-
{$APPTYPE CONSOLE}
defined constants. For a typical three-button mouse, the constants
program ConsoleOutput;
you will use are:
uses  FROM_LEFT_1ST_BUTTON_PRESSED
Windows;  RIGHTMOST_BUTTON_PRESSED
var
 FROM_LEFT_2ND_BUTTON_PRESSED
hOutput: THandle;
sbiAttributes: TConsoleScreenBufferInfo; which correspond to the three least-significant bits of the DWORD
wDefColors: WORD; (in that order: left, right, second-from-left — keep this in mind if you
coorCurrent, coorTopLeft: TCoord; build your own bit masks!).
const
FOREGROUND_BRCYAN = FOREGROUND_GREEN or
The code in Figure 4 demonstrates how to intercept keyboard and mouse
FOREGROUND_BLUE or FOREGROUND_INTENSITY; events. When the program is run, a few interesting things occur. First,
begin note that CC, rather than printing out a message, exits the program.
hOutput := GetStdHandle(STD_OUTPUT_HANDLE); This is due to an internal key-press handler that Windows will install for
coorTopLeft.X := 1;
coorTopLeft.Y := 1;
you, specific to handle CC and Cak.
{ Read the default colors first. }
GetConsoleScreenBufferInfo(hOutput, sbiAttributes); You’ll notice that occasionally the string “ key.sed” appears immedi-
wDefColors := sbiAttributes.wAttributes; ately upon running the program. This is the result of R being
coorCurrent := sbiAttributes.dwCursorPosition;
released immediately after the program runs. It’s the string:
{ Change output color to bright cyan and move
cursor to top of screen. }
SetConsoleTextAttribute(hOutput, FOREGROUND_BRCYAN); 'Released' + #13 + 'key.'
SetConsoleCursorPosition(hOutput, coorTopLeft);
{ Write some stuff. } being printed, with the carriage return causing the printing to
Writeln('This test is in cyan.');
Writeln('As is this text.');
overlap.
Writeln('');
Writeln('Hit <Enter> to exit'); Lastly, the documentation for the KEY_EVENT_RECORD type speci-
Readln; fies that pressing and releasing A, without pressing any other keys,
{ Restore default colors. }
does not generate an input event. However, the sample program seems
SetConsoleTextAttribute(hOutput, wDefColors);
SetConsoleCursorPosition(hOutput, coorCurrent);
to indicate that the documentation is incorrect, and you will get both a
end. “pressed” and “released” message.

Figure 5: Positioning the cursor and changing the colors.


Advanced I/O: Writing to the Output Buffer
As with input, by using the standard run-time functions Write and
Writeln, we are accessing the console via high-level output functions.
The KEY_EVENT is generated whenever a key is pressed while The two basic high-level functions used are:
the console is active. This includes all normal printable keys,
plus any shift/control or function keys. This is analogous to a function WriteFile(hFile: THandle; const Buffer;
WM_KEYPRESS event, but is more informative in that you receive a nNumberOfBytesToWrite: DWORD;
var lpNumberOfBytesWritten: DWORD;
KEY_EVENT_RECORD type with the following data in it:
lpOverlapped: POverlapped): BOOL; stdcall;
 bKeyDown — True if the key was being pressed, False if being function WriteConsole(hConsoleOutput: THandle;
released. const lpBuffer: Pointer; nNumberOfCharsToWrite: DWORD;
 wRepeatCount — Number of times this key press occurred in var lpNumberOfCharsWritten: DWORD; lpReserved: Pointer):
a row. BOOL; stdcall;

 wVirtualKeyCode — Virtual key code for this key.


 wVirtualScanCode — Keyboard scan code for this key. Again, as with input, the Delphi functions map to WriteFile, as
 UnicodeChar — For Unicode input, contains the character pressed. opposed to WriteConsole.
 AsciiChar — For ASCII input, contains the character pressed.
 dwControlKeyState — Indicates the state of various control keys. In addition, there are high-level functions to move the cursor and change
the attributes of the characters it outputs. By default, console windows
The MOUSE_EVENT is generated when the console is in the correct support the ANSI character set, but not the IBM-ANSI character control
mode (which it is by default), and a mouse event occurs over the sequences for color, etc. (You can still load ANSI.SYS during the real-
screen buffer. This event is analogous to several different messages. mode initialization of Windows, and it will affect all console windows
The data record you receive has the following information: created, but there are better solutions.) Because a console is actually just
 dwMousePosition — Coordinates of the mouse when the event a character-mode GUI window, you can use other Windows API calls to
occurred. change colors, set cursor position, etc. The API functions are:
 dwButtonState — Bit mask indicating which buttons were
pressed. function SetConsoleCursorPosition(hConsoleOutput: THandle;
 dwControlKeyState — Indicates the state of various control keys. dwCursorPosition: TCoord): BOOL; stdcall;

 dwEventFlags — Indicates which event occurred. function SetConsoleTextAttribute(hConsoleOutput: THandle;


wAttributes: Word): BOOL; stdcall;
function GetConsoleScreenBufferInfo(
The only confusing part of this event is the button bit mask. It hConsoleOutput: THandle; var lpConsoleScreenBufferInfo:
contains space for 32 mouse buttons to be pressed, but since most TConsoleScreenBufferInfo): BOOL; stdcall;

14 August 2001 Delphi Informant Magazine


On Language
The last function is actually much more powerful than what we’re There are numerous, far more advanced techniques available for
using it for here, but we haven’t discussed console modes yet. For the your console programs, some of which were hinted at already. Next
time being, we will use it to extract the default position and color month’s article will detail the ability to create multiple, independent
attribute information, and save it. output windows, and explain how to override much of the default
behavior of consoles. This includes replacing the default CC
SetConsoleTextAttribute affects any characters drawn to the console handler, and altering the console modes that affect the behavior of the
from the point it’s called. That includes the default behavior input and output functions.
of echoing characters back to the console when they
are input, and of course, text sent to the console by Additionally, future articles will explain how to use console windows
the program. The attribute here isn’t a typical windows to perform several advanced programming tasks, including how to
RGB color, but rather the original 16 IBM-ANSI colors, handle threads in console applications, how to create a console in a
made up of various combinations of FOREGROUND_BLUE, GUI application, how to create GUI windows and message queue
FOREGROUND_GREEN, FOREGROUND_RED, in a console application, and how to redirect the input and output
FOREGROUND_INTENSITY, BACKGROUND_BLUE, handles of a console application. Lastly, the sample programs will
BACKGROUND_GREEN, BACKGROUND_RED, and finally begin to demonstrate real-world techniques that take advan-
BACKGROUND_INTENSITY. tage of the basic techniques shown here.

Combine any of these eight attributes with or to form the 16 base In the meantime, detailed explanations of all the API calls in this
ANSI colors for foreground and background. (Note to old DOS article can be found as part of Microsoft’s Platform SDK. The latest
programmers: The high bit of the background sets the intensity version of the Platform SDK is available on the MSDN Web site
flag for the background color, not the blinking attribute as it did (http://msdn.microsoft.com), or the latest MSDN CD set. Console
under ANSI.SYS.) Of course, all three together make white, none applications are listed under “Base Services” as part of the “Files and
make black, etc. I/O” section. You should definitely take time to review the descrip-
tions of the console functions, and — as always — experiment. ∆
As you can see in Figure 5, which demonstrates both positioning
the cursor and changing the colors, we store the attributes and The projects referenced in this article are available on the Delphi Infor-
coordinates before we make any changes. It’s always a good idea mant Magazine Complete Works CD located in INFORM\2001\AUG\
to store these and restore them before your program executes. Your DI200108ME.
application may be running attached to the console of a parent
application, which will still be on screen when you are finished with
it. Any changes you make to the console will remain in effect, even
if your program terminates, which can cause problems, or at least a
difficult-to-read color scheme, for the parent process.

Conclusion
We’ve only scratched the surface of what console applications can do.
At its most basic, console programming allows you to port a large Mike Edenfield is an applications developer for Sylvan Learning Systems in
portion of DOS Pascal code into the Windows environment with Baltimore, MD, and an MCSD. He has five years experience with Delphi and Visual
little change. But, as we’ve seen, the console window is actually a Basic, and specializes in Microsoft SQL Server development. He can be contacted at
full-featured user interface. It just uses characters instead of pixels. Michael.Edenfield@educate.com.

15 August 2001 Delphi Informant Magazine


Inside OP
Procedural Types / Event Handlers / Object Pascal / Delphi 1-6

By Rick Spence

Procedural Types
Putting Them to Use On Purpose

F ed up with writing yet another routine to search a StringList? Do you know about
procedural types? A procedural type is a native Pascal data type that you very well
may not know about, but are undoubtedly using.

In this article I’ll explain where you’re using them We’ll now declare a new type, which will allow
already, dissect the syntax, and suggest some situ- us to declare variables, which can reference one of
ations in which they are invaluable. Learn this these procedures:
and you’ll never need to write code to search a
StringList again. type TProcedurePtr = procedure;

The Basics This declares a new type, TProcedurePtr, as a refer-


From the Delphi 5 help file: “Procedural types ence (or pointer) to a procedure. We can then
allow you to treat procedures and functions as declare variables of that type:
values that can be assigned to variables or passed
to other procedures and functions.” In other var
words, you can store references to code in vari- MyProc : TProcedurePtr;
ables, which you can use to subsequently call
that piece of code. This takes a bit of getting Before you can use the variable, you must assign a
used to; you’re used to using variables to store procedure to it:
pieces of data, such as numbers or strings. Pro-
cedural types, on the other hand, allow you to MyProc := DisplayError;
store references to pieces of code. The variable
containing the procedural type is actually a It’s important to note that this assignment does not
pointer to the piece of code. call the DisplayError procedure. Rather, it stores a ref-
erence to DisplayError in the variable MyProc (actually
Let’s start with the basic syntax. Bear with me it stores its address). Alternatively, you could assign
during this contrived example; you’ll appreciate the the DisplayWarning procedure to the variable MyProc:
real uses in a moment. Imagine you have the fol-
lowing two procedures: MyProc := DisplayWarning;

procedure DisplayWarning;
begin In fact, as you will see, you can assign any proce-
ShowMessage('This is a warning'); dure that takes no arguments.
end;
Now we have a variable that references a pro-
procedure DisplayError;
begin
cedure. At some point we’ll want to run that
ShowMessage('This is an error'); procedure, which we do simply by using the vari-
end; able in a statement:

16 August 2001 Delphi Informant Magazine


Inside OP

procedure DisplayMessage(s: string); The syntax to the right of the equal sign may take a little getting
begin used to. Think of it as a normal procedure declaration without the
ShowMessage(s); procedure name.
end;

// ...
You can now declare variables of type TProcedureMsgPtr:
var
MyMsgProc : TProcedureMsgPtr; var
begin MyMsgProc : TProcedureMsgPtr;
MyMsgProc := DisplayMessage;
end;
but you must assign a procedure with one parameter of type string,
Figure 1: Assigning a procedure with one parameter of type as shown in Figure 1. If you attempt to assign to MyMsgProc a
string to a procedure variable. procedure that doesn’t accept one parameter of type string, the
compiler will complain.

MyProc; Now, when you invoke the procedure using the procedural type, you
must supply an argument:
This simple statement calls the subroutine referenced by the variable
MyProc. Think of it as an indirect call. This does not call a subroutine MyMsgProc('This is my message');
called MyProc — there isn’t one. MyProc is a variable. It calls the
subroutine to which MyProc refers, in this case DisplayWarning. Run This calls the DisplayMessage routine, passing the string to the
the following through the debugger to convince yourself: subroutine’s formal parameter named s. Take a moment to make
sure you understand this. MyMsgProc is a pointer to a subroutine
var that accepts a single parameter of type string. When you call the
MyProc : TProcedurePtr; procedure to which MyMsgProc points, in this case DisplayMessage,
begin
you must pass the proper argument. In fact, the following two
MyProc := DisplayError;
MyProc; // Call DisplayError lines of code have the same effect:

MyProc := DisplayWarning; MyMsgProc('This is my message');


MyProc; // Call DisplayWarning; DisplayMessage('This is my message');

That’s the basics of procedural types. You may not know how you The first is calling DisplayMessage indirectly, via a pointer to it. You
would use them yet, but by now you should understand the syntax. might ask, why bother? In this case you wouldn’t, you’d just call
DisplayMessage directly. Bear with me a moment longer.
Assigning Procedural Types
You can assign procedural types to each other in the same way you Procedural Types as Methods
assign other variables. You’re just copying the address of a procedure The procedural types you’ve seen so far have been references to
from one variable to another: stand-alone subroutines. You use a very similar syntax to work with
methods; in essence a method is a subroutine that can access an
var object’s data. To declare a procedural type that references a method
MyProc1 : TProcedurePtr; rather than a stand-alone subroutine, simply append the key words of
MyProc2 : TProcedurePtr;
object to the type declaration:
begin
MyProc1 := DisplayError;
MyProc2 := MyProc1; type TMethodMsgPtr = procedure(msg: string) of object;
...
MyProc1; // Call DisplayError var
MyProc2; // Call DisplayError MethodMsgPtr : TMethodMsgPtr;

As you’ll see in a moment, this is exactly what you do when you share
This is sufficient to inform the compiler that TMethodMsgPtr is a
event handlers among multiple components.
pointer to a method of an object, rather than a stand-alone proce-
dure. Then, when assigning it to a variable, use the object name
Procedural Types with Parameters followed by the method name:
What you just saw were two trivial procedures that did not receive
parameters. You can also use procedural types with subroutines that MethodMsgPtr := SomeObject.MsgMethod;
do take arguments, but you must declare the parameters along with
the type. Previously we used: and call it indirectly in the same way you’ve already seen:

type TProcedurePtr = procedure; MethodMsgPtr('Some string for the method');

to declare a new type, TProcedurePtr, which was a reference to a proce- Event Handlers Are Procedural Types
dure that did not take arguments. If the procedure has parameters, you Earlier, I said that you’re already using procedural types in your
must explicitly list them with their type as part of the type declaration: Delphi applications. Where have you been using them? With your
event handlers. Load Delphi’s help for TForm and look at the help for
type TProcedureMsgPtr = procedure(msg: string); one of the events, such as OnActivate. You’ll see it declared as:

17 August 2001 Delphi Informant Magazine


Inside OP

procedure TForm1.FormCreate(Sender: TObject); Controls’ events are declared in a similar way. Load help and look at
var the OnClick event of TButton:
iniFile : TIniFile;
s : string; property OnClick: TNotifyEvent;
sl : TStringList;
i : Integer;
mnuItem : TMenuItem; OnClick is a property, which is a pointer to a method that accepts one
begin parameter of type TObject.
iniFile := nil;
sl := nil;
If you poke around for long enough in the VCL source, you can
mnuItem := nil;
try find the Pascal code that responds to messages from the Windows
iniFile := TIniFile.Create(ExtractFilePath( API and calls the programmer-assigned event handlers. Since you’re
Application.ExeName) + 'MruFiles.Ini'); not obligated to write code for events, the VCL must check whether
sl := TStringList.Create; you assigned a handler, and only call it if you have. You will see the
iniFile.ReadSection('MruFiles', sl);
for i := 0 to sl.Count - 1 do begin
following sort of code throughout the VCL:
s := IniFile.ReadString('MruFiles',
sl.strings[i], 'N/A'); if Assigned(FOnDblClick) then
mnuItem := TMenuItem.Create(Self); FOnDblClick(Self);
mnuItem.Caption := s;
mnuItem.OnClick := MruOpen;
File1.Insert(2 + i, mnuItem); This statement (from the TControl class) calls the event handler
end; for the double-click event, if that event handler has been assigned
// Add a separator at the end. a value.
mnuItem := TMenuItem.Create(Self);
mnuItem.Caption := '-';
File1.Insert(sl.Count - 1 + 3, mnuItem);
Most events, but not all, are declared as type TNotifyEvent. TForm’s
finally OnClose event, for example, expects two parameters. Here are the
iniFile.Free; pertinent declarations:
sl.Free;
end; TCloseEvent = procedure(Sender: TObject;
end; var Action: TCloseAction) of object;
...
property OnClose: TCloseEvent;
Figure 2: A main form’s OnCreate event handler.

OnClose is declared as being of type TCloseEvent, which in turn


TForm1 = class(TForm) is declared as a pointer to a method that receives two parameters,
MainMenu1: TMainMenu;
the first one of type TObject, the second of type TCloseAction. The
File1: TMenuItem;
Open1: TMenuItem;
OnClose property, then, is a procedural type.
N1: TMenuItem;
exit1: TMenuItem; TForm’s OnCloseQuery event is also different:
OpenDialog1: TOpenDialog;
Button1: TButton;
type TCloseQueryEvent = procedure(Sender: TObject;
procedure Open1Click(Sender: TObject);
var CanClose: Boolean) of object;
procedure FormCreate(Sender: TObject);
...
procedure Button1Click(Sender: TObject);
property OnCloseQuery: TCloseQueryEvent;
private
{ Private declarations }
public
{ Public declarations }
OnCloseQuery is also a property, which is a procedural type. This
procedure mruOpen(Sender: TObject); time, however, the procedural type is a procedure that accepts two
end; parameters: the first of type TObject, the second a Boolean. When
you use the Object Inspector to define event handlers, Delphi is
Figure 3: The main form’s class declaration. generating entries in the .dfm file which assigns the address of the
event handler to the property used to reference it. To verify this, open
property OnActivate: TNotifyEvent; up a .dfm file as text, and you’ll see entries such as:

If you follow the hyperlink to the declaration of TNotifyEvent, object Button1: TButton
you’ll see: Left = 304
Top = 104
Width = 75
type TNotifyEvent = procedure(Sender: TObject) of object; Height = 25
Caption = 'Button1'
which you should now understand. Variables of type TNotifyEvent are TabOrder = 1
pointers to methods (note of object in the declaration) that expect OnClick = Button1Click

one parameter of type TObject. Events, then, are procedural types.


Put another way, events are implemented by having pieces of data As you can see, this entry is defining a TButton named Button1,
contain pointers to pieces of code. An object of type TForm, or — and declaring initial values for some of its properties, including the
more likely — a subclass of TForm, contains many such pieces of data OnClick property. Button1Click is a method of the form in which
that reference pieces of code. Button1 is declared.

18 August 2001 Delphi Informant Magazine


Inside OP

function slInsensitiveSearch(sl: TStringList; function slLeftSubString(sl: TStringList;


s: string): Integer; s: string): Integer;
var var
i : Integer; i : Integer;
lFound : Boolean; lFound : Boolean;
begin begin
i := 0; i := 0;
lFound := False; lFound := False;
while (i <= sl.count - 1) and (not lFound) do while (i <= sl.count - 1) and (not lFound) do
if UpperCase(sl.Strings[i]) = UpperCase(s) then if Copy(sl.Strings[i], 1, Length(s)) = s then
lFound := True lFound := True
else else
i := i + 1; i := i + 1;
if lFound then if lFound then
Result := i Result := i
else else
Result := -1 Result := -1
end; end;

Figure 4: A case-insensitive search of a StringList. Figure 5: A case-sensitive substring search of a StringList.

It’s very common to have toolbar buttons use the same event as handlers, and how you can use them to assign event handlers
a menu item. You may have a menu item labeled Open File, for dynamically. Now we’ll look at a more general use of procedural
example, and a toolbar button that performs the same action. To types — as parameters to subroutines. Because procedural vari-
accomplish this, write the code for the OnClick event of one of ables are simply variables that contain pointers to pieces of code,
the components, then use the Object Inspector to associate the you can pass them as arguments just as any other variable.
same event with the other component. Delphi is simply making
entries in the .dfm file to have the two OnClick properties refer- To see why this is useful, consider how you search a StringList.
ence the same event handler. The TStringList class has a method named IndexOf, which searches
a StringList for a particular string, and returns its position in the
Note that the Object Inspector only allows you to assign events that list. IndexOf is useless, however, if you want to perform a case-
have the same parameter lists. For example, if you’ve written code for insensitive search, a substring search, or search a StringList’s objects.
the OnCloseQuery event, you cannot assign this event to a push button’s
OnClick property. OnClick is declared as a TNotifyEvent, which accepts Fortunately, writing your own search routine is pretty easy. For
one parameter of type TObject. OnCloseQuery is declared as a TCloseQuery example, the code in Figure 4 performs a case-insensitive search.
event that expects two parameters. The two types are incompatible. This is a basic StringList search. It loops through each element
of the StringList, terminating when the item is found, or the
Dynamically Creating Controls StringList is exhausted. The routine uses the expression:
Another example of using procedural types is when you’re creating
controls dynamically, i.e. at run time. In this situation, you must UpperCase(sl.Strings[i]) = UpperCase(s)
assign the event handlers; otherwise the controls won’t do anything.
to determine if it has found the desired element.
Consider the code in Figure 2, which is taken from a main form’s
OnCreate event handler. The handler is reading a list of most- Now consider Figure 5, which searches a StringList for a string in
recently-used files from an .ini file, MruFiles.Ini, stored in the same which the item being sought can be a left substring of the item in
directory as the executable. It’s adding the names of these files as new the string list. If you are searching for the string “Spence”, and an ele-
menu items to the bottom of an existing menu item named File1. ment in the StringList contains “Spencer”, that would be considered
a valid match. In contrast, the routine in Figure 4 is performing an
Note how the code assigns the procedure MruOpen to the OnClick exact comparison (except for case). This routine uses the expression:
property of the new menu item. When the user clicks on any of
these new menu items, Delphi will call the MruOpen event handler. Copy(sl.Strings[i], 1, Length(s)) = s
For this to work, you must declare MruOpen in the form’s class
declaration, as shown in Figure 3. Then, to implement it as you to determine whether this is the desired element. Otherwise, the
would any other method, you write: routine is identical. So how would you perform a case-insensitive
substring search? You’d duplicate the routine, except you’d use the
procedure TForm1.mruOpen(Sender: TObject); following expression to perform the comparison:
begin
OpenFile((Sender as TMenuItem).Caption);
UpperCase(Copy(sl.Strings[i], 1, Length(s))) = UpperCase(s)
end;

How would you search the StringList for an object whose class name
Note how MruOpen is declared as a method that accepts one is passed as a parameter? You’d use the following expression:
parameter of type TObject. It must be declared that way, because a
TMenuItem’s OnClick event is declared that way. sl.Objects[i].ClassName = s

Procedural Variables as Arguments The point is that the only thing that’s changing is the comparison.
You’ve seen how Delphi uses procedural types to implement event Therefore, it should be possible to parameterize this expression

19 August 2001 Delphi Informant Magazine


Inside OP
somehow, and leave the rest of the routine alone. A procedural // This function is passed the StringList and index of the
parameter is the answer. We need a generic search routine in // item being examined. The function must return True if
which the routine’s caller can pass in an expression that defines // this is the desired element; otherwise False.
the comparison to make. The loop of this generic routine would type TSLSearchFunc = function(sl: TStrings; i: Integer):
Boolean;
then read something like:
while (i <= sl.count - 1) and (not lFound) do // Generic StringList search with procedural type
if CallUserSuppliedRoutine then // <-- Note // handling the comparison.
lFound := True function SLSearch(sl: TStrings;
Checker: TSLSearchFunc): Integer;
else var
i := i + 1; i : Integer;
lFound : Boolean;
The user-supplied routine will return True if this is the element it begin
wants, False otherwise. Obviously, the user-supplied routine must i := 0;
lFound := False;
know which element is being examined and which StringList is being while (i <= sl.count - 1) and (not lFound) do
searched, so the generic routine must pass these as parameters: if Checker(sl, i) then // <-- Note
lFound := True
while (i <= sl.count - 1) and (not lFound) do else
if CallUserSuppliedRoutine(sl, i) then // <-- Note i := i + 1;
lFound := True if lFound then
else Result := i
i := i + 1; else
Result := -1;
end;
Let’s look at the syntax we need to make this happen. The user-
supplied routine, the procedural type, must be a function that
Figure 6: A generic StringList search routine with a procedural
returns a Boolean, and expects two arguments: the StringList, and
parameter handling the comparison.
the position of the element. First, we’ll declare a new type named
TSLSearchFunc:
Result := UpperCase(sl.Strings[i]) = UpperCase(s);
// This function is passed the StringList and index of end;
// the item being examined. The function must return
// True if this is the desired element; otherwise False.
type TSLSearchFunc = function(sl: TStrings; i: Integer):
Then, just pass Insensitive to slSearch, thus:
Boolean;
i := slSearch(ListBox1.Items, Insensitive)

The generic routine must accept this as a parameter:


Now, for each additional type of search, you need only write a simple
// Generic StringList search with procedural Boolean function.
// type handling the comparison.
function SLSearch(sl: TStrings;
Conclusion
Checker: TSLSearchFunc): Integer;
Procedural types are probably the most underused type in Object
Pascal. As this article has demonstrated, they’re simply variables that
and must call it from inside the loop, passing the StringList and the contain the address of a subroutine, and as such you can treat them
element number as parameters: much like any other variable. You can assign them and pass them as
parameters. Delphi uses procedural types for event handlers, and you
if Checker(sl, i) then can use them yourself to parameterize the logic of a subroutine, as we
did here with a generic StringList search. ∆
Figure 6 shows the entire generic search routine.

So how do you call this routine? First you must write the routine
whose address you will pass to slSearch. As an example, here’s a case-
insensitive search routine, Insensitive, that looks for a string stored in
a global variable named s: Rick Spence is technical director of Web Tech training and Development
(http://www.WebTechCorp.com), a company with offices in the US and UK,
function InSensitive(sl: TStrings; i: Integer): Boolean; specializing in Delphi training and development.
begin

20 August 2001 Delphi Informant Magazine


OP Tech
Windows Help / Delphi

By Bill Todd

A Little Help
Adding Online Help to Your Applications

D elphi does a great job of giving you the tools to easily add Windows Help to
your applications. However, there’s limited documentation about how to use
them. This article will provide a complete overview of adding context-sensitive help
to Delphi programs.

Assigning a Help File to Your Program If you enter only the name of your help file in
The most obvious and easiest way to assign a help file the edit box, your application will look for the
to your program is to choose Project | Options from help file in the current directory, which is usually
the main menu, then select the Application tab in the directory where the EXE resides. You can
the Project Options dialog box, as shown in Figure also enter a relative path to the help file if you
1. Here you can enter the name of your help file in want to place the help file or files in a sub-
the Help file edit box, or click the Browse button to directory, e.g. help\helpdemo.hlp. In this case
locate your help file and add its path to the edit box. your program will look for the help file in the
The problem with using the Browse button is that it help subdirectory under the current directory.
inserts the complete path to your help file into the However, if your application changes the current
Help file edit box; this won’t work if a user installs directory, it won’t be able to find the help file.
your application in a different location. Even if your program doesn’t change the current
directory, what happens if a user decides to add
a shortcut to his or her desktop and, for some
reason, sets the Start In directory to some other
location? In either case, help will no longer work.

The safest way to assign a help file to your program


is to set the Application.HelpFile property at run
time. A good place to do this is in the main form’s
OnCreate event handler. If your help file has the
same name as your EXE and is in the same direc-
tory, add the following statement to your main
form’s OnCreate event handler:

Application.HelpFile := ChangeFileExt(
Application.ExeName, '.HLP');

The Application.ExeName property returns the full


path to your executable file. The ChangeFileExt
function changes the file extension from whatever
it is, in this case .EXE, to .HLP. If your help file
doesn’t have the same name as your EXE, or isn’t in
the same directory, use the following statement to
Figure 1: The Application page of the Project Options dialog box. assign the help file:

21 August 2001 Delphi Informant Magazine


OP Tech
Application.HelpFile := ExtractFilePath( Help Command Description
Application.ExeName) + 'help\helpdemo.hlp';
HELP_FINDER Displays the Help Topics dialog box.
HELP_FORCEFILE Ensures that the help file for your application
ExtractFilePath takes a path as a parameter, and returns the path with is being displayed. If not open, it’s opened.
the file name removed from the end. If Application.ExeName returns HELP_KEY Displays the topic that contains the specified
c:\demo\helpdemo.exe, then ExtractFilePath(Application.ExeName) keyword. If more than one topic contains the
returns c:\demo\. This will set Application.HelpFile to c:\demo\help\ keyword, displays the Topics Found list box
so the user can choose a topic.
helpdemo.hlp. Because setting the path in code using either of
HELP_QUIT Closes WinHelp.
these statements assigns the full path on the user’s machine to
HELP_SETWINPOS Sets the size and position of the help window.
Application.HelpFile, help should always work, even if the application
changes the current directory as it runs. Figure 3: HelpCommand constants.

The Application object is not the only VCL object that has a integer. The value of the second parameter depends on the first. In
HelpFile property. Using TForm you can have a separate help file this case, the second parameter isn’t used, so zero is passed. Passing
for any or all of your forms, as well. All of the considerations HELP_FINDER as the first parameter displays the Help Topics
for assigning the Application.HelpFile property apply to assigning dialog box, as shown in Figure 2. When you start your program and
a form’s HelpFile property. The best way is to set the property in display this dialog box the first time, the Contents tab is selected.
code in the form’s OnCreate event handler. Because a form’s help file If you select the Index tab and close the dialog box, the Index tab
won’t have the same name as the application’s EXE file, you’ll always will be displayed the next time you open the dialog box. The same
want to use code similar to this: is true of the Find tab. Figure 3 lists the most useful constants
you can pass as the first parameter to HelpCommand and what each
Self.HelpFile := ExtractFilePath( does. For information about other HelpCommand constants, search
Application.ExeName) + 'help\custform.hlp'; for “WinHelp” in the Microsoft SDK help file.

When you implement context-sensitive help using the HelpContext The Delphi HelpCommand method is simply a wrapper around the
property of a form, your program will look first to see if the form Windows API function WinHelp. The WinHelp function takes four
has a help file assigned. If it does, that file will be searched for the parameters. The first two are the window handle of the window calling
context ID. If the form’s HelpFile property is blank, the application’s WinHelp and the path to the help file. These are supplied automatically
help file is used. when you call Application.HelpCommand. The second two parameters
are identical to the parameters of Application.HelpCommand.
Adding Help to the Menu
The first choice on an application’s Help menu usually displays the You might also want to have choices on your Help menu to
table of contents of the help file. To do this, add the following line to display a specific topic, such as Getting Started or Tutorial. To
the menu item’s OnClick event handler: display a specific topic, use the Application.HelpContext method,
or Application.HelpJump method:
Application.HelpCommand(HELP_FINDER, 0);
Application.HelpContext(10020);
This line calls the Application object’s HelpCommand method. The
first parameter is a constant of type Word, and the second is a long Application.HelpJump('TOPIC_ONE');

HelpContext takes a single parameter, which is the context ID of the


topic to show; HelpJump takes the topic ID string as its parameter. You
can use whichever you find most convenient. Both are also wrappers
around the WinHelp Windows API function, but in this case the window
handle, help file path, and command constant parameters of WinHelp are
supplied by the method. All you have to supply is the context ID or topic
ID, which is placed in the Data parameter of WinHelp.

Another way to display help for a topic is to search for a topic by a key-
word that appears in the help file index using Application.HelpCommand,
as shown in Figure 4. Application.HelpCommand is called with the
HELP_KEY command constant. When HELP_KEY is passed as the first
parameter, the second parameter must be a pointer to a null-terminated
string that contains the keyword for which to search. In this code, the

procedure TMainForm.HelpForKeyword(Keyword: string);


var
HelpStr: array[0..255] of Char;
begin
StrCopy(HelpStr, PChar(Keyword));
Application.HelpCommand(HELP_KEY, LongInt(@HelpStr));
end;

Figure 2: The Help Topics dialog box. Figure 4: Displaying help by keyword.

22 August 2001 Delphi Informant Magazine


OP Tech
keyword passed as a parameter to the method is copied to the character procedure TMainForm.HelpForAKeyword(Keyword: string);
array HelpStr by the StrCopy procedure, which takes two PChars as var
its parameters. The first is the destination string; the second is the MultiKeyHelp: PMultiKeyHelp;
source string. MultiKeySize: Integer;
begin
MultiKeySize := SizeOf(DWORD) + SizeOf(Char) +
When you search for a topic by keyword, and only one topic contains Length(Keyword) + 1;
that keyword, the Windows Help system displays that topic. If two GetMem(MultiKeyHelp, MultiKeySize);
or more topics contain the keyword, Windows Help displays a dialog try
MultiKeyHelp^.mkSize := MultiKeySize;
box that lists the topics so the user can choose which one to view.
MultiKeyHelp^.mkKeyList := 'A';
StrCopy(MultiKeyHelp^.szKeyPhrase, PChar(Keyword));
Windows Help also supports keywords that don’t appear in the index, Application.HelpCommand(HELP_MULTIKEY,
commonly called “A” keywords (because they appear in a footnote LongInt(MultiKeyHelp));
whose footnote character is “A” in the rich text format file for the finally
FreeMem(MultiKeyHelp);
help topic). In addition to the standard “A” footnotes, you can have end;
keywords in footnotes that use other letters, with the exception of end;
“K”. “K” footnotes hold the keywords that appear in the index.
When you compile a Windows Help file, a separate keyword table is Figure 5: Using a hidden keyword.
created for each footnote letter that appears in the rich text files (from
which the compiled help file is created).

Searching for a help topic using a hidden keyword is more com-


plex because you need to pass two parameters to the Windows
Help system. The first is the keyword to search for; the second is
the letter that identifies the keyword table to search. This creates
a problem, because, as you have seen in the previous examples,
the Application.HelpCommand method only takes two parameters.
And the first is used for the command constant that tells the help
system what to do. The solution is to use the second parameter
to pass a pointer to a structure that contains the required informa-
tion. The structure is called MULTIKEYHELP; its declaration
from the Windows SDK help file is shown here:

typedef struct tagMULTIKEYHELP


{
Figure 6: The sample application’s main form.
DWORD mkSize;
TCHAR mkKeylist;
TCHAR szKeyphrase[1]; the values to the three fields in the MULTIKEYHELP structure,
} MULTIKEYHELP; then calls Application.HelpCommand with HELP_MULTIKEY as
the first parameter, and the pointer to the MULTIKEYHELP
The MULTIKEYHELP structure contains three fields. You must structure as the second. Finally, a call to FreeMem releases the
set the first, mkSize, to the size of the structure. The second, memory allocated for the MULTIKEYHELP structure.
mkKeylist, is set to the letter that identifies the keyword table to
search. The final field, szKeyphrase, holds the keyword to search for Context-sensitive Help
as a null-terminated string. At first it appears that this won’t work Delphi makes implementing context-sensitive help easy by providing
because szKeyphrase is only one character. The trick is to allocate a HelpContext property for all descendants of TWinControl. If a user
a block of memory for MULTIKEYHELP that’s large enough to presses 1, Delphi checks to see if the component with focus has a value
hold mkSize, mkKeylist, and the actual length of the keyword. assigned to its HelpContext property. If it does, the help topic is displayed.
Figure 5 shows the HelpForAKeyword method that does this. If it doesn’t, Delphi checks the control’s parent to see if it has a value
assigned to its HelpContext property. This process repeats until the form is
The Windows unit contains a declaration of the MULTIKEYHELP reached. Nothing happens if the form doesn’t have a value assigned to its
structure (as a Pascal record), and a pointer to it named PMultiKeyHelp. HelpContext property.
The HelpForAKeyword method begins by declaring the variable
MultiKeyHelp as type PMultiKeyHelpI, and MultiKeySize as an integer. You can see this in action with the sample program that accompanies
The method takes a single string parameter, Keyword, that contains the this article (see end of article for download details). Figure 6 shows
keyword for which to search. The first line of code computes the size of the main form. The form and the left-hand edit box both have
the block of memory required to hold the MULTIKEYHELP structure values assigned to their HelpContext property. If the left edit box has
as the sum of the sizes of the structure’s three fields, plus one. focus and you press 1, the Topic Two help screen appears, because
its context ID is assigned to the edit box’s HelpContext property. If
To compute the amount of memory to allocate the length of any other component, except the combobox, has focus and you press
the Keyword parameter, plus one is used so that the szKeyphrase 1, the Topic One help screen is displayed, because its context ID
field will be long enough to hold the keyword, plus a terminating is assigned to the form’s HelpContext property, and none of the other
null. When the amount of memory required is known, a call components on the form have a value assigned to their HelpContext
to GetMem allocates the memory and assigns its address to the properties. You’ll see why the combobox behaves differently under
variable MultiKeyHelp. The code in the try..finally block assigns “The OnHelp Event” section later in this article.

23 August 2001 Delphi Informant Magazine


OP Tech

function TMainForm.FormHelp(Command: Word; The OnHelp Event


Data: Integer; var CallHelp: Boolean): Boolean; Both TApplication and TForm have an OnApplication event that
begin fires whenever any of the Delphi help methods described in
if (ActiveControl = Combo) and this article are called, and whenever the user presses 1. There
(Combo.Text <> '') then begin
Self.OnHelp := nil;
are only two exceptions of which I am aware. First, invoking
try “What’s This?” help on a form by calling SendMessage to send the
HelpForAKeyword(Combo.Text); SC_CONTEXTHELP message to the form does not trigger the
CallHelp := False; event. Second, invoking “What’s This?” help in a modal dialog
finally
box by clicking the question mark button in the title bar, then
Self.OnHelp := FormHelp;
end; // try clicking a component, causes the OnHelp event to fire twice.
end; // if
end; Figure 7 shows the main form’s OnHelp event handler from the sample
application, and provides an example of using the OnHelp event handler
Figure 7: The form’s OnHelp event handler. to allow context-sensitive help to dynamically adapt to the user’s actions.
This code checks if the control that has focus is the combobox, and if the
What’s This? Text property is not null. If so, it sets the form’s OnHelp event handler
Another type of context-sensitive help that you may have seen in to nil, then calls the HelpForAKeyword method (described earlier), passing
some Windows applications is the help provided in dialog boxes the value of the Text property of the combobox as the keyword to
when you click on the question mark button in the title bar. When find in the help file. This causes a different help topic to be displayed
you do, the help cursor (an arrow and a question mark) appears, for each choice in the drop-down list. Next, the CallHelp parameter is
and you can click any component on the form to display its help set to False to tell the form not to call WinHelp, because the call to
topic. All you have to do to implement this type of help in your HelpForAKeyword has already displayed the help topic. Finally, the form’s
program is set the dialog box’s BorderStyle property to bsDialog, then OnHelp event handler is set back to the FormHelp method.
expand the BorderIcons property and set biHelp to True. As long as
each component, or one of its parents, has its HelpTopic property set, The form’s OnHelp event handler is set to nil to disconnect the event
users can click the question mark button in the title bar, then click handler from the event. This is necessary because HelpForAKeyword
the component to get help. calls the Application.HelpCommand method, which triggers the
OnHelp event handler again. If the event handler was still connected
There’s one problem you need to be aware of when using this to the event, you would be in a recursive loop and a stack overflow
type of context-sensitive help if you want to use the same help would occur. Disconnecting the OnHelp event handler before calling
topics for both dialog boxes and forms. Help topics displayed in a HelpForAKeyword prevents the event handler from being called again
dialog box by using the question mark button in the title bar are by the call to Application.HelpCommand. You’ll need to follow this
displayed in a popup help window, not a normal window. same procedure to call any of the Delphi help methods from an
OnHelp event handler, because they all trigger the OnHelp event.
This limits the amount of text you can display. Also, if the help
topic begins with a non-scrollable region, only the non-scrollable The OnHelp event handler has three parameters. The first two are the
region will appear in the popup help window. You can see context- same parameters passed to the WinHelp Windows API function by
sensitive help in a dialog box in the sample application by choos- calling Application.HelpCommand. The third parameter, CallHelp, is
ing File | Show Dialog Box. set to True by default. If it’s still True when the event handler exits,
the Windows Help system will be called with the same Command and
Since a dialog box cannot have a menu or toolbar, the question Data parameters that were passed to the event handler. Set CallHelp
mark button in the title bar of dialog boxes is provided as a to False if you don’t want the Windows Help system called.
way to get context-sensitive help. You can easily implement this
same type of help in forms by adding a “What’s This?” choice to Conclusion
the Help menu, and a corresponding button to the toolbar. The Adding help to Delphi programs is a snap thanks to the support for
OnClick event handler only needs the statement shown here: context-sensitive help that’s built into the VCL components and the
help-related methods of TApplication. The sample application that
SendMessage(Self.Handle, accompanies this article is a very simple one that uses a very simple
WM_SYSCOMMAND, SC_CONTEXTHELP, 0); help file, but it does demonstrate all the techniques you’ll need to
add a complete help system to any Delphi application. ∆
This sends a Windows message to the form that contains the code
telling the form to switch to “What’s This?” help mode. When the The sample application referenced in this article is available on the
form receives the message, the cursor will change to a pointer and Delphi Informant Magazine Complete Works CD located in INFORM\
question mark, and the user can click any component to get help. 2001\AUG\DI200108BT.

You can also provide context-sensitive help in dialog boxes you Bill Todd is president of The Database Group, Inc., a database consulting and
create by calling the MessageDlg function. The fourth parameter development firm based near Phoenix. He is co-author of four database program-
in a call to MessageDlg is the context ID of the help topic you ming books, author of more than 80 articles, a Contributing Editor to Delphi
want to associate with the dialog box. If you pass a context ID Informant Magazine, and a member of Team Borland, providing technical support
and include a Help button in the third parameter, the help topic on the Borland Internet newsgroups. Bill is also a nationally known trainer and
is displayed automatically when the user clicks the Help button. has taught Delphi programming classes across the country and overseas, and is a
Select File | Message Dialog from the sample application’s menu to frequent speaker at Borland Developer Conferences in the US and Europe. Bill can
see an example of this. be reached at bill@dbginc.com.

24 August 2001 Delphi Informant Magazine


Kylix Tech
Shared Libraries / Linux / Kylix

By Rick Ross

Shared Objects
Creating and Using Linux Shared Libraries

L inux shared libraries can dramatically reduce memory and disk space requirements
for applications that share common code. Applications that require a plug-in
architecture also benefit from shared object libraries. And of course, existing libraries
can be leveraged to reduce development time, and therefore time to market.

Kylix allows you to create shared object libraries as where library name indicates the name of the file.
easily as Delphi creates DLLs. Linux, however, has For Kylix, it’s also the name of the project (.dpr)
certain naming and versioning conventions that file. major, minor, and micro refer to the release
can make them tricky to write and use. This article numbers of the library.
will give you the necessary background to write
shared object libraries efficiently and effectively; The major release number indicates compatibil-
we’ll also discuss exception-handling techniques. ity at the interface, or API level. Any interface
change in a library that requires applications to
Before shared libraries existed, applications were be recompiled in order to work should incre-
required to link in references to all of the functions ment the major version number of the library.
used at compile/link time. The linker resolved all For example, in major release one, a function
addresses when the files were linked. When two or requires an integer parameter, but in release
more applications share the same code, they need two, the same function now takes a string as a
to be linked to the same source files. Using this second argument. For the application to work
technique, the file sizes and memory requirements with release two, make the appropriate changes
of the applications increased. While there’s nothing in the application, recompile, and relink.
wrong with this commonly used approach, shared
object libraries were created to help reduce these For minor releases, any changes refer to addi-
memory and file-size requirements. tional interfaces that have been added, and are
completely compatible with previous releases.
A shared object library is a group of functions All existing interfaces remain unchanged.
that the linker does not resolve until run time. Finally, a micro release does not add functional-
Instead, these functions are specially marked, so ity; changes are made to the implementation.
they’re resolved (assigned an address) by loading Typical changes that affect micro releases are bug
the appropriate shared object library when the pro- fixes and performance tuning.
gram is executed.
Having a file-naming standard is certainly a step
Versioning in the right direction. But using all the version
Windows DLLs and executables can embed ver- numbers to identify a library is overkill. Also, any
sion information into the resource section of the changes to a file name would bring disastrous results.
file. Unlike Windows, Linux has a built-in mecha- Therefore, each library has an internal identifier that
nism for identifying the version of a shared library. refers to the name and the version. This is called the
The file-naming convention of a shared object soname. It is given to the linker when creating the
takes the form of: shared object. The soname takes the form:

lib<library name>.so.<major>.<minor>.<micro> lib<library name>.so.<major>

25 August 2001 Delphi Informant Magazine


Kylix Tech

-rw-r--r-- 1 root root 44456 Aug 4 2000 libmenu.a variable LD_LIBRARY_PATH


lrwxrwxrwx 1 root root 12 Oct 7 03:33 libmenu.so -> libmenu.so.5 are searched. Second, the pro-
lrwxrwxrwx 1 root root 14 Oct 7 03:33 libmenu.so.4 -> libmenu.so.4.0 gram loader searches a cache
-rwxr-xr-x 1 root root 25168 Jul 12 2000 libmenu.so.4.0 file named /etc/ld.so.cache that
lrwxrwxrwx 1 root root 14 Oct 7 03:24 libmenu.so.5 -> libmenu.so.5.1
-rwxr-xr-x 1 root root 23860 Aug 4 2000 libmenu.so.5.1
lists directories where previous
libraries were found. Third, the
program loader searches the list
Figure 1: The output from the command: ls -l/usr/lib/libmenu*.
specified in the text file /etc/
ld.so.conf. Finally, if the library
Notice that the minor and micro releases are not part of the soname. hasn’t been located, the dynamic linker searches the /usr/lib directory.
This allows for bug fixes and performance enhancements to be For security reasons, programs that have the set user ID, or set group
released, and should not affect any applications that link to it. ID bits set on an executable, do not search the LD_LIBRARY_PATH
environment variable.
Another benefit of having the version number as part of the file name
is to keep older applications running, while improving or adding Compiler Directives
functionality. Linux allows older versions to remain alongside the Kylix has several compiler directives useful for working with
latest versions by using file links to point to the proper current shared object libraries: SOPREFIX, SOSUFFIX, SOVERSION, and
versions. Figure 1 shows the output, from my system, using the SONAME. All of them take one string argument. SONAME is used
following command: to set the internal library name, and the other three are used to
change the name of the output file. When using these file name
ls -l /usr/lib/libmenu* directives, they take the form:

The output contains three links that are indicated by the “->” char- <SOPREFIX><library name><SOSUFFIX>.so.<SOVERSION>
acters and the letter “l” as part of the permissions. There are two
major versions of libmenu: 4 and 5. By convention, the library name SOPREFIX defaults to “lib” for shared objects and “bpl” for pack-
without a version number points to the highest major version, 5 in ages. If the SONAME directive is used, it will create a symbolic link
this case. Then for each major version, there is also a link to the that points to the actual file name. For more information on these
most current release. Version 4 is a “zero release” (libmenu.so.4.0), directives, look in the Kylix help file.
while version 5 is “release one” (libmenu.so.5.1). Using a link to
point to the current version of a library can cause problems as well. Calling Conventions
Poorly written installation programs can change the link to point to a A calling convention is an agreement between the caller of a routine
version that’s incompatible with other installed applications. and the routine itself. All functions have a calling convention that
indicates how parameters are passed, how parameters are cleaned up,
When loading an application that uses a library, the program loader how registers are used, and how errors and exceptions are handled.
loads the shared object file name listed in the application’s function The default Kylix and Delphi calling convention is called register.
import table (the soname). If the program loader cannot find the For specifics regarding register and other calling conventions, search
library, an error message is displayed that indicates which version of the Kylix help under the topic “Calling Conventions.”
the library the application requires.
If a library is only going to be called by other Kylix applications,
If two or more libraries contain the same function definition, the first there’s no need to change it. However, applications written in another
library will be the one used. The search order used to look for librar- language normally use a different method of passing parameters. Two
ies is as follows: First, the directories specified by the environment of the most commonly used calling conventions are cdecl and stdcall.

library Project1;

{ Important note about shared object exception handling: In


order for for exception handling to work across multiple
modules, ShareExcept must be the first unit in your
library's USES clause AND your project's (select
Project-View Source) USES clause if 1) your project loads
(either directly or indirectly) more than one Kylix-built
shared object, and 2) your project or the shared object
aren't built with run-time packages (baseclx). ShareExcept
is the interface unit to the dynamic exception unwinder
(libunwind.so.6), which must be deployed along with your
shared object. }

uses
SysUtils, Classes;

begin
end.

Figure 2: The SO option in the New Items dialog box. Figure 3: Code generated for a new shared object.

26 August 2001 Delphi Informant Magazine


Kylix Tech
different exported name. That way both functions can be visible to
library Cube;
calling routines. Suppose, for example, a shared object contains two
uses functions named MyCube, with one version taking an integer and
SysUtils, Classes; another a double. The exports section would look like this:

function MyCube(x: Integer): Integer; stdcall; exports MyCube(x: Integer) name 'MyIntCube',
begin MyCube(x: Double) name 'MyDblCube';
Result := x * x * x;
end;
In shared object libraries, function imports and exports are bound by
exports name only, and are always case-sensitive.
MyCube;

begin Using Shared Objects


end. Using a shared object requires the binding of functions at either link
time (statically), or dynamically at run time. Most often they are
Figure 4: A function exported by name. statically linked. There are several ways of binding to a routine that’s
in a shared object:
library Cube;  Use a single-line definition.
 Write an import unit that contains a collection of related routines
uses
(Libc.pas is a good example).
 Dynamically bind to a routine at run time.
SysUtils, Classes;

function MyCube(x: Integer): Integer; stdcall;


begin Figure 6 demonstrates calling the MyCube function within the
Result := x * x * x; libCube.so library. It contains a simple console application that
end;
prompts for a number, then calls the MyCube function to calculate
exports the value. When running the demonstration application, make sure
MyCube name 'MyCube'; the environment variable LD_LIBRARY_PATH contains the current
directory, i.e. a period. For users of the bash shell, use the command:
begin
end.
export LD_LIBRARY_PATH=.:$LD_LIBRARYPATH

Figure 5: A function exported by an alias.


to add the current directory to the path used to locate shared objects.
Both pass arguments “backward” from right to left. The difference is
who is responsible for cleaning up afterward. For cdecl functions, the Single-line definitions work well when there are only a few functions
caller is required to clean up, while the called routine cleans up stdcall that need to be referenced. An example for the MyCube import can
functions. Whichever method is chosen, both the caller and the routine be found in Figure 7. Figure 8 contains an example of how to use the
need to use the same calling convention. Both calling conventions are import unit style of declaring external routines.
fully supported by the GNU C/C++ compiler on Linux.
An important item to note is that the function name used within a
Another calling convention is safecall, which does more than either Kylix or Delphi application doesn’t need to be the same as the name
cdecl or stdcall. In Windows, they are used frequently in COM of the routine in the shared object. This is accomplished by using the
methods. All routines that are declared as safecall have an implicit name directive to specify the exact, case-sensitive external name of the
return value of type HResult. They also wrap the entire function routine. Furthermore, if the name of the function used is the same as
with an implicit try..except block to catch any software exceptions the routine in the shared object, the name directive isn’t needed. Using
that may occur, which converts them to a HResult value. The the index directive in Kylix generates a platform warning, and binds to
safecall calling convention in Linux requires that all modules use the routine by name, ignoring the index directive.
the ShareExcept unit, described later in this article.
Dynamic binding waits until run time to search the shared object
Creating Shared Objects for a specific routine. Linux provides four functions for this purpose:
Kylix makes creating shared object libraries easy. They are created by dlopen, dlclose, dlsym, and dlerror. Figure 9 shows an example of
choosing the SO option on the New page of the New Items dialog dynamic binding. Although the binding to a routine occurs at run
box, shown in Figure 2. Once selected, Kylix generates the code time, the application still needs to know the arguments, return value,
shown in Figure 3. and calling convention at the time of the compilation. (Technically,
all function imports are resolved when the function is actually called,
Adding routines to a shared object is straightforward. Either add the not at load time. This is the default convention and can be changed
function or procedure in the .dpr file, or include the appropriate by setting the environment variable LD_BIND_NOW.)
unit by adding it to the uses clause. Once a routine is added, it
isn’t automatically available to be called outside the shared object. It Exceptions
must be explicitly exposed to the outside world by using the exports There are two kinds of exceptions in Kylix: operating system (or
keyword. Figure 4 shows an example of a function called MyCube external) exceptions, and software exceptions. External exceptions are
that is exported. An alternative way to export a function is shown descendants of the class EExternal, found in SysUtils.pas. Certain
in Figure 5. Linux signals are mapped to language exceptions, as shown in Figure
10. Note that SIGSEGV, SIGILL, and SIGBUS signals are mapped
When exporting overloaded routines, each routine must be given a to multiple exceptions, depending on the fault that occurs.

27 August 2001 Delphi Informant Magazine


Kylix Tech

program CubeTest; unit cubedef2;

{$APPTYPE CONSOLE} interface

uses function MyCube(x: Integer): Integer; stdcall;


// Pick one of the two listed below.
cubedef in 'cubedef.pas'; implementation
// cubedef2 in 'cubedef2.pas';
function MyCube; external 'libCube.so' name 'MyCube';
var
x : Integer; end.

begin Figure 8: Example of an import unit.


Writeln('Enter a number to cube..');
Readln(x);
Writeln(x, ' cubed is ', MyCube(x)); program DynamicCubeTest;
end.
{ $APPTYPE CONSOLE }

Figure 6: How to use the MyCube function from a shared object. uses
Libc;

unit cubedef; var


x : Integer;
interface handle : Pointer;
MyCubeFunc : function (x: Integer): Integer; stdcall;
function MyCube(x: Integer): Integer; stdcall;
external 'libCube.so' name 'MyCube'; begin
handle := dlopen('libCube.so', 0);
implementation if handle = nil then begin
Writeln('dlopen failed. ', GetLastError);
end. Exit;
end;
try
Figure 7: Single-line function import definition.
mycubefunc := dlsym(handle,'MyCube');
if @mycubefunc = nil then begin
Writeln('Error loading function MyCube. ErrMsg:',
By default, Kylix maps these signals to exceptions for applications, dlerror);
but not for libraries. However, there are a couple of run-time library Exit;
functions that can be used to map the signals to exceptions. They end;
Writeln('Enter a number to cube..');
are HookSignal and UnhookSignal and are found in SysUtils.pas. Readln(x);
Two other functions exist for more advanced signal handling: Writeln(x, ' cubed is ', MyCubeFunc(x));
InquireSignal and AbandonSignalHandler. Information regarding finally
these functions can be found in SysUtils.pas. dlclose(handle);
end;
end.
Here are the rules for dealing with shared objects and hardware
exceptions. Shared objects created with Kylix and not built with
Figure 9: Dynamically linking to a shared object routine at run time.
packages shouldn’t trap any signals when they’re loaded by a Kylix
application. Building a shared object with packages, that is loaded
Signal Description Kylix Exception
by a Kylix application also built with packages, can trap hardware
exceptions in the shared object. Finally, if a shared object created with SIGINT User Interrupt EControlC
Kylix is called by a non-Kylix application, it’s able to trap hardware (CC)
exceptions as long as the calling application, and any other libraries, SIGFPE Floating Point EDivByZero, EInvalidOp,
Exception EZeroDivide, EOverflow,
do not install signal handlers.
EUnderflow
SIGSEGV Segmentation ERangeError, EIntOverflow,
Exception handling in shared objects is messy because the POSIX Violation (AV) EAccessViolation, EPrivilege,
pthread and signal standard does not provide adequate support for EStackOverflow
multiple bodies of code to participate in signal handling for the process. SIGILL Illegal Instruction ERangeError, EIntOverflow,
Unfortunately, hooking POSIX signal handlers is very much like install- EAccessViolation, EPrivilege,
ing interrupt service routines: It’s easy to hook in and chain to the previ- EStackOverflow
ously installed handler, but it’s difficult to unhook without corrupting the SIGBUS Bus Error ERangeError, EIntOverflow,
(Hardware Fault) EAccessViolation, EPrivilege,
chain. When robust exception handling is needed, consider compiling
EStackOverflow
the shared object code as a package to remove these limitations. SIGQUIT User Interrupt EQuit
(Control Backslash)
ShareExcept Figure 10: Mapping Linux signals to Kylix exceptions.
The ShareExcept unit is used to handle exceptions between non-
package Kylix applications and non-package Kylix shared objects. This the first unit listed in the uses statement for both the library and the
unit is a wrapper for the libborunwind.so.6 that allows for exceptions application. Remember to deploy libborunwind.so.6 with any library
to cross from shared objects to the application. ShareExcept must be that uses the ShareExcept unit.

28 August 2001 Delphi Informant Magazine


Kylix Tech

library SafeSO; for your application when calling routines in shared objects. Recall
that there are two ways of referring to routines in a shared object:
uses statically and dynamically. And make sure that the calling conven-
ShareExcept, SysUtils, Classes; tions are identical when calling routines in shared objects.
function SomeSOFunction: Boolean; stdcall;
begin Exceptions need to be handled properly with shared objects. The
try general rule is to not allow exceptions to escape exported functions,
// Do something. and to make ShareExcept the first unit in the uses statement.
Result := True;
except
on e : exception do Shared objects allow for the division of large applications into
// Handle any exceptions. several functional modules. When multiple applications need the
Result := False; same functionality, the result is increased reusability. Kylix pro-
end; vides the tools necessary to unleash the power of shared objects,
end;
quickly and effectively. ∆
exports
SomeSOFunction; The author would like to thank Danny Thorpe, Eric Whipple, and
Ken Faw for their help and suggestions with this article.
procedure DLLHandler(Reason: Integer);
begin
// 0 means unloading, 1 means loading. Additional Resources
if Reason = 0 then  Kylix Help files
// Now we want to remove our signal handler.  SysUtils.pas
end;
UnhookSignal(RTL_SIGDEFAULT);
 Delphi for Linux Developer’s Guide by Eric Whipple, Rick Ross,
and Fletcher Bumpus, [Wordware Publishing, July 2001]
begin  Advanced Programming in the UNIX Environment by W. Richard
// We want to map Linux Signals to Kylix Exceptions, so Stevens, [Addison-Wesley, 1993]
// we call HookSignal to hook all the default signals.  ELF Standard, http://developer.intel.com/vtune/tis.htm
HookSignal(RTL_SIGDEFAULT);
// Install the Exit handler.
 “Library Interface Versioning in Solaris and Linux” by
DLLProc := @DLLHandler; David J. Brown and Karl Runge, http://www.usenix.org/
end. publications/library/proceedings/als2000/full_papers/
browndavid/browndavid_html
Figure 11: A sample libray that maps Linux signals to Kylix exceptions.  “Making Shared Libraries” by Luis Colorado,
http://www.linuxfocus.org/English/November1997/article6.html

When dealing with libraries, especially with non-Kylix applica- The files referenced in this article are available on the Delphi Informant
tions, make sure each exported library routine catches all software Magazine Complete Works CD located in INFORM\2001\AUG\
exceptions. Figure 11 shows a sample library that traps the external DI200108RR.
exceptions, and catches all exceptions that may occur in an
exported function. Remember that this example should only be
used if the calling application and any other libraries do not install
signal handlers.
Rick Ross is a Senior Consultant at PILLAR Technology Group, Inc. in Detroit, MI.
Conclusion He is a co-author of the upcoming book Kylix Development by Wordware Publish-
When designing and writing shared objects, remember to version ing. He has been developing utilities, database applications, and distributed
them properly. This will allow for the coexistence of new and exist- systems for DOS, Windows, Solaris, and UNIX for over 15 years. Rick is also an
ing applications that use them. Compiler directives play an impor- accomplished network designer and administrator and has worked with a number
tant role with versioning shared objects. Choose the best method of Fortune 500 companies.

29 August 2001 Delphi Informant Magazine


Columns & Rows
Microsoft SQL Server 2000 / XML / IE / IIS

By Alex Fedorov

Using the XML Features


of SQL Server 2000
Part I: An Introduction to the Features

T his series will show Delphi developers how to use the XML features of Microsoft SQL
Server 2000. There’s a lot of ground to cover, so we’ll take it in pieces. This month’s
article introduces the XML features.

Next month’s article will demonstrate how to use FOR XML Queries
Delphi to work with the Microsoft XML Docu- The XML-based data extraction is implemented
ment Object Model (DOM). Then the series will in SQL Server 2000 by the FOR XML clause
move on to XML templates, hooking up Delphi’s that’s part of the SELECT statement. Note that
data-aware controls to XML data, and other topics. this clause is only valid in a SELECT statement,
and can’t be used with other statements such as
This series assumes you’re familiar with the basics INSERT, UPDATE, etc. The syntax of the FOR
of XML and XSL. [See Ron Loewy’s “XML from XML clause is:
Delphi” and Keith Wood’s “XSL Transforma-
tions” articles in the July 1999 and November FOR XML Mode [ ,XMLDATA] [ ,ELEMENTS]
2000 issues of Delphi Informant Magazine.] [ ,BINARY Base64]

SQL Server 2000 XML Features The Mode argument specifies the shape of
Several new features introduced in Microsoft SQL the resulting XML document; it can be RAW,
Server 2000 make it an XML-enabled database AUTO, or EXPLICIT. The RAW mode returns
server. Among them are the ability to access SQL the XML document, where each row of the
Servers using the HTTP protocol; support for XDR resulting recordset is stored with the generic
(XML-Data Reduced) schemas, and the ability to <row /> element. Each column is mapped to an
specify XPath queries against these schemas; and the attribute of this element, and the attribute name
ability to retrieve and write XML data. The XML for equals the name of the column. For example,
Microsoft SQL Server 2000 Web Release provides this query:
even more functionality. It’s a free add-on for SQL
Server 2000, and is available for download at http://
SELECT *
msdn.microsoft.com/xml/default.asp. For example, FROM Customers
we can use the updategrams mechanism to easily WHERE CustomerID = 'AROUT'
update, insert, and delete data, as well as efficiently FOR XML RAW
bulk load large XML documents into SQL Server
2000 using XML Bulk Load functionality. returns the XML document shown in Figure 1.

30 August 2001 Delphi Informant Magazine


Columns & Rows

<row <Schema name="Schema3"


CustomerID="AROUT" xmlns="urn:schemas-microsoft-com:xml-data"
CompanyName="Around the Horn" xmlns:dt="urn:schemas-microsoft-com:datatypes">
ContactName="Thomas Hardy" <ElementType name="row" content="empty" model="closed">
ContactTitle="Sales Representative" <AttributeType name="CustomerID" dt:type="string"/>
Address="120 Hanover Sq." <AttributeType name="CompanyName" dt:type="string"/>
City="London" <AttributeType name="ContactName" dt:type="string"/>
PostalCode="WA1 1DP" <AttributeType name="ContactTitle" dt:type="string"/>
Country="UK" <AttributeType name="Address" dt:type="string"/>
Phone="(171) 555-7788" <AttributeType name="City" dt:type="string"/>
Fax="(171) 555-6750" <AttributeType name="Region" dt:type="string"/>
/> <AttributeType name="PostalCode" dt:type="string"/>
<AttributeType name="Country" dt:type="string"/>
Figure 1: Example result of a FOR XML RAW query. <AttributeType name="Phone" dt:type="string"/>
<AttributeType name="Fax" dt:type="string"/>

<Customers <attribute type="CustomerID"/>


CustomerID="AROUT" <attribute type="CompanyName"/>
CompanyName="Around the Horn" <attribute type="ContactName"/>
ContactName="Thomas Hardy" <attribute type="ContactTitle"/>
ContactTitle="Sales Representative" <attribute type="Address"/>
Address="120 Hanover Sq." <attribute type="City"/>
City="London" <attribute type="Region"/>
PostalCode="WA1 1DP" <attribute type="PostalCode"/>
Country="UK" <attribute type="Country"/>
Phone="(171) 555-7788" <attribute type="Phone"/>
Fax="(171) 555-6750" <attribute type="Fax"/>
/> </ElementType></Schema>

Figure 2: Example result of a FOR XML AUTO query. <row xmlns="x-schema:#Schema3"


CustomerID="AROUT"
CompanyName="Around the Horn"
<Customers> ContactName="Thomas Hardy"
<CustomerID>AROUT</CustomerID> ContactTitle="Sales Representative"
<CompanyName>Around the Horn</CompanyName> Address="120 Hanover Sq."
<ContactName>Thomas Hardy</ContactName> City="London"
<ContactTitle>Sales Representative</ContactTitle> PostalCode="WA1 1DP"
<Address>120 Hanover Sq.</Address> Country="UK"
<City>London</City>< Phone="(171) 555-7788"
PostalCode>WA1 1DP</PostalCode> Fax="(171) 555-6750"
</Customers> />

Figure 3: Example result from a FOR XML query in AUTO mode Figure 4: Example result of a FOR XML RAW query, using the
using the ELEMENTS argument. XMLDATA argument.

The AUTO mode returns the result as nested XML elements.


Each table in the FROM clause is represented as an XML EXPLICIT mode is used to explicitly define the shape of the resulting
element; columns are mapped to the appropriate element attributes. document. To use this mode you should write the queries in a particular
way, and provide additional information about the expected nesting of
For example, this query: the document. Further discussion of the EXPLICIT mode is outside
the scope of this article. For more information refer to SQL Server
SELECT * Books Online, currently found at http://msdn.microsoft.com/library/
FROM Customers
default.asp?URL=/library/psdk/sql/portal_7ap1.htm.
WHERE CustomerID = 'AROUT'
FOR XML AUTO
The optional XMLDATA argument can be used to return the XDR
returns the XML document in Figure 2. schema. This includes XML-formatted metadata, as well as the data
itself. For the following SQL query:
Note that the only difference in this XML document from the previ-
ous one is the name of the XML element. In the latter case it’s the SELECT *
FROM Customers
name of the table used to obtain the data. With AUTO mode, we
WHERE CustomerID = 'AROUT'
can use the ELEMENTS argument. In this case, the list of columns FOR XML RAW, XMLDATA
forms subelements.
the XML document with schema in Figure 4 will be received.
The following query: This format may look familiar to those who have used the
ADO Save method to produce an XML representation of the
SELECT *
FROM Customers
recordset.
WHERE CustomerID = 'AROUT'
The optional BINARY BASE64 argument is used to specify that a
FOR XML AUTO, ELEMENTS
query returns the binary data in the binary base64-encoded format.
returns the XML document in Figure 3. The following is an example of using this argument:

31 August 2001 Delphi Informant Magazine


Columns & Rows

Figure 5: XML data in Query Analyzer.

General Tag
Figure 8: XML in Internet Explorer.
Virtual Directory Name Northwind
Local Path c:\inetpub\northwind
Security Tag Now that we’ve learned how to query SQL Server 2000 to return
Set the options according to the type of your access to SQL Server: XML documents, it’s time to try it in action. First, however, we
Windows-based, SQL-based, or mixed. should configure a virtual root on a Web site to communicate with
Data Source Tag SQL Server.
SQL Server local
Database Northwind Preparing Virtual Directories
Settings Tag Go to the Programs | Microsoft SQL Server menu, and select
Configure SQL XML Support in IIS to invoke the IIS Virtual
Allow URL Queries ✓
Directory Management for SQL Server MMC application.
Allow Template Queries ✓
Allow XPath ✓ Here, we can create virtual directories for SQL Server databases
Allow POST accessed through URL requests. First, click on your server name,
Virtual Names Tab and select the Default Web Site element. Next, right-click on
Virtual Directory: northwind dbobject type
this element, and select New | Virtual Directory. You’ll get the
Virtual Directory: templates template type, physical catalog
New Virtual Directory Properties dialog box with several tabs:
c:\inetpub\northwind\templates General, Security, Data Source, Settings, and so on. Figure 6
Advanced Tab shows which options you should specify to run the examples in
this article.
Here you can specify additional parameters for Microsoft SQL
Server OLE DB Provider. For more information see SQL
Server Books Online. Writing XML and SQL Statements
Using the virtual directory just created, we can start to query the
Figure 6: Options to set for the examples in this article.
Northwind database using URL-based queries. This can be done
directly from the Web browser, or from the client application —
Symbol Hex Value Description the one we’ll implement with the help of Delphi. Please note that
+ %20 Space. because the queries will be specified as a URL, we can’t use some
/ %2F Separator for directories and symbols. For example, spaces should be replaced with the %20
subdirectories. escape sequence, or with the + sign. There are also other symbols to
? %3F Parameters separator between watch out for, as shown in Figure 7.
URL and actual parameters.
% %25 Indicates special character. The format of the URL query looks like this:
& %26 Parameters separator.
Figure 7: Symbols that require substitutes in a query. http://server_name/vdir_name?SQL=SELECT+*+FROM+Employees+
FOR+XML+AUTO&Root=Northwind
SELECT EmployeeID, LastName, FirstName, Photo
FROM Employees where server_name is the name of the IIS server, and vdir_name
WHERE EmployeeID = 1
is the name of the virtual directory. The query itself follows
FOR XML AUTO, BINARY BASE64
SQL=, and the Root parameter is used to specify the name of the root
element of the resulting XML document. If you run this query in
Before using the FOR XML queries in URLs, you can try them Internet Explorer, you’ll get the XML document shown in Figure 8.
in the SQL Server Query Analyzer (see Figure 5). To see the whole
XML document returned by the query, click on Tool | Options. Please note how the ? symbol is used to separate the parameter (the
Then, in the Results tab, specify 8100 as the Maximum characters SQL query) from the name of the virtual directory, and how the
per column value. Root parameter is separated with the & symbol.

32 August 2001 Delphi Informant Magazine


Columns & Rows

Windows Browser
client client

HTTP Request

IIS / ISAPI Filter


XSL-templates XDR-schemas
SQLOLEDB / SQLXML.DLL

Microsoft
SQL Server

Figure 9: SQL Server 2000 XML support architecture.

Implementation Details
Before we switch to Delphi, let’s briefly look at how XML support
is implemented in SQL Server 2000. The diagram in Figure 9
shows the entire architecture, separated into three layers.

The top layer is Windows or Web-based clients that consume the


data stored in the SQL Server database using URL-based queries.
As we already know, this can be queries written directly in the
application code, or template-based queries. The latter are stored
on the middle layer and accessed through the HTTP protocol.

The middle layer consists of Internet Information Server (IIS)


with the set of special virtual directories to access various data-
bases on the SQL Server. In case of accessing such virtual
directories, IIS utilizes the ISAPI filter implemented in the
SQLISAPI.DLL file. This filter communicates with the OLE DB
provider for SQL Server (SQLOLEDB) that establishes the con-
nection with SQL Server. All XML functionality is implemented
in the SQLXML.DLL file that receives the XML query, executes
it, and returns the result to SQLOLEDB.

The bottom layer is the SQL Server itself. Manipulated through


SQLOLEDB, it extracts, inserts, updates, or deletes the data, and
returns results. Also, the FOR XML and OPENXML logic is
implemented on the SQL Server.

Conclusion
That’s the essentials of Microsoft SQL Server 2000 XML support.
Next month, we’ll learn how to use Delphi to work with the Micro-
soft XML Document Object Model (DOM). See you then. ∆

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

33 August 2001 Delphi Informant Magazine


New & Used

By Robert Leahey

ModelMaker Code Explorer


A Powerful Enhancement to the Delphi Code Editor

B ased in the Netherlands, ModelMaker Tools offers two products to Delphi program-
mers. The first, ModelMaker, is a CASE tool for Delphi which supports reverse-
engineering, UML, documentation generation, etc. It’s a stand-alone application that
interacts with Delphi through the Open Tools API.

The subject of this review is ModelMaker Code You can create and edit classes and class mem-
Explorer (MM Code Explorer), a Delphi expert bers, copy or move members between classes, and
that exists in the Delphi IDE. (I reviewed Model- change the ancestors of classes — all within the
Maker version 5 in the October 1999 issue of MM Code Explorer window. As you make these
Delphi Informant Magazine. ModelMaker won Best changes, the code in the active unit is automati-
CASE Tool in Delphi Informant Magazine’s 2001 cally updated by the expert to reflect your edits.
Readers Choice Awards.) MM Code Explorer does all this in a quick and
responsive manner.
ModelMaker has become an invaluable part of my
toolkit. The product is so powerful and versatile A Closer Look
that I cannot imagine Delphi development with- The main window of MM Code Explorer is
out it. However, that power and versatility has shown in Figure 1. The upper pane contains a
made me dissatisfied with the Delphi IDE; many hierarchical representation of the classes in the
ModelMaker elements belong in the Delphi IDE. active unit. As you change active units, this list
updates to reflect the classes in the new unit. Note
My discontent is now waning, because Model- that the tree is displaying classes and interfaces,
Maker Tools has released a new expert for the and a Module node, which represents unit-level
Delphi IDE: ModelMaker Code Explorer. MM code elements.
Code Explorer, a direct replacement for Delphi’s
Code Explorer, brings many of ModelMaker’s This isn’t a static view; within this pane you can
award-winning editing features directly to the create and edit class and interface declarations,
Delphi IDE. navigate to class declarations, or drag and drop
classes to change their ancestors. One of the most
What Does It Do? powerful features of the expert is its ability to
MM Code Explorer is technically an expert, but copy and paste whole classes within this pane.
that term tends to minimize its value. MM Code Imagine you are refactoring your code, and need
Explorer integrates seamlessly with Delphi, only to change the location in which a class resides.
to make me realize how Delphi is supposed to You can select the class’ current unit, copy (or
work. Basically it parses the active unit to pro- cut) the class from within MM Code Explorer’s
vide an organized display of its code elements. Class Tree, select the new unit, and paste the

34 August 2001 Delphi Informant Magazine


New & Used
class there. The entire class declaration and implementation will be This kind of intelligent class member handling permeates the MM
inserted into the new unit. Code Explorer. If, for example, I rename a property in the member
list, that property’s associated access methods and/or state field are
In the lower pane, you will see a listing of the members of the automatically renamed as well. I don’t have to remember to search
currently selected class or interface, or methods of the unit if the for the related members to rename them. Likewise, if I copy and
Module node is selected. Via this Member List, MM Code Explorer paste a member from one class to another, again, the associated
does a good job of condensing what can be an overwhelming amount members are copied and pasted as well.
of information into a manageable list. In this list you will see not just
members (fields, properties, and methods), but — depending on your Just think of what that can do to ease code management. For
preferences — you can see member type, visibility, method binding, example, imagine you need to move five properties from one class
property read/write status, and method parameters. To aid man- to another. To do this with the Delphi Code editor, you’d need to
ageability, you can locate the properties, move them; find any state fields associated
also sort and filter with those properties, move them; find access method declarations,
the member list in a move them; and finally, find the access method implementations,
variety of ways. As and move them. Whew! Refactoring can be a pain, eh?
with the class tree,
not only does the To move those five properties using MM Code Explorer, just select
member list display the five properties, copy, select the new class, and paste.
class members and
allow you to nav- A Word on Filtering
igate to the mem- Do you suffer from information super saturation? I certainly do,
bers in code, it also and all too often it’s in the form of some utility class that I’ve
affords a simple way allowed to grow overly large. Sometimes I find myself trying to
to create and edit locate some half-remembered method that I seem to recall was
members. somewhere in this class. Searching through all the members of a
class is a bit much in that case.
Notice in the screen
shot that there are Here’s where MM Code Explorer’s filtering becomes invaluable. If
buttons to add you look again at Figure 1, you’ll see two rows of buttons just above
fields, methods, the member list. The lower row is a set of filters that allow you to
properties and local specify whether to show the following: fields, methods, properties,
methods. Clicking local methods, visual components belonging to forms, and event
each button will handlers. You can also combine those filters with visibility filters.
bring up an For example, you might specify a filter that shows only methods
extensive dialog (not including local methods) which are private or protected. I’ve
box, which allows found this level of filtering extremely useful.
you to fully describe
the member being Customization
added. This is Many of us have coding standards to which we must adhere.
where some of the Therefore, any tool that’s going to generate code for us must be
automation, and able to meet those standards. The standards at my workplace, for
therefore the bene- instance, require us to prefix any function argument with an a.
fits, of MM Code Thus, if I’m going to name a parameter Index, it becomes aIndex.
Explorer are found. We find that this makes our code clear and easier to maintain, but
If you add a prop- using MM Code Explorer to generate any of my code wouldn’t be
erty, for instance, easy if I had to go alter my parameter lists every time the expert
you can specify the generated a write access method for me. Fortunately, MM Code
type of read/write Explorer has a set of options for coding style to which it conforms
access for the prop- when it generates code for you. By setting these options, you can
erty, and MM Code have MM Code Explorer generate code that looks like you went to
Explorer will auto- the trouble of coding it by hand.
matically generate
that access for you, Likewise, there are a wide variety of options to control how MM
whether a state field Code Explorer works, and how it looks. If you’re a long-time
or access methods. ModelMaker user and are very comfortable with how ModelMaker
In short, you can looks, you can set up MM Code Explorer to look like ModelMaker.
click one button, On the other hand, if you’ve never used ModelMaker and simply
specify a few set- want MM Code Explorer to resemble the rest of the Delphi IDE,
tings, and have a you can specify that as well.
property, a state
Figure 1: ModelMaker Code Explorer in field, and read/write Intelligent Code Maintenance
action, just to the left of Delphi’s Code access methods all Let me stress the value of refactoring code via MM Code Explorer.
editor. generated for you. I’ve recently been working on a set of QA cases for a module

35 August 2001 Delphi Informant Magazine


New & Used
Adding to the usability of this feature is the built-in ability to create
your own code templates from existing code. Custom-designed
code templates are stored as Pascal units (parameterized templates
contain special tags that you can specify) and can be registered
ModelMaker Code Explorer is a Delphi IDE expert that can dra-
with MM Code Explorer, so they’ll appear on the Code Templates
matically improve your use of time within Delphi. It offers power-
palette for easy access during development.
ful, intelligently implemented code navigation and code editing
features.
Another powerful feature is the Rearrange Code wizard. This dialog
box allows you to change the order in which class members appear
ModelMaker Tools in the interface and implementation sections. It features a pair of
Gerrit Beuze list boxes that display the current order of the members; you can
Stenenkruis 27 B manually reorder the members, or choose from an ordering scheme,
6862 XG Oosterbeek e.g. alphabetical, alphabetical by member type, etc. When you close
Netherlands the dialog box, the code will be rearranged as you specified. I’ve
found this feature to be exceedingly useful in keeping my code
E-Mail: support@modelmaker.demon.nl ordered by functional categories.
Web Site: http://www.modelmakertools.com
Price: US$75; multi-user discounts available. Some other nice features include:
 Member visibility conversion: the ability to select multiple class
members and change their visibility via the context menu.
 Member type conversion: the ability to convert a selected class
member from one member type to another. Say you have a
that generates SQL. As you can imagine, the possible cases could private field that you now need to make a property. Simply
run into the hundreds. My QA application has an individual unit right-click the field, select Convert to Property, and the MM
for each case, which performs the tests to validate the output. Code Explorer automatically generates a new property and state
After having created about 80 cases, I realized I needed to change field. It’ll even smartly remove the F prefix from the name of
the architecture of the QA application. This was going to involve the field for the name of the new property. Likewise, you can
adding some methods to every one of my test classes, one for each convert members to fields or methods.
case. If I had to do this by hand, I would still be correcting the  In-place editing: by selecting a class or member and editing its
units. However, by using MM Code Explorer, I was able to copy the name in the Class Tree or Member List, the edited name will
new methods from the first case, paste them into the other cases, be propagated in the unit’s source code. If you rename a class,
and make some necessary minor changes in a short time. the new name will appear in all its method implementations
automatically.
What was truly wonderful, and what saved so much time was
that when the methods were pasted to the new classes, the class Final Thoughts
names were changed in the code. In other words, when I copied Given the lack of any compelling improvements to the Delphi IDE
Tsql001Form.DoSomething from the first case, and pasted it into the in the last few years, it’s an understatement to say that MM Code
next, the code appeared as Tsql002Form.DoSomething. I didn’t need Explorer brings a welcome set of intelligent, effort-saving features
to go back and update the code I had pasted. Imagine if you were to my daily workspace. In the serious development world, where
doing this with just the Delphi Code editor; not only would you have so much of our time can be wasted on the minutia of physically
to copy and paste the code in segments (declaration, implementation, producing code, there should be more tools like this to smartly
etc.), but once you had pasted the code, you would need to go automate the process. If you’re currently using ModelMaker, you’ll
back and update the method implementations to reflect the new class welcome the addition of MM Code Explorer to your Delphi IDE.
name. With the MM Code Explorer, it was a one-step process. If not, do yourself a favor and try MM Code Explorer — you’ll
wonder how you’ve gotten by with the standard Delphi IDE. ∆
Advanced Features
We’ve seen some of the features of the MM Code Explorer that
can improve your productivity on an hourly basis. Now let’s look
at some features that may not be used often, but will still markedly
increase your productivity.

One particularly impressive feature is Code Templates, definable


patterns that can be easily inserted into your code. A good example
is the Array Property template that comes with the product: With
a class selected in MM Code Explorer, I can apply this template,
and an array property will be added to my class — complete with Until recently, Robert was Director of Product Management-ReportBuilder for
access methods, internal list, and a property to return the count of Digital Metaphors Corp. He now provides custom creative solutions (including
the internal list. This particular template is parameterized: When I report designs) via his company, Thoughtsmithy. He graduated with a degree in
activate the Array Property template, a dialog box will appear that Music Theory from the University of North Texas, but has been writing code since
allows me to specify the name of the new property, its type, the his Apple II+ and AppleBasic days. He has been programming in Object Pascal
name of the internal list which would provide storage for the new since Delphi 1 and currently resides in Texas with his wife and daughters. He can
property, and the name of the method to return the count of the be reached via his sporadically maintained Web site at http://www.leahey.com.
internal list. Then the code is generated to reflect those choices.

36 August 2001 Delphi Informant Magazine


New & Used

By Warren Rachele

Protection PLUS
A Software Licensing Toolkit

T he Internet and other forms of electronic software distribution are increasing in


popularity as channels between developers and purchasers for two reasons. First,
the buyer can get the software and immediately put it to use. Second, demonstration
or “lite” versions of software products give the user a hands-on opportunity to decide
whether the software meets an immediate need.

Simplifying the registration process on both design addresses these needs in unique ways.
counts increases user satisfaction, and — in the One unique aspect of the toolkit is that when
latter case — converts more tire kickers into reg- your application is compiled into an executable
istered owners. The developer wrestles opposing and distributed, Protection PLUS doesn’t display
interests: making it easier to register and license a fingerprint identifying itself as the locking
their software, while protecting their work from method of choice for the application. This fea-
unauthorized use. ture is designed to dissuade hackers who under-
stand the algorithms used on other competing
Protection PLUS tools, and therefore know how to crack the locks
Protection PLUS (Professional Licensing and on software products. Protection PLUS hides
Unlocking System) by Concept Software, Inc. is a itself to prevent this temptation. To extend this
software library that enables developers to seamlessly feature, the license files and keys are protected
integrate numerous software security and licensing and generated by a password and code seed pro-
functions into their products. The tool works alone vided by the user. This prevents purchasers of the
or with Concept Software’s companion product, Protection PLUS library from reverse engineering
Instant SOLO (Secure Online License Ordering), the codes to products they have purchased.
to add complete feature remote control to unlock
your product. Protection PLUS is designed to work Consider the creation of a demonstration version
in a variety of scenarios, from taking demonstration of your product as the simplest starting point
versions and converting them into full retail licenses in an examination of the library. The PLUS
to protecting network seat-licensed software. The system gives you a choice of disabling the copy
product has a vast array of abilities that include after an expiration date or a certain number of
enabling specific modules of your program, imple- uses. With its sophisticated monitoring of system
menting pay-per-use restrictions, and the ability to states, PLUS will prevent the user from gaining
rent or lease your product within specifically con- more usage by reinstalling the demonstration or
tracted time frames. backdating the system date. When the demon-
stration expires, you can provide a number of
Protection PLUS is a complex tool designed conduits for the user to register his or her copy.
for serious security needs. Its well thought-out A link to a Web site running SOLO can generate

37 August 2001 Delphi Informant Magazine


New & Used

Figure 1: The EZ Trial mode nag screen.

the necessary keys, or the user can call you. With your distribution
of the trigger codes, the client will automatically regain use of your
application and all the features you’ve enabled. You can also offer
to demonstrate the application, or simply unlock it altogether.

While the creation of a demonstration version of their software is


Figure 2: Enter data to control security of your application at
a goal of many shareware authors, Protection PLUS offers many run time.
other features. Control over networked applications is an impor-
tant feature of the PLUS system. Your software can be locked to
run on the server only, or controlled via the number of licenses
registered. Control of the use of the application can even be
extended to specific workstations within the network, if that level
of control is more advantageous to the developer.

Interspersed with these broad levels of application protection are


the nuances that distinguish Protection PLUS. The design of the
licensing controls used by the application allows you to distribute
your application with specific features disabled, creating a “lite”
version that can be individually or corporately enabled through reg- Figure 3: Setting the number of authorized LAN users for your
istration with you. If your product is a suite-type application, you product.
can individually enable the components of the suite, allowing users
to choose which parts of the application they want to purchase. You control the span of usage of the product, and allow the user to
can also offer a mixture of these two levels of security. unlock the full retail mode of your software. This nag screen is set
to fire up on startup to remind the user of the time remaining on
Protection PLUS is implemented via an ActiveX control or Win- their demonstration. Your application might be more subtle and
dows DLL. This provides you with an enormous amount of flex- provide this information on a help screen.
ibility in the use of the product. The control that you exercise over
your application is limited mainly by your designer’s imagination. Protection PLUS options put the ability to register the software right
The extensive security combined with its stealthy nature make at the user’s fingertips. The Visit Web Site button can take the user
this tool a desirable inclusion in your software destined for wide directly to your Web site via their browser to register the software.
distribution. The Unlock button displays the code fields that will be needed by
your application’s license file. When these codes are fed into the
Using Protection PLUS SofwareKey system, the proprietary algorithm will generate unique
Installing Protection PLUS into the Delphi environment is largely registration keys for the user to input and unlock their product.
a manual process. Running the supplied setup program installs the
LFEdit program and numerous sample files. The necessary headers Embedding the PLUS system into your application is only half of
(.H, .PAS) for the languages supported by the system are found the job. You must also generate a license file that’s distributed with
in the sample directories and are easily copied and added to your your application. The license file contains the parameters of your
development folders. Distributing your applications is simply a security scheme. Defining these parameters is a multi-step process
matter of compiling the executable and ensuring that the DLL file that begins with the creation of a product definition. This process
is included and registered by your installation program. You will is performed using the LFEdit program shipped with Protection
also include the necessary license file controlling the Protection PLUS. LFEdit is the license editing tool that gives you (in the
PLUS features of your program. registered version) the ability to define a unique set of properties
for each of the products you sell. The license files created for your
Putting Protection PLUS to use, however, involves much more application are encrypted and password protected. These license
than simply dropping a component onto a form, setting a few files can be stored in a file, the Windows registry, on a Web server,
properties, and compiling the application. Even in simple sce- or even in hardware keys.
narios, Protection PLUS requires quite a bit of hand coding to
embed the tool into your software. Figure 1 shows the system in The license file itself contains multiple fields used for the security
EZ Trial mode, a trialware-demonstration mode that allows you to control of your application. All the data you enter in the fields is

38 August 2001 Delphi Informant Magazine


New & Used
available at run time for use by your application. Though the fields
are all named, Protection PLUS leaves the application of each to
you. For example, in the General tab shown in Figure 2, you may
not care about the user’s name. You can put whatever data you Protection PLUS (Professional Licensing and Unlocking System) is
wish in this field and make it available to your application. For the a software library that lets developers seamlessly integrate numer-
trialware sample I built, I selected the Expiration Fields tab and ous software security and licensing functions into their products.
set the parameters to give the user a fixed length of time in which The tool works alone or with its companion product, Instant SOLO
to use the program. (Secure Online License Ordering), to add complete feature remote
control to unlock your product.
Trigger codes are the secret to the wide range of security options
available in Protection PLUS. You can trigger up to 50 different
actions within your program through this facility. Depending on Concept Software, Inc.
the functionality you want to provide to the user, you will choose 1017 Woodall Drive
one of the engine selections. As an example, Figure 3 shows the Altamonte Springs, FL 32714
dialog box used to set the number of authorized LAN users for
your product. Using the codes shown in your distribution, the user Phone: (407) 786-4457
will request a trigger code to expand the number of users. You E-Mail: tracy@softwarekey.com
will feed these numbers into the trigger-code engine and, using Web Site: http://www.softwarekey.com
the software’s proprietary algorithm, a unique trigger code will be Price: Standard, $349; Enterprise, $499; Internet $799.
generated. You then give the resulting codes to the client to activate
the feature.

Conclusion guess. Because the license files are protected via a programmer-
Protection PLUS provides all the tools necessary to protect your determined password and seed number, they’re virtually unbreak-
software. The wide range of options gives you the tools to meet able as well. All of this contributes to the tight control provided
the most complicated set of protection options. The design makes by Protection PLUS.
it as useful for the small developer who wants to make his or
her shareware into a trialware distribution as it is for the software This review has described only a few of the many features of Pro-
giant that wants to control every facet of the use of their software. tection PLUS. It’s a well-designed and well-implemented product.
Developers who rent or lease their software will appreciate the Software developers wanting to exercise some protection of their
ease with which they can now enable or disable their products. intellectual property would do well to consider this toolkit. ∆
Those who license their products on a seat-by-seat basis across a
network will also find it easier to control the use and distribution
of their product.

The transparency of Protection PLUS adds an additional layer of Warren Rachele is Chief Architect of The Hunter Group, an Evergreen, CO software-
security to your products in the current environment in which development company specializing in database-management software. The com-
hacking codes are publicly traded on Usenet. None but the most pany has served its customers since 1987. Warren also teaches programming,
sophisticated of users will be able to detect the signature of the hardware architecture, and database management at the college level. He can be
security system, so their cracking efforts will be at best an educated reached by e-mail at wrachele@earthlink.net.

39 August 2001 Delphi Informant Magazine


TextFile

Cryptography and E-Commerce


In part, the explosive growth of adequately describes three popu- person, because they confront
the Internet can be attributed lar security technologies: symmet- and challenge the reader with the
to the Secure Sockets Layer rical key, Kerberos, and public need to understand the under-
(SSL) technology developed by key cryptography. lying mathematics of the field,”
Netscape. SSL brought the ability Graff dedicates a high percentage
to secure protected commercial Even when interjecting intricate of content to the discussion of the
transactions using the nascent mathematical theory, Graff’s math involved. While the math-
Internet’s hostile and unpredict- explanations are clearly delivered, ematic formulas described are rel-
able environment. The specific and are often accompanied by atively straightforward algebraic
algorithms that enabled the cre- helpful illustrations. His knowl- expressions, Graff contradicts his
ation and inclusion of SSL had edge and enthusiasm for the sub- objective. I concede that the math
been available for many years, but ject are obvious, and his humor, is nowhere near the level of com-
like any highly advanced technol- appropriately peppered through- plexity that highly sophisticated
ogy, it needed a killer app (Navi- out, help lighten the otherwise cryptographic systems employ. It
gator) to catapult its importance dry content. Furthermore, it suc- is, nevertheless, an exercise few
to the mainstream consumer. ceeds as a tech brief; I read the nontechnical people will spend cryptography is applied, a secure
book in one night. the time to work through to com- system is only as strong as its
Since then, the awareness of, and prehend the underlying founda- weakest link. To complement
demand for, good security prac- Unfortunately, the book has some tions of these systems. Cryptography and E-Commerce,
tices have matched the exponen- gaping holes. The title is unin- I recommend Bruce Schneier’s
tial growth of the ’Net itself. The tentionally misleading; it should Another problem was that only Secrets & Lies: Digital Security in a
reason is obvious: There’s more at be Popular Internet Cryptography three Internet sites were listed Networked World [John Wiley &
stake. B2B, B2C, C2C, and P2P as there is little discussion on the in the bibliography, as compared Sons, 2000].
couldn’t exist as secure, viable e-commerce systems that employ to 11 magazine articles and
platforms from which to conduct the approaches described in the six books. With such a rapidly Overall, Cryptography and
business without the insurance book. And even though Graff advancing field as cryptography E-Commerce was a disappoint-
of non-repudiated transactions. provides two case studies, he and the propensity of security ment. It’s too expensive for a
In Cryptography and E-Commerce, doesn’t provide detailed, step- compromises on the Web, Graff 222-page book that could easily
Jon C. Graff, Ph.D. attempts by-step implementations of the failed to recommend several obvi- be the first three chapters of a
to explain popular cryptographic three main approaches discussed. ous security sites, including two larger “Introduction to Cryptog-
processes used on the Internet The high-level planning is clearly of the most popular: the CERT raphy” textbook. While it’s obvi-
today. Graff succeeds in his described, but anyone looking Coordination Center (http:// ous the author is a qualified
basic objective, but falls short on for implementation details will www.cert.org) and the Sans Insti- specialist, and his knowledge and
presenting concrete commercial need to look elsewhere. That is, tute (http://www.sans.org). passion for the subject are clearly
applicability and raising aware- beyond the (sub-standard) Ker- communicated, there are simply
ness about today’s cryptographic beros implementation on Micro- My final criticism exemplifies the too many content compromises
shortcomings. soft Windows 2000, and a single most glaring omission: there’s no and missing details to recom-
sentence on Network Associate’s coverage on the fallibility of cryp- mend this book.
Part of Wiley’s “Tech Brief” PGP, no other popular commer- tographic systems. Sure, Graff
series, this book is designed to cial encryption product examples introduces Eve, the “woman in — Mike Riley
rapidly explain fundamental tech- are mentioned — such as RSA’s the middle” (in cryptography cir-
nological concepts. Hence, the B-Safe library, Microsoft’s Crypto- cles, this character is usually Mal-
book never dives any deeper than API, and Sun’s Java 2 Cryptogra- lory, the “man in the middle”)
it has to to explain its subject phy Extension. At the very least, who sometimes plays the role of
matter. This means there are an appendix with a list of prod- a malicious intruder. However, as Cryptography and E-Commerce
no specific programming syntax ucts would be appropriate. presented in this book, a reader by Jon C. Graff, Ph.D., John
examples, no step-by-step com- might naively believe they will Wiley & Sons, Inc., http://
mercial product configurations, I found it ironic that after citing be immune to security breaches www.wiley.com/compbooks.
and no dissertations on the in the preface that he wrote if they adopt the cryptographic
cryptographic design process. this book because other introduc- practices described. Graff fails to ISBN: 0-471-40574-4
Although the content is delivered tory cryptography books “...are remind readers that no system Cover Price: US$29.99
from a high-level overview, Graff intimidating to the nontechnical is unbreakable: Even if advanced (222 pages)

40 August 2001 Delphi Informant Magazine


TextFile

The Official InstallShield for Windows Installer Developer’s Guide


If you’re going to use Installer technology, showing custom actions. Parts IV and V
InstallShield Professional for how installations are created cover advanced topics, includ-
Windows Installer to develop from features and how features ing transforms, localization
complex software installations, are created from components. and updating, and upgrading
this book is for you. Micro- Windows Installer packages, existing installations.
soft’s Windows Installer tech- which are the files used by
nology, which made its debut Windows Installer to install This book is well organized and
in Windows 2000, is much an application, are explained assumes you know nothing. It
more than a new way to install in detail, as are transforms, starts from zero and covers the
software. It’s an integral part of patches, and merge modules. subject completely and in great
Microsoft’s Zero Administra- Windows Installer packages are detail. I particularly liked the
tion Windows initiative that’s really a relational database detailed coverage of Windows
designed to make it easier for stored in a COM-structured Installer before getting into the
large organizations to deploy storage file. All the tables product-specific sections. I also
and administer software across and their relationships to each like the way the book covers the book, as well as demonstra-
large networks serving thou- other are described, so you creating basic installation pack- tion versions of the complete
sands of PCs. If all your PCs can really see how Windows ages first, then goes back and InstallShield product line. The
use Windows 2000, this tech- Installer works. The last two dives into the advanced tech- CD also contains the entire
nology should make it possible chapters in this section take niques. This lets new users get book in a single PDF file. I
for your organization to deploy you through building an instal- a good overview of the product really like this feature, because
and administer all your users’ lation using the tools in the and start building installations it lets me take the book with
software without ever having to Windows Installer SDK. quickly without getting bogged me on my notebook computer.
visit a PC. down in the details of the more If you spend $999 for IWSI,
The rest of the book is prod- complex features. There is a you’d be foolish to not spend
The book is divided into five uct-specific, and takes you detailed table of contents and a the extra $50 for this book.
parts. The first 200 pages through using all the features 42-page index so you can use
review past software instal- of InstallShield Professional this book as a reference as well — Bill Todd
lation techniques and their Windows Installer Edition as a text book. If you master the
associated problems. Next, it (ISWI) using detailed, step-by- information in this book, you’ll The Official InstallShield for
introduces Windows Installer step examples to make sure you be able to create an installation Windows Installer Developer’s
and describes how it integrates can do anything you need to for the most complex applica- Guide by Bob Baker, M&T
with Windows 2000 and the with this tool. Part II covers tions, and take full advantage of Books, http://
Zero Administration Windows the basics of building installa- all the new features introduced www.hungryminds.com/
initiative, and how it installs tion packages with IWSI. Part in Windows Installer and Win- mandtbooks/index.html.
and removes software without III examines extending installa- dows 2000.
the problems of the past. The tion functionality with custom ISBN: 0-7645-4723-2
book then provides a detailed actions, and using the IWSI The accompanying CD con- Cover Price: US$49.99
explanation of the Windows scripting language to create tains all the examples from (950 pages, CD-ROM)

41 August 2001 Delphi Informant Magazine


TextFile

Red Hat Linux 7 Server


Do you need to get a Red Hat simple. It takes you step by step the alphabetical list found in a reference or text book. The
Linux 7 server up and running? through all the tasks involved many Linux command refer- 24-page index seems complete,
This book is designed to help you in going from a PC with an ences, because it gives you a rea- but make sure you’re wearing your
do just that. If you plan to use empty hard drive to a function- sonable way to find a command reading glasses because the font is
your Linux box as a workstation or ing server. Each chapter encom- when you don’t know its name. tiny. Get this book if you need to
if you need help with Gnome or passes a discrete task so you can build a Red Hat Linux 7 server.
KDE, you’ve come to the wrong skip tasks that don’t apply to This book is well written and
place. Red Hat Linux 7 Server by you. For example, if you’re not comes with the complete Red — Bill Todd
Mohammed J. Kabir focuses on setting up an e-mail server, Hat Linux 7 distribution on three
using Linux as a server, and starts you can skip Chapter 11 and CDs; you have everything you Red Hat Linux 7 Server
by assuming that you don’t have X move on to Chapter 12, which need to get started — except a by Mohammed J. Kabir,
Windows installed and, therefore, takes you through getting your computer. Kabir’s explanations of M &T Books,
must do everything from the com- Apache Web server running. each of the tasks involved in set- http://www.hungryminds.com/
mand line. That’s a good assump- ting up a Red Hat Linux 7 server mandtbooks/index.html.
tion, because that’s the way most After leading you through install- are clear, and include step-by-step
Linux servers are configured. GUI ing Red Hat Linux 7 as a server, instructions. I particularly like the ISBN: 0-7645-4786-0
tools you can use to configure the book explains the boot-up and way Red Hat Linux 7 Server is Cover Price: US$39.99
some aspects of your server are shut-down processes, including a organized; it can be used as either (729 pages, 3 CD-ROMs)
covered too, but always after the very good explanation of the init
command-line approach. This is a scripts that are run as part of the Designing Web Usability
great way to learn about Linux; it boot process. Kabir then moves on Jakob Nielsen’s Designing Web revealed. One big no-no I now real-
exposes you to the command-line to Linux files and devices and pro- Usability could become the Strunk ize is the “Click here” hyperlink.
way of doing things first, so you vides an excellent chapter on fre- and White for Web page design- The chapter on Accessibility taught
understand what the GUI tool is quently used commands. Separate ers. But it will have to undergo me how frustrating this design flaw
doing to your system. chapters on using Linuxconf, user an update, perhaps in the eBook can be for the visually impaired
administration, process adminis- format to which Nielsen alludes in audience. And adding the file
You don’t have to know anything tration, and network administra- the preface. Old screen shots of size of a large file to its corre-
about Linux to get a Red Hat tion follow. Internet-related topics popular Web sites and a lack of sponding hyperlink label, thereby
Linux 7 server on-line. The first are covered in five chapters on any model-view-controller abstrac- setting retrieval time expectations,
chapter, “Why Red Hat Linux?” DNS, e-mail, Web, FTP, and tions wrinkle this otherwise blem- is another example of the book’s
is redundant; it’s unlikely many Internet Relay Chat and News ish-free title. However, it’s these many valuable suggestions.
people will buy the book if they servers. File and printer sharing are before-and-after color screen cap-
haven’t decided that Red Hat is also covered. The last two sections tures that best convey the author’s Considering all the useful hints
the Linux distribution they want of the book focus on securing and simple design principles. Nielsen provides, I was expecting
to use. Starting with Chapter 2, monitoring your Linux server, and his site at http://www.useit.com to
however, the book is all business. on performance tuning. The book contains some real gems be a shining example of all these
The organization of the book is that seem obvious once they’re principles put into practice. It is,
One of the problems that Win- but to an extreme that is nearly as
dows users have when coming to unattractive as the botched efforts
Linux is learning to work from denounced in his work.
the command line, and coming
to grips with the large number — Mike Riley
of commands and options. The
chapter on common commands Designing Web Usability: The
is designed to help new users Practice of Simplicity by Jakob
by separating the commands into Nielsen, New Riders Publishing,
functional areas. For example, http://www.newriders.com.
there are general file and direc-
tory commands, system status
commands, network administra- ISBN: 1-56205-810-X
tor commands, etc. This is Cover Price: US$45.00
a much better structure than (419 pages)

42 August 2001 Delphi Informant Magazine


Best Practices
Directions / Commentary

Delphi Hall of Fame: Class of 2001

T he first inductions into my Delphi Hall of Fame are now a year old, and it’s time to introduce the second class
of inductees. There are dozens, maybe hundreds, of developers who could seriously be considered for inclusion
into this elite set, but only a few have been influential enough to be nominated and approved. Without further ado,
here are the newest members listed in first-name alphabetical order:

Anders Ohlsson, Borland Developer Support guru, for his tireless Borland-certified Delphi developers than any other employer
promotion and support of Delphi, and for his involvement in Code- (http://www.marotzdelphi.com).
Central, such as his contribution to the ClientDataSet grid compo-
nent (ID#: 15099, co-written with John Kaster). Maxim Peresada, for Torry’s Delphi Pages (http://www.torry.net).

Bill Todd, for the two editions of his excellent database-centric book, Nick Hodges, for his leading role in pioneering the use of Delphi in
Delphi: A Developer’s Guide (published by M&T Books; co-written by creating Internet applications, and his inestimable TSmiley component.
Vince Kellen, et al.), and a host of well-written, wide-ranging articles
for Delphi Informant Magazine. Philippe Kahn, for founding Borland and infusing it with its distinct
flavor and attitude.
Cary Jensen, for his lead role in the book Delphi in Depth [Osborne/
McGraw-Hill, 1996], and his clearly written articles in Delphi Informant Stefan Hoffmeister, for his key role in GExperts, work on Kylix, and
Magazine, especially his “DBNavigator” series. Like Bill Todd, his articles superlative responsiveness on newsgroups.
frequently make important contributions to the Delphi literature.
Team JEDI (Joint Endeavor of Delphi Innovators), for making vari-
Chad Hower, for his Winshoes/Internet Direct (Indy) components, ous C-based APIs accessible to the Delphi masses by creating Object
http://www.nevrona.com/indypro. Pascal wrappers for them. They’re available via their free JCL, which
contains hundreds of utility procedures and functions, as well as non-
Dale Fuller, President and CEO of Borland, for bringing back the visual components (http://www.delphi-jedi.org).
pride, fire, and Borland name.
Wayne Niddery, for his Free Delphi Stuff Web site (http://
Dan Miser, for his above-and-beyond-the-call-of-duty contributions home.ican.net/~wniddery/freedelphistuff.html), and his book Bor-
to the MIDAS/multi-tier community, including his work on land Delphi How-To [SAMS, 1995], co-written with Gary Frerking
MIDAS itself, and support in related newsgroups, e.g. and Nathan Wallace.
http://www.distribucon.com.
Zarko Gajic, leader of the About Guide to Delphi Programming
Earl F. Glynn, for his efg’s Computer Lab Web site, which Web site (http://delphi.about.com) and its free newsletter. Sign up at
contains vast amounts of Delphi graphic examples and code http://delphi.about.com/library/blnewsletter.htm.
(http://www.efg2.com/lab).
That’s it for this year! If there’s anyone that you feel deserves to be in
Erik Berry, for his role as the current lead of the virtually indispens- the Delphi Hall of Fame, feel free to nominate him or her. To do so,
able open-source Delphi add-in GExperts (http://www.gexperts.org). provide me with all the pertinent details: name, Delphi-related feat, etc.
and message me at bclayshannon@earthlink.net. If you missed them, you
Gerald Nunn, for his role as the original lead of GExperts. can access last year’s Hall of Fame online at http://www.DelphiZine.com/
opinion/2000/09/di200009bp_o/di200009bp_o.asp. ∆
Jeff Overcash, for the InterBase Express components, which allow us
to bypass the BDE and use the world’s greatest development tool with — Clay Shannon
the world’s greatest database.

John Kaster, for his “Behind the Screen” series on the Borland
community site (http://community.borland.com), pivotal role in
and contributions to CodeCentral (http://community.borland.com/ Clay Shannon is an independent Delphi consultant, author, trainer, and mentor
codecentral), tireless promotion of Delphi and Kylix, and constant operating as Clay Shannon Custom Software. He is available for remote development
monitoring of the Delphi and Kylix newsgroups. or short-term on-site assignments in the Coeur d’ Alene, ID/Spokane, WA area. Clay
is the author of Developer’s Guide to Delphi Troubleshooting [Wordware, 1999],
Marotz Delphi Group, for their vision in adopting Delphi very which will soon be revised. To view his resume, go to http://www.sysadminsrus.net/
early on, their ASP Express components, and boasting more clayshannon/ClayShannon.doc. You can reach him at bclayshannon@earthlink.net.

43 August 2001 Delphi Informant Magazine


File | New
Directions / Commentary

Delphi on the Web, Revisited

D uring my first year of writing this column I devoted several articles to Delphi Internet sites, exploring author
sites, vendor sites, and more general (non-commercial) sites. I was inspired by one of Richard Wagner’s last
“File | New” columns (July, 1997) entitled “Delphi on the Web.” In it he concentrated on a handful of his favorite
sites. We’ll start here by taking a fresh look at two of the sites he praised that continue to flourish, sites I call
enduring classics. We’ll also discuss other good starting points. After that we’ll discuss educational sites and other
sources of components and tools. We’ll conclude with some newer sites that bear watching.

Enduring classics and other good starting points. Two sites that A more general site, Delphi and Pascal Programming Tutorials
Richard Wagner wrote about four years ago remain at the top of (http://ourworld.compuserve.com/homepages/TK_Boyd/Tut.htm)
many developers’ list of favorites. The Delphi Super Page (DSP) includes information on a variety of Delphi and Pascal topics, conve-
(http://delphi.icm.edu.pl), for which its creator Robert Czerwin- niently grouped by level of difficulty. The site is inspired by the book
ski won the 1998 Spirit of Delphi Award, continues to be one Borland Delphi How-To by Frerking, Wallace, and Niddery. As the author
of the best sources for Delphi components and other resources. describes them, most entries are “self-contained explanations of how
I doubt there is another single source with more components to accomplish a specific task, or use a particular component of the
and tools. The other perennial favorite, Torry’s Delphi Pages language.” This site appears most useful to the newer Delphi developer.
(http://www.torry.net) also won the Spirit of Delphi award, and
also includes a large number of tools and components. Its organiza- About Delphi Programming (http://delphi.about.com/compute/
tion and layout are superb, and its main page includes a link to the delphi/) is a rich site with much to offer. It includes a section
Delphi Programming Source Code (DPSC) Web site’s list of top for beginners, and covers many popular subjects, including database
Delphi sites (http://www.sandbrooksoftware.com/TS/index.shtml) development, graphics, multimedia, Object Pascal, the VCL, and
to which I referred while writing this column. more. There’s a small “How-to” section and examples of complete
Delphi projects. You can even explore the lighter side of program-
All of these sites are great places to begin a search for Delphi ming in the “Humor and Fun” section.
components and tools. If you are searching for Delphi informa-
tion and news, however, then a site with well-organized links such Robert Vivrette’s UNDU (Unofficial Newsletter of Delphi Users at
as DPSC is your ticket. One older site from which to launch is http://www.undu.com) remains one of my favorites, particularly for
Richey’s DELPHI-BOX (http://inner-smile.com/delphi5.htm). It the quality of material offered. In addition to the fine articles going
includes many links, all organized by categories such as news, tips back several years, you can find Robert’s favorite links, job sites, and
and tricks, tools, general Delphi, and more. The Research page user groups.
contains various search engines, some general, others devoted to
the Borland Web site or its newsgroups. An even better choice Delphi City (http://delphicity.net) is a small but attractive site. While the
is Delphi32.com (http://www.delphi32.com). One great feature of number of components available may not be as high as DSP or Torry’s,
this site is that it provides the latest Delphi news with appropriate there are some very specialized ones here. I was intrigued by some of the
links on its initial page. specific functionality covered in the multimedia collection; I’ll be coming
back to download some of them. You might also want to check out the
Delphi education and more. Some Delphi sites are general, covering MAS Delphi Page (http://w1.545.telia.com/~u54503556). The author
a variety of issues; others are more specific, concentrating on a single provides many of his own components, tools, and writings. The latter
topic. EFG’s Delphi Algorithms is one of the latter. Located at include some interesting topics.
http://www.efg2.com/Lab, it contains a plethora of links to sources
of information on Delphi algorithms, including just about every New sites to keep an eye on. As I was working on this column
category you can imagine from mathematical to sorting, game-related I received an e-mail from someone asking if I would contribute
to artificial intelligence. an article to his new site. Instead, I decided to check it out. It’s

44 August 2001 Delphi Informant Magazine


File | New
called www.HowToDoThings.com (also the URL) and contains including vendor and author sites. I’d like to hear from you about
articles on many subjects, including programming (from assembly your favorite sites. Next month we’ll do something completely
language to Visual Basic). The Delphi section includes about 60 different: we’ll take a look at the lighter side of Delphi by lurking
“articles.” Most of them aren’t really articles, but rather code frag- on a couple of Internet discussion lists. We’ll see how humor can
ments — and quite useful ones at that. Particularly impressive are help in the face of incredible stupidity in the workplace, and relive
the reprints of magazine articles that authors have donated, such a great April fools prank. Until next time... ∆
as one on “Converting WAV to MP3 and Back” by Peter Morris
(originator of the site). This new site is well-organized and easy — Alan C. Moore, Ph.D.
to navigate.

One of the exciting newer sites is Delphi3000.com (http://


www.delphi3000.com). It includes over 1,000 “articles” organized
according to categories and subcategories. Main categories include
General, Database, Special (Component writing, Multimedia,
Communications, etc.), OOT (various object-oriented technol-
ogies like COM and MIDAS), Miscellaneous (object-oriented
development, algorithms, and more), and Kylix. As with the pre-
vious site, many of the “articles” consist of briefly commented
source code. One nice feature is that each has comments by Alan Moore is Professor of Music at Kentucky State University, teaching music theory
people who have read it, potentially saving you time with tech- and humanities; he was named Distinguished Professor for 2001-2002. He has been
niques that may be flawed. developing education-related applications with the Borland languages for more than
15 years. He is the author of The Tomes of Delphi: Win32 Multimedia API [Wordware
There are many Delphi sites out there — hundreds, if not thou- Publishing, 2000] and co-author (with John Penman) of an upcoming book in the
sands. I’ve left out some important ones, such as the Project JEDI Tomes series on Communications APIs. He has also published a number of articles in
site, as I plan to devote a column to it soon. Also I decided to various technical journals. Using Delphi, he specializes in writing custom components
limit myself here to general, non-commercial sites. However, in and implementing multimedia capabilities in applications, particularly sound and
future columns I will once again explore more specialized sites, music. You can reach Alan on the Internet at acmdoc@aol.com.

45 August 2001 Delphi Informant Magazine

You might also like