You are on page 1of 35

October 2001, Volume 7, Number 10

Delphi 6 Enterprise Makes


Web Development a Snap

Cover Art By: Arthur Dugoni

ON THE COVER
5 DBNavigator 22 On Language
Introducing WebSnap — Cary Jensen, Ph.D. Console Applications: Part III — Mike Edenfield
Cary Jensen introduces WebSnap, a powerful collection of components Mike Edenfield completes his myth-dispelling console programming
and helper classes for creating sophisticated Web sites with a mini- series, by demonstrating how console applications can employ mes-
mum of effort. In short, many developers will find that WebSnap sage queues, create GUI windows, multithread, and more.
alone will justify the upgrade to Delphi 6 Enterprise.
REVIEWS
FEATURES 26 GTSizer
11 Kylix Tech Product Review by Bill Todd
7 Things You Need to Know about CLX — Nick Hodges
28 ExpressPrinting System 2
From “Qt Is the Operating System,” to case-sensitivity, to “It Ain’t
Product Review by Ron Loewy
the Win32 Messaging System,” Nick Hodges thinks there are some
things you need to know before diving into a Kylix project.
DEPARTMENTS
14 Multi-tier
2 Delphi Tools
Multiple Remote Data Modules, One Connection — Bill Todd
31 Best Practices by Clay Shannon
Bill Todd demonstrates how to share a single connection to multiple
remote data modules, and how to design multiple remote data 33 File | New by Alan C. Moore, Ph.D.
modules so they can easily interact with each other. 35 Symposium by Jerry Coffey

17 Columns & Rows


Using the XML Features of
SQL Server 2000: Part III — Alex Fedorov
Alex Fedorov returns to the FOR XML clause, describing two additional
modes (AUTO and EXPLICIT), and demonstrates how to use XML
templates in your Delphi applications.
1 October 2001 Delphi Informant Magazine
nTierIt Releases nTierIt Application Framework for Delphi
Delphi nTierIt has released nTierIt
T O O L S Application Framework for
Delphi, which is designed to
New Products simplify the creation of client/
and Solutions server and multi-tier database
applications. The product is
made up of pre-coded templates
that together comprise a full-
featured, pre-written application.
All source code is provided.
It has many data-related
features: a new TClientDataSet
(CDS) subclass; a type-safe
CDS Manager handles brows-
ing, AutoInc key generation,
editing, commits, bookmarks,
Book Picks record duplication; and a
TAction-driven replacement for
TDBNavigator adds new capa- Applications can be configured conserve memory, and persist
bilities, reflects the mutual for portability, so the executable from session to session. A form/
A Pattern Approach to
status of related master/detail can be run against any database control resizer handles differ-
Interaction Design
Jan Borchers files, and permits the use of without recompiling, or can ences between Small Font and
John Wiley & Sons, Inc. any action-enabled button and be configured for a specific Large Font screen resolutions.
menu controls. RDBMS. A demonstration program is
The product also features thin- Visual Form Inheritance available at the nTierIt Web site.
client table security, down to promotes an OO development nTierIt Application Framework
the field level, so no DBA is approach that supports multiple includes both the Delphi 5 and
required to maintain multiple form styles (three ship with the 6 versions.
views or login IDs, and flexible product). Most modifications
login encryption security. Other consist of reviewing source code nTierIt
features include a Persistent Data and making minor changes. All Pricing: US$399 through October 31,
ISBN: 0-471-49828-9 option, a Resident Memory framework-based forms are inde- 2001; US$499 thereafter.
Cover Price: US$44.99 Table option, and an Offline pendent and reusable, dynami- Contact: sales@ntierit.com
(245 pages) Briefcase Mode.
http://www.wiley.com/ cally created and destroyed to Web Site: http://www.ntierit.com
compbooks

Dart Communications Announces SOAP for PowerTCP


Extreme Programming Dart Communications with .NET services from any soft’s HTTP connector for con-
Examined
announced SOAP support for COM environment, including trol over the transport of a
Giancarlo Succi and Michele Marchesi
Addison-Wesley its PowerTCP product line. Delphi, C++Builder, and Pow- user’s SOAP payload. It allows
Dart’s WebServer Tool 1.5 and erBuilder. for advanced debugging, fine-
WebEnterprise Tool 1.5 controls Dart’s WebServer Tool 1.5 tuned security requirements,
are now fully compatible with allows any application to be header flexibility, and the
the Microsoft SOAP Toolkit. turned into a Web Services choice of creating a synchro-
The object-oriented archi- provider, to act as a SOAP nous or asynchronous archi-
tecture of Dart’s PowerTCP server and respond to incoming tectural model in a client-side
product line has allowed them SOAP requests without relying SOAP solution. The WebEn-
to plug into Microsoft’s core on IIS or another large Web terprise Tool also includes the
SOAP objects and replace the Server implementation. The Trace method for comprehen-
ISBN: 0-201-71040-4
Cover Price: US$39.99
Microsoft HTTP transport WebServer Control’s Request sive debugging information,
(569 pages) protocol implementation with and Response objects are fully enabling users to see all the
http://www.awl.com/cseng/xp Dart’s native objects. SOAP compatible with Microsoft’s request and response packets
solutions can be portable on SOAP objects, allowing porta- traveling to and from the
the server-side without IIS ble SOAP solutions to be cre- server; and additional cookie
requirements and ated in Delphi, Visual Basic, control for situations where
super-charged on the client- Visual C++, and PowerBuilder. cookies are not desired.
side with security settings, Dart’s WebEnterprise Tool 1.5
debugging support, and sup- provides client-side SOAP com- Dart Communications
port for both synchronous patibility with the Microsoft Price: PowerTCP WebServer Tool 1.5 and
and asynchronous communica- SOAP Toolkit. WebEnterprise WebEnterprise Tool 1.5, US$499 each.
tions. Developers can fine-tune Tool 1.5 contains the WebASP Contact: info@dart.com
their solutions to interoperate control, which replaces Micro- Web Site: http://www.dart.com

2 October 2001 Delphi Informant Magazine


Delphi Xceed Announces Binary Encoding Library, Streaming Compression Library, and Zip
Compression Library 4.5
T O O L S
Xceed Software announced handle Zlib, Info-Zip, and Java- With the optional Xceed Self-
New Products the release of two new products compatible data. Xceed Zip 3.x Extractor Module, the library
and Solutions and Xceed Zip Compression and 4.x memory-compressed can create self-extracting Zip
Library 4.5. (not Zip) data is also supported. files that are able to uncom-
Xceed Binary Encoding Xceed Zip Compression press their contents without
Library allows you to easily Library 4.5 is a software com- the need for an unzipping
convert binary to text and ponent that provides a host application to be present on
text to binary. Xceed Binary of useful functions for compress- the system.
Encoding Library can encode or ing and uncompressing files and Self-extracting Zip files pro-
decode memory buffers, strings, data. Its most popular function- duced with the help of the Self-
blobs, streaming data, or single ality is the ability to create, read, Extractor Module are actually
files. The library supports a and write Zip files. fully customizable “mini install
variety of encoding methods, The Xceed Zip Compression programs” that can display
including UUEncode, XXEn- Library 4.5 also offers a wide custom introduction messages,
Book Picks code, Base64, Binhex, and variety of advanced Zip file prompt for the unzipping
Quoted-Printable. manipulation functions such as destination, create desktop
Xceed Binary Encoding the ability to read and write icons, run other programs,
Library is designed to be flexible Zip files that are split into mul- and much more.
Extreme Programming
in Practice
and easy to use. It only takes tiple parts or that span multiple
James Newkirk and Robert C. Martin one line of code to encode or media. The library offers a rich Xceed Software
Addison-Wesley decode memory or files. feature set while remaining light- Price: One Xceed Binary Encoding Library
A single line of code can even weight, efficient, and easy to use. license, US$119.95; four licenses,
be used to chain together multiple The Xceed Zip Compression US$249.95; eight licenses, US$399.95; site
Xceed components, such as the Library 4.5 also provides basic license, US$599.95. One Xceed Streaming
Xceed Encryption Library and the support for compressing and Compression Library license, US$149.95;
Xceed Binary Encoding Library, uncompressing memory buffers four licenses, US$299.95; eight licenses,
so you can compress, encrypt, and or streaming data directly in US$449.95; site license, US$749.95. One
encode data into text. memory, without reading or Xceed Zip Compression Library 4.5 license,
Xceed Streaming Compression writing to Zip files. For develop- US$299.95; four licenses, US$599.95;
ISBN: 0-201-70937-6 Library is a high-performance ers interested in advanced stream- eight licenses, US$899.95; site license,
Cover Price: US$29.99 “raw” compression library offer- ing data compression capabilities, US$1,499.95.
(205 pages)
http://www.awl.com/cseng/xp
ing the ability to compress and Xceed offers the Xceed Streaming Contact: (800) 865-2626
decompress streaming data, buf- Compression Library. Web Site: http://www.xceedsoft.com
fers, strings, or single files. It
supports multiple compressed
HTML 4.0 Sourcebook data formats. Unlike the Xceed
Ian S. Graham
Zip Compression Library, this Borland Announces Kylix Open Edition
John Wiley & Sons, Inc.
library doesn’t offer Zip file Borland Software Corp. way visual development of GUI,
handling capabilities. announced at the 12th Annual Internet, database, and server
The Xceed Streaming Com- Borland Developer Conference applications. Kylix is powered by
pression Library offers two the availability of Borland Kylix a new high-speed native Delphi
compression algorithms: For Open Edition. Kylix Open Edi- compiler for Linux and imple-
fast compression and decom- tion enables the Linux com- ments a native Linux and cross-
pression speeds, use the library’s munity to create sophisticated, platform version of the Borland
Deflate algorithm, the same one highly visual, end-user, GUI, VCL architecture called CLX.
used in the zip file format. For open-source applications with CLX is designed to radically
ISBN: 0-471-25724-9
maximum compression, use the drag-and-drop development. speed native Linux application
Cover Price: US$34.99
(631 pages) new BZip2 algorithm (Burrows- Included in the Kylix Open development and simplify the
http://www.wiley.com/ Wheeler Transform). BZip2 Edition is FreeCLX, the open porting of Delphi applications
compbooks often achieves between 20 to 30 source, GPL-licensed version of between Windows and Linux.
percent better compression on the cross-platform component
many file types, such as data- library. Kylix Open Edition Borland Software Corp.
base, picture, text, and execut- delivers a palette of over 100 reus- Price: Borland Kylix Server Developer Edi-
able files. able, customizable, and extensible tion, US$1,999; Borland Kylix Desktop
The Xceed Streaming Com- FreeCLX components for open Developer Edition, US$199 for a limited
pression Library can do file source Linux applications. time. Borland Kylix Open Edition for open
formats, too. It can compress Kylix is a high-performance source software (GPL) developers is available
and decompress GZip (.gz) and RAD environment for the Linux for free download.
BZip2 (.bz2) files from Unix platform. Kylix is a compo- Contact: (800) 632-2864
and Linux platforms, as well as nent-based environment for two- Web Site: http://www.borland.com/kylix

3 October 2001 Delphi Informant Magazine


Wise Solutions Introduces Application Integration Suite
Delphi Wise Solutions announced the
T O O L S release of Application Integra-
tion Suite.
New Products Application Integration Suite
and Solutions helps system administrators pre-
pare software applications for
distribution to their organiza-
tion’s desktop computers. This
two-part process consists of
repackaging and conflict man-
agement. Repackaging allows
the administrator to customize
the installation to meet organi-
zation needs; conflict manage-
ment ensures that the software
package works well with other
Book Picks applications.
PackageManager, the repack-
aging component of Applica- with a step-by-step project software applications before
tion Integration Suite, contains approach, with industry best they are installed on desktop
XHTML 1.0 Language
and Design Sourcebook
technology advances that will practices incorporated into the PCs. ConflictManager supports
Ian S. Graham help administrators create reli- product. SmartMonitor watches both Windows Installer and
John Wiley & Sons, Inc. able packages using either and records operations during script-based installation tech-
script-based or Windows installation to capture events. nology to allow conflict check-
Installer technology. Existing When combined with snapshot ing against any combination of
applications can be repackaged technology, SmartMonitor pro- MSI or script-based packages.
into Windows Installer (.MSI) vides accurate installation cap-
format. tures, which can be customized
Other PackageManager fea- to meet organization needs.
tures include a process-oriented ConflictManager, the conflict Wise Solutions
interface and SmartMonitor management component of Price: Contact Wise Solutions for pricing.
ISBN: 0-471-37485-7 installation capturing technol- Application Integration Suite, Contact: (800) 554-8565
Cover Price: US$39.99 ogy. The process-oriented inter- allows organizations to detect Web Site: http://www.wisesolutions.com/
(692 pages)
face provides repackaging teams and resolve conflicts between ais7.htm
http://www.wiley.com/compbooks

Data Mining with Microsoft SQL


dbPAL Universal Database Toolkit, a New Concept in Database Design
Server 2000 Technical Reference and Management
Claude Seidman IT-Map International intermediary to enable instant without the programmer learn-
Microsoft Press launched dbPAL, a complete translation and synchroniza- ing another language.
suite of tools linked by a user tion between all the leading The dbPAL suite consists
interface that dispenses with DBMS formats such as Oracle, of the database design facility
the need to write SQL scripts MS SQL Server, Sybase, Inter- (dbPAL Studio), an interactive
and uses a drag-and-drop icon- Base, Access, Paradox, and database manipulation and
based approach. dBASE. dbPAL enables proj- management facility (dbPAL
dbPAL was created in ects to be developed in a famil- Workshop), a scripted database
the form of a new database iar language and converted into management facility (dbPAL
format that acts as a universal the database format required Director), and a database
ISBN: 0-7356-1271-4 design and test data inventory
Cover Price: US$49.99 for change control (dbPAL
(367 pages, CD-ROM)
Central).
http://www.mspress.microsoft.com
IT-Map offers a 12-month
money-back guarantee on
dbPAL.

IT-Map International
Price: US$875
Contact: usinfo@it-map.com,
ukinfo@it-map.com
Web Site: http://www.dbpal.com,
http://www.it-map.com

4 October 2001 Delphi Informant Magazine


DBNavigator
Web Development / Database Development / WebSnap / Delphi 6 Enterprise

By Cary Jensen, Ph.D.

Introducing WebSnap
Delphi 6 Enterprise Makes Web Development a Snap

D elphi 6 Enterprise introduces a large number of enhancements and many new


features. And, without a doubt, the one area that has received the most attention
from Borland is Internet development.
Among the new and improved features is enhanced Overview of WebSnap
support for XML, including XML document WebSnap is an extension of the Internet Express
object model (DOM) support and XML transfor- tools first introduced in Delphi 5. Similar to Inter-
mations, the introduction of Web services using net Express, WebSnap is built upon the basic Web
SOAP, and a major addition to Web server exten- Broker technology, the tool for building Web server
sion support named WebSnap. Over the next few extensions included in Delphi 5 and 6 Profes-
months I will introduce each of these new features. sional. However, WebSnap goes much further than
We’ll begin this month with a look at WebSnap. Internet Express and Web Broker, as far as code
generation and design-time layout is concerned.
WebSnap is a very large part of Delphi 6 Enter- Specifically, WebSnap is capable of generating a
prise, consisting of dozens — if not hundreds — greater number of different types of Web inter-
of new classes. These classes are used to build Web faces, and provides a more intelligent mechanism
server extensions, which are applications that pro- for implementing database applications in HTML.
cess HTTP requests on behalf of a Web server,
and which typically produce dynamic HTML that For example, instead of generating XML that is
the Web server returns to a Web browser. Most manipulated on the Web browser using JavaScript,
Web sites that generate HTML on request, includ- as Internet Express does, WebSnap generates rich
ing those that serve database information, do so HTML references (such as image <IMG>, anchor
through Web server extensions. <A>, and form <FORM> tags) that refer back to
the Web server extension, passing back any data
WebSnap is a large and impressive collection of the Web server extension might need to implement
classes and designers — a detailed description the particular feature. In other words, WebSnap
could easily fill a book. Consequently, this article retains all application logic on the server, while
is intended simply to provide you with a general at the same time providing the client with suf-
overview of WebSnap, including its various cat- ficient information to invoke those server-side fea-
egories of features, as well as a step-by-step dem- tures. This approach represents the state-of-the-art
onstration of how to create a simple WebSnap in Web server extension development.
application. For details on using a particular Web-
Snap feature, you should refer to the Delphi 6 WebSnap applications are organized around
Enterprise online help. HTML templates, typically with one HTML

5 October 2001 Delphi Informant Magazine


DBNavigator

Figure 2: Use the Web App Components dialog box to choose


which Web App components to include in your WebApp module.

and Web application components. Each type is described in the


following sections.

Figure 1: The WebSnap Application Wizard. Web Modules


Web modules are containers for WebSnap components, and
all WebSnap applications have at least one. There are four
template for each Web page that WebSnap will serve. These tem- kinds of Web modules: WebAppPageModule, WebAppDataModule,
plate files can contain standard HTML, but can also include WebPageModule, and WebDataModule.
transparent tags and scripting instructions. Transparent tags are
similar to those supported by page produces in Delphi’s Web The TWebAppPageModule or TWebAppDataModule class is
Broker, where tags of the format <#tagname> are replaced by designed to be the primary container for your WebSnap applica-
HTML or XML at run time by event handlers. By comparison, tion, similar in many respects to the main form of a regular
the scripting instructions consist of processing directives, output Delphi application. As is the case with the main form, you can
directives, and include statements defined by a scripting language have only one WebApp module, and the WebSnap Application
such as JScript (an ECMAScript nearly indistinguishable from Wizard, shown in Figure 1, permits you to choose which type you
JavaScript), VBScript, or any other language supported by an want (page or data).
installed scripting engine.
A WebAppPageModule has the advantage of supporting a producer
These scripting instructions are essentially identical to those used component (such as a PageProducer or AdapterPageProducer), which
in Active Server Pages (ASPs). They differ, however, in that these means the WebAppPageModule has an associated HTML template.
instructions are executed by the WebSnap Web server extension, As mentioned earlier, this HTML template can contain HTML/
and not by the Web server. In fact, the Web server never sees the XML, as well as transparent tags and/or scripting instructions. By
scripting instructions, as they are replaced by text such as HTML comparison, a WebAppDataModule is simply a container for the
or XML by the WebSnap Web server extension before it returns Web application components.
the HTML or XML result to the Web server (which in turn
returns this content to the Web browser). In many cases, these In addition to permitting you to define the type of WebApp module
scripting instructions invoke behavior of WebSnap components, your WebSnap application will use, the WebSnap Application wizard
known as adapters, to aid in the generation of HTML at run also permits you to choose the type of Web server extension your
time. The result is a dynamic, component-based environment for WebSnap application will be compiled as, the default name for your
generating run-time HTML and XML. WebApp module, and caching style (either permitting or denying the
caching of your Web server extension).
There are a large number of WebSnap components on the Com-
ponent palette, as well as three WebSnap-related Wizards in the You can also use the WebSnap Application Wizard to configure the
Object Repository. Together, you use these to visually build your default components that will appear on your WebApp module. To con-
WebSnap Web server extensions. Whether they’re generated by the trol which Web application components you want on your WebApp
WebSnap Wizards, or placed from the WebSnap page of the Com- module, click the Components button. The wizard responds by display-
ponent palette, WebSnap components can be roughly divided ing the Web App Components dialog box, shown in Figure 2. Enable
into five categories: modules, producers, adapters, dispatchers, the check box on the Web App Components dialog box for each type

6 October 2001 Delphi Informant Magazine


DBNavigator
of Web application component you want to include, and then click
OK to close it. The various Web application components are discussed
later in this article.

When you are creating a WebAppPageModule, the WebSnap Applica-


tion Wizard also permits you to define the type of producer component
that will appear on the module. To change the default producer (a
PageProducer), click the Page Options button to display the Application
Module Page Options dialog box, shown in Figure 3. This dialog box
contains the same options you will find on the WebSnap Page Module
Wizard. These options are described shortly.

Unlike the WebApp modules, of which you may have only one in your
application, you typically have many Web modules (WebPageModules
and/or WebDataModules) in your WebSnap application. Normally
you will have one WebPageModule for each Web page that your
application can serve up. WebDataModules, like their data module
counterparts, are containers for components, such as data access com-
ponents, that you want to share among two or more WebPageModules
or WebDataModules. The design of your WebSnap application will
determine how many WebDataModules — if any — you will use.

It’s interesting to note that all Web module units declare a function in
their interface section that returns a reference to a module instance.
Figure 3: Use the Application Module Page Options dialog box to
This function is similar to the instance variable used for forms (such as select the type of producer to add to your WebAppPageModule.
Form1 for a form class named TForm1). By using a function, however,
the Web application can ensure you’re referring to the correct instance
of your Web module if you compile your Web server extension as The final check box on this dialog box permits you to save your page
an ISAPI/NSAPI or Apache DLL (since these types of servers can be module settings as a default, defining the default values that will be
accessed simultaneously by two or more threads of the Web server). displayed the next time you use this wizard.

You create WebPageModules using the WebSnap Page Module WebSnap Producers
Wizard on the WebSnap page of the Object Repository. This In Web Broker applications you use producer components, such as
wizard, shown in the Figure 4, permits you to control various the PageProducer and QueryTableProducer, to hold, manipulate, and
aspects of the page module generation. Use Type to identify the even generate HTML. The same is true of producers in WebSnap. In
type of producer component you want to appear on the page fact, WebSnap can make use of all of the same producers available to
module. If you select a producer component that supports script- Web Broker applications.
ing, such as a PageProducer or AdapterPageProducer, select the
scripting language using the Script Engine combo box. (The various In WebSnap, however, even the relatively simple PageProducer
producer components are described later in this article.) has an important new capability. Specifically, the PageProducer,
as well as all of the other standard types of producers used
Normally you will want an HTML or XSL template generated for in WebSnap applications (AdapterPageProducer, DataSetPagePro-
your page. This template will typically include some static HTML or ducer, InetXPageProducer, and XSLPageProducer), can have an
XSL/XML, but can also include scripting instructions (if your producer HTML or XSL/XML template associated with it. In addition to
component supports it), and transparent tags. If you don’t want the this template holding regular HTML and transparent HTML tags
HTML or XSL/XML template generated for you, uncheck New File. (tags that have the form <#tagname> and which are designed to
be replaced at run time by the producer’s OnHTMLTag event
Use the Page area of the WebSnap Page Module Wizard to define the handler), these templates can also include scripting instructions,
name and title for your page. If your page is a published page, the name just like Active Server Pages (only the XSLPageProducer compo-
corresponds to the pathinfo part of the URL that will automatically cause nent cannot include scripting instructions, but XSL is itself a
your page to be displayed. If you don’t want this page to be available, programming language). Unlike ASPs, however, these instructions
uncheck Published. You can also require that the user has logged into your are executed by a scripting engine at the request of your WebSnap
Web site before serving this page. If so, enable Login Required. application, instead of being executed at the request of the Web
server itself.
In the Module Options area you can define how the page’s life cycle
will be managed. Set Creation to On Demand if you want the page Many of the scripting instructions added to an HTML template
module to be created the first time it is requested, or to Always when are generated by the various WebSnap components that you use,
you want the module to be created when the Web server extension is based on the WebSnap features you configure at design time. You
first accessed. Use the Caching combo box to define how the page is can also add your own scripting instructions, which permits you
destroyed. Set it to Cache Instance to instruct your Web server extension to do things such as add <!#INCLUDE> directives to insert
to not destroy your Web page module when it has finished processing HTML, or other content, directly into the HTML stream, invoke
a request. Set Caching to Destroy Instance to request that the page COM servers, access Java objects, or just about anything that your
module’s destructor be invoked when it has completed its response. scripting language permits.

7 October 2001 Delphi Informant Magazine


DBNavigator
The role of adapters is to
insert data and instruc-
tions into the resulting
HTML document. They
do this through Actions
and Fields. Fields can
be used to insert text
into the HTML docu-
ment through scripting
commands. Actions, on
the other hand, insert
HTTP requests using
<IMG> image and <A>
anchor tags that will
be sent from the Web
browser to the Web
server (and then passed
onto the WebSnap appli-
Figure 5: The WebAppPageModule for cation) to invoke behav-
this project. ior based on the adapter.

Some adapters are more complicated than others. The DataSet-


Adapter, for example, generates sufficient code, and inserts it into
the resulting HTML file, to define state information in the HTML
sent to the Web browser. This state information is then sent back to
the Web server extension in response to certain operations initiated
by the user (such as clicking a button that appears on the Web
page). This is important since Web server extensions don’t maintain
state on their own, since the instance of the Web server extension
that serves a particular Web page isn’t likely to be the one to which
Figure 4: The WebSnap Page Module Wizard permits you to the next HTTP request issued by the browser will be processed by.
define the initial configuration of a page module.
Other adapters are very simple. For instance, the Application-
Adapter component simply stores the title of the application. This
Delphi ships with the Microsoft scripting engine, which provides title can then be inserted into the HTML template using a scripting
you with access to both JScript (a JavaScript-compatible scripting instruction such as the following:
language) and VBScript. These scripting engines are available once
you install Microsoft Internet Explorer 5.0 on your server, but if <HEAD><TITLE><%=Application.Title%></TITLE></HEAD>
you have any other scripting engine installed, you can use that as
well. One of the more powerful things that the scripting languages where the <%= %> is a special type of the scripting instruction
permit is the automatic insertion into your HTML document of called an output directive.
data retrieved by adapter components. Adapters are described in
the next section. All HTML templates have access to a variety of adapters that are glob-
ally available, so long as the appropriate classes have been associated
Of all the WebSnap producers, the most powerful is AdapterPage- with the WebAppComponents object (normally the DataSnap Appli-
Producer. This producer includes a visual editor that permits you cation Wizard does this for you). These adapter names, and the classes
to visually design the user interface of your Web page, including that implement them, are Application (WebApplication), EndUser
text inputs, check boxes, grids, and buttons. This editor is the Web (EndUserAdapter), Session (SessionsService), Pages (PageDispatcher),
Page Editor (refer to Figure 7). To see this editor, right-click an and UserList (WebUserList). Additional adapters include Modules,
AdapterPageProducer and select Web Page Editor. which provides a reference to each Web module in your application
(WebPageModule or WebDataModule); Page, which references the
You specify the controls of the Web page by adding and configuring WebPageModule associated with this template; Producer, which
helper classes in the Web Page Editor. To add a helper class, right- refers to the producer component on the current page; Request,
click an object that appears in the top-left pane, called the Parent which provides access to the WebRequest object; and Response,
Components pane, and select New Component. The resulting dialog giving you access to the WebResponse object.
box will display the types of objects that you can add, based on the
type of object you right-clicked. You can also refer to individual adapters that you place in your applica-
tion. If the adapter is on the Web page module associated with the
WebSnap Adapters current template, you can simply refer to it by name. For example, if
Adapters are specialized components that are referenced within the your template is associated with a WebPageModule onto which you
scripting commands that you or WebSnap’s code generators insert placed an adapter named Adapter1, and you have created a field for that
into the HTML template. Adapters can also run at the request of a adapter named Field1, you can insert the display text of that field of
producer, such as the AdapterPageProducer. that adapter into the HTML file using the following output directive:

8 October 2001 Delphi Informant Magazine


DBNavigator
<%=Adapter1.Field1.DisplayText%>

Similarly, if Adapter1 isn’t located on this WebPageModule, but


instead is located on a WebPageModule whose name is Page5, you
can insert the display text value using this statement:

<%=Modules.Page5.Adapter1.Field1.DisplayText%>

Before you can use an adapter, you must create at least one field for
it, although in most cases you will create many. For example, to use
a LoginFormAdapter, you will likely create at least two fields: one
associated with a user name, and another associated with the password.

WebSnap Dispatchers
Dispatchers are the components that manage the handling of the
requests that are received by your Web server extension. WebSnap
includes three types of dispatchers: WebDispatcher, PageDispatcher, and
AdapterDispatcher.
Figure 7: The Web Page Editor with a partially configured
While WebDispatcher is a crucial component in Web Broker applica- AdapterFieldGroup and an AdapterCommandGroup that isn’t
tions, it’s far less common in WebSnap applications. The WebDispatcher yet configured.
component considers the pathinfo of an HTTP request, and sends it to a
WebActionItem. In WebSnap applications, this process is handled by the
PageDispatcher, which considers the pathinfo part of the HTTP request
and sends it to the WebPageModule of the same name.

The AdapterDispatcher manages HTTP requests that are intended for


adapter objects, including the triggering of before and after dispatch
event handlers. Both the PageDispatcher and AdapterDispatcher classes
are default classes added by the WebSnap Application Wizard. These
classes must be referenced by your WebAppComponents component, or
the WebSnap application will not work.

WebSnap Web Application Components


Web application components are the classes provided by WebSnap to
manage your overall WebSnap application. These components can be
created for you by the WebSnap Application Wizard, and are typically
located on your WebAppPageModule (or WebAppDataModule).

Web application components are organized using the WebApp-


Figure 8: The fully configured Web Page Editor. The graphic field
Components component. If you wish to add additional Web
image isn’t visible here, but the generated Web page will include
application components to your project after using the WebSnap the display image.
Application Wizard, you must not only drop these components
onto your WebAppPageModule (or WebAppDataModule), you
must also assign them to the appropriate property of your
WebAppComponents component (in most cases, this assignment
is performed for you automatically in response to your
dropping the specific Web application component onto your
WebAppModule). For example, if you decide to add a
WebUserList component after creating your project using the
WebSnap Application Wizard, you must ensure that this
WebUserList is assigned to the UserListService property of your
WebAppComponents component (again, this assignment typi-
cally happens for you automatically).

Figure 6:
Creating a Web-
DataModule using
the WebSnap Data
Module Wizard.
Figure 9: This Web page is generated entirely by WebSnap
adapter components, and required no design-time code.

9 October 2001 Delphi Informant Magazine


DBNavigator
Creating a WebSnap Application Example 20) Select the newly added AdapterDisplayField and set its
There are many steps required to create a WebSnap application, making FieldName property to Graphic and its DisplayType to dftImage.
a step-by-step example difficult for all but the simplest types of applica- 21) Select the AdapterCommandGroup and set its DisplayComponent
tions. As a result, this section will demonstrate how to build a very simple property to AdapterFieldGroup1.
DataSnap CGI stand-alone executable. For examples of more compli- 22) Now right-click the AdapterCommandGroup, select Add
cated WebSnap applications, check the /Dephi6/Demos/WebSnap direc- Commands, and highlight only the following commands:
tory. (This sample WebSnap application is available for download; see FirstRow, PrevRow, NextRow, LastRow, and RefreshRow. Click OK when
end of article for details.) you’re done. You have now finished working with the Web Page
Editor. It should look like that shown in Figure 8.
Use the following steps to create a simple WebSnap application for 23) From Delphi’s main menu, select View | Forms to display the View
viewing the contents of the BIOLIFE.DB table: Forms dialog box, and select Home.
1) Select File | New | Other from Delphi’s main menu. 24) Select the PageDispatcher component and set its DefaultPage prop-
2) Select the WebSnap page of the Object Repository and double-click erty to Home.
the DataSnap Application Wizard. 25) Next, select the ApplicationAdapter component and set its Title
3) Set Server Type to CGI Stand-alone executable and Page Name to Home property to Fun Fish Facts.
(refer to Figure 1). 26) Select File | Save All. Finally, select Project | Compile.
4) Click OK to create the WebAppPageModule, shown in Figure 5.
5) Select File | New | Other from Delphi’s main menu. Select the Web- All you must do now is copy your CGI stand-alone executable, and all
Snap page of the Object Repository and double-click the WebSnap HTML files generated by WebSnap, to a directory on your Web server
Page Module Wizard (refer to Figure 4). In the Producer section set that has execute privileges. If you’re using IIS or Personal Web Server, the
Type to AdapterPageProducer, and in the Page section set Name to Scripts directory is likely configured this way.
Fish.
6) Click OK to create the Web page module. Now open a Web browser and request the FishApp.exe resource from
7) Now, select File | New | Other from Delphi’s main menu. Select your executable directory. Assuming that you’ve stored your application
the WebSnap page of the Object Repository and double-click the in a virtual directory named \Scripts on your local machine, this URL
WebSnap Data Module Wizard. Accept this wizard without changing will look like the following:
any of the default values, as shown in Figure 6.
8) Now take a moment to save all files. Select File | Save All. Save Unit3 http://localhost/scripts/fishapp.exe/
as DataMod, Unit2 as FishForm, and Unit1 as HomePage. Finally,
save the project as FishApp. Now click on the Fish link. This will take you to the page that is gener-
9) Place on the Web data module a Session and Table component ated by the AdapterPageProducer. This page should look something like
from the BDE page of the Component palette. Set the that shown in Figure 9. Notice that if you now click on the navigation
SessionName property of the Session to ThreadSession. Set buttons, the default actions associated with the AdapterCommandGroup
the SessionName property of the Table to ThreadSession, the will fetch a new record to view. Because of the way that the adapter
DatabaseName property to DBDEMOS, and the TableName component’s Actions produce HTML, the HTML displayed by the
property to BIOLIFE.DB. Finally, set the Table’s Active browser has sufficient information to request the next record and the
property to True. previous record when those buttons are clicked.
10) Select the Web page module you created in step 5. Add to it a
DataSetAdapter from the WebSnap page of the Component palette. Conclusion
11) Select File | Use Unit from Delphi’s main menu, and select DataMod. WebSnap is a powerful collection of components and helper classes
This adds the DataMod unit to an implementation section uses that you can use to create sophisticated Web sites with a minimum of
clause in the FishForm unit. effort. In addition, the HTML templates that WebSnap is based upon
12) Select the DataSetAdapter and set its DataSet property to permit you to include powerful scripting instructions that are executed
WebDataModule3.Table1. Next, right-click the DataSetAdapter, and by your WebSnap Web server extension. In short, it’s so useful that
select Fields to view the Fields editor. Right-click in the Fields editor many developers will find that WebSnap alone will justify the upgrade
and select Add all fields. Close the Fields editor when you’re done. to Delphi 6 Enterprise. ∆
13) Now right-click the AdapterPageProducer on the Web page module,
and select Web Page Editor. The project referenced in this article is available on the Delphi
14) Right-click the AdapterPageProducer component in the Parent pane Informant Magazine Complete Works CD located in INFORM\2001\
(the top-left pane) of the Web Page Editor. Select New Component, OCT\DI200110CJ.
then add an AdapterForm.
15) Right-click the AdapterForm, select New Component, and select an
AdapterCommandGroup.
16) Right-click the AdapterForm again, select New Component, and select
an AdapterFieldGroup. Cary Jensen is president of Jensen Data Systems, Inc., a Houston-based training
17) Select the AdapterFieldGroup and set its Adapter property to DataS- and consulting company. He is the author of 18 books, including Building Kylix
etAdapter1. Next, right-click the AdapterFieldGroup and select Add Applications (Osborne/McGraw-Hill), Oracle JDeveloper (Oracle Press), and Delphi
All Fields. The Web Page Editor should now look like that shown In Depth (Osborne/McGraw-Hill). Cary is also Contributing Editor to Delphi Infor-
in Figure 7. mant Magazine, and an internationally respected trainer and speaker. For infor-
18) Next, right-click the FldGraphic field in the Children pane (the mation about Cary’s on-site training, public Delphi and Kylix seminars, and
top-right pane), and select Delete. consulting services, please visit http://www.jensendatasystems.com, or e-mail Cary
19) Return to the Parent pane, right-click the AdapterFieldGroup, select at cjensen@jensendatasystems.com.
New Component, and add an AdapterDisplayField.

10 October 2001 Delphi Informant Magazine


Kylix Tech
CLX / Qt / X Window System / Kylix

By Nick Hodges

7 Things You Need to Know


about CLX
Before Plunging into Kylix

M any Delphi programmers are giving Kylix a close look. Naturally, one of the first
things new Kylix developers will do is try to port existing VCL-based applications
to Kylix. Of course, they’ll also be building CLX applications from scratch.

While the VCL and CLX are similar in many from the operating system more than even the
ways, they’re different enough to make moving a VCL does. Of course, you have access to libc via
VCL application to Kylix more than a copy-and- the libc.pas unit, but you can build robust CLX
recompile exercise. Programmers experienced applications and never use it.
with how Windows and the VCL behave will
find a number of differences in using the two CLX relies on Trolltech’s Qt libraries. Thus, Qt
class libraries. This article presents a few things becomes the base library for all visual controls
that a VCL programmer might want to know used in an application. In Kylix, the Qt libraries
before porting to Kylix, or creating applications are wrapped up in the shared objects, libqt.so
with CLX from scratch. and libqtintf.so. These two files contain all the
logic to display your GUI-based application. At
1. Qt Is the Operating System its root, CLX rarely interfaces with the Linux
The biggest and most obvious difference operating system. Instead, it talks almost exclu-
between Delphi and Kylix is the operating sively to Qt. So it’s Qt that provides the equiva-
system. Kylix runs on, and builds native applica- lent of the Windows message loop (more on this
tions for, Linux. The applications should run on later), and all calls used to create and manage
any Linux distribution and are desktop-neutral. Windows are made to the Qt libraries.
That’s the view from the user’s side.
There are only a few places in CLX where
From the developer’s side, Kylix does a very good the framework calls the X library directly, and
job of shielding the developer from Linux. While almost none where it calls Linux directly. CLX
you have access to all the X-library and libc makes use of the RTL, but the actual presenta-
calls, and all your CLX controls can be used tion of GUI-based applications, and all the event
directly with Xlib calls, you can build robust and handling, are done via Qt. Thus, CLX program-
complex applications without having to delve mers will rarely deal directly with Linux in the
into these lower levels of Linux programming. same way that a VCL programmer deals with the
CLX and the Kylix run-time library (RTL), as Win32 operating system. Indeed, if they did, the
wrapped up in the SysUtils unit, insulate you code wouldn’t be platform-independent.

11 October 2001 Delphi Informant Magazine


Kylix Tech
In brief, the Qt libraries are to CLX what the Win32 API is to 4. The Two Frameworks Are Different, and Can Behave
the VCL. Differently
Many CLX and VCL controls have the same names, and many of
2. Porting Won’t Be as Easy as You Might Think the same methods. For instance, all the components on the Standard
Despite Borland’s warnings, it’s possible that many Delphi devel- page of Delphi’s Component palette are on the Standard page in
opers believe that Kylix will be able to compile VCL applications Kylix. However, because Qt and Linux behave differently than Win-
virtually unchanged. This isn’t true. Kylix will read your Delphi dows, CLX controls will naturally behave differently as well.
and VCL forms, but it’s unlikely that unchanged VCL-based
applications will compile in Kylix. VCL programmers have become used to a certain class hierarchy,
with certain components descending from others. For instance,
A quick glance at the CLX source reveals little difference between VCL users quickly become aware that TMemo descends from
it and the VCL. Indeed, the architecture is much the same. The TEdit, both of which wrap up the standard Windows text box
class structure is the same down to the TControl level, although (edit) control. CLX is different in a number of places. For
the interfaces are not. The two are very similar, however, and instance, the Qt libraries have both a single-line edit control
Kylix is designed to read and understand forms created with and a multi-line edit control, so in CLX, TEdit and TMemo
Delphi. Kylix has the TWidgetControl as its basic control class. both descend from TWidgetControl instead of TCustomEdit. In
TWidgetControl is equivalent to TWinControl in the VCL, in addition, TTreeView and TListView, rather than being distinct
that it’s the class that introduces a control handle. A closer look, controls as in Win32, both descend from a common ancestor,
however, reveals many fundamental differences. TCustomViewControl, which wraps up the Qt control, QListView.

Some things will trip you up when you try to port VCL-based Kylix opens the door to cross-platform code, which raises the issue
applications to Kylix. CLX is modeled on the VCL and is similar to of compiler directives to ensure that the correct code is compiled
it, but rests on top of an entirely different framework (Qt). What for the correct platform. The Kylix compiler defines LINUX; the
is “standard behavior” for the VCL and the underlying Windows Delphi 6 compiler defines MSWINDOWS. Borland recommends
controls isn’t necessarily standard for CLX — on Linux or Windows. you use:
Borland has tried to make CLX as much like the VCL as possible, but
the two frameworks don’t behave exactly the same. Some differences {$IFDEF MSWINDOWS}
will be minor, some major, some resolvable, and some not. // Windows code here...
{$ENDIF}

3. It Ain’t the Win32 Messaging System {$IFDEF LINUX}


Windows applications do nothing more than sit around and wait // Linux code here...
to react to Windows messages, constantly scrubbing the Windows {$ENDIF}
message queue for mouse clicks and keyboard input to tell the
application what to do next. Delphi applications grab these mes- for your platform-specific code. This way, adding any platform-
sages and respond to them using message handlers, one method specific code will be easier, and your application will have the
for each type of message. proper code compiled for each operating system. The following
construct (or its reverse for MSWINDOWS):
On Linux, CLX works quite differently. In the X Window System
(X), user events are dispatched by signals instead of by messages.
The CLX component architecture in Kylix doesn’t use message {$IFDEF LINUX }
methods to handle user events as the VCL does in Windows. // Linux code here...
{$ELSE}
Instead, X posts events to the event queue, and Qt translates
// Don't assume this is Windows code.
those events and routes them through the EventFilter method. Qt
{$ENDIF}
controls, in turn, emit signals in response to these X events that
are hooked by CLX methods as well. Thus, you won’t see all those
won’t necessarily compile in the future if you have Linux-specific
message handling methods in CLX that are ubiquitous in the VCL.
code in the {$ELSE} clause. Using the first construct will ensure
that the right code gets compiled for the right target. This
However, the message method syntax you’re used to is fully sup-
is important when using CLX in Delphi 6, and even more
ported by the compiler. You can declare message methods using
important if CLX is moved to a third platform.
your own message constants just as you can in Delphi, and you
can dispatch and call message methods using TObject.Dispatch,
Beyond that, the two frameworks won’t always behave the
just as you can in Delphi.
same. Events can fire at different times, and — in some
cases — different orders. For example, the OnDrawItem and
However, Qt does not use that. Instead, all X events are handled
OnMeasureItem events for custom drawing of list boxes and check
through the EventFilter method, in a way similar to the classic
boxes fire at different times in CLX and VCL.
WinMain procedure in an old-fashioned Windows application
that used a large switch statement to handle each message. There’s
no PostMessage, no SendMessage, and no message queue in CLX. 5. Linux Windows Are Very Different from Windows
Therefore, if a component or application relies on the asynchro- Windows
nous nature of Win32’s PostMessage to defer events until some The Windows operating system is all encompassing; it manages
later time, you’ll need to develop an alternate solution. In Kylix, everything from low-level file management to high-level windowing
you can create a Qt event and either post it or send it to a CLX and other GUI functions. Windows allows you almost complete
control using QApplication_sendEvent or QApplication_postEvent. control over the features of any window you create, and thus gives
the programmer a lot of flexibility in what is presented to the user.

12 October 2001 Delphi Informant Magazine


Kylix Tech
GUIs on Linux more closely resemble the Windows 3.1 model; 7. File Names Are Case-sensitive
the windowing system rides on top of the actual operating system, This one will drive the average Delphi programmer crazy. All file
just as Windows 3.1 rode on top of DOS. Most standard Linux name references everywhere in a project — from the unit names
workstations have an X server of some sort, and then a windows and the $R directive declarations, to the file names of your PAS
manager that’s responsible for decorating the non-client areas of and DFM files — all are case-sensitive and must match up. For
its windows, and that provides a graphical interface that users are instance, your current Delphi 5 projects likely have lowercase
accustomed to (KDE and GNOME are two of the more popular extensions, i.e. *.pas and *.dfm. But your source code likely has:
windows managers). From the programmer’s perspective, however,
these GUIs aren’t always as flexible as the Win32 system. {$R *.DFM}

For instance, some windows managers, such as KDE, create all their Guess what? Kylix won’t compile this. It will look for
windows as sizable. Thus, setting the form’s BorderStyle property to MyUnit.DFM and won’t find it, because the file is likely named
fbsDialog or fbsSingle will change its appearance, but the form will MyUnit.dfm.
still size on some of your users’ windows managers. You can force
a window not to size with its constraints property (or the fbsNone Here’s another situation: one of your units is named myconsts.pas,
property, although that’s usually a bit much), but it will always look and you have declared the unit name as:
like it’s sizable with the sizing border and grips (again, depending
on the windows manager you use). This means that as a developer, unit MyConsts;
you can’t count on a true dialog box, and you have to code for that.
Kylix developers should get familiar with the Anchors property of The compiler will object to this as well. New Kylix projects get
TForm to ensure their windows look right when resized. the file naming correct, but you may run into trouble when you
move code over from Delphi.
In addition, you can only paint on the client area of a window.
There’s no equivalent to WM_NCPAINT messages. For example, Conclusion
you can’t paint on a window’s title bar, because the windows While Kylix is often referred to as “Delphi for Linux,” that’s not
manager controls it, and X and Qt know nothing about it. exactly true. The IDEs behave similarly, and it’s easy to be con-
fused when looking at the two applications — they appear nearly
6. Not All Your Favorite Controls Are There identical. While Delphi 6 will include the ability to build CLX
While Borland tried to make the interface of CLX as close as applications, Delphi 5 users will find the CLX framework differ-
possible to that of the VCL, there are, of course, differences. As ent in some subtle but important ways from the VCL. Remember-
mentioned previously, all the components on the Standard page of ing a few key differences between CLX and the VCL will enable
the Component palette are in Kylix, but some of the controls on you to smoothly make the transition from Delphi and Windows
other palette pages are not. to Kylix and Linux. ∆

One control you’re likely to miss is the RichEdit component. CLX


has the TextViewer component that will display rich text, but it
provides no facility for designing rich text. Also, since ActiveX
controls are obviously not available in Linux, the WebBrowser
component that’s part of Internet Explorer isn’t available in Kylix.
And Linux doesn’t provide a similar control. The TextBrowser
control does provide basic HTML formatting and image display
using the W3C HTML 1.0 specification, but it’s not a full-
featured browser control.

If your application relies on the BDE, you’ll have to convert it to


dbExpress. The BDE isn’t available in Linux. Database controls,
such as Table and Query, are now based on dbExpress, and your
current code won’t compile — much less work — without an
extensive conversion.

While Kylix does extend into the realm of the Apache Web server,
it only includes support for the basic WebBroker Web modules.
Internet Express, which relies on MIDAS, isn’t available in current
editions. It remains to be seen what Internet development support
will exist in the coming edition(s).

The TeeChart and QuickReport components are also not


included. However, the TeeChart controls will be available on Nick Hodges is president of HardThink, Inc., a Delphi consulting company,
Kylix as a third-party add-on from its vendor. The good news is and a member of Borland’s TeamB. Nick lives with his family in St.
that there are a few new controls in CLX, such as LCDNumber Paul, MN. A frequent speaker at Borland Conferences, this year he is a
and IconControl. And of course, the dreaded FastNet Internet member of the Borland Conference Advisory Board. He can be reached at
controls are gone, replaced with the open-source Internet Direct nick@hardthink.com.
(Indy) components.

13 October 2001 Delphi Informant Magazine


Multi-tier
Multi-tier Database Apps / Delphi 6

By Bill Todd

Multiple Remote Data Modules,


One Connection
Using the New Delphi 6 SharedConnection and
ConnectionBroker Components

O ne problem many developers have encountered when creating multi-tier applications in


prior versions of Delphi is trying to fit all the required data access components onto a
single remote data module. One solution was to use multiple remote data modules.

The first problem with this approach is that the The SQLConnection component is named
client application required a separate connection com- EmpConn, and its driver name property is set to
ponent for each remote data module in the applica- InterBase. Its Params property has been modified to
tion server. This increased system resource usage on include the correct path to the sample InterBase data-
both the client and server. A second problem is that base. If you download the sample application that
the remote data modules that belonged to a single accompanies this article, you’ll need to change this
client are completely unaware of each other. There’s path before you can run it. (The sample application is
no way, for example, for one remote data module to available for download; see end of article for details.)
call a method of another remote data module instance
that belongs to the same client. The SQLDataSet component, named EmpDs, has its
SQLConnection property set to the EmpConn con-
Delphi 6 solves these problems by allowing the client nection component. Its CommandText property is set
application to share a single connection to multiple to SELECT * FROM Employee. The DataSetProvider
remote data modules in the application server. Build- component has its DataSet property set to EmpDs.
ing an application that takes advantage of this new
technology isn’t difficult once you’ve done it. How- The next step is to add a second remote data module
ever, it’s not accomplished just by dropping compo- to the server project. The second remote data module
nents or running wizards. To build an application that is shown in Figure 2. This data module contains a
uses a shared connection, you have to add interfaces single SQLDataSet component with a CommandText
to the type library on the application server, and add property set to: SELECT * FROM Customer. The data
properties to the interfaces. module also contains a DataSetProvider connected to
the SQLDataSet.
The best way to understand this process is to walk
through building a sample application; that’s what Now you must add the necessary interfaces and
this article does. Before reading this article, however, properties to the application server’s type library.
you should know how to build a multi-tier applica- Figure 3 shows the Type Library editor with the
tion using the MIDAS components in Delphi 5. IEmployeeDm interface expanded. To use a single
shared connection to access multiple remote data
Building the Server modules, one of the remote data modules must be
To build this server, create a new project and designated as the master, and all the other remote
add a remote data module to it. The sample data modules will be children. In this example, the
application for this article uses the sample Inter- employee remote data module will be the master and
Base database, and connects to the database using the customer remote data module will be the child.
dbExpress. Figure 1 shows the completed data
module. The data module contains a SQLCon- You must add a read-only property to the
Figure 1: The employee data nection component, a SQLDataSet component, master remote data module’s interface for
module. and a DataSetProvider. each child data module. In Figure 3, the

14 October 2001 Delphi Informant Magazine


Multi-tier

Figure 5:
The client
application’s
Figure 2: The customer remote
data module.
data module.

EmployeeRdm, has been added to this interface with its type set
to IEmployeeDm. Next, the Parent interface of ICustomerDm has
been changed to IChildDm. By changing the parent interface, you
allow ICustomerDm to inherit the EmployeeRdm property from the
IChildDm interface. You need to make the same change to the
Parent interface property of every child remote data module.

After clicking the Refresh button and closing the Type Library editor,
it’s time to add the code to the getter and setter methods for the
properties you’ve added. Start by going to the initialization section
of the child remote data module’s unit. There you will see the code
shown in the comment labeled “Before:”

initialization
// Before:
// TComponentFactory.Create(ComServer, TCustomerDm,
// _CustomerDm, ciInternal, tmApartment);
// After:
Figure 3: The child remote data module property added to the CustRdmFactory := TComponentFactory.Create(
master’s interface. ComServer, TCustomerDm, Class_CustomerDm,
ciInternal, tmApartment);

Change this code so it looks like the uncommented code, by adding


a variable to store the component factory instance reference returned
by the call to TComponentFactory.Create. You must declare this vari-
able in the interface section of the unit:

var
CustRdmFactory: TComponentFactory;

Now that you have a global variable that points to the


instance of the child remote data module’s component factory
object, you can call the component factory’s CreateCOMObject
method from anywhere in the application to create an instance
of the child remote data module. The call to the child remote
Figure 4: The IChildDm interface with its EmployeeRdm property. data modules’ component factory object goes in the getter
method of the CustomerRdm property that was added to the
IEmployeeDm interface earlier. The code for the Get_CustomerRdm
CustomerRdm property has been added to the employee data module’s method is:
interface, with its type set to the child data module’s interface. The
child data module’s interface, ICustomerDm, appears in the Type function TEmployeeDm.Get_CustomerRdm: ICustomerDm;
begin
drop-down list, so all you have to do is select it. Result :=
CustRDMFactory.CreateCOMObject(nil) as ICustomerDm;
This is the only change you have to make to the application server’s Result.EmployeeRdm := Self;
type library to take advantage of the shared connection. However, end;
one of the advantages of this technology is the ability of any child
data module instance to access the interface of the master remote The first line of this method calls the CreateCOMObject method of
data module for the same connection. To do that, you have to add a the child remote data module’s component factory object to create
property to the interface of every child remote data module. The easiest an instance of the child remote data module and assign its interface
way to accomplish this is to add a new interface to the type library, add reference to the CustomerRdm property. The second line of this
the necessary property to this new interface, and use this new interface method assigns Self (i.e. the master remote data module) to the
as the ancestor for all of your child remote data modules. EmployeeRdm property of the new instance of the child remote data
module. This allows the child remote data module, CustomerDm, to
Figure 4 shows the Type Library editor with a new interface, access the properties and call methods of the master remote data
IChildDm, of type IAppServer added. A read-write property, module, EmployeeDm. The last code you must add to the server

15 October 2001 Delphi Informant Magazine


Multi-tier
CustShrd. The SharedConnection component has a property named
ParentConnection that’s set to the EmpDcon connection component.

As of this writing, you cannot set the ParentConnection property to point


to a ConnectionBroker component. This seems to defeat the purpose of
the ConnectionBroker, because you can no longer change connections by
simply changing the Connection property of a single ConnectionBroker.

The SharedConnection component also has a ChildName property.


When you select this property in the Object Inspector and click its drop-
down arrow, you’ll see a list of all the child remote data modules in the
application server. In the sample application for this article there’s only
one child remote data module, CustomerRdm.

If you plan to connect more than one ClientDataSet to the SharedCon-


nection component, you can add a ConnectionBroker component and
Figure 6: The client application’s main form.
connect it to the SharedConnection component. Although the sample
application only has one ClientDataSet, CustCds, using the shared con-
application is for the getter and setter methods of the EmployeeRdm nection, it does use a ConnectionBroker component just to show how
property. These methods simply get and set the value of the property: the ConnectionBroker is used with a SharedConnection component.

function TCustomerDm.Get_EmployeeRdm: IEmployeeDm; Building the rest of the client application is no different than building
begin any other multi-tier client in Delphi. Just connect a DataSource compo-
Result := FEmployeeRdm;
nent to each ClientDataSet, and connect your user interface components
end;
to the DataSource components.
procedure TCustomerDm.Set_EmployeeRdm(
const Value: IEmployeeDm); Conclusion
begin The SharedConnection component is a very useful addition to Delphi
FEmployeeRdm := Value;
if you’re going to build a multi-tier application. You can now share
end;
a single connection to multiple remote data modules. And, multiple
remote data modules can be designed so they can easily interact with
Building the Client each other by accessing the master remote data module’s properties and
The sample client application consists of a single data module (shown calling its methods, if necessary. Since the master remote data module has
in Figure 5), and a single form (shown in Figure 6). The data module a property that contains an interface reference to each child remote data
contains a DCOMConnection component with its ServerName property module, the master can access any child’s properties and call its methods.
set to EmpSrvr.EmployeeDm to connect it to the master remote data You can even have child-to-child calls by having a child use the property
module in the server application. of the master that refers to the interface of another child.

Below the DCOMConnection component, EmpDcom, is a Connection- The ConnectionBroker component is also a useful addition, because it
Broker component. This component is new to Delphi 6 and lets makes it much easier to use different connections in the same client.
you add a level of indirection between multiple ClientDataSet com- One example would be using a DCOMConnection component when
ponents and the connection component they’re connected to. This you are running both the client and the application server program on
lets you change the connection component that your ClientDataSet the same machine during development and testing and easily switching
components use by changing the Connection property of the Connec- to a SocketConnection, WebConnection, or CorbaConnection to con-
tionBroker component, instead of having to change the RemoteServer nect to a remote application server. The only flaw in ConnectionBroker
property of every ClientDataSet. component is that you cannot connect a SharedConnection component
to a ConnectionBroker. ∆
In this application it makes no sense to use a ConnectionBroker,
because there’s only one ClientDataSet. I included the Connection- The sample application referenced in this article is available on the Delphi
Broker here just to show how it is used. To use a ConnectionBroker, Informant Magazine Complete Works CD located in INFORM\2001\
just drop it on your data module and set its Connection property to OCT\DI200110BT.
the connection component you want to use. When you add a Client-
DataSet, you’ll notice that it has a new property, ConnectionBroker.
Just set the ConnectionBroker property of the ClientDataSet and
leave its RemoteServer property blank. The EmpCds ClientDataSet Bill Todd is president of The Database Group, Inc., a database consulting and
component on the data module has its ConnectionBroker property set development firm based near Phoenix. He is co-author of four database program-
to the EmpBrkr ConnectionBroker component, and its ProviderName ming books, author of more than 80 articles, a Contributing Editor to Delphi
property set to EmpProv. Informant Magazine, and a member of Team Borland, providing technical support
on the Borland Internet newsgroups. Bill is also a nationally known trainer and
Before you can connect a ClientDataSet to the customer provider in has taught Delphi programming classes across the country and overseas, and is a
the child remote data module in the server application, you need frequent speaker at Borland Developer Conferences in the US and Europe. Bill can
to add a SharedConnection component to the client’s data module. be reached at bill@dbginc.com.
The SharedConnection component (refer to Figure 5) is named

16 October 2001 Delphi Informant Magazine


Columns & Rows
Microsoft SQL Server 2000 / XML / XML Templates / Delphi 5, 6

By Alex Fedorov

Using the XML Features of


SQL Server 2000
Part III: Two New Modes and XML Templates

T his series examines basic data querying techniques that allow us to use Microsoft
SQL Server 2000 XML features from Delphi applications. In Microsoft SQL Server’s
Transact-SQL (or T-SQL) language, these XML features are implemented in great part by
adding a FOR XML clause to SELECT statements. This month, we’ll continue our exploration
of the FOR XML clause and its various modes, e.g. RAW, EXPLICIT, AUTO, etc. We’ll also learn
how to use XML templates to separate our code from XML-based queries.

Using AUTO Mode


As mentioned earlier in this series, the AUTO <Product ProductID="1" ProductName="Chai"
SupplierID="1" CategoryID="1"
mode returns a result as a nested set of XML
QuantityPertnit="10 boxes x 20 bags"
elements. The following rules are applied to such UnitPrice="18" UnitsInStock="39"
XML documents: UnitsOnOrder="0" ReorderLevel="10" s
 Each table is represented as an XML ele- Discontinued="0" Highlight="0" />
ment, in which the table name (or alias) is <Product ProductID="2" ProductName="Chang"
...
mapped to the element name.
 Columns listed in the SELECT clause are
mapped to attributes of this element. How- In this case each record in the Products table
ever, this default mapping can be controlled (which is specified with the Product alias) is
by using the ELEMENTS argument. represented by a Product element, and each field
 Multi-table queries result in a nested set of with an appropriate attribute within it. We use
elements. the alias, Product, to create a more logical doc-
ument from a processing point of view. We’ll
Let’s look at several AUTO mode examples. specify the name of the root element as Products
First, this simple query: in the URL that contains the query.

SELECT * FROM Products AS Product This query results in the XML document shown
FOR XML AUTO in Figure 1:

produces the following XML document (par- SELECT * FROM Products AS Product
FOR XML AUTO
tially shown here):

17 October 2001 Delphi Informant Magazine


Columns & Rows

<?xml version="1.0" encoding="utf-8" ?>


<Products>
<Product ProductID="1" ProductName="Chai" SupplierID="1"
CategoryID="1" QuantityPerUnit="10 boxes x 20 bags"
UnitPrice="18" UnitsInStock="39" UnitsOnOrder="0"
ReorderLevel="10" Discontinued="0" Highlight="0"/>
<Product ProductID="2" ProductName="Chang" SupplierID="1"
...

Figure 1: Example result of a FOR XML query, using AUTO mode.

<?xml version="1.0" encoding="utf-8" ?>


<Products>
<Product>
<ProductID>1</ProductID>
<ProductName>Chai</ProductName>
<SupplierID>1</SupplierID>
<CategoryID>1</CategoryID>
<QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit>
<UnitPrice>18</UnitPrice> Figure 4: The same query as shown in Figure 3 as an XML
<UnitsInStock>39</UnitsInStock> document.
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>10</ReorderLevel>
<Discontinued>0</Discontinued> The third case of AUTO mode usage is more interesting, and
<Highlight>0</Highlight> shows the difference between traditional recordset-based data and
<Product>
XML-based data. Assume the following query contains identifiers
<ProductID>2</ProductID>
... of all orders made by all clients:

Figure 2: The same query as shown in Figure 1, except that the SELECT Customers.CustomerID, Customers.ContactName,
ELEMENTS argument has been specified. Orders.OrderID
FROM Customers INNER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID

The result of this query is a flat set of records, part of which is


shown in Figure 3. Now if we use the same query, and turn it
into an XML document, it will be properly nested, as shown in
Figure 4.

Using this XML document, we can easily create an application


that shows all order details for the selected client. Let’s outline
the basic steps required to create this application. In our XML
document we have a <Customers> node that includes one or
more <Orders> nodes. To create a list of all customers, we should
process all <Customers> nodes and extract the CompanyName
attribute (see Figure 5). Note that we use the GetNamedItem
method of the IXMLDOMNamedNodeMap object to extract the
value of the CompanyName attribute (and not with an index, e.g.
Attribs[2].Text).

Now we have a list of all customers stored in the ComboBox


component. Each item corresponds to the <Customers> node that
contains all the order data. This allows us to create the following
code to show order details:

procedure TForm1.ComboBox1Click(Sender: TObject);


begin
with Sender as TComboBox do ShowOrders(ItemIndex);
Figure 3: The result of an inner join on Customers and Orders. end;

The ShowOrders procedure gets one node from the XML docu-
Instead of using columns and their data as attributes of the record-based ment, and iterates all child nodes to show the order details in the
element, we can create subelements from it. Change the previous query to: ListView component. The code to do this is practically identical
to the code we used to fill the ListView component in the RAW
SELECT * FROM Products AS Product mode example in the previous article. The only difference is that
FOR XML AUTO, ELEMENTS
here we need to extract only one node, and clear the contents
of the ListView component before showing our data (see Figure
to obtain the results shown in Figure 2. 6). The resulting application is shown in Figure 7. (The example

18 October 2001 Delphi Informant Magazine


Columns & Rows

// Create a list of all customers.


Tag Parent Customer!1!CustomerID Order!2!OrderID
Root := XMLDoc.DocumentElement; 1 NULL ALFKI NULL
for I:= 0 to Root.ChildNodes.Length-1 do begin
2 1 ALFKI 10643
Attribs := Root.ChildNodes[I].Attributes;
ComboBox1.Items.Add(
2 1 ALFKI 10692
Attribs.GetNamedItem('CompanyName').Text); 2 1 ALFKI 10702
end; 2 1 ALFKI 10835
ComboBox1.ItemIndex := 0; 2 1 ALFKI 10952
2 1 ALFKI 11011
Figure 5: Extracting the CompanyName attribute from each 1 NULL ANATR NULL
<Customers> node. 2 1 ANATR 10308
2 1 ANATR 10625
2 1 ANATR 10759
Node := Root.ChildNodes[CustIndex];
Attr := Node.FirstChild.Attributes; 2 1 ANATR 10926
ListView1.Columns.Clear; Figure 9: SQL Server creates this table internally, as a result of
ListView1.Items.Clear;
the query shown in Figure 8.
// Add column headers.
...
// Add row data.
... <Northwind>
<Customer CustomerID="ALFKI">
<Order OrderID="10643"/>
Figure 6: Extracting XML data and clearing the ListView control, <Order OrderID="10692"/>
before presenting data in the example application that accompa- <Order OrderID="10702"/>
nies this article. <Order OrderID="10835"/>
<Order OrderID="10952"/>
<Order OrderID="11011"/>
</Customer>
<Customer CustomerID="ANATR">
<Order OrderID="10308"/>
...

Figure 10: The XML document created by the query shown in


Figure 8.

the tag of the current element, and Parent is used to define the
tree structure of the resulting XML document.

Let’s look at the same Customers and Orders XML document


shown in the previous example, but this time with the help
of EXPLICIT mode (see Figure 8). The T-SQL query is more
complex, reflecting the requirements of EXPLICIT mode.
Figure 7: Example application.

The table shown in Figure 9 will be created by SQL Server for


SELECT 1 AS Tag, NULL AS Parent, internal use. The Tag and Parent columns define the place of the
Customers.CustomerID AS [Customer!1!CustomerID], element in the resulting XML document. When Parent is NULL, it’s
NULL AS [Order!2!OrderID]
the topmost element; a 1 in the Parent column indicates that it’s a
FROM Customers
UNION ALL child element. The resulting XML document is shown in Figure 10.
SELECT 2, 1, Customers.CustomerID, Orders.OrderID From the application’s point-of-view, processing this document is the
FROM Customers, Orders same as processing the document generated with AUTO mode.
WHERE Customers.CustomerID = Orders.CustomerID
ORDER BY [Customer!1!CustomerID], [Order!2!OrderID]
FOR XML EXPLICIT
Let’s create two more examples that show how to use EXPLICIT
mode. The first:
Figure 8: Example of a FOR XML EXPLICIT query.
SELECT 1 AS Tag, NULL AS Parent,
CustomerID AS [Customer!1!CustomerID],
CompanyName AS [Customer!1!CompanyName!element],
applications discussed in this article are available for download.
ContactName AS [Customer!1!ContactName!element]
See end of article for details.) FROM Customers ORDER BY [Customer!1!CustomerID]

Using EXPLICIT Mode creates a nested list of customers:


EXPLICIT mode is used to define the shape of the resulting
document. To use this mode you should write the queries in <Northwind>
a particular way, and provide additional information about the <Customer CustomerID="ALFKI">
expected nesting of the document. The result of a FOR XML <CompanyName>Alfreds Futterkiste</CompanyName>
<ContactName>Maria Anders</ContactName></Customer>
EXPLICIT query is stored in the “universal table” by Microsoft <Customer CustomerID="ANATR">
SQL Server. Two columns are always required: Tag is used to store ...

19 October 2001 Delphi Informant Magazine


Columns & Rows

procedure TForm1.Button1Click(Sender: TObject);


var
URL : string;
begin
XMLDoc := CoDOMDocument.Create;
URL := 'http://terra/northwind/templates/delphidemo.xml';
XMLDoc.Async := False;
XMLDoc.Load(URL);
Root := XMLDoc.DocumentElement;
Memo1.Text := Root.XML;
XMLDoc := nil;
end;

Figure 11: Loading an XML document into an instance of the


XML DOM.

<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql"> Figure 15: Results of the ShowDetails procedure.


<sql:header>
<sql:param name='CustID'>ALFKI</sql:param>
</sql:header>
<sql:query> If for some reason we need to use attributes instead of sub-
SELECT * FROM Customers WHERE CustomerID = @CustID elements, we can change the query, i.e. remove the !element
FOR XML AUTO specifier:
</sql:query>
</ROOT>
SELECT 1 AS Tag, NULL AS Parent,
CustomerID AS [Customer!1!CustomerID],
Figure 12: This XML template has been modified to accept one CompanyName AS [Customer!1!CompanyName],
parameter. ContactName AS [Customer!1!ContactName]
FROM Customers ORDER BY [Customer!1!CustomerID]

procedure TForm1.Button1Click(Sender: TObject); Now, instead of elements, we have attributes of the <Customer>
var
element:
URL : string;
I : Integer;
begin <Northwind>
URL := 'http://terra/northwind/templates/CustList.xml'; <Customer CustomerID="ALFKI"
XMLDoc.Async := False; CompanyName="Alfreds Futterkiste"
XMLDoc.Load(URL); ContactName="Maria Anders"/>
Root := XMLDoc.DocumentElement; <Customer CustomerID="ANATR"
for I := 0 to Root.ChildNodes.Length-1 do ...
with Root.ChildNodes[I].Attributes do
ListBox1.Items.Add(GetNamedItem('CustomerID').Text); This ends our brief discussion of XML queries in SQL Server
ShowDetails('');
2000. Here’s a brief review of the limitations of the FOR
ListBox1.ItemIndex := 0;
end; XML clause:
 It’s only valid in the SELECT statement.
Figure 13: Executing an XML template.  It can’t be used with subselects, or COMPUTE BY or FOR
BROWSE clauses.
 There’s no support for aggregate functions.
procedure TForm1.ShowDetails(CustID: string);  It can’t be used with cursors.
var
URL : string;
For additional limitations, see the complete list in SQL Server
begin
URL := 'http://terra/northwind/templates/CustDetail.xml'; Books Online, currently found at http://msdn.microsoft.com/
if CustID <> '' then library/default.asp?URL=/library/psdk/sql/portal_7ap1.htm.
URL := URL + '?CustID=' + CustID;
XMLDoc.Async := False;
XMLDoc.Load(URL);
Using XML Templates
Root := XMLDoc.DocumentElement;
So far we’ve seen how to use XML queries that were hard-coded
// Fill in edit boxes. in our application’s code. To provide more separation between
with Root.FirstChild.Attributes do begin the code and T-SQL queries, we can use XML templates. In
Edit1.Text := GetNamedItem('CompanyName').Text; general, an XML template is an XML document with a predefined
Edit2.Text := GetNamedItem('ContactName').Text;
structure. The following example shows a simple XML template:
Edit3.Text := GetNamedItem('ContactTitle').Text;
Edit4.Text := GetNamedItem('Address').Text;
Edit5.Text := GetNamedItem('City').Text; <ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
Edit6.Text := GetNamedItem('PostalCode').Text; <sql:query>
Edit7.Text := GetNamedItem('Country').Text; SELECT * FROM Customers FOR XML AUTO
Edit8.Text := GetNamedItem('Phone').Text; </sql:query>
end; </ROOT>
end;
Note that this XML document consists of two parts: the defini-
Figure 14: The ShowDetails procedure. tion of the root node and appropriate namespace, and a SQL

20 October 2001 Delphi Informant Magazine


Columns & Rows
node that contains the T-SQL query we want to execute. To Conclusion
run an XML template from the Delphi code, we need to create In this article, we discussed two FOR XML modes that allow us to
an XML document that contains this query, and store it in create different XML representations of our data. We also exam-
the /templates directory of the virtual directory for the SQL ined several examples of such queries. After this we used XML
Server database. Then we must load this XML document into an templates to separate application code from the XML-based que-
instance of XML DOM; this will execute the T-SQL query inside ries, and created an example of how to use these templates from
the document (see Figure 11). Delphi applications. Next month, we’ll see how to use Delphi’s
data-aware controls with XML data sources. See you then. ∆
Now change the XML template to accept a parameter named
CustID. The new version of the query is shown in Figure 12. The projects referenced in this article are available on the Delphi
Upon execution, this XML template will return data for the Informant Magazine Complete Works CD located in INFORM\2001\
customer based on the provided CustID, or for the customer with OCT\DI200110AF.
the CustID ALFKI, if there’s no parameter specified.

Let’s create an example that uses two XML templates: the first one
will return a list of Customer IDs, while the second will retrieve
details for the specified customer. Execute the first template in the
OnClick event handler for the button, as shown in Figure 13.
The ShowDetails procedure executes the second template, which
extracts the details for the selected client (see Figure 14). The
results are shown in Figure 15. 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 Professional
The important thing to note is that because we have separated the Active Server Pages 2.0 [Wrox, 1998] and ASP Programmer’s Reference [Wrox,
Delphi code from the SQL queries, through the use of XML templates 1998], as well as Advanced Delphi Developer’s Guide to ADO [Wordware, 2000].
we can easily change the queries without touching the code.

21 October 2001 Delphi Informant Magazine


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

By Mike Edenfield

Console Applications
Part III: Message Queues, Threads, and More

O ver the last two months, we’ve discussed the details of creating and using a
console in your application. Now we’re ready to start using consoles in more
complex and useful ways. This article will describe ways to add consoles to your appli-
cation, or to enhance your console-only applications, using the techniques discussed
previously. In addition, it will demonstrate how to most effectively combine the console
API with other Win32 programming features — and how to avoid some potential pitfalls
along the way.

Message Queues
Most Delphi programmers are aware that each messages to a windowless message queue. Instead,
windowed application has a message queue, and Windows provides the PostThreadMessage function
each of its windows has a window procedure to for this purpose:
handle messages from that queue. Since typical
console applications don’t have windows, many function PostThreadMessage(idThread: DWORD;
people assume there’s no way to send messages to Msg: UINT; wParam: WPARAM; lParam:
the application, but this is simply not true. That LPARAM): BOOL; stdcall;
message queue can be used to receive messages even
when there’s no GUI window present. Other than taking a thread ID as opposed
to a window handle, PostThreadMessage
Figure 1 lists a small console application that functions exactly like PostMessage. We used
posts itself four messages. Windows will auto- GetCurrentThreadId to get our own thread’s thread
matically create one for us the first time we ID, but most often it will come from the
call any GDI or User function. The call to dwThreadID field of a PROCESS_INFO record, as
PeekMessage, one of the User functions, causes populated by CreateProcess.
Windows to create our message queue. The mes-
sage loop shown here is artificially simplified, Consoles and GUI Windows
since there’s no need to translate or dispatch the While console applications are — in many ways
messages once we receive them. Note that the — just as powerful and useful as GUI applica-
calls to PeekMessage and GetMessage both were tions, the simple fact is that Windows users like
passed an hWnd parameter of HWND_NULL graphics. The GUI is the most important aspect
(a constant defined in the .dpr file as 0). This of Windows, and of Windows programming.
causes all messages in the thread’s queue to be Fortunately, you don’t need to choose between
returned, regardless of what window (if any) they writing a console application or writing a GUI
were sent to. application. You can very easily mix and match
the two in the same application, to take full
Since PostMessage and SendMessage both take an advantage of all the features Windows (and
hWnd parameter, they’re no good for sending Delphi) provides.

22 October 2001 Delphi Informant Magazine


On Language
Let’s consider the input loop in Listing One. While it looks similar
{$APPTYPE CONSOLE} to previous input loops, there is one key difference. This time,
program Listing1; execution doesn’t block while waiting for input. Instead of calling
ReadConsoleInput blindly, WaitForSingleObject is used on the con-
uses
Messages, Windows, SysUtils;
sole’s input handle. In this case, the handle will become signaled
only when there is readable input. Doing this allows the forms
const to process their messages, including repainting and handling user
CM_THREAD = WM_USER + 100; input. (We’ll explore a more elegant solution to this problem later
in this article.) Figure 2 shows the results of running this program.
var
msg: TMsg;
dwSelf: DWORD; As far as creating a console inside a GUI application, we’ve actu-
cInput: Char; ally done that already. Simply call AllocConsole in your applica-
begin tion, and FreeConsole when done. We will use this technique again
{ This call to PeekMessage creates a message queue in the next section.
for the primary thread of this application. }
PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
dwSelf := GetCurrentThreadId;
Redirecting the Standard Handles
{ Post a few sample messages to our thread's queue. } One of the more powerful aspects of console-based programming
Write('Posting messages to this thread's queue: '); is the concept of standard input and standard output. UNIX and
PostThreadMessage(dwSelf, WM_USER, 0, 0); DOS power users will be intimately familiar with the ability to
Write(' WM_USER');
redirect the input and output of command-line programs to/from
PostThreadMessage(dwSelf, WM_USER, 100, 0);
Write(' WM_USER');
files, devices, or even into oblivion. While it’s possible to build a
PostThreadMessage(dwSelf, CM_THREAD, 0, 0); DOS command to include redirection, then parse the output file,
Write(' CM_THREAD'); Windows provides a better way. By using a console window and a
PostThreadMessage(dwSelf, WM_QUIT, 0, 0); Win32 object called an anonymous pipe, you can set up input and
Write(' WM_QUIT');
output redirection completely in code.
Writeln(#13#10 +
'Press any key to begin message processing...');
Read(cInput); Figure 3 demonstrates one handy use for this feature. Instead of
while True do begin delving into the SNMP API, the sample program executes the
GetMessage(msg, 0, 0, 0);
NETSTAT.EXE program found on all Win32 systems, and processes
if msg.hwnd = 0 then
begin
the input. The key to this is the creation of the anonymous pipe,
case msg.message of which contains a read and a write handle. The child process will
WM_USER: inherit the open read handle, and will also inherit the parent’s console,
Writeln('Got WM_USER message, wParam = ', including the current state of its standard file handles. By combining
msg.wParam);
these two facts, we can redirect the child process’ output to our pipe,
CM_THREAD:
Writeln('Got CM_THREAD custom message.'); and process it internally. The result is shown in Figure 4.
WM_QUIT:
begin This process is fairly straightforward. After creating a console
Writeln('Got WM_QUIT, aborting.');
and an anonymous pipe, we attach one end of the pipe to the
Break;
end;
console’s standard output handle with SetStdHandle. The child
end; // case msg.message... process should only inherit the write end of this pipe, so an
end; // if msg.hwnd... uninheritable copy of the read end is created, and the original is
Readln; closed. All of this is done as part of our SetChildHandles function,
end; // while True...
shown in Figure 5.
end.

Figure 1: This console application processes Windows messages. Once the child process is created, we can reset our own standard
output handle, and begin using our end of the pipe to read our
child process’ output. The API calls that we haven’t discussed yet,
which are used in this process, are:
More importantly, the VCL already contains much of the code
needed to use its objects in a console application. This means not function CreatePipe(var hReadPipe, hWritePipe: THandle;
lpPipeAttributes: PSecurityAttributes; nSize: DWORD):
only forms, but non-visual VCL objects, such as threads, synchro-
BOOL; stdcall;
nization objects, sockets — even the database components. The function DuplicateHandle(hSourceProcessHandle,
power of the entire VCL is available to you. hSourceHandle, hTargetProcessHandle: THandle;
lpTargetHandle: PHandle; dwDesiredAccess: DWORD;
To demonstrate, the sample program in Listing One (on page bInheritHandle: BOOL; dwOptions: DWORD): BOOL; stdcall;
function CloseHandle(hObject: THandle): BOOL; stdcall;
25) will create two TForm descendants and display them. By
including the Forms unit in our console application, we gain all
of the required Windows code to create forms and process their The parent process waits for the child process to finish before
messages. We use the VCL objects, such as Application, exactly trying to read its output and close any handles. This makes the
as we would in a forms-based application. TApplication even has parent process a bit simpler to program, but is by no means
an event, OnMessage, which permits us to handle thread messages required. The parent is free to read data from the anonymous pipe
not directed at a specific window (similar to the message loop in at any time. This is especially useful when redirecting standard
Listing One). input, as input can be sent interactively to the child process.

23 October 2001 Delphi Informant Magazine


On Language

procedure TForm1.cmdNetstatClick(Sender: TObject);


var
piChild: TProcessInformation;
sItem, sBuf, sOutput: string;
dwRead: DWORD;
begin
{ Run the netstat command and wait for it to finish.
All of its output will be sent into the anonymous
pipe attached to fsPipe. }
SetChildHandles;
piChild := CreateChildProcess('NETSTAT.EXE -A -N');
WaitForSingleObject(piChild.hProcess, INFINITE);
memOutput.Clear;
memOutput.Lines.Add('Listening TCP Sockets:');
SetLength(sBuf, 4097);
{ Read all available data from our file handle. }
repeat
ReadFile(hParentStdout, sBuf[1], 4096, dwRead, nil);
sOutput := sOutput + sBuf;
while Pos(#13, sOutput) > 0 do begin
sItem := Copy(sOutput, 1, Pos(#13, sOutput) - 1);
Figure 2: The sample application from Listing One at run time. Delete(sOutput, 1, Pos(#13, sOutput) + 1);
ParseNetstatLine(sItem);
end;
until dwRead < 4096;
The parent could also have closed both the console and the { Clean everything up when we're done. }
parent’s copy of the pipe’s write handle at any time. Once the Cleanup(piChild);
child process was spawned, it maintains separate copies of inher- end;

ited resources, and the parent is free to modify or close them as


Figure 3: Executing NETSTAT.EXE as a child process.
needed. The only caveat is, as always, to be aware of conflicts
between two processes attempting to read or write the same
handle at once.

Threads and the Console


One of the major differences between console applications and
GUI applications is how user input is handled. While it’s possible
to write either type of application to behave any way you want,
console and GUI applications are each best suited to a different
user input model. GUI applications generally present the user a
variety of input options, and handle them in whichever order they
arrive. On the other hand, console applications usually follow a
much more rigid flow of requesting user input, then blocking
until it arrives. This can cause problems when you try to mix and
match GUI and console code in the same application. The sample
program in Listing One, for example, needed special handling to
allow the GUI windows to continue processing while waiting for
console input.

One solution to this problem is to move the console input code


Figure 4: The NETSTAT.EXE-calling application at run time.
into a separate thread. By doing this, your console can use the
standard console input functions, blocking all processing until
there is something to do, without interrupting the remainder of
your program. In Figure 6, we’ve rewritten the application shown The sample program relies on Windows and Delphi to clean up
in Listing One to eliminate the need to call WaitForSingleObject when it terminates, which is fine for simple programs. However, if
or Application.ProcessMessages. Apart from creating the thread, the you create any objects or open any handles in any of your threads,
body of the main program looks very much like any other Delphi you should install a control handler so your application will exit
main program file. gracefully from a CC or Ck. These key presses will cause
Windows to terminate your entire application, including any GUI
Consoles are associated with an entire process, both those created windows or other threads, unless you specifically prevent it.
with AllocConsole and those created by Windows for {$APPTYPE
CONSOLE} programs. This means that reading from and writing A complete discussion of threaded programming is beyond the scope of
to the console file handles is not inherently thread-safe. Two this article. The June 2000 issue of Delphi Informant has a very good
threads trying to access the console simultaneously could result in article on an agent-based approach to threading (“Waking from Thread-
a garbled or jumbled display, or worse, failure to process all of the mare” by Nikolai Sklobovsky), and most advanced Delphi programming
user’s input. As with any multi-threaded application, you should books will have chapters on TThread and threaded programming. I also
use the synchronization objects (in this case, most often critical strongly recommend reading the “DLLs, Processes and Threads” section
sections) to avoid these kinds of problems. of MSDN, especially the section on synchronization objects.

24 October 2001 Delphi Informant Magazine


On Language

procedure TForm1.SetChildHandles; Mike Edenfield is an applications developer for Sylvan Learning Systems in
var
sa: TSecurityAttributes;
Baltimore, MD, and an MCSD. He has five years experience with Delphi and Visual
begin Basic, and specializes in Microsoft SQL Server development. He can be contacted at
{ We need this to force the user of our inherited Michael.Edenfield@educate.com.
stdout handle. }
AllocConsole;
{ Save the old standard handle first. }
hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE);
{ Create the pipe, marking it inheritable. Then create a Begin Listing One — Creating Windows, Handling
non-inheritable copy of the read end of the pipe, so Messages
the child only inherits the write end. } {$APPTYPE CONSOLE}
sa.nLength := SizeOf(sa); program Listing2;
sa.bInheritHandle := True;
sa.lpSecurityDescriptor := nil; uses
Windows, Messages, Forms, SysUtils,
CreatePipe(hParentTemp, hChildStdOut, @sa, 0); Dialog1 in 'Dialog1.pas' {frmDialog1},
DuplicateHandle(GetCurrentProcess, hParentTemp, Dialog2 in 'Dialog2.pas' {frmDialog2};
GetCurrentProcess, @hParentStdout, 0, False,
DUPLICATE_SAME_ACCESS + DUPLICATE_CLOSE_SOURCE); var
{ The write end of the pipe becomes stdout. The other end hInput: THandle;
gets attached to our stream object for input later. } inRec: TInputRecord;
SetStdHandle(STD_OUTPUT_HANDLE, hChildStdOut); dwCount: DWORD;
fsPipe := THandleStream.Create(hParentStdout);
end; begin
{ Create two forms in the usual manner. The Forms unit
Figure 5: Creating a console, and an anonymous pipe, and ensures that the Application object is around to "own"
attaching one end of the pipe to standard output. the forms. }
Write('Creating the first dialog box...');
frmDialog1 := TfrmDialog1.Create(Application);
frmDialog1.Show;
{$APPTYPE CONSOLE} Writeln(' done.');
program Listing4;
Write('Creating the second dialog box...');
uses frmDialog2 := TfrmDialog2.Create(Application);
Windows, Forms, SysUtils, frmDialog2.Show;
Dialog3 in 'Dialog3.pas' {frmDialog3}, Writeln(' done.');
Dialog4 in 'Dialog4.pas' {frmDialog4}, Writeln('Press 1, 2, or 3 to change the dialog box. ' +
ConsoleThread in 'ConsoleThread.pas'; ' Press CTRL-C to exit.');
{ Handle the console input until the user cancels. In a
var real app, we would want to install a control handler to
thrInput: TConsoleThread; clean up our forms and such before exiting. }
hInput := GetStdHandle(STD_INPUT_HANDLE);
begin while True do begin
Application.CreateForm(TfrmDialog3, frmDialog3); { Avoid blocking on user input, so the forms have a
Application.CreateForm(TfrmDialog4, frmDialog4); chance to operate as normal. If we had a message
frmDialog4.Show; queue present, this would be a normal message
{ Hook thread's OnTerminate event, so we know when dispatch loop. }
to close. } Application.ProcessMessages;
thrInput := TConsoleThread.Create(False); if WaitForSingleObject(hInput, 0) = WAIT_OBJECT_0 then
thrInput.OnTerminate := frmDialog1.ConsoleOnTerminate; begin
Application.Run; ReadConsoleInput(hInput, inRec, 1, dwCount);
end. if (inRec.EventType = KEY_EVENT) and
inRec.Event.KeyEvent.bKeyDown then
begin
Figure 6: Placing the console input code into a separate case inRec.Event.KeyEvent.AsciiChar of
thread eliminates the need to call WaitForSingleObject or '1': begin
Application.ProcessMessages. Writeln('-> 1');
frmDialog2.opt1.Checked := True;
end;
'2': begin

Conclusion Writeln('-> 2');


frmDialog2.opt2.Checked := True;
The concepts described here are not unique to console applications. end;
Many of you have already used threads, and perhaps even built your '3': begin
own message queue. Fortunately, Delphi makes the job of using the Writeln('-> 3');
VCL, both visual and non-visual, as simple in a console application frmDialog2.opt3.Checked := True;
end;
as any other. By taking advantage of these features of Windows end;
programming, you can write console applications that are every bit end;
as powerful as your graphical applications. ∆ end;
end;
end.
The sample programs referenced in this article are available on the
Delphi Informant Magazine Complete Works CD located in INFORM\ End Listing One
2001\OCT\DI200110ME.

25 October 2001 Delphi Informant Magazine


New & Used

By Bill Todd

GTSizer
Scales Forms to Fit and Look Good

I f you want your Delphi forms to automatically scale to any screen resolution, and
still look good, you need GTSizer.

You need a 17” monitor to get the same image monitor at 640x480. You need a 21” monitor
size at 800x600 resolution that you see on a 14” to get the same image size at 1024x768. Why
users don’t understand that I do not know, but
they don’t. Users seem to think that your appli-
cation should look the same on a 14” monitor
at 1024x768 as at 640x480, but it doesn’t work
that way.

GTSizer lets your users play the multiple


resolution game while keeping your application
looking good. GTSizer does this by automat-
ically resizing your forms, components, and
fonts for any resolution, from 640x480 to
1280x1024. On top of that, GTSizer also pro-
portionately resizes and
repositions all of the
components on your
form when a user resizes
the form by dragging a
side or corner. GTSizer
also resizes your forms as
needed when run on a
PC using large fonts.

GTSizer gives you two


ways to handle resolution
changes. The default
mode maintains the
form’s size in pixels when
the resolution changes. If
the user increases the res-
olution from 800x600 to
1024x768, the form will
appear smaller in relation
to the screen size. If you
set the SameSize property
Figure 1: A test form containing a frame at two different sizes, with all compo- to True, GTSizer will
nents visible and its proportions maintained. display the form so it

26 October 2001 Delphi Informant Magazine


New & Used
appears to be about the same size at the higher resolution. In both
modes controls will be resized and repositioned automatically
when the user resizes the form.
GTSizer is one of those marvelous products that does a difficult
GTSizer works with modal and modeless forms, dialog boxes, and job well, and is very easy to use. If you need to create resolution-
supports all border styles. GTSizer also gives you control over independent applications, you won’t go wrong with GTSizer.
the form’s size at run time from within your application. You
can set the RtDisplayHeight, RtDisplayWidth, RtMaximumHeight,
RtMaximumWidth, RtMinimumHeight, and RtMinimimumWidth GenoTechs, Inc.
properties at design time or run time to control the form’s size at 2741 W. Southern Ave., #10
run time, and to limit the user’s ability to resize the form. Tempe, AZ 85282

GTSizer lets you override its automatic font size changes on a US Phone: (800) GENOTEX
component-by-component basis using its IgnoreTag property. Set International Phone: (602) 438-8647
the IgnoreTag property to any integer value, then set the Tag E-Mail: info@genotechs.com
property of a component to the same value, and that component’s Web Site: http://www.genotechs.com
font size will not be changed. This is particularly useful with grids Price: GTSizer, US$124.95; upgrades, US$39.95 each.
or memo components in which you would rather let the user
see more data than the same data in a larger font when the user
increases the size of the form.
the GTForm component, however, you will have to assign the
The best news about GTSizer is that it is incredibly easy to handler to the GTForm component. If you do need to add, move,
use; just drop the GTForm component on each of your forms or resize components after you have dropped GTSizer on your
and you’re done. If you want to make it difficult for users to form, just set the GTUpdate property to True. GTSizer will store
make your forms look bad, you can set the GTForm component’s the new component information, and set the GTUpdate property
SizeBalancing property to True at run time. With SizeBalancing back to False automatically.
enabled, the height and width of the form retain their original
proportions as the user changes its size. Figure 1 shows a test GTSizer comes with an excellent online help file that covers every
form containing a frame at two different sizes with all components aspect of using the component. If you want to try before you buy,
visible and its proportions maintained. The GTForm component you can download a trial version of GTSizer from the GenoTechs
also provides a FormRestore method that you can call to restore the Web site. ∆
form to its original design size and shape.

There are only two things you need to keep in mind when design-
ing a form that you plan to use with GTSizer. The first is to set Bill Todd is president of The Database Group, Inc., a database consulting and
the Align property of all components to alNone. GTSizer cannot development firm based near Phoenix. He is co-author of four database program-
properly resize a form that has components with other Align set- ming books, author of more than 80 articles, a Contributing Editor to Delphi
tings. The second thing is to add the GTForm component to Informant Magazine, and a member of Team Borland, providing technical support
your form after the form is finished and tested. The GTForm on the Borland Internet newsgroups. Bill is also a nationally known trainer and
component needs to reassign the form level event handlers to has taught Delphi programming classes across the country and overseas, and is a
itself. This happens automatically when you add the GTForm frequent speaker at Borland Developer Conferences in the US and Europe. Bill can
component to a form for all of the event handlers that have been be reached at bill@dbginc.com.
defined. If you add another event handler to the form after adding

27 October 2001 Delphi Informant Magazine


New & Used

By Ron Loewy

ExpressPrinting System 2
Create Hard-copy Output Based on Your UI

D elphi ships with a set of reporting components named QuickReport, and plenty
of third-party solutions are available, such as ReportBuilder, Shazam Report
Wizard, and ACE Reporter. As a casual user of some of these tools, I know you can
create beautiful reports, sometimes using great-looking visual tools that minimize
the coding. However, once you allow users to customize them, you’ll need to write
another user interface. This is often different from the user interface you supply users
to select, filter, or highlight records or items, so the users need to learn another
interface. Often, to save time and money, most of the selection and preparation
options you provide in the main code don’t find their way into the printing code.

In previous reviews I mentioned my satisfaction clicked on it to display the component editor. Using
with several Developer Express user interface prod- the Add button, I added a link to one of the grids
ucts: ExpressQuantumGrid, ExpressInspector, and on my form (a list of books from a database) and
ExpressBars. I recently had the chance to try named it BookPrinter. All I needed then, to provide
the newly introduced ExpressPrinting System printing of the books grid, was a simple call to the
(EPS), a set of VCL components that create hard- component’s Preview method passing two param-
copy output, and complement these and other eters: True to display the preview window as a
Developer Express products. Support for other modal dialog box, and the name of the report link
common Delphi components such as StringGrid I created (BookPrinter). I ran my program, clicked
and TeeChart is also provided, and if you own the the button, and there was my grid, including all
source code, creating new Report Links to other filtering information I assigned, shown in a preview
user interface components is possible. dialog box.

A key feature of other Developer Express products The same customization options I got during the
like ExpressBars and ExpressQuantumGrid is “design” of the report are available at run time,
easy-to-use customization. This design philosophy including definitions specific to the grid print order,
makes it easy for a developer to get started with a a definition of the titles, headers, footers, and fonts,
component in a hurry with a minimum of code. and a comprehensive page setup dialog box. I spent
Fortunately, the same visual customization tools are 20 minutes experimenting with header and footer
also available at run time, and allow the users to definitions, colors, fonts, and minimal customiza-
customize the product to their liking. This philoso- tion, and finished with an application that could
phy is still apparent in EPS. print attractive reports of the six different grids that
form the application’s user interface. It’s amazing that
My introduction to the product was brief. I in such a short amount of time an application can be
dropped a TdxComponentPrinter component on the enhanced with almost no learning curve (see Figures
form that houses my grid controls, and double- 1 and 2). And the product offers a lot more.

28 October 2001 Delphi Informant Magazine


New & Used

Figure 2: A couple of clicks, a line of code, and the same data


is displayed in the print preview window.

Figure 1: The application’s user interface.

The Components
Installing the ExpressPrinting System adds five components to
Delphi’s Component palette:

TdxComponentPrinter is the main component of the product and


is used as the conduit that prints the selected components. Drop
one of these components on the form and use it during design
time to add and define the properties of report links. As the main
component you work with, it contains global printing properties,
including the number format for page numbers, preview window
options, designer window options, and more. In addition, the
format dates and time values are printed. Events allow you to
intervene in printing at common hard-copy creation occurrences,
such as when a new page is started, before or after a preview,
etc. Finally, the methods of the component, especially Preview and Figure 3: The Express MasterView report link designer can be
Print, are how you activate the hard-copy generation processes. used during development or run time.

TdxPageSetupDialog allows you to present a dialog box for the user


to define page setup attributes such as paper type, print orienta- Report Links
tion, print order, dimensions, margins, header and footer defini- The heart and soul of EPS is the report links. These smart classes
tions, and scaling. If your application uses the Preview method understand a specific visual component and are able to translate it
of the component printer, all this functionality is available as from its online representation into a meaningful and useful hard-
part of the preview window. If your application provides direct copy rendition.
printing capabilities and does not use the Preview function, this
component allows you to offer a customization tool to your users. Starting with the TdxDBGridReportLink, activating the report
designer presents the user with a dialog box that allows you to specify
TdxPSEngineController controls the look and feel of all the dialog how you want several grid-specific items to be handled during the
boxes and windows displayed by the printing system at run time. print process. For example, you can specify if you want to display grid
This includes pointers to help files, the look, the registry path bands, if you want to display grid headers and footers, group footers,
used to store user preferences, and the form positions. and preview lines. If you aren’t familiar with these QuantumGrid
capabilities, the review can be found in the January 2001 Delphi
TdxPrintDialog is used to customize printer dialog box attributes Informant Magazine. Likewise, the designer allows you to customize
such as print to file, page ranges to print, and the option availability. the colors and fonts at the different grouping levels and specify if
you want to limit the printing to only the selected nodes, if you
TdxPrintStyleManager allows you to define styles such as pre- want to expand all nodes before printing, and what effects are used
defined option sets for different paper styles, printers, and the to display images. Additional options are available at design and run
like, and switch them on the fly. This allows your users to choose time through the report link’s properties. A set of events that mirror
from these options without manually changing all the attributes of some of the custom drawing events of the grid are also available in the
the print job they need. report link so you can define different background colors, text values,
or fonts at run time based on node values.
In addition to these, several other objects are available at run and
design time, from the ability to define a background “watermark” After adding support to print my application’s grids, I tested some
for your print jobs, to run-time customization objects. of the other report links supported by the product. One of the more

29 October 2001 Delphi Informant Magazine


New & Used
interesting is the report link for the Developer Express MasterView I could go on about all of the other report links included in the package,
component. This component provides a read-only master-detail grid but it’s easy to see that if a report link exists to a component you need to
that allows your users to view nested data sets in a hierarchical manner. print, it’s likely you can customize it to your heart’s content.
While the component is a tour de force of technical capability, I never
saw the need for it in my own applications until the connection with Documentation, Help, and Support
the printing system was made. By connecting a master view object As is customary with Developer Express products, EPS comes with
to a TdxComponentPrinter via a TdxMasterViewReportLink report link, a set of PDF files, a complete help system that is integrated into
one can create multi-level summary and detail reports without writing Delphi’s help, and a set of tutorials and samples. The no-coding
any code. There are no control breaks to write, and you don’t need approach implemented using the component editor allows you to
to handle all the code to manually tie the master-detail relations in a start quickly, but the functionality of the component set and report
report. Corresponding to the grid report link, the MasterView report links is so diverse that if you want to take full advantage of the
link allows you to customize the printing attributes of the control at product, you will need to consult the documentation.
both design and run time (see Figure 3). Again, you can specify if
all nodes are automatically expanded before printing the component, I find samples and tutorials the easiest way to learn about a product.
change the colors and fonts of the different levels of lines, and custom- Luckily, different samples are provided for the different report links.
ize individual cell attributes. The Developer Express Web site also provides an article about version
2 of ExpressPrinting System and the newsgroups are stuffed with
After dealing with report links to two Developer Express products, expert users and Developer Express technical support employees who
it was time to check how nicely EPS plays with other kids in are quick to point to a solution. While I didn’t experience any
the Component palette. I started with TdxTeeChartReportLink, the problems with the product and my applications, with such extensive
report link that allows you to print charts created with TeeChart. functionality you can expect bugs and problems to appear. I have
Unlike the previous report links mentioned, a report designer is not learned with other Developer Express products that support is just
available for this report link. Given the simplicity of user interface an e-mail message away, and is usually handled very quickly. The
that is built into TeeChart (compared to the other components problems I have had were more often related to my understanding
mentioned before), this is an acceptable limitation. If there is not of one arcane feature or another than to code deficiencies. When a
that much to change in the user interface of the component, there legitimate problem was brought to the developer’s attention, it was
is really no need to create a designer to change (or not change) it either fixed in a hurry or a work-around was suggested.
for printing. The report link component still allows you to specify
properties to control the report title, the page setup, and events to Conclusion
handle custom header, footer, and title printing. If you want an easy, productive way to create printed output tied to
your user interface, ExpressPrinting System is a great solution. If your
Of the other standard Delphi components that have report links right application requires complicated reports, performing complicated data
out of the box (e.g. StringGrid, DrawGrid, ListBox, CheckListBox, manipulation, summary, and selection, a report writer is still needed.
TreeView, and Rich Edit), only the RichEdit report link doesn’t
have a designer (again because of the simplicity of the user interface For many applications, a simple point-and-shoot technique for creating
customization of the core component). The designer for the TreeView reports would be to connect a QuantumGrid, string grid, or a Mas-
component, for example, allows you to specify if you want to print terView component to the printing system products. I am especially
grid lines around the printed items of the tree, choose if images (and impressed with the ability to perform selection and filtering using
state images) will be printed, specify node expansion, and customize QuantumGrid and have it used as a source for the printed output. I
the colors and fonts of the printed items. am also very impressed with the ability to create multi-band reports
with the MasterView/Printing System combination; unfortunately, this
solution does not offer the filtering capabilities of the grid and will
require additional code and user interface implementation.

Unlike most reviews, I started this one with a product I don’t already
own or know. As a result of my experience with ExpressPrinting
The ExpressPrinting System component publishing system brings System, my company purchased a copy for our use. Two of our
your user interface to print. Its ReportLink renders and prints applications already incorporate the printing capabilities, connected
visual controls, such as the ExpressQuantumGrid and Express- to the application’s grids.
MasterView, and other standard VCL controls like StringGrid and
RichEdit. Its design-time capabilities, and set of run-time design- If you already own QuantumGrid or MasterView, adding Express-
ers, help reduce the time you spend developing custom reporting Printing System should be an easy decision. If you don’t, it will offer
solutions. another advantage to differentiate it from the other grids and user
interface options available on the market. It’s definitely worth your
Developer Express consideration. ∆
6340 McLeod Dr., Suite 1
Las Vegas, NV 89120

Phone: (800) GO-DEVEX


E-Mail: info@devexpress.com
Web Site: http://www.devexpress.com Ron is a software developer specializing in Business Intelligence applications. He
Price: US$179.99 can be reached at rloewy@transport.com.

30 October 2001 Delphi Informant Magazine


Best Practices
Directions / Commentary

Evil Numbers

I n a previous column, “Be Resourceful,” I covered why it’s good to avoid sprinkling string literals throughout your
code (in the July 2000 issue of Delphi Informant Magazine). As I made clear in that column, there are many
reasons to separate string literals out to a resourcestring section in a separate unit. But what about hard-coded
numbers? Should you avoid them, also? Yes, for two reasons:

1) Business rules can and usually do, eventually, change; and You would have a problem, because at least one of the array boundar-
2) The number of elements in an enumerated type, array or other ies would no longer match the code. A run-time error — or worse, a
range of values can change. In fact, almost any number you logic error without a run-time error — would be the result. To avoid
might use in your code is subject to change. that, it’s better to use Delphi’s Low and High functions when dealing
with range boundaries:
Even if a number doesn’t change throughout the software’s lifetime, you
make your code more readable by declaring a constant that makes the procedure Whatever;
number’s purpose clear. For example, instead of writing code like this: var
List1: array[0..3] of Double;
X: Word;
TransmissionDueDate := IncBusinessDays(Date, 2); begin
for X := Low(List1) to High(List1) do
It will be much clearer to the reader and maintainer of your code if ...
you declare a constant (preferably in a separate constants unit, named
YourProjectNameConst.pas): Referring to literal numbers in code, Steve McConnell wrote in his
classic book Code Complete, “A good rule of thumb is that the only
const literals that should occur in the body of a program are 0 and 1.”
... The literal number 0 is reasonably allowed because the index of the
BUSINESS_DAYS_IN_ADVANCE_TO_TRANSMIT_NACHA_FILES = 2;
first in a list of items likely will never change. For example, if you
have a string list named sl, the following code is acceptable:
Then replace the hard-coded number with the constant:
for i := 0 to sl.Count-1 do begin
TransmissionDueDate := IncBusinessDays(Date,
BUSINESS_DAYS_IN_ADVANCE_TO_TRANSMIT_NACHA_FILES); The literal number 1 is allowed for similar purposes. The same
example as shown above illustrates this. Starting from the index 0,
You could end up maintaining your code several months — even you don’t want to read past the last element in a string list, so you
years — from now, when the business rules and underlying logic of define Count-1 as being the upper limit of the loop.
your code may not be as obvious to you as they were when you wrote
it. Besides making the code more readable and thus maintainable, However, even the number 1 can normally be avoided. You can
this practice also is advantageous in that, when the business rules do do this by using Delphi’s Pred (predecessor) and Succ (successor)
change, you can simply modify the value of the constant in one place functions. Here’s an example of using Pred instead of -1:
(i.e. YourProjectNameConsts.pas), rather than search the entire body
of code, changing n instances of the hard-coded value. for i := 0 to Pred(sl.Count) do begin
...
In the following example, what would happen if you changed the
declaration of the array, but failed to change the lower and upper and of using Succ instead of +1:
bounds of the for loop?
if AnsiPos('^', s) > 0 then begin
XRefPortion := Copy(s, Succ(AnsiPos('^', s)), Length(s));
procedure Whatever; ...
var
List1: array[0..3] of Double;
X: Word;
begin McConnell also said in Code Complete, “Use named constants instead.
for X := List1[0] to List1[3] do Be a fanatic about rooting out literals in your code. Use a text editor to
... search for 2, 3, 4, 5, 6, 7, 8, 9, “, and ‘ to make sure you haven’t used

31 October 2001 Delphi Informant Magazine


Best Practices
them accidentally.” McConnell recommends searching for single and const
double quotation marks to find string literals and replace them with DISTANCE_FROM_TOP_TO_DRAW = 4;
constants. Because we’ve already covered that subject, we’ll concentrate ...
on searching for numbers in the 2 through 9 range. strgrd.Canvas.TextOut(Rect.Left+StartPoint,
Rect.Top+DISTANCE_FROM_TOP_TO_DRAW, s);
As a good example of a bad example, let’s search through a program
I wrote in a less enlightened era. Here are two examples of evil As is true with almost everything, there are a few cases where you
numbers I found lurking in my code. may find it best to go ahead and use numeric literals in your code.
These instances, though, should be rare. You should have a good
Example 1: reason for using literals, and, as this should be an unusual situation,
your reason for doing so should be in your code’s comments.
if ZipObj.IncludeSubDirs then begin
if Length(FilePath) > 3 then Segregating literal numbers to a separate constants unit and giving
RemoveRoot
them meaningful names will make your code easier to read, easier to
maintain, and less likely to hide potential causes of errors when busi-
which is better as: ness rules change. The world is dynamic, and so are the conditions
in which we work. If you plan for the inevitable changes in advance,
const
MAX_ACCEPTABLE_FILEPATH_LENGTH = 3; you’ll save yourself and others a lot of time and trouble. ∆
...
if ZipObj.IncludeSubDirs then begin
if Length(FilePath) > MAX_ACCEPTABLE_FILEPATH_LENGTH then — Clay Shannon
RemoveRoot

Example 2:
Clay Shannon is a certified Delphi 5 developer and the author of Tomes of Delphi:
strgrd.Canvas.TextOut(Rect.Left+StartPoint, Rect.Top+4, s); Developer’s Guide to Troubleshooting (Wordware, 2001). He also wrote the soon-to-
be-published novel Twisted Roads (whose protagonist is a Delphi developer). Readers
which is better as: may reach him at BClayShannon@aol.com.

32 October 2001 Delphi Informant Magazine


File | New
Directions / Commentary

BorCon 2001: A Delphi Odyssey

E ach year’s Borland Conference begins with a keynote speech featuring a theatrical event followed by a showcase
of the company’s development arsenal. This year, the theme was the Stanley Kubrick movie that includes the
current year in its title. Not surprising, but I don’t think anyone was quite prepared for Borland’s specific treatment of
the movie, borrowing ideas from its beginning and end.

Imagine this: While Richard Strauss’ powerful Also Sprach Zara- ered both Delphi and Kylix. Although a number of developers
thustra plays in the background, large, sculpted letters spelling wish the initial release of Kylix were more feature-rich, everyone
INPRISE become majestically illuminated. Suddenly, an ape-like appreciated that Borland fulfilled its promise to provide a truly
creature approaches these letters with a bone weapon in hand and cross-platform development solution.
smashes them all, eliciting tumultuous cheers from the audience.
Then, we see larger letters further back, proclaiming BORLAND. Now let’s turn our attention to a session I attended that had little
Powerful symbolism! educational value, but which will be among the most memorable
for those who attended it: Mark Miller’s “Fun with Delphi: Step
Continuing, we see the famous monolith from the movie and the Over to the Dark Side.” At this outrageous excursion into the
red mechanical eye of a computer, this one named DALE 9000. bizarre, our unique presenter gave out Dark Side T-shirts and other
The voice of the computer is (can you guess?) the company’s prizes, but didn’t do any of his frenetic coding improvisations.
CEO, Dale Fuller. The man in the gorilla suit is, of course, Instead, he treated us to several scandalous new applications:
David Intersimone. Following the impressive demonstration of  The “Dr. Bob Hat Remover” searched a hard drive, found
Borland’s current products in one integrated e-commerce system, every utility by Bob Swart, located the graphic of Dr. Bob
Fuller heads into the audience to field questions. It’s a great begin- with his legendary hat, then cut off the top of that hat. The
ning to a great conference. companion “Dr. Bob Hat Replacer” then replaced the missing
hat with outlandish substitutes.
As always, the Borland Conference provided an opportunity to  An unscrupulous new component, TShipItNow, designed to
renew friendships, make acquaintances, share development tri- distribute a bug-ridden application prematurely while disguis-
umphs and frustrations, sharpen technological prowess, and have ing its faults in a clever way. In the “demo,” instead of generat-
a great time. I had the opportunity to attend many informative ing the usual divide-by-zero exception message, TShipItNow
sessions as well as one unique presentation. brought up a dialog box that blamed the error on Microsoft
Word, Excel, etc.
Ray Konopka conducted a valuable vendor showcase called  My wife’s favorite, and one to bring cheer to many of us in the
“Advanced Debugging with CodeSite,” which gave me several new United States next April, “Receipt Maker Pro 2000;” it asks the
ideas for using this wonderful tool. Konopka also presented two user to specify the amount of the refund he or she would like
excellent sessions related to Delphi’s ActionLists. Based on what I to receive for their income taxes and then generates the proper
learned, I may finally try using them in my own applications. Charlie receipts to make it happen.
Calvert’s session on “Graphics and Multimedia” was spellbinding and
demonstrated, again, his skill in presenting complex topics (such as Miller also shared a new online magazine called BYTE ME and its
OpenGL and related technologies) in a clear and entertaining way. first uninstall issue featuring an article called, “How to Reformat
Your Hard Drive.” Can you guess what operating system this was
I also attended several Kylix sessions, including one with Danny ridiculing? I cannot go into any details about Miller’s DotSuck
Thorpe that explored BaseCLX, the foundation of CLX (Compo- Domain extensions without sinking to a level of bad taste unchar-
nent Library for Cross Platform). Some sessions, such as Ray acteristic of this magazine. You’ll just have to ask around to find
Lischner’s insightful exploration of multitasking and threads, cov- out about that. The cleverest part of Miller’s presentation was a

33 October 2001 Delphi Informant Magazine


File | New

CodeRush plug-in that used a fierce-looking Microsoft agent that ect JEDI. While hardly the most active, I was honored to be included
watched as Miller entered code, found and pointed out syntax errors, in that group. I cannot overstate the extent to which members of
and then made insulting comments to him. I want a copy of that! Project JEDI are excited and gratified by this recognition.

The high point was a special cartoon Miller created, called The There’s more to discuss: Delphi 6, Kylix, new directions in tech-
Borlands (based on The Simpsons) in which a former CEO of nology, and Project JEDI. I will explore all of these in future
Borland returns to take it over again and do all kinds of mischief. columns. For now, I will close with the slogan we have adopted in
At one point, we see a developer who looks a lot like Bart Simpson Project JEDI: May the source be with you. ∆
write over and over on the blackboard, “I will not hack the IDE.”
Miller even was able to incorporate material from the previous For an exciting visual tour of the conference, please see
night’s keynote speech. At the end, the audience was exhausted Marco Cantù’s excellent article on his Web site at http://
from laughing nearly continuously for an hour. Miller received www.marcocantu.com/development/borcon2001.
a standing ovation, something I have never seen at one of these
conferences in the past. — Alan C. Moore, Ph.D.

Many stayed after Miller’s presentation for a session called, “Meet


the Delphi/Kylix Development Team.” While attendees raised a
number of serious issues and suggestions (as has been the case in past
years), the tone was different from previous years. Many attendees Alan Moore is Professor of Music at Kentucky State University, where he
began by expressing their appreciation for Delphi 6 and Kylix. As in teaches music theory and humanities. He was named Distinguished Professor for
other question-and-answer sessions, there was honesty and openness. 2001-2002. He has developed education-related applications with the Borland
We learned of some exciting new marketing strategies for which languages for more than 15 years. He is the author of The Tomes of Delphi:
many of us have been lobbying for years: Borland will provide free Win32 Multimedia API [Wordware Publishing, 2000] and co-author (with John
downloads of entry-level versions of Kylix and Delphi 6! Penman) of an upcoming book in the Tomes series on Communications APIs.
He also has published a number of articles in various technical journals. Using
The emotional highlight of the convention took place for me toward Delphi, he specializes in writing custom components and implementing multime-
the beginning of this session, with the announcement that this year’s dia capabilities in applications, particularly sound and music. You can reach
“Spirit of Delphi Award” would go to the active contributors to Proj- Moore on the Internet at acmdoc@aol.com.

34 October 2001 Delphi Informant Magazine


Symposium

“I Wish It Had Been Microsoft”


I don’t want to get this guy in trouble, so I’ll keep my description general. It was the first day of sessions at the 12th Annual Borland Conference
(held this July in Long Beach, California) and the particular talk was on Microsoft’s .NET technologies. The speaker was a Microsoft employee and
did a great job of describing Microsoft’s huge ongoing initiative to unite their development tools on a single foundation, the Common Language
Runtime (or CLR).
Toward the end of the session, the Microsoft speaker congratulated Borland on shipping the first implementation of a Microsoft .NET technology.
He was referring to WebSnap’s implementation of Microsoft Web Services. “Congratulations,” he said. “Borland was first. I wish it had been
Microsoft, but Borland was first.” I could see Simon Thornhill (VP and general manager of Borland’s RAD business unit) and Michael Swindell
(director of product management) from where I sat. Both were fairly bursting with a mixture of surprise, glee, and pride. It was a special, unexpected
moment, and got the conference off to an auspicious start.
It was also a good example of how inclusive the Borland/Delphi community is compared to that of Microsoft (and other software vendors for that
matter). There were several Microsoft speakers at the conference and many Microsoft products were in evidence. This isn’t particularly remarkable
to readers of this magazine. However, you will never see a Borland speaker at a Microsoft conference, and you will never hear of Borland, or of
a Borland product, at a Microsoft conference.
It was the most upbeat conference in years, with each attendee excited about a new Borland product or marketing strategy. The biggest buzz was
generated by Borland’s sage decision to release Delphi 6 Personal Edition. The attendance was down slightly from last year, but that must be put in
perspective. It’s no secret that conference attendance is off this year due to the economy — by 20, 30, even 50 percent in some cases. So Borland was
justifiably proud of the turnout. Borland would appear to be back. More than ever, I was struck by the international flavor the conference has taken
on. The Brazilian contingent was sizable as always, while Eastern Europeans seem to make up much of the new blood.
Another memorable .NET session was given by international Borland veteran Alain “Lino” Tadros, who advised: If you can’t remember a C#
property or method, try using the same name as you would with Delphi. He also urged attendees to press Borland on the question of whether Delphi
will support the CLR. It’s an interesting prospect. It wouldn’t be surprising to see Delphi offer better support for the CLR than Microsoft does; it could
well be quicker and easier to develop CLR applications with a CLR-capable Delphi.
I heard many speculate that .NET will be good for Borland and Delphi. The reasoning is that Visual Basic developers are unhappy with the wholesale
changes represented by Visual Basic .NET, and are therefore ripe for defection to Delphi. On the other hand, I heard sotto voce from those who
despair for Delphi, because they see C# as a “Delphi Killer” that will be extremely attractive to Delphi developers, in part because it’s written by
one Anders Hejlsberg. Me? I’d like to hear what you think.

Thanks for reading.

Jerry Coffey, Editor-in-Chief


jcoffey@DelphiZine.com

35 October 2001 Delphi Informant Magazine

You might also like