On The Cover: July 2000, Volume 6 Number 7

Download as pdf or txt
Download as pdf or txt
You are on page 1of 39

July 2000, Volume 6 Number 7

Cover Art By: Arthur Dugoni

ON THE COVER
6 On the ’Net
Real-world Web Apps — Dr Mark Brittingham
You know the basics; Dr Brittingham tells us what it takes to build a
Web application to withstand the rigors of daily use, from WebBroker
components, to ISAPI DLLs, to tracking state, and more.

FEATURES
14 Columns & Rows
Exploiting SQL Server 7 DMO: Part II — Jason Perry REVIEWS
Mr Perry ends his two-part series on Microsoft SQL Server 7 Distributed 30 Wise for Windows Installer 2.0
Management Objects by sharing an invaluable Database Information Product Review by Bill Todd
and Reconciliation Tool, as well as a simple security object.
35 Delphi 5 Developer’s Guide
20 Greater Delphi Book Review by Alan C. Moore, Ph.D.
Palm Conduits: Part II — Ron Loewy
Completing his two-part series, Mr Loewy builds a conduit that moves
information from the Palm device to a Paradox database, and a conduit
that synchronizes the Paradox database and Palm device.
DEPARTMENTS
24 DBNavigator 2 Delphi Tools
Delphi and ASP — Cary Jensen, Ph.D.
Microsoft Active Server Pages are extremely popular. Dr Jensen shows 5 Newsline
you how to get in on the action by explaining the basics of ASP, and 36 Best Practices by Clay Shannon
demonstrating how to create and use ASP objects with Delphi. 38 File | New by Alan C. Moore, Ph.D.
1 July 2000 Delphi Informant Magazine
Delphi Joseph D. Booth Consulting Releases JBC UI/Scan for Delphi
Joseph D. Booth
T O O L S Consulting, Inc. released
JBC UI/Scan for Delphi,
New Products a lint checker that allows
and Solutions developers to provide a
more professional look
and feel to all their appli-
cation’s GUI design.
The developer simply
scans an application, and
UI/Scan will report tab
order problems, menus
without code attached,
hard-coded colors, fonts
that are too small or too
large, hard-to-read color
combinations, and other
user interface concerns. Developers can customize colors, oper to print a report or save it
UI/Scan will assist the devel- fonts, lists of obsolete controls, to a file.
oper in putting in the final etc. There are also filters so
touches to the visual design UI/Scan can be set to only focus Joseph D. Booth Consulting, Inc.
of an application. In addition, on fixing one problem at a time. Price: US$149
UI/Scan allows the developer to Another feature is the reporting Fax: (610) 409-8859
customize all aspects of a scan. option, which allows the devel- Web Site: http://jbooth-consulting.com
Quma Releases QVCS 3.4
Quma Software, Inc. announced file revisions. You can set up a used to compare files to earlier
the release of QVCS 3.4, a Win- global journal file to keep track revisions, or compare revisions
dows 95/98/NT4/2000 version- of all changes to files in all to other revisions. In an emer-
control system designed to bring your projects. You can insert key- gency, you can restore a project
order and control to the applica- words into source code or binary to an earlier-labeled release.
tion development process. files for automated documenta- In addition to using the Win-
QVCS automates the tracking tion and audit trail generation. dows program, QVCS allows
of files as they change during At the programmer level, QVCS programmers to use batch files to
the development of a new soft- keeps track of which changes are check files in and out, by provid-
ware application. By requiring made to which files, and when. ing 14 command-line utilities.
programmers to log files in and When a programmer checks out QVCS provides a range of
out, QVCS prevents multiple a file, QVCS will retrieve the reports, including reports of all
programmers from unknowingly latest version of the file, and locked revisions (by individual or
working on the same file simul- record a date/time stamp for that team), revisions created after a
taneously. programmer. QVCS ensures that release, revisions created between
QVCS makes it easy to control the programmer is authorized to any two dates, revisions with com-
which file revisions are included work on the module. It will warn ment lines containing any given
in which releases, and to recover the programmer if a writeable text string, or any combination of
previous versions of files. QVCS copy of the file already exists, and these criteria.
allows you to control executa- deny access to the file if another
bles, documentation, Web pages, programmer is already working Quma Software, Inc.
and all files associated with a on that file. Price: US$25; QVCS-Pro, US$40.
development project. When questions arise about E-Mail: [email protected]
QVCS stores and retrieves program versions, QVCS can be Web Site: http://www.qumasoft.com
O&A Productions Announces oaAgent 1.0
O&A Productions released The first component, component. ToaAgentScript man-
oaAgent 1.0, a pair of native ToaAgent, wraps the Microsoft ages the interaction of multiple
VCL components that make Agent server COM. Addition- agents and other program ele-
working with the Microsoft ally, ToaAgent features enhance- ments. Developers can also extend
Agent COM server easer. ments not found in the ActiveX the scripting language to add their
oaAgent brings Microsoft Agent control, including persistent own commands.
technology to the Delphi and voice commands and custom
C++Builder IDEs in the form context menus. O&A Productions
of a non-ActiveX, native VCL The second component, Price: US$35
wrapper of the Microsoft Agent ToaAgentScript, is an extensible Phone: (858) 618-1904
COM server. scripting language and interpreter Web Site: http://www.o2a.com/agent.htm

2 July 2000 Delphi Informant Magazine


Delphi Objective Software Technology Announces TRANSFORM 5.0.1
Objective Software Tech- properties and events, into a developers to encapsulate the
T O O L S nology Pty Ltd. announced single component that can be function of a related set of com-
TRANSFORM 5.0.1, the newest reused inside other forms. ponents and selectively publish
New Products version of the company’s Delphi TRANSFORM creates reusable properties, methods, and events
and Solutions IDE expert that lets you convert groups of components or “aggre- relating to the group.
entire forms, including form gate components,” which allow The aggregate component can
be installed in Delphi’s Compo-
nent palette and reused without
having to recreate each individ-
ual component.
Aggregate components can be
used as business components to
encapsulate business logic and user
interface controls; to create com-
plex user interface controls, such
as toolbars and editors; and as
a tool for increasing productivity
and object reuse on larger projects.

Objective Software Technology


Pty Ltd.
Price: US$125
Phone: +61 8 8357 1805
Web Site: http://www.obsof.com
InstallShield Announces RTPatch for InstallShield Professional
InstallShield Software Corp. made since the last release in pending) compares updated and
announced the availability of the form of a simple “patch,” original software versions to
RTPatch for InstallShield Pro- which can be easily distributed include only the necessary data
fessional. Developed by Pocket via an InstallShield Professional in the patch.
Soft, Inc., and sold through setup package. RTPatch typically Once the patch file is created,
InstallShield and its worldwide reduces the actual size of the the developer includes the
network of authorized resellers, software update by over 90 per- patch in an InstallShield Profes-
RTPatch for InstallShield Pro- cent and provides developers sional installation package and
fessional provides byte-level soft- currently using InstallShield Pro- distributes it to end users for
ware patching capabilities spe- fessional with a bandwidth-sen- installation via the Internet or
cifically for InstallShield Profes- sitive solution that enhances the intranet. The end user simply
sional 6.x and 5.x setups. End delivery of software updates over runs the new installation pack-
users can now download update the Internet and other mediums. age and the patch is automati-
“patches” instead of reinstalling The GUI-driven Patch Build cally applied.
the entire program. Wizard walks developers
RTPatch for InstallShield Pro- through a simple, four-step pro- InstallShield Software Corp.
fessional allows software devel- cess to create a patch. Behind the Price: US$495
opers to upgrade a file or a set of Wizard, RTPatch’s “byte-level Phone: (800) 374-4353
files by sending only the changes differencing” technology (patent Web Site: http://www.installshield.com
DeVries Data Systems Announces Release of OfficePartner 1.5
DeVries Data Systems, Inc. C++Builder is easier and more print methods for Word docu-
announced the release of stable. ments and Excel workbooks; a
OfficePartner 1.5, the latest New features include new element for accessing sub-
version of its suite of software expanded and enhanced docu- folders in Outlook 98/2000;
components designed to inte- mentation to provide how-to and the ability to run in hun-
grate the Microsoft Office suite and reference information; dreds of development tools/-
with Borland software develop- mail merge capability in Word platform scenarios, including
ment tools. components that supports VCL the latest versions of Delphi
OfficePartner’s expanded Datasets and ODBC; an and C++Builder.
functionality will enable devel- expanded connection frame-
opers to produce results with- work that allows OfficePartner DeVries Data Systems, Inc.
out a great deal of Automation components to attach to run- Price: US$399 per user; discounts available
expertise. With new function- ning instances of Office appli- for multi-license packages.
ality added to all main com- cations and OLE containers; Phone: (888) 866-8031
ponents, automating Office find-and-replace functionality Web Site: http://www.dvdata.com/dvnew/
applications from Delphi and included in Word components; pages/index.asp

3 July 2000 Delphi Informant Magazine


Delphi Lingscape Announces MultLang Suite 3
Lingscape Ltd. announced East, and European languages retrieve localized editions, team
T O O L S MultLang Suite 3, a new version together on the same platform. development, context-sensitive
of the company’s globalization Unique features include one dictionaries, and more.
New Products tool for Delphi and C++Builder. code base for all languages,
and Solutions MultLang Suite targets general, machine translation, quality assur- Lingscape Ltd.
intermediate, and advanced ance guidelines, form validation Price: US$998; printed manual/CD/-
developers involved with inter- routines to detect form defects, postage, US$39.
national projects with full qual- leverage feature for previous work, E-Mail: [email protected]
ity control. migration utility to dynamically Web Site: http://www.lingscape.com
MultLang Suite 3 helps reduce
internationalization and localiza-
tion costs using wizards and
machine translation. It is based
on the Unicode standard, and,
together with a conversion
engine, provides target support
for languages such as Japanese,
Chinese, Arabic, Hebrew, Hun-
garian, Russian, and all Euro-
pean languages.
The integration with the
IDE and the compiler helps
produce thin localized EXE,
DLL, and ActiveX, or option-
ally link multi-language sup-
port into the same application.
With MultLang, it’s possible
to compile Far East, Middle

EliteSys Announces SuperBot 2.2


EliteSys announced the release fore able to surf the Net without file transfers.
of SuperBot 2.2, an automated missing calls. A copied site can be With the “Monitor clipboard”
download utility for Windows transferred directly to any other feature, you can download a Web
95/98/NT4 that can copy entire storage medium for archival or site by merely copying its address
Internet sites with one click. distribution purposes. and pressing J. SuperBot can
Thanks to SuperBot’s HTML SuperBot can be configured automatically launch a copied site
rewriting technology, copied sites to filter downloads by location, in your browser, as well as record
look and act just like their online date, file type, link type, depth, its location in a log file.
counterparts. and file count. Once a copy
Once SuperBot has downloaded operation has begun, it can be EliteSys
a Web site, it can be viewed in paused, stopped, and restarted. Price: US$24.95
any Web browser, at high speed, SuperBot utilizes HTTP/1.1 E-Mail: [email protected]
and without an Internet con- “Smart Restart” technology to Web Site: http://web.idirect.com/
nection. Modem users are there- resume any partially completed ~elitesys/superbot

4 July 2000 Delphi Informant Magazine


News TurboPower Announces Support for C++Builder 5
Colorado Springs, CO — Turbo- turbopower.com/tpslive. www.turbopower.com, or call
Power Software Co., developer To learn more, visit http:// (800) 333-4160.
L I N E of tools and libraries for profes-
sional Delphi and C++Builder
Inprise/Borland’s Kylix Project Builds Third-party
programmers, announced full Network for Linux
July 2000 and free support for Inprise Bor- Scotts Valley, CA — Inprise/- tool for the Linux platform. The
land C++Builder 5. Compatibil- Borland hosted more than 200 Kylix project is a component-
ity updates will be available for third-party authors, consultants, based development environment
all current version TurboPower trainers, and tool and compo- for two-way visual development
products. The free updates will nent vendors for the first in of graphical user interface, Inter-
be available as no-charge down- a series of worldwide events net, database, and server appli-
loads from the company’s Free designed to prepare third-party cations. Kylix will be powered
Update Center at http://www. products and services for Kylix. by a new native Delphi/C/C++
turbopower.com/updates/. Also participating were Linux compiler for Linux and will
Updates will also be available on distributors, including Mandrake- implement a native Linux ver-
CD-ROM for a nominal charge. Soft, TurboLinux, Corel, Caldera, sion of the Borland VCL (Visual
In addition, customers can and SuSE. Announced in Septem- Component Library) architec-
subscribe to free TurboPower ber 1999, Project Kylix will be a ture. The Borland VCL for
electronic newsletters to receive Linux rapid application develop- Linux is designed to radically
instant e-mail notification when ment environment that will sup- speed native Linux application
patches for the products they port Delphi, C, and C++. development and simplify the
use are available for download. The Kylix project is planned porting of Delphi and
The address for e-newsletter to be the first high-performance C++Builder applications
subscriptions is http://www. rapid application development between Windows and Linux.
Inprise/Borland Opens Public Field Test of InterBase 6.0
Scotts Valley, CA — Inprise/- natively, users are encouraged to made publicly available in the
Borland announced a public participate in the field test news- near future.
field test of InterBase 6.0, its groups at http://www.interbase. To learn more, visit
cross-platform relational data- com/open/community/ Inprise/Borland at http://
base, for the Linux, Windows, 60beta_newsgroups.html. www.borland.com, the
and Solaris operating systems. Field test versions of InterBase community site at http://
A beta version of the database 6.0 for Windows, Solaris, and community.borland.com, or call
— which will be open-sourced other operating systems will be the company at (800) 632-2864.
with multiple platform support
in mid-2000 — is now
Inprise/Borland Java Development Tools Win
available as a free download Multiple Awards
from the InterBase Web site Scotts Valley, CA — Inprise/- uct Excellence Award in the
at http://www.interbase.com/ Borland announced that two of Language and Development
open/downloads/. its popular Java development Environments category, and the
Inprise/Borland invites inter- tools, JBuilder and JBuilder JIT, 1999 JavaWorld Readers’ Choice
ested parties to freely download have won multiple awards over Award for best IDE (integrated
and test this latest version of the past weeks. JBuilder, a rapid development environment). The
InterBase for Linux, Windows, application development (RAD) JBuilder JIT, Borland’s Just-In-
and Solaris. Any feedback on environment for Java develop- Time compiler, won the Java-
this new version can be sent to ment, won both the Software World Readers’ Choice Award for
[email protected]. Alter- Development Magazine Jolt Prod- best compiler.
Inprise/Borland and Hitachi Strengthen Co-development Relationship
Scotts Valley, CA — Inprise/- code for functional innovation, to license Inprise/Borland’s Visi-
Borland Corp. and Hitachi, Ltd. technical enhancement, and qual- Broker and Inprise’s Java devel-
announced they have signed a ity improvement. opment technology for inclusion
worldwide amendment to an Amendments to the current in Hitachi’s CORBA technology
existing licensing agreement for agreement extends the relation- products, such as TPBroker, Hita-
CORBA and Java development ship between the two companies, chi’s CORBA object transaction
technology. reaching back to 1995, for co- service product, and the Cos-
Under the terms of the new developing CORBA-based prod- minexus Application Server. (Cos-
agreement, the research and ucts and will broaden the com- minexus Application Server is cur-
development laboratories of the mitment of the two organizations rently marketed only in Japan.)
two organizations will begin to to share enterprise middleware Financial terms of the agree-
share their own innovations to technical innovations. ment were not disclosed. For
Inprise/Borland’s VisiBroker As provided in a previous more information on Hitachi,
product line, including source agreement, Hitachi will continue visit http://www.hitachi.co.jp.

5 July 2000 Delphi Informant Magazine


On the ’Net
WebBroker / HTML / Web Servers / Delphi 5

By Dr Mark Brittingham

Real-world Web Apps


Building Session-aware ISAPI DLLs

I f you’ve been working in Delphi for a while, you know it’s the most productive Windows
development tool available. What you may not realize is that it’s also an excellent tool
for developing Web applications. In this article, we’ll cover how to build a session-aware
ISAPI DLL that can be used in a real-world application.

Page Requests Page Responses If the rise of Internet technologies has you worried that you’ll have
to leave the world’s best development environment behind, fear no
more. Delphi’s WebBroker technologies give you an easy way to build
fast, scalable Web applications.
Web Server Web applications are defined by a client-server model, where the
(IIS, Omni) client is a Web browser that interacts with the Web server by sending
page requests and form results and receiving HTML in response. In
ISAPI Requests & Responses a very important sense, your job as a Web developer is to “program”
(each in its own thread) the user’s browser to behave the way you want it to by sending it the
appropriate HTML and JavaScript code.

WebModule Pool Getting Started


Web Before starting, make sure your environment has all of the tools
Application needed for building your site. You’ll need a Web server, a Web
Object
WebModule browser, an HTML editor, and either Delphi Enterprise or Delphi
N
Professional with the WebBroker libraries (also available at extra
charge from Inprise). We assume that you’ll be developing an ISAPI
DLL because of the very significant boost in performance these Web
TWebRequest applications provide. Not only do they avoid the overhead of loading
& a CGI executable for every page request, they permit you to create
TWebResponse an extremely fast state management system in memory, rather than
relying on database access to maintain state.

Matching For a Web server, you may choose to use Microsoft’s Personal Web
WebActionItem Server (PWS) or Internet Information Server (IIS) because these
Linked come free with Windows 98, NT, and 2000. However, I recommend
PageProducer that you download and install the OmniHTTPd server from Omni-
cron. It’s available at their Web site: http://www.omnicron.ab.ca/
httpd. Omni is far easier to use as a debugging host in Delphi than
the Microsoft Web servers, and is free for local and development use.
When it comes time for deployment, you can either substitute IIS, or
Other Actions license the commercial Omni distribution. Because both support the
same ISAPI extension standards, they’re interchangeable.
WebBroker ISAPI DLL In practical terms, your work will proceed by creating your HTML
Figure 1: Delphi’s ISAPI architecture. pages, building and compiling your ISAPI DLL, and refreshing your

6 July 2000 Delphi Informant Magazine


On the ’Net
browser to review the results. Because your browser and server are on
the same machine, your Web URLs will all start with http://localhost/.
Behind the scenes, the browser will contact the server, and the server will
load your DLL and request the HTML to be sent on to the browser.

Note that you won’t be able to compile your ISAPI DLL while the
server is running, because Delphi won’t be permitted to overwrite
the old DLL being run by the server. Thus, the server will have to
be shut down each time you’re finished testing and started up again
before requesting another page. To shut down Omni, just use its
icon in the system tray. Use the Internet Services Manager to stop
PWS or IIS.

Life will be considerably easier if you configure Delphi to run the


server as the Host Application under the Run | Run Parameters menu.
For the Omni Web server, you need only to place the location of the Figure 2: A Web action item in the Object
Inspector.
“ohttpd.exe” executable in the host field. Configuring the Microsoft
Web servers is far more complex, although configuration for IIS 4.0 is
well documented at the “D files” Web site at http://www.fulgan.com/
delphi/index.asp.

If this will be your first ISAPI application, then you need to know
how to tell Delphi to create an ISAPI DLL. Do this by using the File |
New menu. On the New tab in the New Items dialog box, select Web
Server Application. In the dialog box, select the ISAPI/NSAPI Dynamic
Link Library radio button, and press OK. You’re in business!

Delphi’s ISAPI Architecture


Writing raw ISAPI DLLs isn’t rocket science, but it still involves quite
a bit of specialized knowledge and a knack for thread-safe coding.
Fortunately, Delphi’s WebBroker libraries make it much less tedious,
and a lot more fun. Figure 1 captures the essential components of
a WebBroker application. As you can see, everything is driven by Figure 3: A PageProducer in the Object
requests from the server. Inspector.

When the Web server receives a request, it bundles the data Note that, in addition to the PathInfo property, this action item has
related to the request and sends it in a new thread to the ISAPI a producer named Page2. In my development, I give the PathInfo, the
DLL. The TWebApplication object in the DLL, in turn, uses an action, and the producer the same name to make it clear they all work
existing WebModule, or, if necessary, creates a new WebModule together. (Yes, you can give an action and a producer the same name.)
instance to service the request. This WebModule executes within
the thread context passed by the server. This means you can use There are two ways in which you can respond to a request arriving at
variables defined within the TWebModule class and be assured they your DLL: with a producer, or via the action item’s OnAction event.
aren’t shared with other threads. However, global variables will be If you respond to the OnAction event, you’ll have to generate your
shared across threads, so you should avoid them, or be certain HTML and pass it back by assigning the Response.Content field:
your access is thread-safe.
Response.Content := SomeHTMLGenFunction;
If you need data access, you’ll be happy to know that the BDE
and ADO datasets can be placed on the WebModule and used in If you’re using a PageProducer, you’ll generally assign a file to the
a thread-safe manner. The only constraint is that, for the BDE, a PageProducer’s HTMLFile property (see Figure 3). This will cause the
TSession object must be placed on the WebModule, and its file’s contents to automatically be sent as a response.
AutoSessionName property must be set to True.
If you wish, you can keep your entire HTML document in a
A URL that calls an ISAPI DLL should name an action to be PageProducer’s HTMLDoc property. The advantage of this is that
executed. For example, a URL like: you won’t have to distribute HTML pages with your DLL. The
disadvantage is that your site is much more difficult to update: Every
www.Mysite.com/ISAPI/MyIsapi.dll/Signin update will require a re-compile, and your work will slow down every
time you need to cut-and-paste your HTML between Delphi and
will call MyIsapi.dll and hand it the Signin action. When the your HTML editor. Personally, I never use the HTMLDoc property.
WebModule receives the request, it attempts to locate a
TWebAction whose PathInfo matches the action passed in the URL Of course, if your DLL did nothing more than pull HTML files
(see Figure 2). You should generally create one action item with a from disk using PageProducers, then there would be no sense in
Default property set to True to handle the case where no other action creating the DLL. The PageProducer family (TPageProducer,
item handles a request made to the DLL. TQueryTableProducer, TDataSetTableProducer, and

7 July 2000 Delphi Informant Magazine


On the ’Net
TDataSetPageProducer) is powerful because of its OnHTMLTag procedure TWebModule1.WebModuleBeforeDispatch(
event. The OnHTMLTag procedure for a PageProducer is called during Sender: TObject; Request: TWebRequest;
the parsing of the HTMLFile or HTMLDoc streams. Every time a tag Response: TWebResponse; var Handled: Boolean);
(marked with <#> delimiters) is encountered, this function receives a var
cookieStrings : TStringList;
call. The call contains the tag, any arguments to the tag, and a var
begin
parameter named ReplaceText that you can fill with the output you SessionID :=
want to appear in place of the tag. StrToIntDef(Request.CookieFields.Values['SID'], 0);
if (SessionID = 0) then // No sessID passed.
For example, if your PageProducer is loading a file named begin
SessionID = Random(2000000000);
DateTime.htm, and it encounters the tag <#Date>, then the cookieStrings := TStringList.Create;
OnHTMLTag function will be called with the <Date> tag. After cookieStrings.Add('SID=' + IntToStr(SessionID));
matching on the appropriate tag in this function, you could fill the Response.SetCookieField(
ReplaceText parameter with something like “March 15, 2000.” When the cookieStrings, ", ", Now+10, False);
end;
HTML is sent to the browser, this date will appear in place of the tag.
end;

To close out the topic of basic ISAPI development, note that one of Figure 4: Using a cookie to manage a session ID.
the best things that Delphi does for you is take all of the form, URL,
and cookie arguments submitted by a visitor and package them neatly or fat URLs. In all three cases, you might consider storing the
in the ContentFields, QueryFields, and CookieFields string lists. For user’s entire state in the cookie/URL/form. However, unless you’re
example, if you want to know the value a user recorded in the “Name” implementing a very small site, it’s far more appropriate to include
field of a form, you need only look at the result contained in: a numerical ID in the cookie/URL/form that serves as the key to
finding that information on the server.
Request.ContentFields.Values['Name']
Forms. To maintain state using forms, you would have to place every
To become a good ISAPI developer, working with string lists must link on your site in an HTML form, and store the state ID in a
become second nature! hidden field. Then, as the user navigates the site, the ID would be
passed in the Request.ContentFields variable in each request. This isn’t
Dynamic Web Pages and Sessions a viable approach, because of the overhead and pain involved in
After the elation of building your first ISAPI DLL, you’ll probably implementing every link as a form.
come to the realization that you’re nowhere close to a real Web
application. This is because the Web is a completely stateless environ- Cookies. To maintain state in a cookie, you’ll generate a unique
ment. In standard Windows development, you can pop up a dialog session or user ID, and store it in a cookie that is placed on the
box, have the user fill in some answers, and return to the main form visitor’s computer when they first enter the site. On subsequent pages,
without any worry that a different user has requested each of these this cookie is pulled from the page request and used to access the
actions. This isn’t true on the Web! Each request your DLL receives is visitor’s state information. Figure 4 shows Delphi code to accomplish
logically independent of every other request. this. Note that I’ve placed this code in the WebModuleBeforeDispatch
method of the WebModule. This is because BeforeDispatch is called at
That means that, although your simple ISAPI DLL can now produce the beginning of every page request, and is thus an ideal place to set
a Web page that includes dynamically-generated information, it will up the session for the rest of the request. Note that all functions called
either generate the same information for every user, or will use only in the process of satisfying a request are within a single thread. This
the immediately preceding page to generate the response, e.g. process- means you can assign a variable in one function, and use it in another
ing a form page. if the variable is defined in your WebBroker class.

To get around this limitation, we need to implement some mecha- The advantage of a cookie-based solution is that you can set the
nism for state maintenance. A state mechanism permits you to cookie up in your DLL just once, and not have to worry about it
store information about a visitor (their “state”) so that each page again. Also, you can use cookies to store a permanent ID for a visitor
request has access to any information the visitor entered on any so that when he or she returns, you can immediately provide them
earlier page. A prime example of the utility of state management with personalized information.
is an e-commerce application that must remember the products
a visitor has selected, their name and address, and their payment The disadvantage of cookies is that some users turn them off. There is a
information, even though all of this information has been entered great deal of fear-mongering, and downright ignorance, when it comes
on different pages at different times. to cookies — as well as some legitimate concern — so it’s not surprising
that some people disable them. If you’re using cookies to maintain
Typically, applications also differentiate between a user’s session (state state, your site simply won’t work for people who turn them off.
information stored in the course of their current visit) and a user’s
permanent information. Of course, information entered in a session It’s also important to recognize the distinction between using cookies
usually becomes part of a user’s permanent information. However, to maintain state, and using them to uniquely identify a user. If you
sessions are usually set up to expire after a relatively brief period of use cookies to maintain state, they should expire soon after their last
inactivity (20-30 minutes). use. If you use cookies to identify a user, it’s very important that he
or she have some control over this process. Not all computers are used
The Art of Maintaining State by a single individual, so storing a cookie on the assumption that the
There are only three mechanisms for identifying and tracking a visi- same visitor is using the cookie on each visit could lead to confusion,
tor as they progress from page to page in your site: forms, cookies, or worse, to a significant breach of personal privacy.

8 July 2000 Delphi Informant Magazine


On the ’Net
Fat URLs. Another solution is to embed a session ID in the URLs doesn’t handle any particular page request; it works with all
visitors use as they navigate your site. To do this, you will generate a page requests.
unique identifier when the first page is requested. On all subsequent
pages, any internal links will have an argument in their URL that You develop with the MDWeb components just as you do with standard
passes along the ID. For example, an internal link in your raw WebBroker components. However, you must drop an MDSessionMgr
HTML page might be: component on your WebModule and make sure it’s moved to the first
position in the creation order list. This ensures that basic session manage-
<a href='/ISAPI/Demo.dll/page2.htm?SID=<#SessID>'> ment capabilities are implemented. Then, instead of using WebBroker
PageProducers, you use the corresponding MDWeb components.
In your page-handling code, you must ensure that every page handles
the <#SessID tag>, substituting the current session ID appropriately. The operation of the MDWeb components is identical to the Web-
So, to your user, that internal link shown above might be: Broker components, except that when these components are finished
processing their HTML, they call the MDSessionMgr so it can
<a href=' /ISAPI/Demo.dll/page2.htm?SID=374834'> process any session-level tags. These can include any tags you want to
support on a global basis: Session ID, Date, Time, random number
In all page requests after the first, you would pull the session ID from generation, or even file includes.
the Request.QueryFields StringList whenever you needed to access
session information. If you’re using URLs to maintain state, you’ll embed the
<#SessID> tag in the links that tie your pages together as previ-
As you may already be thinking, the drawback of this approach is that ously described. When a page is sent to a visitor, its Session ID
you not only have to include the session ID tag in every internal link, will be automatically inserted into these links so that subsequent
you also have to write code in every action item or PageProducer to page requests pass the ID along. Although you still have to ensure
substitute in the session ID. Also, every page must be run through that all of your page links have the embedded session tag, this
your DLL, even if it wouldn’t otherwise need dynamic processing. automatic tag substitution makes URL-based session management
This is because it will now need dynamic processing to substitute in quite a bit more tractable. Note that the MDWeb library also
the session ID. provides a function that can be used in an action item’s OnAction
event to simplify the loading of pages that need no other process-
Magic bullet. It’s tempting to believe there is some other magic bullet ing than the substitution of session-level tags.
for state management. Indeed, Web tool vendors often advertise
“automatic state management,” and may even imply that they use TMDSessionMgr.ContentFrom-File(filename: string): string;
some method beyond or outside of this set. However, there isn’t
any magic in Web development, and, upon closer inspection, every If you use this function, you can load an HTML file without having
vendor’s state management always comes down to one of these meth- to create a PageProducer. Of course, you may still prefer to use cook-
ods, or some combination of them. ies to store/access the session ID to avoid the hassle of embedding
the ID in the URL.
Even with these tips for keeping a session ID around, you may still
be wondering how you can maintain a robust, thread-safe repository While global substitution is an important task for TMDSessionMgr,
for session information. If so, grab a SoBe (or a cup of Joe) and the big advantage in using this class is its management of session data.
settle in. We’re heading for the Delphi Web Application Component When a visitor arrives at the site, their first page request won’t have
power tour. an associated session ID in the URL or in a cookie. When the DLL
sees this request, it will trigger the creation of a session data object
The MDWeb Components (see Figure 5). When created, this object will automatically generate
One of the truly great things about Delphi is that you can simply sub- a random session ID in the range of 2 to two billion. In addition to
class and override any behavior you don’t like in the VCL’s components. the session ID, the session data object holds a string list in which all
I truly like the WebBroker libraries; they do a lot to make my life easier. data for a session will be stored.
However, they really just don’t do enough to make Delphi a contender
in real-world Web development. And that’s where Delphi’s strength in The session data is stored in a thread-safe, in-memory, B-tree for fast
component subclassing comes in handy. The MDWeb components, access. The TMDSessionMgr object (shown in Listing One, beginning
derived from Delphi’s PageProducer family, take all of the WebBroker’s on page 11) hides all access to the B-tree, so you can swap in a different
strengths and add automatic, thread-safe session management. If you storage mechanism without having to change your project code. For
follow the discussion of the MDWeb components, you should gain example, if your site is so busy that you need to move to another server
a good idea of how to implement your own session management
procedure TWebModule1.WebModuleBeforeDispatch(
controls. Of course, you’re also welcome to visit the Delphi Informant Sender: TObject; Request: TWebRequest;
Magazine Web site and download the MDWeb components that Response: TWebResponse; var Handled: Boolean);
accompany this article (see end of article for details). begin
SessMgr.SessID :=
StrToIntDef(Request.QueryFields.Values['SID'], 0);
The heart of the MDWeb components is TMDSessionMgr. This class if (SessMgr.SessID = 0) then // No session ID passed.
is derived from TPageProducer, and has been modified to do two SessMgr.CreateSessionObject
things: else
1) Automatically replace “session-level” tags (Session ID, Date) in if (Not SessMgr.FindSessionObject) then
Response.SendRedirect('/Timeout.htm');
every file that passes through the system.
end;
2) Provide seamless access to an in-memory session variable store
(a B-tree implementation). Note that the session manager Figure 5: The OnBeforeDispatch procedure.

9 July 2000 Delphi Informant Magazine


On the ’Net
or servers, you could move session data to a shared database, and access Delphi Web Application does when it receives a request is call the
it from within your TMDSessionMgr object instead of the B-tree. OnBeforeDispatch function of the WebModule. This occurs even before
an action is selected to handle the request. This is the perfect opportunity
You may or may not choose to make use of our sample code when to check whether the current visitor has been assigned a session, and to
starting your session-aware project, but the outline should be clear: either create or find a session object that can be used in the remainder of
A session ID must be generated if a user doesn’t already have one, and this request. Again, refer to Figure 5 to see the code to do this.
this ID must index a data structure or database record holding all of a
user’s session information. If you use a database table to hold session In the example in Figure 5, we attempt to pull a session ID from
information, you won’t have to worry about the thread safety of your the URL using the Request.QueryFields parameter. Thus, if the URL
data access, but you may have performance issues. If you do store requesting this page is:
session information in memory, you should gate your data access with
critical sections to ensure thread safety. www.mysite.com/ISAPI/Demo.dll/Action?SID=1234

If you choose to manage sessions by embedding session IDs in the then the “SID” index to the QueryFields StringList will retrieve the
intra-site URLs, some mechanism for simplifying the substitution of value “1234.” If no SID argument was passed, then the result of
actual session IDs should be implemented as well. this retrieval would be 0. Note that we could use cookies instead by
simply using Request.CookieFields.Values['SID'] in the state-
Enough Theory! ment. We would also have to make sure the cookie is stored in the
Now that you know some session management theory, let’s see how call to CreateSessionObject.
things work in a sample application. Recall that the first thing a
If the Session ID is 0, then this request comes from a new visitor. In
<FORM METHOD="post"
Action="/ISAPI/DemoWeb.dll/Page3?SID=<#SessID>"> this case, we generate a new session object with the CreateSessionObject
<TABLE WIDTH="550" CELLPADDING="0" CELLSPACING="0" procedure. Otherwise, we tell the SessMgr to get the session object ready
BORDER="0" ALIGN="center"> for use with the FindSessionObject function. In either case, the result
<TR> is the same: The SessMgr object in the current thread will now be
<TD ALIGN="right" WIDTH="50%">
What is your favorite color?&nbsp;<BR>
able to store or retrieve user values specific to the current visitor. The
</TD> actual storage or retrieval will occur as this particular request percolates
<TD ALIGN="left"> through the Web action items and/or MDPageProducer classes. Let’s see
<INPUT NAME="COLOR" MAXLENGTH="12" ><BR> how this happens.
</TD>
</TR>
<TR> Assume for a moment that our site needs to assess the current color
<TD ALIGN="middle" COLSPAN="2"> preference of our visitor. This information will be used later in the visit,
<BR> and thus must be stored in the current user’s session variables for later
<INPUT TYPE="submit" NAME="Submit" VALUE=" Submit "> use. The HTML for the color request form is shown in Figure 6.
&nbsp;&nbsp;
<INPUT TYPE="reset" NAME="reset" VALUE=" Reset ">
</TD> When the color preferences form is filled out and submitted, the Page3
</TR> action will be requested. Note the inclusion of the SessionID in the page
</TABLE> request. It’s in our response to this new action that we process the informa-
</FORM>
tion sent from this form. In this case, we store the color preference via a
Figure 6: The color preference HTML form. function call in the action item that handles the page request:

procedure TWebModule1.WebModule1SummaryAction( procedure TWebModule1.WebModule1Page3Action(


Sender: TObject; Request: TWebRequest; Sender: TObject; Request: TWebRequest;
Response: TWebResponse; var Handled: Boolean); Response: TWebResponse; var Handled: Boolean);
begin begin
SessMgr.Values['MOVIE'] := SessMgr.Values['COLOR'] :=
Request.ContentFields.Values['MOVIE']; Request.ContentFields.Values['COLOR'];
// Ask the "Summary" PageProducer to get the page. end;
Response.Content := Summary.Content;
end;
In case you’re wondering, the actual page request is handled by an
Figure 7: Getting information from a form before generating a MDPageProducer class (see Listing Two on page 13). Recall that there
response page. are two ways to respond to a page request: via an OnAction procedure
in the action item, and via a PageProducer (or TableProducer, etc.).
procedure TWebModule1.SummaryHTMLTag(Sender: TObject; As shown here, it’s also acceptable to use both. However, a word
Request: TWebRequest; Response: TWebResponse; of warning is in order. You may be tempted to pull some data
var Handled: Boolean);
from your database in the OnAction procedure hoping to use it in
begin
if (AnsiCompareText(TagString, 'FOOD') = 0) then the PageProducer when responding to tags in the page. This won’t
ReplaceText := SessMgr.Values['FOOD'] work if you attach the PageProducer to the action item, because the
else if (AnsiCompareText(TagString, 'COLOR') = 0) then WebBroker will always call the PageProducer before the OnAction
ReplaceText := SessMgr.Values['COLOR'] procedure. This seems to be glaringly non-obvious behavior for the
else if (AnsiCompareText(TagString, 'MOVIE') = 0) then
ReplaceText := SessMgr.Values['MOVIE'];
WebBroker. Fortunately, a work-around is easy enough: Access the
end; PageProducer manually in the OnAction procedure, and don’t link
the PageProducer and action item. For example, in an OnAction
Figure 8: Using session values. procedure, you might write the code shown in Figure 7.

10 July 2000 Delphi Informant Magazine


On the ’Net
Note that the Response.Content string is where all the HTML to
be streamed out to the user is stored. We fill it manually here with Dr Mark Brittingham believes the secret of happiness is to radically change careers
the output of the Summary PageProducer. In a database application, every four to five years. He has worked at Bell Laboratories (now Lucent Technologies)
you would place your database access code before the call to the Page- in Artificial Intelligence Research, at AT&T in user interface design, and as president
Producer, if the producer needed data to perform its tag substitutions. of Brittingham Software Design. He created a Windows-based vertical market health
and fitness package whose royalties now pay the bills. He is currently doing Web
Now that you’ve seen how session data is stored, it’s probably pretty development in Delphi and Cold Fusion as a freelance Internet consultant.
obvious how to access this information. For purposes of illustration,
however, see Figure 8, wherein we pull all of the visitor’s favorites and
use them in tag substitutions.
Begin Listing One — TMDSessionMgr Object
As you can see, we pull data the same way we set the data. There unit MDSessMgr;

are also functions for storing and accessing integer values in the interface
MDWeb library.
uses
Conclusion Windows, HTTPApp, Classes, SysUtils, MDBTree;
The problem I sometimes have in explaining session management is that
type
storing and accessing data in the Session Manager seems pretty boring TMDSessionMgr = class(TPageProducer)
and obvious. It’s not! Keep in mind that a busy site might have hundreds private
or even thousands of visitor sessions running concurrently. The data protected
storage and access for every one of these visitors must be kept straight. function HandleTag(const TagString: string;
TagParams: TStrings): string; override;
The art isn’t in storing and retrieving the data; it’s in doing so in a public
manner that ensures that every visitor sees only their own information. SessID : Cardinal;
SessNode : PTNode;
Having a lightweight mechanism for maintaining state is the gateway procedure DoTagEvent(Tag: TTag;
const TagString: string; TagParams: TStrings;
to all of the more advanced work you’ll do on the Web. Do you
var ReplaceText: string); override;
want to create an e-commerce site? Do you want to provide a highly function ContentFromFile(filename: string): string;
personalized experience for your visitors? Need to manage logins and procedure CreateSessionObject(inUID: Integer = 1);
secure site access? In all of these cases, session management is the function FindSessionObject: Boolean;
foundation from which you will build. ∆ procedure SetValue(const Name, Value: string);
function GetValue(const Name: string): string;
procedure SetIntValue(const Name: string;
All source described in this article (including the B-tree code) is available const Value: Integer);
on the Delphi Informant Magazine Complete Works CD located in function GetIntValue(const Name: string): Integer;
INFORM\00\JUL \DI200007MB. property Values[const Name: string]: string
read GetValue write SetValue;

An Extension of Your Own


ISAPI URLs are pretty ugly and hard to remember. A typical URL might Enter your extension in the Extension field (.dwm). Select the radio
look like this: button labeled All Verbs so your DLL will handle all of the page
request types. Make sure that Script Engine is checked and click
www.something.com/ISAPI/Your.DLL/Action the OK button. Now, as far as your Web server is concerned, all page
requests that end with .dwm will be sent to your DLL.
However, there is a way to skip the explicit DLL call and simply name
the files you want to use in your URLs. All you need to do is make a There are several things you now need to keep in mind while developing
simple change in your Web server, and a matching change in your Delphi your DLL. First, all actions must use the complete file name and path
DLL. In the examples that follow, I assume that you’ll name your HTML from the Web server root. Thus, if you place a file named Signin.dwm at
files with a .dwm extension (for Delphi Web Markup) in order to clearly the Web root, the corresponding action in your DLL will have a PathInfo
differentiate them from standard .htm files. entry of /Signin.dwm. You can’t omit the extension. Next, be aware that
even if you’d like to simply handle an action that redirects the caller to
To set up .dwm files in Omni, you have to make an addition to the External another page, you’ll still need to create a corresponding .dwm file even
files listing. To do this, select Properties | Web Server Globals if you decide to leave it empty.
Settings and click the External tab. Once there, you’ll add a new entry. In
the Virtual field, place the name of your extension: .dwm. In the Actual Be aware that there is no way to stream image or other binary data out
field, place the path to the DLL you are developing. Next, click on the MIME to a Web page when using custom extensions. If you stream charts or
tab, and add a new MIME type: enter wwwserver/isapi in the Virtual other binary data, do so in a separate DLL.
field, and .dwm in the Actual field. In the External and the MIME tabs, be
sure to press the Add button after filling in your fields! Finally, the MDWeb components have to be slightly modified to
work with custom extensions. These modifications are included in the
In IIS 5.0, you’ll right click on the HTTP server and select Properties. MDWeb components accompanying this article.
In the resulting dialog box, select the Home Directory tab and press the
Configuration button. In the dialog box that results, click the Add — Dr Mark Brittingham
button and enter the path to your DLL in the field labeled Executable.

11 July 2000 Delphi Informant Magazine


On the ’Net

property IntValues[const Name: string]: Integer ReplaceText := IntToStr(Random(1000));


read GetIntValue write SetIntValue; end
end; // If you place the STOREFORMVARS tag in a Form-response
// page, it will automatically copy all form responses
procedure Register; // to the visitor's session.
procedure MergeStrings(Dest, Source: TStrings); else if (aStr = 'STOREFORMVARS') then
try
implementation if (SessNode <> nil) then
MergeStrings(SessNode^.Data,
procedure MergeStrings(Dest, Source: TStrings); Dispatcher.Request.ContentFields);
var ReplaceText := ' ';
I, DI: Integer; except
begin end
for I := 0 to Source.Count - 1 do begin else
try // User can implement his own global subsets.
if Pos('=', Source[I]) > 1 then inherited DoTagEvent(Tag, TagString,
begin TagParams, ReplaceText);
DI := Dest.IndexOfName(Source.Names[I]); end;
if DI > -1 then
Dest[DI] := Source[I] function TMDSessionMgr.HandleTag(const TagString: string;
else TagParams: TStrings): string;
Dest.Add(Source[I]); var
end astr : string;
else i : Integer;
if (Dest.IndexOf(Source[I]) = -1) then begin
Dest.Add(Source[I]); astr := Inherited HandleTag(TagString, TagParams);
except if (astr =") then
end; begin
end; astr := '<#' + TagString;
end; for i := 0 to TagParams.Count - 1 do
astr := astr + ' ' + TagParams[i];
procedure TMDSessionMgr.CreateSessionObject( astr := astr + '>';
inUID: Integer = 1); end;
begin Result := astr;
SessNode := SessTree.CreateNode(inUID); end;
SessID := SessNode^.ID;
end; function TMDSessionMgr.ContentFromFile(
filename: string): string;
function TMDSessionMgr.FindSessionObject: Boolean; var
begin InStream: TStream;
SessNode := SessTree.FindNodeByID(SessID); begin
Result := (SessNode <> nil); InStream := TFileStream.Create(filename,
end; fmOpenRead + fmShareDenyWrite);
if InStream <> nil then
procedure TMDSessionMgr.DoTagEvent(Tag: TTag; try
const TagString: string; TagParams: TStrings; Result := ContentFromStream(InStream);
var ReplaceText: string); finally
var InStream.Free;
astr : string; end;
InStream : TStream; else
Month, Day, Year : Word; Result := '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML ' +
begin '3.2 Final//EN"><HTML><HEAD><TITLE>Error</TITLE>' +
aStr := Uppercase(TagString); '</HEAD>'<BODY>Error: The page you have requested ' +
if (aStr = 'SESSID') then 'cannot be found. Please report this error to the' +
ReplaceText := IntToStr(SessID) ' webmaster of this site!</BODY></HTML>';
else if (aStr = 'INCLUDE') then end;
begin
try procedure TMDSessionMgr.SetValue(
InStream := TFileStream.Create(TagParams[0], const Name, Value: string);
fmOpenRead + fmShareDenyWrite); begin
if InStream <> nil then SessNode^.Data.Values[Name] := Value;
try end;
ReplaceText := ContentFromStream(InStream);
finally function TMDSessionMgr.GetValue(
InStream.Free; const Name: string): string;
end; begin
except Result := SessNode^.Data.Values[Name];
ReplaceText := 'Error in file include! '; end;
end;
end procedure TMDSessionMgr.SetIntValue(const Name: string;
else if (aStr = 'CURDATE') then const Value: Integer);
ReplaceText := DateToStr(Date) begin
else if (aStr = 'RAND') then SessNode^.Data.Values[Name] := IntToStr(Value);
begin end;
Randomize;

12 July 2000 Delphi Informant Magazine


On the ’Net

function TMDSessionMgr.GetIntValue( // and easier to overlook things during development.


const Name: string): Integer; astr := Inherited HandleTag(TagString, TagParams);
begin if (astr = ") then
Result := StrToIntDef(SessNode^.Data.Values[Name], -1); begin
end; astr := '<#' + TagString;
for i := 0 to TagParams.Count - 1 do
procedure Register; astr := astr + ' ' + TagParams[i];
begin astr := astr + '>';
RegisterComponents('Internet', [TMDSessionMgr]); end;
end; Result := astr;
end;
end.
function TMDPageProducer.Content: string;
begin
End Listing One // Key part - after passing the content through the
// normal WebBroker channels(the inherited Content),
Begin Listing Two — TMDPageProducer Object // I pass it through the session object as well.
Result := Inherited Content;
unit MDPageProducer; if (FSessionMgr <> nil) then
Result := FSessionMgr.ContentFromString(Result);
interface end;

uses procedure Register;


Windows, SysUtils, HTTPApp, Classes, MDSessMgr; begin
RegisterComponents('Internet', [TMDPageProducer]);
type end;
TMDPageProducer = class(TPageProducer)
private end.
FSessionMgr : TMDSessionMgr;
protected
function HandleTag(const TagString: string; End Listing Two
TagParams: TStrings): string; override;
public
constructor Create(AOwner: TComponent); override;
function Content: string; override;
end;

procedure Register;

implementation

constructor TMDPageProducer.Create(AOwner: TComponent);


var
I: Integer;
Component: TComponent;
begin
// This MDPageProducer Create routine will look through
// all the components already created and, if one is a
// SessionMgr, capture a pointer to it. This way we don't
// have to manually chain the Session handling on to the
// normal page handling. This is why it's critical that
// the SessionMgr comes before any MDPageProducers in
// the creation order of the WebModule.
inherited Create(AOwner);
if Owner <> nil then
for I := 0 to Owner.ComponentCount - 1 do begin
Component := Owner.Components[I];
if Component is TMDSessionMgr then
FSessionMgr := TMDSessionMgr(Component);
end;
end;

function TMDPageProducer.HandleTag(const TagString: string;


TagParams: TStrings): string;
var
astr : string;
i : Integer;
begin
// We override this function because we don't want the
// default behavior. By default a PageProducer leaves a
// blank if it can't handle a tag. However, we want the
// tags to survive so that the Session Manager has a
// chance to handle any that we don't. So, I just undo
// the erase that is normally done here. Also, I have a
// philosophical problem with not leaving the tag in; it
// makes it harder to see what work remains to be done,

13 July 2000 Delphi Informant Magazine


Columns & Rows
Microsoft SQL Server 7 / Distributed Management Objects / Windows NT / Delphi 3-5

By Jason Perry

Exploiting SQL Server 7 DMO


Part II: A Database Tool and a Security Object

I n Part I of this two-part series, we looked at the basics of SQL Server DMO objects. We
also looked at a script-writing tool for SQL developers, named SSB (SQL Script Builder).

In this installment, we’ll look at DIRT, or Database function ConnectSource(sServerName:


Information and Reconciliation Tool, for cross- string): Boolean;
function ConnectDest(sServerName:
database comparisons. We’ll also look at a simple
string): Boolean;
security object, and demonstrate how it can be
used in application development.
The argument for each is the server name for the
Building a Database Information and source and for the destination. The methods create
Reconciliation Tool (DIRT) two separate server objects to be referenced as public
How do you keep your test database in sync with members of the class. Next, I have two methods that
your production database? How do you keep your create and store a reference to two database objects:
replicated servers in sync? What if you have multiple
environments for the same database? SQL-DMO can function ConnectDBSource(sDBName:
be used to compare server objects and look for incon- string): Boolean;
function ConnectDBDest(sDBName:
sistencies. In this wimpy demo, I’ll show you how
string): Boolean;
to use the Tables, Columns, and StoredProcedures col-
lections and their corresponding Table and StoredProc
objects to build an analysis utility to compare server The argument is the name of the database for
objects for consistency. I will also demonstrate how each one. This is pretty much a no-brainer. It’s
the SQLServer and Database objects can be used to almost exactly what I did to create the database
report on database sizes and server activity. objects in SSB.

The first thing I did was write a method to DIRT uses the same ODBC connection you cre-
connect to the named source server and named ated in SSB. Start it up and you’re presented
destination server: with a screen that has a Conn Source and a Conn
Dest button (see Figure 1). Type in the names
of the server and the database. Click the cor-
responding Conn... button to create the server
and database objects.

If the objects connected correctly, you will hear a


light “beep.”

SQLServer and Database Attributes


The SQLServer and Database objects have many
attributes that can help you observe the status of
your server and databases. I exploited some of these
attributes in DIRT. To see the SQLServer object
attributes, click each of the ...Server Information but-
tons (see Figure 2). These call a small method that
adds lines to the TMemo. They use the various
Figure 1: DIRT disconnected. attributes of the SQLServer object (see Figure 3).

14 July 2000 Delphi Informant Magazine


Columns & Rows
See the SQL Server books online for an accurate definition of each the differences between two databases. Nothing is worse than having a
attribute. Where practical, you could write a simple comparison test database that works, and a production one that doesn’t.
routine to make sure each server has similar attributes.
The source is simple, as shown in Listing One (beginning on page 18).
Next, click each of the ...DB Information buttons (see Figure 4). These, First, we spin through the source Tables collection and get a reference
like the server info method, simply add lines to the TMemo about the to each table object. Next, we spin through the Tables collection of
database sizes (see Figure 5). the destination server object. I want to make an important note here.
Notice the statement:
Running out of disk space in a database can be catastrophic. You
could write a service to poll the database size and e-mail you if it DB_Dest.Tables.Item(lcv2, DB_Dest);
reaches a certain threshold. The possibilities are endless.
I could have passed the physical name of the source table
The Tables, Columns, and StoredProcedures Collections (oSourceTable.Name) here to return the Table object. The problem is
Just like SSB, DIRT uses the Tables, Columns, and StoredProcedures that if the table isn’t in the Tables collection on the destination database,
collections to enumerate the Table, Column, and StoredProcedure it will raise an exception that it is not found. Because I wanted a more
objects for a graceful way of reporting table discrepancies, I spun through the Tables
specific data- collection looking for the same named table. Now I can report on the
base. I wrote discrepancy by adding a line to the TMemo.
a couple of
simple com-
parison rou-
tines to take
the source and
destination
databases and
compare some
entity attri-
butes (see
Figure 6). This
is an invalu-
able technique
Figure 2: DIRT server information. for reconciling

procedure TboDirt.ServerInfo(oServer: _SQLServer);


begin
if Assigned(CompareUI) then begin
Banner(oServer.Name + ' General Information');
CompareUI.Add(' Version: ' +
oServer.VersionString);
CompareUI.Add(' Databases: ' + Figure 4: DIRT database information.
IntToStr(oServer.Databases.Count));
CompareUI.Add(' Host Name: ' +
procedure TboDirt.DBInfo(oDB: _Database);
oServer.HostName);
begin
CompareUI.Add(' Language: ' +
if Assigned(CompareUI) then begin
oServer.Language);
Banner(oDB.Name + ' General Information');
CompareUI.Add(' Connection Id: ' +
CompareUI.Add(' Owner: ' + oDB.Owner);
IntToStr(oServer.ConnectionId));
CompareUI.Add(' Tables: ' +
CompareUI.Add(' Local Network Name: ' +
oServer.NetName); IntToStr(oDB.tables.Count));
CompareUI.Add(' Login Timeout(sec): ' + CompareUI.Add(' Views: ' +
IntToStr(oServer.LoginTimeout)); IntToStr(oDB.Views.Count));
CompareUI.Add(' Query Timeout(sec): ' + CompareUI.Add(' Stored Procs: ' +
IntToStr(oServer.QueryTimeout)); IntToStr(oDB.StoredProcedures.Count));
CompareUI.Add(' Blocking Timeout(ms): ' + CompareUI.Add(' Create Date: ' +
IntToStr(ord(oServer.BlockingTimeout))); oDB.CreateDate);
CompareUI.Add(' Command Terminator: ' + CompareUI.Add(' Database Size(mb): ' +
oServer.CommandTerminator); IntToStr(oDB.Size));
CompareUI.Add(' This Application Name: ' + CompareUI.Add(' Space Avail(kb): ' +
oServer.ApplicationName); IntToStr(oDB.SpaceAvailable));
CompareUI.Add(' Auto Reconnect Flag: ' + CompareUI.Add(' Data Disk Usage(mb): ' +
IntToStr(ord(oServer.AutoReconnect))); FloatToStr(oDB.DataSpaceUsage));
CompareUI.Add(' Default Null Flag: ' + CompareUI.Add(' Index Space Usage(kb): ' +
IntToStr(ord(oServer.AnsiNulls))); FloatToStr(oDB.IndexSpaceUsage));
CompareUI.Add(' Network Packet Size: ' + CompareUI.Add(' Database File Path: ' +
IntToStr(oServer.NetPacketSize)); oDB.PrimaryFilePath);
end; end;
end; end;

Figure 3: Adding lines to the Memo component. Figure 5: Adding information about database sizes.

15 July 2000 Delphi Informant Magazine


Columns & Rows
Lastly, I spin through the Columns collection for each table (don’t In my sample unit (oSecurity.pas), I overrode the Initialize method
forget the SQL-DMO object hierarchy) and compare the source (see Figure 8).
and destination table column attributes. This is a common place for
DBAs to make errors. I did this so the SQLServer object would be created the first time it
was called, and each time additional users called it. Now each user has
You can do other things here, as well. Compare the table scripts its own server connection. If all your users are connecting through
between tables to make sure they were identically created, that a single application, you may want to make the SQLServer object a
indexes are the same, and to check constraints, rules, etc. The singleton to save on resources and increase performance. Note: Set
power is awesome! that ApplicationName and your DBAs will love you.

Building a COM/SQL-DMO Role-based Security Object All you have to do on the application side is create the Automation
You’ve seen a couple of neat tools that can be written using SQL- object and call the Login method:
DMO objects. What about using them in application develop-
ment? In this small demo, I’ll create a simple COM-based security // A login routine. Wimpy.
object. It interrogates the SQLServer object to see if a given function TSQLDMO_Security.Login(const sServer,
sDatabase, sLogin, sPWD: WideString): WordBool;
login is in a particular role, using the DatabaseRoles collection.
begin
Some additional methods are added to return the users’ roles, oServer.Connect(sServer, sLogin, sPWD);
available roles, and the ability to add or remove a person to/from Result := oServer.VerifyConnection(
a particular role. SQLDMOConn_ReconnectIfDead);
end;

First, I created a type library and Automation object named


TSQLDMO_Security. Avoiding a lengthy lesson in the Delphi Simply pass in the name of the server, the database, the login, and
COM Expert, the Automation object has methods named GetRoles, the password of the connection. If the login was successful, it will
GetUsers, IsUserInRole, GetUserRoles, AddUserToRole, return True.
RemoveUserFromRole, and Login (see Figure 7).
Next, let’s talk about the GetRoles and GetUsers implementation,
shown in Figure 9.

procedure TSQLDMO_Security.Initialize;
begin
inherited Initialize;
if Assigned(oServer) then
oServer := nil;
else
begin
oServer := getServer;
oServer.Set_QueryTimeout(5);
oServer.Set_LoginSecure(True);
oServer.Set_ApplicationName(
'SQLDMO Security Object');
end;
end;

Figure 8: Overriding the Initialize method.

// Return a database object by name.


function TSQLDMO_Security.getDB(
sDBName: string): _Database;
begin
Figure 6: Comparing tables with DIRT. Result := oServer.Databases.Item(sDBName, '');
if not Assigned(Result) then
raise Exception.Create('The database ' +
sDBName + ' was not found.');
end;

// Get the roles collection for a specified database.


function TSQLDMO_Security.GetRoles(
const sDBName: WideString): OleVariant;
begin
Result := getDB(sDBName).DatabaseRoles;
end;

// Get a users collection for a specified database.


function TSQLDMO_Security.GetUsers(
const sDBName: WideString): OleVariant;
begin
Result := getDB(sDBName).Users;
end;

Figure 7: The SQLDMO_Security class in Delphi’s Type Library editor. Figure 9: Implementing GetRoles and GetUsers.

16 July 2000 Delphi Informant Magazine


Columns & Rows
I created a private getDB method to return the database object by name. They each return an OleVariant that represents the DatabaseR-
name. Very simply, once the Automation object is created, you call oles and Users collections. Figure 10 shows some of the code from the
its GetRoles or GetUsers collection with the argument of the database test application that fills a TListBox with the resulting collections.

procedure TForm1.FillUsers; Looks familiar doesn’t it? Just spin through the collections and
var exploit the attributes you need, and voilà! You now have a list of
o : OleVariant; DatabaseRoles and Users.
lcv : Integer;
begin
To help convince you that the SQL-DMO objects are flexible, I
o := Security.GetUsers(txtDatabase.Text);
lstUsers.Items.Clear; implemented the GetUserRoles method using the IStrings interface,
for lcv := 1 to o.Count do instead of the “built-in” collection mechanism (see Figure 11).
lstUsers.Items.Add(o.Item(lcv).Name);
end; This method will return an IStrings of roles for a specific login and
database. What I did was spin through the Users collection to get the
procedure TForm1.FillUserRoles(sLogin: WideString);
var
User object for the specified Login. If I find a user, I return a NameList
o : IStrings; collection of the roles the user is a member of. As an interesting twist,
lcv : Integer; I implement the IStrings interface using GetOleStrings, and fill it up by
begin spinning through the NameList collection. Now the test application can
o := Security.GetUserRoles(sLogin, txtDatabase.Text);
treat the list as a TStrings collection.
if Assigned(o) then
begin
lstUserRoles.Items.Clear; Now for the last methods — IsUserInRole, AddUserToRole, and
for lcv := 0 to o.Count-1 do RemoveUserFromRole (see Figure 12). These methods are simple.
lstUserRoles.Items.Add(o.Item[lcv]); They each take the arguments Login, Role, and Database Name.
end
else
What’s important here are the IsMember method of the User object,
raise Exception.Create('User not found in database');
end; and the AddMember and DropMember methods of the DatabaseRoles
object. The IsUserInRole method gets the specified database and the
Figure 10: This fills a TListBox with the resulting collections. specified user in the database, and asks the question: “Is this user
in this role?” The Add/Drop methods get the specified database and
function TSQLDMO_Security.GetUserRoles(const Login,
the specified Role object, and invoke the AddMember/DropMember
sDBName: WideString): IStrings;
var methods with the login argument. Could this be any easier?
lcv : Integer;
oUser : _User; So, what good is an Automation object without a test application?
oStrings : TStrings; Well, I included one of those too (see Figure 13).
oRoles : NameList;
lFound : Boolean;
oDB : _Database;
Simply fill out the pertinent server information and click Login. Select
oSA : IStrings; a database role and a user. In the far right-hand side, TListBox will
begin show a list of roles that the person has. Click the Add Selected Role
lFound := False; to Selected User and Remove Selected Role from Selected User buttons,
for lcv := 1 to getDB(sDBName).Users.Count do begin
and watch the user role list. Open a copy of Enterprise Manager to
oDB := getDB(sDBName);
if Pos(Login, oDB.Users.Item(Login).Login) > 0 then
confirm that the role was added. The Is Selected User in Selected Role?
begin button tests whether ... never mind — it’s obvious.
oUser := oDB.Users.Item(Login);
// So, is this user login a member of the specif ied role?
oRoles := oUser.ListMembers;
function TSQLDMO_Security.IsUserInRole(
lFound := True;
const Login, Role, sDBName: WideString): WordBool;
Break;
begin
end
Result :=
else
getDB(sDBName).Users.Item(Login).IsMember(Role);
begin
end;
lFound := False;
Continue;
// Add a user to a role.
end;
procedure TSQLDMO_Security.AddUserToRole(
end;
const Login, Role, sDBName: WideString);
if lFound then
begin
begin
getDB(sDBName).DatabaseRoles.Item(Role).AddMember(Login);
oStrings := TStringList.Create;
end;
GetOleStrings(oStrings, OSA);
for lcv := 1 to oRoles.Count do
// Remove a user from a role.
oSA.Add(oRoles.Item(lcv));
procedure TSQLDMO_Security.RemoveUserFromRole(
Result := oSA;
const Login, Role, sDBName: WideString);
end
begin
else
getDB(sDBName).DatabaseRoles.Item(Role).
Result := nil; DropMember(Login);
end; end;

Figure 11: Implementing the GetUserRoles method using the Figure 12: The IsUserInRole, AddUserToRole, and
IStrings interface. RemoveUserFromRole methods.

17 July 2000 Delphi Informant Magazine


Columns & Rows

// Update the UI;


if Assigned(CompareUI) then
CompareUI.Add('-------------------------------');
if Assigned(CompareUI) then
CompareUI.Add('Table: ' + oSourceTable.Name);
// if Assigned(CompareUI) then
// Application.Processmessages;
// See if the number of tables is consistent.
if DB_Source.Tables.Count > DB_Dest.Tables.Count then
begin
if Assigned(CompareUI) then
CompareUI.Add(
' ** There are additional tables in Source.');
end
else if DB_Source.Tables.Count <
DB_Dest.Tables.Count then
begin
if Assigned(CompareUI) then
CompareUI.Add(
' ** There are missing tables in Source.');
Figure 13: A test application. end
else if DB_Source.Tables.Count =
DB_Dest.Tables.Count then

Conclusion begin
if Assigned(CompareUI) then
SQL-DMO is vast. This series has touched on the major collections CompareUI.Add(' Table Counts Match');
and objects to help get you started on your enterprise tool. You can end;
rebuild indexes, check for page integrity, add indexes, change object // Look for that table name in destination database.
attributes, replicate your servers, and more. Writing smart tools to // Note: The table could be out of order. If it isn't
// found, the table should be noted as missing.
manage routine database maintenance, such as status reporting and
lFound := False;
user security issues, are now a simple chore with the SQL-DMO for lcv2 := 1 to DB_Dest.Tables.Count do begin
objects. Hopefully, I’ve helped open a new world of possibilities for oDestTable := DB_Dest.Tables.Item(lcv2, DB_Dest);
managing your enterprise SQL Servers. ∆ // In case the table doesn't exist.
if not (Assigned(oDestTable)) then

Resources begin
if Assigned(CompareUI) then
§ Compass Technology Management: http://www.compass.net CompareUI.Add(' ** Missing Table: ' +
§ Object-oriented JAVA Enterprise Architecture Experts: oSourceTable.Name);
OOP.COM, http://www.oop.com Break;
§ Microsoft SQL-DMO FAQ: http://msdn.microsoft.com/library/ end;
// Looking for the same table.
techart/msdn_dmoovrvw.htm
if UpperCase(oSourceTable.Name) =
§ Object-oriented Programming in Delphi and Java: http:// UpperCase(oDestTable.Name) then
www.oop.com begin
lFound := True;
// Check each column.
if Assigned(CompareUI) then
Jason ‘Wedge’ Perry is a system architect for OOP.COM in Chesapeake, VA. CompareUI.Add(' Column Discrepencies');
Prior to accepting this position, Wedge was a self-employed consultant in if Assigned(CompareUI) then
development positions ranging from grunt programmer to system architect. CompareUI.Add(' --------------------');
lColErr := False;
In his spare time, Wedge races a Kawasaki KX250 moto-cross motorcycle for
for lcv3 := 1 to
the Elizabeth City MX Club. oSourceTable.Columns.Count do begin
// Test the data type.
if oSourceTable.Columns.Item(lcv3).Datatype <>
oDestTable.Columns.Item(lcv3).Datatype then

Begin Listing One — DIRT begin


if Assigned(CompareUI) then
procedure TboDirt.CompareTables; CompareUI.Add(' Datatype: ' +
var oSourceTable.Columns.Item(lcv3).Name +
lcv, lcv2, lcv3 : Integer; oSourceTable.Columns.Item(lcv3).
oSourceTable, oDestTable : _Table; DataType + ' ' +
lFound, lColErr : Boolean; oDestTable.Columns.Item(lcv3).DataType);
begin lColErr := True;
if not(Assigned(SQLDMO_Source)) or end;
not(Assigned(SQLDMO_Dest)) then // Test the physical data type.
Exit; if oSourceTable.Columns.Item(lcv3).
PhysicalDatatype <>
Banner('Table Discrepencies'); oDestTable.Columns.Item(lcv3).
for lcv := 1 to DB_Source.Tables.Count do begin PhysicalDatatype then
// Get the first table's name. begin
oSourceTable := DB_Source.Tables.Item(lcv, DB_Source); if Assigned(CompareUI) then
oDestTable := nil; CompareUI.Add(' PhysicalDatatype: ' +
oSourceTable.Columns.Item(lcv3).Name +

18 July 2000 Delphi Informant Magazine


Columns & Rows

oSourceTable.Columns.Item(lcv3).
PhysicalDatatype + ' ' +
oDestTable.Columns.Item(lcv3).
PhysicalDatatype);
lColErr := True;
end;
// Test the AllowNulls property.
if oSourceTable.Columns.Item(lcv3).AllowNulls<>
oDestTable.Columns.Item(lcv3).AllowNulls then
begin
if Assigned(CompareUI) then
CompareUI.Add(' AllowNulls: ' +
oSourceTable.Columns.Item(lcv3).Name +
IntToStr(ord(Boolean(oSourceTable.
Columns.Item(lcv3).AllowNulls))) + ' '+
IntToStr(ord(Boolean(oDestTable.
Columns.Item(lcv3).AllowNulls))));
lColErr := True;
end;
// Test the Length property.
if oSourceTable.Columns.Item(lcv3).Length <>
oDestTable.Columns.Item(lcv3).Length then
begin
if Assigned(CompareUI) then
CompareUI.Add(' Length: ' +
oSourceTable.Columns.Item(lcv3).Name +
IntToStr(oSourceTable.Columns.Item(
lcv3).Length) + ' ' + IntToStr(
oDestTable.Columns.Item(lcv3).Length));
lColErr := True;
end;
Break;
end;
// If no errors, then put a NONE in.
if not lColErr then
if Assigned(CompareUI) then
CompareUI.Add(' NONE');
end;
end;
if not lFound then
// Update the UI;
if Assigned(CompareUI) then
CompareUI.Add(' **Table: ' + oSourceTable.Name +
' Not found in destination database.');
end;
end;

End Listing One

19 July 2000 Delphi Informant Magazine


Greater Delphi
Palm Handheld Devices / COM

By Ron Loewy

Palm Conduits
Part II: Writing a Sample ToDo Application

I n Part I of this series, we introduced the concept of programming for the Palm handheld
device and the use of conduits, which performs the data synchronization between the Palm
and the PC. We also introduced EHAND Connect, a COM-based product that allows you to
write conduits with every Windows development tool that can create automation objects.

In this half of the series, I’ll demonstrate the use of to the user. The record will remain in the database
EHAND Connect to create a conduit in Delphi by until it is synchronized with the Palm device; oth-
writing a simple ToDo application that will store erwise, we won’t know that the record (assuming
information from the Palm device ToDo appli- it exists on the Palm) needs to be removed. In our
cation to a Paradox database. We will also write application, however, all the records are always dis-
a conduit that synchronizes between the Paradox played, including the “deleted” ones that are only
database and the Palm device. marked as deleted for synchronization purposes.

About the PC ToDo Application The ToDo Database


The goal of this article is to discuss conduit cre- I used the Database Desktop to create a simple,
ation. Therefore, the ToDo application we’re going two-table database (see Figure 1). I later used
to write will be a primitive application that will the Control Panel BDE Administrator applet to
simply allow us to create data for synchronization. create a BDE alias called “PALMSAMP” to point
A lot of the operations that a well-designed ToDo to this database.
application will perform behind the scenes will be
exposed in this application. The sample tables are included with the source
archive of this article (see end of article for
For example, the database we will use will have an download details). You can unzip it to a direc-
IsDeleted field for every row. In a real application, tory of your choice, and set the alias to point
when the user chooses to delete a row, this field to that directory. The database includes a Users
will be set to True, and the data won’t be displayed table, which has a LastSync field. The database
can be used by many users and we don’t want
to synchronize the ToDo list of one user with
the information of another user. (In reality, the
synchronization code I wrote does update this
table, but it always assumes that all the records
in the Entries table belong to the synchronized
device, and will always synchronize all of them.
In a real-life application, you’ll define the Entries
table as a detail table with the Users table as its
master table.)

The Entries table is the interesting data storage


for our application. It includes the fields that
describe the ToDo information (DueDate, Com-
pleted, Description, and Notes) and three flags:
IsSynced determines if the record was ever syn-
chronized with the Palm device, IsDeleted deter-
Figure 1: Our sample PC database application. mines if the user deleted the record on the PC

20 July 2000 Delphi Informant Magazine


Greater Delphi
and it needs to be deleted on the Palm device, and IsModified the methods that are available for this record on the Palm. The PCRecNo
determines if the record has been modified on the PC. The property is used to provide a cursor pointer on the PC database for a
RecordID field is the unique identifier of the record in the data- record created from entry on the PC. This allows us to position the
base. The value in this field is used to link a record on the PC with cursor in the Paradox table to the specific record, if we need to modify
a record on the Palm device. some of the fields in the record.

Synchronization Strategy The class provides some utility methods, including SetPalmRecord,
When we need to synchronize databases on different devices, we need which sets the attributes and properties from an EHAND Connect
to determine a strategy for clash resolution. Consider the case where Palm Data Record interface pointer, and SetPCRecord, which sets these
the same record is modified on the PC: It’s also modified on the Palm attributes and properties from the current record of a TTable instance.
device before synchronization can be performed. What should our
strategy be when we come to synchronize the databases? Should the Synchronization in Action
PC record prevail, or should the Palm record prevail? The project ToDoSync.dpr was created using Delphi’s ActiveX
Library wizard. It includes the automation object class, named
The situation could be even more complicated. What happens to a DIConduit, that hosts our code.
record that was modified on the Palm device, but was deleted on the
PC? Should we remove the record from both devices, or should we The file, SyncUnit.pas, contains the synchronization code and imple-
update the record on the PC and “undelete” it? ments the strategy we discussed earlier. I created an Automation
object (using Delphi’s Automation Object wizard) and used the
The truth is that there are no absolute answers. What you decide Type Library editor to add the OpenConduit method. This is the
to do is the way your application resolves these conflicts. Taking method called by EHAND Connect when the conduit code starts the
the first scenario (the record modified on both platforms before synchronization process.
synchronization), we can solve the problem by adding a Modifica-
tionTime field that is updated whenever the record is modified. In In OpenConduit, we set the variable ConduitObj to point to the
this case, the more recently modified record will prevail. Another conduit interface passed by EHAND Connect. We call the func-
solution (the one I am taking in the sample application) is to opt tion HandleUser, which checks against, and updates, the Users
for handheld modification over PC modification; if both records table. In a real application, the user information would be used
that need to be reconciled have been modified, I prefer the Palm to synchronize information only against the user information, but
modification. for simplicity’s sake, we assume that all the records in the Entries
table will be synchronized. I won’t discuss HandleIUser in this
In the sample application, I decided to ignore the archive bit that article, but you can inspect the source to see how the information
can be assigned to a Palm record. When this bit is turned on, the is retrieved from the Palm device.
PC needs to store a copy of the record for archival purposes during
synchronization. However, the size of the code that is needed to The next call is to the DefineSchema method. This method opens the
illustrate basic synchronization that takes care of essential stuff, like ToDoDB database on the Palm device, and defines the schema (data
record addition, modification, and purging, is large enough as it is, structure) of the database. The schema on the Palm device consists
and it seemed reasonable to try and keep the size of the code small of four fields: DueDate, CompletedFlag, Desc, and Note. Notice the
since this is an article, not a book about Palm programming. The use of field type constants (eDate, eByte, and eString) in the call to the
strategy I decided to follow is summarized in the table in Figure 2. DefineField method of the conduit interface:

Supporting Cast if (ConduitObj.OpenDatabase('ToDoDB') <> 0) then begin


The file SyncData.pas includes the definition of the TToDoRecord ConduitObj.DefineField('DueDate', eDate, '');
class. This class represents data that is stored in memory during the ConduitObj.DefineField('CompletedFlag', eByte, '');
ConduitObj.DefineField('Desc', eString, '');
synchronization process. We will keep two memory databases: one ConduitObj.DefineField('Note', eString, '');
for the “interesting” PC records, and one for all the records in the end;
Palm database. Every record in these “memory” databases will be
represented by a TToDoRecord object.
After the schema has been defined, we determine the type of synchro-
A TToDoRecord object has properties for the information related to the nization requested. The options are Do Nothing, Copy from the
ToDo database — DueDate, Completed, Description, and
Palm record PC record Operation
Note. It also has a field called Operation that includes
the synchronization operation our conduit decides to Modified Nothing Modify PC Record.
perform on the record. The available options are Modified Modified Modify PC Record.
opAddPalm, opModifyPalm, opDeletePalm, opAddPC, Modified Deleted Modify PC Record and Undelete it.
opModifyPC, opDeletePC, and opNoOp. The last one Modified Does not exist Create a new PC Record.
represents a “No Operation” option. Does not exist Modified/Not Synced Create a new Palm Record.
Nothing Modified Modify Palm Record.
The class also has a RecordID property. This is the Deleted Modified Modify Palm Record and Undelete it.
unique key that allows us to match records between Nothing Deleted Delete Record on both platforms.
the PC and Palm databases. Deleted Nothing Delete Record on both platforms.
Deleted Deleted Delete Record on both platforms.
The PalmRecord property points to an EHAND Con- Nothing Nothing Nothing to do.
nect Palm Data Record interface and provides access to Figure 2: My synchronization strategy.

21 July 2000 Delphi Informant Magazine


Greater Delphi
PalmRecords memory structure and, based on the operation property
of every record, deletes the record or updates it on the Palm device
using the SetField and WriteRec methods exposed by the EHAND
Connect interfaces.

Four. The UpdateDBRecords method is called. It traverses the


DatabaseRecords memory structure and performs the physical operations
of updating both the PC and the Palm device as needed. Because we
use the RecNo property of a TTable to position the cursor, we actually
perform three passes on the DatabaseRecords structure. In the first, we
perform the opAddPalm, opDeletePalm, opModifyPalm, and opModifyPC
operations. In the second pass, we perform the opDeletePC operations. In
the third, we add new records to the PC.

Notice that during step Four we call the conduit interface’s


PurgeDeletedRecs method. This method physically deletes records in
the Palm device that have been marked as deleted earlier. Figure 3
shows the Palm ToDo List application after synchronization.

Final Steps
To activate our conduit code, we need to register it with the
HotSync.exe application. After building the project in Delphi and
registering it (select Run | Register ActiveX Server from Delphi, or run
regsvr32.exe from the command line), I used CondCfg.exe, a utility
that can be found in the Bin sub-directory of the EHAND Connect
installation (C:\program files\ehand\ehand connect on my machine).

Using this utility, you need to associate the conduit with an appli-
Figure 3: The data in the Palm ToDo List application cation installed on the Palm device. We could, of course, associate
after synchronization. the conduit with the ToDo application, but this would remove the
standard ToDo conduit that ships with the Palm Desktop software.
Because we don’t really want to replace this conduit, and just want
PC to the Hand Held device, Copy from the Hand Held device to disable it while we test our code, I chose to associate our conduit
to the PC, or synchronize both. In the sample application, I only with the Calc application (the Palm application that displays a
implemented the synchronize both option, because it’s the most calculator), which does not store data in a database and therefore
common and most complicated option (the others are sub-sets). does not need to synchronize information with a desktop. It is
thus perfect for our sample. Obviously, in the real world, you will
Complete synchronization is performed in the method called probably synchronize with a custom application you created and
SyncBoth, which we’ll examine in a moment. After synchronization installed on the Palm device, and you will associate the conduit
is finished, the database is closed and an entry of our success is with this application.
written to the synchronization log. The synchronization log can be
inspected on the Palm device after the HotSync program finishes the At this point (before adding the reference to our conduit code), if
synchronization process. you use the ToDo application on the Palm device, I would suggest
using HotSync to synchronize your data with the PC. The data we
SyncBoth is the method that performs the actual data synchroniza- might scramble playing with the conduit code can then be restored
tion. Let’s take a “grand” view of its operation: later from the PC.

One. The “suspect” records are read from the database into a memory Right-click on the HotSync application’s icon in the tray and choose
structure (held in a TStringList named DatabaseRecords) using the Custom from the popup menu. Select the To Do List conduit from the
DBToMemory method. Initial suspect operations are assigned to the list box and click the Change button. In the Change HotSync Action
memory records, e.g. opModifyPalm, opAddPalm, etc. dialog box, set the action to Do Nothing and click the OK button. This
will ensure that the records we add, modify, or delete to the Palm
Two. All records from the Palm device are read into a memory database will not find their way into the PC application and we’ll be
structure, held in a TStringList named PalmRecords. The record’s able to restore the real data later.
attributes are inspected, and an initial operation is assigned to
the memory record. At this time, clashes are inspected against the Activate the CondCfg.exe application mentioned previously and use
DatabaseRecords structure, and we determine the operation that the Add button. Set the Creator ID to “calc” (no quotes). This will
will be taken based on the strategy we discussed previously. If associate the conduit with the Calc application. In the Conduit Set-
needed, the DatabaseRecords record object’s operation property is ting dialog box, point the DLL Name entry to the file EHConnect.dll
modified. At the end of this stage, we have all the information we in the EHAND Connect installation directory, C:\program files\
need to physically perform the synchronization. ehand\ehand connect\EHConnect.dll by default. In the Class Name
entry, enter the name of the conduit automation object we created —
Three. The UpdatePalmRecords method is called. It traverses the ToDoSync.DIConduit in our example.

22 July 2000 Delphi Informant Magazine


Greater Delphi
You’re now ready to synchronize using the HotSync application. You
can build the sample ToDoDB application that will allow you to
edit records in the Paradox table on the PC. Try to add and modify
entries on this application, or do the same on the Palm device and
synchronize them to see the data transferred between the platforms.

Conclusion
While the official Palm Conduit Development Kit supports only
Visual C++, with a little help from EHAND Connect, every
COM-enabled development tool can be used to create conduits
to exchange and synchronize data between a Palm device and a
Windows desktop.

Colin Chapman believed in small, light-weight race cars, but even he


would have loved a truck (or, being English, a lorry) to ferry his pre-
cious works of art to the Grand Prix circuits, where they dominated
the opposition. Think of the Delphi conduit as the on-ramp to the
truck — the piece that makes the small Palm device a really useful
tool instead of a plaything. ∆

The project files referenced in this article are available on the Delphi
Informant Magazine Complete Works CD located in INFORM\00\
JUL \DI200007RL.

Ron Loewy is a software developer for HyperAct, Inc. He is the lead developer
of eAuthor Help, HyperAct’s HTML Help-authoring tool. For more information
about HyperAct and eAuthor Help, contact HyperAct at (515) 987-2910, or visit
http://www.hyperact.com.

23 July 2000 Delphi Informant Magazine


DBNavigator
ASP / HTML / Delphi 3-5

By Cary Jensen, Ph.D.

Delphi and ASP


Getting Started with Active Server Pages

I n brief, Active Server Pages (ASP) are text files that contain standard HTML commands
that are processed by a Web browser, and scripting commands that are executed on
a Web server. These scripting commands can be written in VBScript, JScript (the Internet
Explorer equivalent of Netscape’s JavaScript), or any other language for which a valid
scripting engine is installed, e.g. Perl and Python. Using these scripting commands,
you can introduce basic programming operations into your HTML, such as performing
conditional execution and expression evaluation, as well as invoking the features of COM
servers installed on the server. In this article, we’ll take a look at using these scripting
commands to achieve the results you want.

When an HTTP request for an ASP document § ASP code is often easier to write than other
is received by an ASP-enabled Web server, such CGI programs, in that it doesn’t require com-
as Microsoft’s Internet Information Server (IIS), pilation or the installation of a secondary pro-
the server processes the scripting commands cessor (although when you create a COM
within that file. The output generated by the server designed to be accessed by an ASP, the
scripting language is combined with the stan- COM server requires compilation).
dard HTML in the ASP to produce the HTTP
response returned to the Web browser. In other As noted already, ASP can only be used with an
words, the server doesn’t return the ASP docu- ASP-enabled Web server. Initially, ASP support
ment to the browser. Instead, it returns static was introduced in Microsoft’s Internet Infor-
HTML, plus the output from the processing mation Server (IIS). However, any Windows-
of the scripting commands. In most cases, the based server can potentially support ASP. This
returned data is pure HTML, although it can is crucial in that ASP servers use COM technol-
potentially contain any valid MIME (Multi- ogy, which is supported almost exclusively by
Purpose Internet Mail Extensions) content type. the Windows operating system. (Although some
non-Windows implementations of COM exist,
There are numerous benefits to using ASP: these are quite rare.)
§ The user can’t readily access your code. Unlike
with browser-side JavaScript or VBScript, in Before continuing, it’s worth mentioning that JSP
which the scripting instructions are delivered (Java Server Pages) is a technology similar to ASP. The
intact to the browser, ASP commands aren’t primary difference is that JSP-enabled servers aren’t
sent to the Web browser. limited to Windows-based servers. Any Web server
§ ActiveX objects accessed using ASP — when that runs on a Java 2-supporting operating system
Microsoft Transaction Server (MTS) isn’t (JDK 1.2) can be a potential JSP-enabled server.
being used — are loaded into the process
space of the server, which reduces overhead Using ASP Commands
when compared with CGI (common gate- In addition to basic HTML, ASP contains four
way interface) server extensions. An ASP types of commands: primary scripting com-
object can also be processed in a separate mands, output directives, processing directives,
process space, reducing the likelihood that and include statements.
a crashing ASP object will bring down your
Web server. With the exception of include statements, com-
§ ActiveX objects used by IIS can be installed mands contained within an ASP document are
into MTS, providing activation on demand, enclosed within <% %> delimiters. As mentioned
transactions, resource sharing, crash protec- earlier, these commands can consist of VBScript,
tion, and additional security. JScript, or any other scripting language for which

24 July 2000 Delphi Informant Magazine


DBNavigator
a valid scripting engine has been installed on the Web server. The keyword is followed by an equals sign (=) and a value. Furthermore,
language used for most of the remainder of this article is VBScript. multiple keywords can appear in a single processing directive, and no
spaces can appear on either side of the equals sign. The following is
Primary scripting commands contain the basic syntactic elements of an example of a processing directive that tells the Web server that the
the scripting language being used. For example, primary scripting VBScript language is being used (the default):
commands may include control structures, expression evaluation,
function invocation, variable declarations, and so on. HTML com- <% @ LANGUAGE=VBScript %>
mands can be interspersed between primary scripting commands to
control the HTML content that is returned to the Web browser. For When using VBScript or JScript, the ASP processing engine removes
example, consider the following segment: white space. When you need to include a blank space in HTML,
use the HTML non-breaking white space character, &nbsp; see an
<% HTML reference for additional special characters.
If Time >= #12:00:00 AM# And Time < #12:00:00 PM# Then
%>
Good Morning!
VBScript Comments and Variables
<% Else %> In VBScript, comments can appear within primary scripting com-
Hello! mands, with the exception of output commands. Comments are
<% End If %> defined by anything to the right of an apostrophe (with the exception
of the end delimiter):
Here, the plain HTML text Good Morning! appears between the
<%If%> and the <%Else%> commands. Alternatively, the Write method <%
of the built-in Response object can be used to write HTML from within If Time >= #12:00:00 AM# And Time < #12:00:00 PM# Then
Response.Write "Good Morning!" ' Write morning greeting.
a primary scripting command. The Response object is one of six available
Else
built-in objects you can use in your ASP. Built-in objects are discussed Response.Write "Hello!" ' It must be later in the day.
in greater detail later in this section. The following is an example of ASP End If
commands that make use of the Response.Write method: %>

<% As you can imagine, comment notation depends on the scripting


If Time >= #12:00:00 AM# And Time < #12:00:00 PM# Then language. For example, while VBScript uses the apostrophe, JScript
Response.Write "Good Morning!"
makes use of the // characters to denote a comment, similar to Java,
Else
Response.Write "Hello!" Delphi, and C++.
End If
%> Although variables don’t need to be declared in VBScript, they
should be declared for good programming practice. Once declared,
In this example, the entire code segment is a primary scripting com- or assigned in those cases in which they aren’t declared, variables are
mand. The result is that one of two strings is written to the HTML accessible to the remaining commands in that ASP page. For example,
file being created by the Web server based on the evaluation of the the following statement declares a variable named UserName, and sets
<%If%> statement. In other words, the result of this segment will its value to that of a value passed in the HTTP query string. The
either be Good Morning! or Hello!. None of the other actual text of following variable can be used in any expression that appears in any
the primary scripting command is delivered to the browser. commands later in this page:

Output directives. Output directives are a special case of primary <%


scripting commands. As with the Response.Write technique, an output Dim UserName
Set UserName = Response.QueryString("Name")
directive inserts text into the HTML stream that will be returned to
%>
the Web browser. Output directives use the following syntax:

<%= expression %> There are two special classes of variables that are visible to more than
one ASP page. These are Session and Application variables. Session
The value of expression can be a variable, constant, formula, property, variables are available to all pages accessed by a particular user within
method, or function. For example, the following line inserts a string a session. By comparison, Application variables are shared by all ASP
indicating the current time, based on the internal clock on the Web pages in an ASP application. These variables are stored using the
server (because the Web server can be at any location in the world, this Session and Application built-in objects, respectfully. These objects
isn’t necessarily the same time as in the time zone of the Web browser!): are described in greater detail later in this article.

The current time on this server is: <%=Now%> You access Session and Application variables by passing the name of
the variable to the Session or Application object. For example, the
Processing directives. Processing directives provide instructions to following code stores the session start time in a Session variable:
the ASP processing engine and typically appear at the beginning of an
ASP page. Processing directives use the following syntax: <% Session("SessionStart") = Now %>

<% @ keyword=value%> All pages on the site accessed by a specific user can then read this
value using the following statement:
The keyword must be a reserved word recognized by the ASP engine
and it must be separated from the @ sign by at least one space. The You began your session at <% = Session("SessionStart") %>

25 July 2000 Delphi Informant Magazine


DBNavigator
VBScript doesn’t permit the declaration of constants. Constants are Java. The following example demonstrates how to create an instance
defined using type libraries. of the java.lang.Integer class:

Include Files <%


Include files are similar to Delphi’s Web Broker TPageProducer in Dim date Set intobj = GetObject("java:java.lang.Integer ")
%>
that they permit you to insert segments of HTML into your page
at specific points. For example, if you have a segment of HTML
that should appear at the top of every page, you can create it once Once you have a reference to the Java object, you can call its meth-
and embed it within each ASP page by using the include directive ods. For example, the following example calls the parseInt method
before any other simple HTML text or commands on the page. of the Integer class:

The include statement has the following syntax: <% = intobj.parseInt(somestring) %>

<!--#include VIRTUAL|FILE="dirname/inc.htm"--> Although the availability of Java objects within ASP pages provides
you with some flexibility, Java objects typically can’t access the
If the keyword VIRTUAL is used, the dirname part is a virtual built-in objects, nor can they be part of an MTS transaction. Con-
directory (defined through server configuration). If FILE is used, sequently, their use is usually reserved for special operations not
the directory can either be an absolute or a relative directory. For normally available through the scripting language of COM objects.
example, if you have created a virtual directory under IIS named
Chunks, you can use the following include statement to include the Using methods. You call an object’s methods using dot notation to
header.htm HTML in your ASP page: invoke the qualified method name. If the method requires one or
more parameters, then follow the method name using the syntax
<!--#include VIRTUAL="Chunks/header.htm"--> of your scripting language. For example, to invoke the built-in
Response object’s Write method, you use a statement similar to the
In addition to including static HTML, include files can also reference following example:
ASP pages or ASP segments. However, note that ASP segments refer-
enced by include directives are processed on the server before the <% Response.Write "Text to return to the browser" %>
referencing ASP page is processed. This processing is unconditional.
That is, all included ASP pages are processed — even those where the Using properties. You reference an object’s property by using dot
include statement is referenced within an <%IF%> statement — before notation to reference the qualified property. You write to the prop-
the first primary command of the referencing ASP page is processed. erty by placing the property reference on the left side of an assign-
ment statement. You read the property by including the qualified
Using Objects property name in an expression. Because properties in COM make
ASP objects are Automation servers, and therefore can be either use of accessor methods, reading and writing properties of ASP
in-process servers or out-of-process servers. There are two general objects results in the execution of the defined read and write meth-
classes of objects that you can use from an ASP: built-in objects ods, respectively. From these methods, you can invoke custom code,
and custom objects. or even reference both built-in and custom ASP objects.

The Server and Application objects referenced previously are examples Built-in objects. ASP pages have access to a number of objects created
of built-in objects. These objects are available to all ASP pages. These automatically by the ASP-enabled server. These objects can be refer-
built-in objects are automatically created for you by the server, and enced by your server-side VBScript, permitting you to get information
can be referenced within any primary commands. By comparison, you about the environment, as well as to control the behavior of your
must explicitly create custom objects before you can access them. You ASP. The following built-in objects are available to all ASP pages:
create custom objects by calling the CreateObject method of the Server Application, ObjectContext, Request, Response, Server, and Session.
built-in object, passing to it the PROGID of the registered object. For
example, if you have registered an Automation server with the PROGID The Application object permits you to store application-wide data.
of Project1.Text, you can create an instance of it, and assign this instance An ASP application is a set of related ASP pages under a common
to the variable DelphiASPObj using the following command: directory structure. The application starts the first time a page of the
application is loaded, and shuts down when the server is shut down.
<%
Set DelphiASPObj = Server.CreateObject("Project1.Test") The ObjectContext object is used to start, commit, abort, and com-
%> plete transactions.

You can also use the HTML <OBJECT> tag to create an instance of The Request and Response objects permit you to get information about
an object. When doing so, however, you must include the RUNAT the HTTP request and to control the HTML response. For example,
directive with the Server option to ensure that this object runs on the the Request object permits you to read query strings, cookies, binary
server, and not on the browser’s machine: data, client certificates, and server variables. The Response object, by
comparison, can be used to write cookies, control page caching (affect-
<OBJECT RUNAT=Server ID=DelphiASPObj ing caching servers), and write the response, among other operations.
PROGID="Project1.Test"></OBJECT>

The Server object is used primarily to create new ActiveX or Java


Java objects. Rather than using COM objects, you can use Java objects. It can also be used to control URL mapping and encoding
objects in your ASP commands, as long as your Web server supports and HTML encoding.

26 July 2000 Delphi Informant Magazine


DBNavigator
The Session object represents one or more hits on pages in a given Creating a Custom ASP Example
ASP application by the same user. To use a session object, the user’s Use the following steps to create a custom ASP object for use with IIS:
browser must be set to accept cookies to be sent back to the same 1) Select File | Close All.
domain. One of the most powerful aspects of the Session object is that 2) Select File | New to display the Object Repository. Select the
it permits you to track information as a user navigates from one page ActiveX tab, then double-click the ActiveX Library Wizard. This
to another within your ASP application. wizard creates a new DLL-based project that exports the essential
four functions for in-process COM server activation.
If you must track sessions without using cookies, you can use Cookie 3) Select File | New again. Then, select the ActiveX tab and double-
Munger. Cookie Munger is an IIS filter that executes for every page click the Active Server Object Wizard. Delphi responds by dis-
request, producing an overall performance penalty as an unwanted playing the New Active Server Object dialog box (see Figure 2).
side effect. Cookie Munger will detect whether the browser will accept 4) At CoClass Name, enter DelphiASP. If you’re using IIS version
cookies. If the browser doesn’t accept cookies, the munger will gen- 3.0 or 4.0, you must set the Active Server Type to Page-level event
erate a session ID for that browser. Furthermore, when a page is methods. If you’re using IIS version 5.0, select Object Context.
being sent back to a browser that doesn’t accept cookies, Cookie Leave Generate a template test script for this object enabled. Click
Munger adds query string information to every URL that references OK when you’re done.
back to the ASP application. The Microsoft Cookie Munger can be
found in the IIS Resource Kit, and more information is available at At this point, creating your ActiveX server object is like creating
http://msdn.microsoft.com/workshop/server/toolbox/cookie.asp. any other type of COM server. You use the Type Library editor to
declare methods and properties. From within the implementation
Custom ASP objects. Even without custom ASP objects, it’s clear of your methods, you write your code logic. What is somewhat dif-
that ASPs can provide you with the power and flexibility to create ferent from other types of COM servers is that the interfaces that
more intelligent Web content. However, one of the more powerful enable ActiveX server objects expose a number of interface refer-
aspects of ASPs is that you can create custom ActiveX servers for ences that provide you with access to the built-in server objects.
use with them. Specifically, you can create ActiveX servers that are For example, your server inherits a Request property, which gives
invoked by a Web server at run time. Those ActiveX servers can read you access to the built-in Request object. Similarly, you have access
information about the HTTP request, as well as execute custom code to the Response property, which you use to invoke methods of the
to provide the necessary response. ActiveX servers used in this context built-in Response object.
are referred to as ASP objects for short.
The following steps demonstrate how to implement a method on the
Since the release of Delphi 3, Delphi has made the creation of COM server. This particular example is being kept fairly simple.
servers almost effortless. With Delphi 5, this convenience has been 1) Using the Type Library editor, add a new method named SayHello.
extended to ASP objects. The Active Server Object Wizard, shown in Don’t define any parameters for this method (see Figure 3).
Figure 1, appears on the ActiveX page of the New Items dialog box. 2) Click the Refresh Implementation button to generate the SayHello
Using this wizard, you can create an ASP object that can be registered on method stub in the DelphiASP CoClass.
your Web server (in that machine’s Windows registry), as well as a sample 3) Implement the DelphiASP.SayHello method. The example in
HTML page that can be used to invoke your custom ASP page. Figure 4 demonstrates the use of several Response.Write meth-
ods, using the ServerVariables interface, as well as accessing the
As mentioned earlier in this section, custom ASP objects can be Request object.
in-process servers or out-of-process servers. The following example 4) Save the project as DelphiASP.DPR. (Save the CoClass unit
makes use of in-process ASP objects. These objects can be loaded using any name you want. In the ASPDemo project, it’s named
in the process space of the Web server, or installed and invoked coclassu.pas.)
from MTS. 5) Compile the project, and then select Run | Register ActiveX to
register this ASP object with the Windows registry. (All files and
projects are available for download; see end of article for details.)

Figure 1: The Active Server Object Wizard is accessible from the


New Items dialog box. Figure 2: The New Active Server Object dialog box.

27 July 2000 Delphi Informant Magazine


DBNavigator
Calling the Custom ASP Object In this modified ASP page text, the CreateObject call has been updated
You now must update the text of the ASP page generated by Delphi. to refer to the ProgID of the newly created ASP object. Also, the call
Originally this page looked like the code shown in Figure 5. to invoke the SayHello method of this object has been added.

Change the generated code to look like that shown in Figure 6. Now save the .asp file to the root directory of your Web server.
Make sure you keep the file extension as .asp. Next, using the
CO (File | Open) selection from your Web browser, type http://
localhost/delphiasp.asp. (This assumes you’re testing this appli-
cation using a local Web server. If you’re using a Web server on
another machine, replace localhost with either the domain name,
the machine name, or the IP address of your Web server.) After a
moment, the page should load. Figure 7 shows what this page looks
like in Netscape Communicator.

<HTML>
<BODY>
<TITLE> Testing Delphi ASP </TITLE>
<CENTER>
<H3> You should see the results of your Delphi Active
Server method below </H3>
</CENTER>
<HR>
Figure 3: Adding the SayHello method in the Type Library editor. <%
Set DelphiASPObj=Server.CreateObject("Project1.DelphiASP")
DelphiASPObj.{ Insert Method name here. }
procedure TDelphiASP.SayHello; %>
var <HR>
i: Integer; </BODY>
begin </HTML>
with Self.Response do begin
Figure 5: Original ASP page.
Write('This is the response '+
'from the Delphi ASP object. <P>Here is a big ' +
'<H2><CENTER><I>Hello !!</I></CENTER></H2><P>' + <HTML>
'This request has come from a '); <BODY>
Write(Request.ServerVariables.Get_Item( <TITLE> Testing Delphi ASP </TITLE>
'HTTP_USER_AGENT')); <CENTER>
Write(' agent at address '); <H3> You should see the results of your Delphi Active
Write(Request.ServerVariables.Get_Item('REMOTE_HOST')); Server method below </H3>
Write('.<BR> Furthermore, the last page you '+ </CENTER>
'visited was (none if blank): '); <HR>
Write(Request.ServerVariables.Get_Item( <% Set DelphiASPObj = Server.CreateObject(
'HTTP_REFERER')); "DelphiASP.DelphiASP") DelphiASPObj.SayHello %>
Write('<P>The request method was '); <HR>
Write(Request.ServerVariables.Get_Item( </BODY>
'REQUEST_METHOD')); </HTML>
Write('<P>There were ');
Write(IntToStr(Request.QueryString.Count)); Figure 6: Modified ASP page.
Write(' items in the query string.');
if Request.QueryString.Count <> 0 then
begin
Write('<P>The query string is :');
Write(Request.QueryString);
end;
if Request.Form.Count <> 0 then
begin
Write('<P>There were ');
Write(IntToStr(Request.Form.Count));
Write(' items sent by an HTML FORM.');
Write('<P>These are ');
for i := 1 to Request.Form.Count do begin
Write('<BR>');
Write(Request.Form.Key[i]);
Write(' : ');
Write(Request.Form.Item[i]);
end;
end;
if (Request.QueryString.Count = 0) and
(Request.Form.Count = 0) then
Write('<P>There was no data sent with this request.');
end;
end;
Figure 7: The updated ASP page as seen in Netscape
Figure 4: Demonstrating the use of several Response.Write methods. Communicator.

28 July 2000 Delphi Informant Magazine


DBNavigator

Figure 8: The HTML generated by ASP.

<HTML>
<TITLE>Demonstrating an HTML form calling an ASP</TITLE>
<BODY>
<H2>Enter some data into this HTML form</h2>
<FORM Method=GET Name=Form Action=delphiasp.asp>
<BR>First Name :<INPUT TYPE="text" NAME="firstname">
<BR>Last Name :<INPUT TYPE="text" NAME="lastname">
<BR>Age :<INPUT TYPE="text" NAME="age">
<INPUT TYPE="hidden" NAME="userstatus" VALUE= "new">
<P><INPUT TYPE="submit" VALUE="Enter">
</FORM> Figure 12: HTML opting for the GET action rather than the
</BODY> POST action.
</HTML>

Figure 9: An HTML form calling an ASP page. Calling Your ASP from an HTML Form
As mentioned earlier, if you had displayed your ASP-generated HTML
by clicking on a link, the HTTP_REFERER server variable would
contain the URL of the linking page. Likewise, if this link was gener-
ated by either a GET or POST action from an HTML form, the ASP
would display the data sent by the form.

This effect is demonstrated using the HTML page defined by the text
in Figure 9, which is found in the TESTASP.HTM file located along
with the sample code for this article.

If you save this page to the same directory in which you have placed
the ASP page, then load it into your browser and enter data into the
Figure 10: The HTML text from Figure 9 displayed in a browser.
text input fields, your browser will look something like that shown
in Figure 10.

If you click the Enter button on the HTML form to invoke your ASP
object, the resulting HTML downloaded to your browser may look
similar to that shown in Figure 11.

Alternatively, if your HTML form used the GET action (the default)
instead of POST, the resulting page may look something like that
shown in Figure 12.

Conclusion
In this article, we’ve seen a number of different ways you can benefit
from the use of ASP in Web development. And when you add
ASP’s ability to utilize Delphi’s COM capabilities, Web development
becomes all that much more robust. ∆

Figure 11: The resulting HTML after invoking your ASP object. The files referenced in this article are available on the Delphi
Informant Magazine Complete Works CD located in INFORM\00\
Because this page was displayed using the Open Page feature, there JUL \DI200007CJ.
was no HTTP referrer. However, if you loaded this page by clicking
an anchor tag link (<A>), or submitting an HTML form with either
a GET or POST action, the URL of that linking page would be Cary Jensen is president of Jensen Data Systems, Inc., a Houston-based database
displayed following the “last page you visited” text. development company. He is co-author of 17 books, including Oracle JDeveloper [Oracle
Press, 1998], JBuilder Essentials [Osborne/McGraw-Hill, 1998], and Delphi in Depth
If you now select View | Page Source, you will see the HTML gener- [Osborne/McGraw-Hill, 1996]. He is a Contributing Editor of Delphi Informant Maga-
ated by the ASP (see Figure 8). Notice that what you see here is plain zine, and an internationally respected trainer of Delphi and Java. For more information,
HTML. None of the ASP commands are visible, because the server visit http://www.jensendatasystems.com, or e-mail Cary at [email protected].
replaced them with the HTML text generated by the ASP object.

29 July 2000 Delphi Informant Magazine


New & Used

By Bill Todd

Wise for Windows Installer 2.0


The Future of Windows Installation

W indows Installer is Microsoft’s new technology for controlling the installation of


software and operating system updates on systems running Windows 2000 and
Windows Millennium. Adding installation support to the operating system is part of
Microsoft’s effort to improve stability. By gaining control of the installation process,
Microsoft can ensure that installing application software cannot damage the operating
system and, hopefully, other applications. Wise Solutions’ Wise for Windows Installer
2.0 lets you quickly and easily build installations that use Windows Installer.

Windows Installer brings a number of new con- want to download the Windows Installer SDK
cepts and terms to the world of software installa- (about 6MB) from http://download.microsoft.
tion. If you are already familiar with Windows com/msdownload/platformsdk/i386/
Installer, you can skip this section. In introduc- InstallerSamples/IntelSDK.msi. Even if you do
ing a new installation technology for its new not plan to use the SDK to develop your own
family of operating systems, Microsoft did not installation software or interact with the Win-
want to force developers to create one installa- dows Installer through COM, the SDK Help
tion using traditional technology for Windows file is a valuable resource for learning about Win-
95, 98, and NT 4, and another using Windows dows Installer.
Installer for Windows 2000 and Millennium. To
solve this problem, Microsoft has created ver- Windows Installer offers a host of new features,
sions of Windows Installer that will run on Win- such as self-healing applications. When you launch
dows 95, 98, and NT 4. a self-healing application, it will automatically
detect missing or damaged files, and automatically
To install software using Windows Installer, you reinstall the missing files. An application can ask
must create a special relational database that con- Windows Installer if a specific application feature
tains all of the information Windows Installer is installed, and alter its appearance and behavior
needs to install your software. The database is con- based on the answer. In large organizations, admin-
tained in a single file with an .msi extension. Wise istrators can advertise applications that are available
for Windows Installer lets you build this database. to users. These applications will appear on the
user’s Start menu, just as if they were installed,
Learning to use Wise for Windows Installer, or and Windows Installer will automatically install
any other Windows Installer tool, is much easier the application the first time the user opens it.
if you have a basic understanding of Windows
Installer concepts and terminology. You will find Windows Installer also lets you install features of your
the “Roadmap to Windows Installer Documenta- application on demand. If a user chooses a feature
tion” at http://msdn.microsoft.com/library/psdk/ that isn’t installed, one of two things will happen,
msi/leglivt_0002.htm. From this page, you can depending on whether the application was installed
access all of the information Microsoft has pub- from removable media or a network location. If the
lished about Windows Installer. You may also original installation was from a network location that

30 July 2000 Delphi Informant Magazine


New & Used
is still accessible, Windows Installer will automatically install
the requested feature. If the original installation was from
removable media, Windows Installer will prompt the user
to insert the media, then install the feature.

Using the Installation Expert


If you have used version 7 or higher of any of the
Wise installation products, Wise for Windows Installer
will look very familiar. When you start Wise for Win-
dows Installer, you will see the installation expert screen,
shown in Figure 1.

The installation expert separates the process of creating an


installation into six steps, most of which are divided into
one or more subtasks. You can move from step to step by
clicking the step buttons at the top of the screen. You can
also click a task in any button at any time to jump directly
to that task. To move through the process in order, use the
Next and Back buttons at the bottom right of the screen.

Figure 1: The Wise for Windows Installer 2.0 installation expert.


Windows Installer installs products. A product may
consist of one or more features. A typical example
might be an accounting package that is separated into
features, such as general ledger, accounts receivable,
accounts payable, payroll, and so on. You must have
at least one feature, so Wise for Windows Installer
automatically creates a feature named Complete. You
cannot delete Complete until you have added at least
one other feature. Assume you are going to install
an accounting system, and that all of the accounting
modules depend on a module named Accounting
Manager that must always be installed.

To create this structure, click Features in step 1 of the


installation expert, then click Installation Features in the
tree view. Click the Add button to display the Feature
Details dialog box, and enter Accounting Manager
as the name. Next, select Complete in the tree view,
and click the Delete button to remove it. Because the
Accounting Manager must be installed for any of the
other modules to work, you can make the other modules
Figure 2: A hierarchy of features. dependent on the Accounting Manager by adding them
under the Accounting Manager feature. Just click on
Accounting Manager in the tree view, click the Add button,
and enter the name of the new feature. Repeat this for
each feature you want to add until you have the structure
shown in Figure 2. By making features dependent on
each other, you can ensure that users always install every-
thing necessary to make the features they select work.

You can organize features to provide uses with the option


of doing a Typical, Complete, or Custom installation,
although the mechanism for doing this was not immedi-
ately obvious. You must set the Level property to Custom in
the Feature Details dialog box, then set the Custom Value
to three or less to include the feature in a typical install, or
1,000 or less to include the feature in a complete install.
Wise plans to improve this process in a future version.

Once you’ve defined your features, you must define the files
that must be installed for each feature. Choose Files in step 1
to display the file selection screen, shown in Figure 3. When
Figure 3: Adding files to your installation. the Files display appears, the Features drop-down list, just

31 July 2000 Delphi Informant Magazine


New & Used
and a minimum color depth. If the configuration on the destination
computer doesn’t meet the minimum requirements, the warning mes-
sage you enter will be displayed.

Step 4 consists of a single choice, Dialogs. Here, you set which dialog
boxes will be displayed when a user runs your installation. If you
choose to include the License or Read Me dialog boxes, you can enter
the text of your license agreement or read me file in .rtf format, or
import the text from an existing .rtf file.

In step 5, you define the releases you want to build. A release is


some combination of features, media, EXE options, and languages.
By creating multiple releases, you can have a single installation that
creates your diskette release and your CD-ROM release for both the
standard and professional versions of your product in each different
language that you need.
Figure 4: The Release Settings dialog box.

below the buttons for steps 5 and 6, is enabled. All of the files and folders There must be at least one release, so Wise for Windows Installer
you add to your installation apply to the selected feature; that is, they will automatically creates one, named Default. You can click the Details
be installed when that feature is installed. button to change the name or other properties of the Default release,
and click the Add button to add a new release. Figure 4 shows the
The lower-left pane in Figure 3 shows the directory structure on Release Settings dialog box used to add or edit a release.
the target computer. To add a new subdirectory, select its parent in
the directory tree and click the New Folder button. The upper-left The key setting in this dialog box is EXE Options. If you know the
pane shows the directory structure on your computer. To add files destination computer is running Windows 2000, or that it already has
to a folder, locate the files on your computer, select them, and Windows Installer installed, you can safely choose the Do not create
click the Add File button to add them to the selected folder in the an EXE file option. In this case, only the MSI database file required by
destination computer pane. Windows Installer will be created when you compile your installation.
If the target computer is running Windows 95, 98, or NT 4, and
If you need to add an entire directory or directory structure, select you cannot be sure that Windows Installer has already been installed,
the parent directory on the destination computer, select the directory you will want to choose one of the options that creates an installation
you want to add to the installation on your computer, and click the EXE. When you create an installation EXE, launching the EXE on
Add Wildcards button. You can specify both include and exclude filters the destination computer will first install Windows Installer, then use
to include all files that match the template you enter, or exclude files Windows Installer to install your application. Creating an EXE adds
that match a template you define. For example, you could set the about three megabytes to the size of your installation.
include filter to *.EXE;*.DLL;*.OCX to include all of the executable,
DLL, and ActiveX control files in the directory. You can also include There are two EXE options. The first is Single File Executable, and is
subdirectories, and tell Wise for Windows Installer to automatically suitable for a single file installation that will fit on a CD-ROM, or will be
update the installation each time it’s compiled to reflect the files downloaded or installed from a network. If you need to create installation
currently in the source directory. diskettes or CDs, choose Executable That Launches External MSI.

The last option in step 1 is Merge Modules. A merge module is a Moving to the Build page lets you set the properties, summary
pre-compiled installation designed to allow third parties to give you items, and features to be included in the selected release. Properties
a way to install their products with your own. Hopefully by the time are variables supported by Windows Installer, such as ProductName,
you read this there will be available a merge module for the BDE. ProductVersion, and DiskPrompt. If you want to know the purpose
To add a new merge module to the feature you have selected, click and legal values for all of the property variables, you will need
the Add New button to display the Add Merge Module Wizard. You the Windows Installer SDK Help file or other Windows Installer
can choose a merge module from the list of modules that comes with documentation. Summary information consists of information that
Wise for Windows Installer, or click the Browse button to locate a can be displayed by right-clicking the MSI file. Examples are Author,
module on your hard disk. If the feature you are working with needs Comments, Keywords, and Minimum Installer Version.
a merge module that you’ve already added to another feature, click
the Add Existing button and choose the module you need. The Build page also shows the feature tree with a checkbox next to
each feature. Simply check the features you want included in this
Moving to step 2 gives you options to install icons; add, delete, or release. The Media page, in step 5, allows you to define the media
change registry settings; create, delete, or change .ini file settings; settings for the selected release. You can opt for a single installation
install Windows services; or add ODBC drivers or data sources. file with all installation files compressed into the MSI database, or
These options are almost identical to the corresponding options in you can choose to have files compressed into CAB files outside the
Wise InstallMaster 8.0, another installation product from Wise Solu- MSI. This is the option you must choose if your installation will span
tions. The only difference is that you set each of these options for multiple disks. You can use any type of media for your installation by
each feature in your installation. setting the media size and the cluster size.

Step 3 lets you check the user’s system configuration for a minimum If your installation will span multiple disks, you must create and
version of Windows or Windows NT, a minimum screen resolution, enter the path to a subdirectory to hold the contents of each disk.

32 July 2000 Delphi Informant Magazine


New & Used
Because the installation will consist of the MSI file and multiple CAB unlikely you will need to change any existing actions, there are times
files, each diskette may hold more than one file. Using subdirectories when you will want to add custom actions. Custom actions let you
for each diskette is the only way to keep the files organized by the run an EXE or call DLL functions as part of your installation. You
diskette on which they belong. The final option in step 5 is to choose can even run VBScript or JScript files. The Tables tab lets you edit
whether you want the installation to run in French, Italian, German, the Windows Installer database tables directly, and is for those who
Portuguese, or Spanish, instead of English. are intimately familiar with the Windows Installer SDK.

The sixth — and final — step in creating your installation starts by Wizards
prompting you for project information. The most important item Wise for Windows Installer includes six wizards to guide you
on this page is the Product Code. The Product Code is a GUID through various tasks. The wizards are described in Figure 6.
used by Windows Installer to determine if this product is already
installed on the destination computer. This code must be different Documentation
for different languages and versions. The Summary page provides Wise for Windows Installer comes with a 132-page Getting Started
another opportunity to enter the summary information described Guide that provides a great introduction to using the product. The
earlier in step 5. The Upgrades page allows you to identify features description of each feature begins with an overview and continues
of an existing installation to remove as part of this installation. with step-by-step instructions. If you’ve had no prior exposure to
The final three options are Windows 2000, Status MIF, and Code Windows Installer, you will find a few places in the manual where
Signing. The Windows 2000 page lets you configure the options you will be scratching your head trying to understand some of the
available to the user in the enhanced Add/Remove Programs dialog terms and concepts. To avoid this, download the Windows Installer
box in Windows 2000. On the Status MIF page, you can configure SDK and read the introductory sections in the Help file, or visit the
your installation to be run under Microsoft Systems Management Windows Installer Web site and read the introductory documenta-
Server. The Code Signing page allows you to create a code-signed tion there. An extensive online Help file gives you context-sensitive
single file installation for Internet distribution. help in both the Installation Expert and the Setup Editor.

Using the Setup Editor


The Setup Editor, shown in Figure 5, provides a
completely different view of your installation, as well
as the ability to edit the Windows Installer database
tables directly.

The Setup Editor screen is divided into three panes.


The left pane contains six tabs that correspond to the
types of information you can work with. A different
tree view appears for each tab. The upper-right pane
displays the detailed values for the selected item in the
left pane. For example, if you select the Product tab,
the left pane contains Launch Conditions, Properties, and
Summary. If you click Properties, all of the properties
and their values appear in the upper-right pane. The
lower-right pane contains help about the selected item
in the left pane.

Properties and Summary information have already


been described. Launch Conditions lets you create
Figure 5: The Setup Editor.
conditions that must be true for the installation
to run. For example, you could require Wizard Description
that the user be logged on as Adminis-
trator to install your application. Wise Windows 2000 This wizard scans your installation to see if it meets Microsoft’s
for Windows Installer tries to make cre- Verification requirements for a Windows 2000 installation. If it doesn’t, error
ating launch conditions easy by pro- messages identify the areas of non-compliance.
viding a Condition Builder. The Condi- Import SMS or This handy wizard lets you import a Microsoft Systems Management
tion Builder lets you build conditional Wise Installation Server installation script or a Wise Installation System installation
expressions by making point-and-click script, and converts it to run in Windows Installer.
choices from lists. Run Application This wizard lets you run your application while Wise for Windows and
Watch for Windows Installer watches to see which files are loaded, and uses
The Features and Dialogs tabs let you Loaded Files this information to build an installation.
define your features and control which Import VB Project Use this wizard to import a Visual Basic 5.0 or 6.0 project file and
dialog boxes are displayed during instal- create an installation.
lation. Using the dialog editor you can Setup Capture This wizard examines your system before and after you install an
create your own custom dialog boxes, or application, and builds an installation based on the changes.
customize any of the built-in dialog boxes. Patch Wizard This wizard allows you to build an installation that will update an
The Actions tab lets you edit existing existing installation on a user’s computer.
actions or add custom actions. While it’s Figure 6: The six wizards in Wise for Windows Installer.

33 July 2000 Delphi Informant Magazine


New & Used

Windows Installer is a giant step forward in making software


installations safe, and Wise for Windows Installer is the perfect
tool to let you take advantage of this new technology. The installa-
tion expert will make anyone who has used Wise 7 or 8 feel right at
home, and help new users build their installations with ease. This is
the future of Windows software installation.

Wise Solutions, Inc.


5880 N. Canton Center Rd., Suite 450
Canton, MI 48187

Phone: (800) 554-8565


Web Site: http://www.wisesolutions.com
Price: US$795

Conclusion
Windows Installer is a giant step forward in making software
installations safe, and Wise for Windows Installer is the perfect
tool to let you take advantage of this new technology. The installa-
tion expert will make anyone who has used Wise 7 or 8 feel right
at home, and help new users build their installations with ease.
The ability to create installation executables that will automati-
cally install Windows Installer on any machine lets you switch
to Windows Installer now for all of your installations. If you’ve
been using Wise products, the import wizard makes the move to
Windows Installer very easy. Again, Wise Solutions has done a
superb job of providing both ease and power in a single product.
The Setup Editor provides a fast, intuitive interface that will take
you all the way to the lowest level of building an installation:
editing the Windows Installer tables directly. This is the future of
Windows software installation. ∆

Bill Todd is president of The Database Group, Inc., a database consulting and
development firm based near Phoenix. He is co-author of four database program-
ming books, author of over 60 articles, a Contributing Editor of Delphi Informant
Magazine, and a member of Team Borland, providing technical support on the
Borland Internet newsgroups. He is a frequent speaker at Borland Developer
Conferences in the US and Europe. Bill is also a nationally known trainer and has
taught Paradox and Delphi programming classes across the country and overseas.
He was an instructor on the 1995, 1996, and 1997 Borland/Softbite Delphi World
Tours. He can be reached at [email protected].

34 July 2000 Delphi Informant Magazine


TextFile

Delphi 5 Developer’s Guide


Since Delphi first appeared, two books have developers moving to Delphi from Visual
competed for the role as the top general Basic, C++, or another language. Many of
Delphi book. One of them is Marco Cantù’s you will recall the numerous extensions
Mastering Delphi; the other is Delphi 5 Devel- to the Object Pascal language in Delphi
oper’s Guide by Steve Teixeira and Xavier 4, such as dynamic arrays and function
Pacheco, which I recently had the pleasure overloading. That entire discussion remains
of reading. in this volume. Chapter 4, “Application
Frameworks and Design Concepts,” is a
In previous reviews, I’ve praised earlier edi- favorite of mine as an excellent introduction
tions (particularly the Delphi 4 version), to good programming practice.
pointing out the expertise of Teixeira and
Pacheco: Both worked for Inprise/Borland in Part 2, “Advanced Techniques,” includes all
the early Delphi days and are able to bring the topics covered previously: graphics pro-
the insightful perspective of an “insider” to gramming, printing, multimedia program-
bear upon this work; at the same time, both ming, working with files, and MDI appli-
are now active as independent Delphi devel- cations. I still consider the chapters on work-
opers, which gives them the practical experi- ing with dynamic link libraries and multi-
ence to address many real-world issues. threading to be among the best I’ve seen.
Chapter 13, “Hard-Core Techniques,” and
All of the qualities I praised in the previous Chapter 14, “Snooping System Informa- nents. It also includes a discussion of client/-
edition are present in Delphi 5 Developer’s tion,” are must-reads for the intermediate- server programming and Internet database
Guide, including the wealth of valuable tips. level developer who aspires to enter the ranks issues, as well as chapters on WebBroker
The organization of this newest edition of advanced developers. One of the delight- (written by Nick Hodges), and MIDAS
of Developer’s Guide is very similar to the ful topics in Chapter 13 is using the built-in development (written by Dan Miser). Even
Delphi 4 edition, and it has grown consid- assembler. The authors explain the tech- more than before, I consider this to be one
erably in length to well over 1,500 pages niques involved, and give excellent advice on of the very best introductions to Delphi
— not counting the additional 500 pages when to use assembler and when not to. Six database development in a general Delphi
on the CD-ROM. This is truly a Delphi of the 12 chapters in Part 2 are presented in work. With Part 5, “Rapid Database Appli-
encyclopedia! Adobe Acrobat format on the CD-ROM that cation Development,” the excursion into
accompanies the book. the realm of Delphi database development
The book is divided into five parts. Part continues. Here, the authors provide a
1, “Essentials for Rapid Development,” pro- Part 3 begins with an overview of the Visual solid model of building client/server and
vides an introduction to Windows program- Component Library (VCL) and contains an desktop applications.
ming using Delphi 5. Part 2 covers a plethora expanded discussion of Run-time Type Infor-
of “Advanced Techniques,” most of which mation (RTTI). It discusses many compo- My recommendation? If you don’t own this
are essential to successful development. Part nent creation topics, including writing com- book, buy it! The coverage is comprehensive,
3, “Component-Based Development,” intro- ponent editors, working with packages, and the writing style is easy to follow, and the
duces the Visual Component Library. Part using the TCollection class in building a examples have many practical applications.
4 provides an introduction to database pro- component. Of course, the new Delphi 5 This is a Delphi encyclopedia that deserves a
gramming in Delphi. And finally, Part 5 feature Property Categories is included. If place on every developer’s bookshelf.
explores database programming in more you’re interested in working with OLE,
detail, getting into client/server issues and COM, or ActiveX, you should find Chapter — Alan C. Moore Ph.D.
some of the new technologies available in 23, “COM-Based Technologies,” to be an
various versions of Delphi. excellent introduction — especially with its Delphi 5 Developer’s Guide by Steve Teixeira
extended treatment. This section ends with a and Xavier Pacheco, SAMS Publishing, 201
In last year’s edition, I especially appreciated great chapter on CORBA development. W. 103rd St., Indianapolis, IN 46290,
the chapter on Object Pascal. Thankfully, http://www.samspublishing.com.
the authors removed nothing, but rather Part 4 provides a thorough introduction to
made some subtle changes that made the database programming, covering the essen- ISBN: 0-672-31781-8
material even more accessible, especially for tial Table, Query, and StoredProc compo- Price: US$59.99 (1,556 pages, CD-ROM)

35 July 2000 Delphi Informant Magazine


Best Practices
Directions / Commentary

Be Resourceful

I f you routinely (no pun intended) use the keyword resourcestring in your programs, read no further. This
article would merely be “preaching to the choir.” But for those of you who are asking: “What in the Dickens is
resourcestring?” read on.

In the “olden” days of Delphi programming (prior to version 4), To take the plunge into the brave new world of using the resourcestring
there were basically two ways of using strings in your pro- keyword, simply add a unit to your project, name it ResStrngs (or
grams. You could either embed them into the source code using whatever), and then add any string literals (especially those the user
string literals: would see — contents of string lists, feedback, error messages, etc.) in
the interface section of the unit, like this:
MessageDlg(
'Leave your stinkin' mitts off that button, fool!', unit ResStrngs;
mtError, [mbOK], 0);
interface

Or, you could create a text file with an .RC extension, such as: resourcestring
// Famous military personalities.
STRINGTABLE DISCARDABLE SGeneralElectric = 'General Electric';
{ SGeneralMills = 'General Mills';
1 "Dialog Expert" SGeneralUsage = 'General Usage';
2 "Dialog Expert from demonstration Expert DLL." SGeneralHospital = 'General Hospital';
3 "Application Expert" SGeneralLedger = 'General Ledger';
4 "Application Expert from demonstration Expert DLL" SGeneralProtectionFault = 'General Protection Fault';
5 "&Create" SGeneralSQLError = 'General SQL Error';
6 "&Next" SGeneralLeeSpeaking = 'General Lee Speaking';
7 "An application name is required!" SCorporalPunishment = 'Corporal Punishment';
8 "The application name is not a valid identifier." SSgtFury = 'Sgt. Fury';
9 "The path entered does not exist." SSgtCarter = 'Sgt. Carter';
10 MAIN.PAS" SSgtSchultz = 'Sgt. Schultz';
11 "MAIN.DFM" SSargentShriver = 'Sargent Shriver';
12 "MAIN.TXT" SCaptKangaroo = 'Capt. Kangaroo';
... SCaptUnderpants = 'Capt. Underpants';
// Variable names. SColonelKlink = 'Colonel Klink';
20 "StatusLine" SPrivateBenjamin = 'Private Benjamin';
... SPrivateProperty = 'Private Property';
} SLeftenantDan = 'Leftenant Dan';
SMutineerChristian = 'Mutineer Christian';
SAtlantaHawks = 'Atlanta Hawks';
Then all you had to do was compile it into a resource file, add // Kindly reminders.
it to a Delphi project or unit, compile it using the command-line SDontSleepInTheSubwayDarlin =
tool Brcc32.exe, and then programmatically extract the strings in the 'Don't sleep in the subway darlin'';
// Additional silly strings left as a reader exercise.
appropriate places in your code using the LoadStr function. That
may have seemed a bit too much of a bother, so you may have stuck implementation
with the tried and true — and now tired — old way of introducing
string literals ad infinitum into your source code. end.

The resourcestring keyword has come to the rescue. It gives us the Add this unit to the implementation uses clause of any unit that
best of both worlds: the simplicity (almost) of string literals, and the refers to any of its strings. Then access them in this way:
convenience of storing all strings in a central location. Also, using
resourcestring provides better memory management, because the if ItIsPetulasVirtualHusband and HeIsLate then
strings in the resourcestring section are saved as resources associated MessageDlg(SDontSleepInTheSubwayDarlin,
mtInformation, [mbOK], 0);
with your application.

36 July 2000 Delphi Informant Magazine


Best Practices
For an example of how Borland/Inprise/Corel (will they be giving automated. Using the ITE, you create a separate resource .dll for
away Bic lighters at the next conference?) uses resource strings, see each target language. If you deploy multiple .dlls, the correct one
consts.pas, dbconsts.pas, etc. in ..\source\vcl. is automatically loaded according to the locale of the computer
on which your program runs.
It’s also beneficial to place your strings in a resourcestring section
because programmers are often not the best people to write feedback The primary tools in the ITE are the Resource DLL Wizard
and error messages that will be seen by users. They tend to be either (File | New | Resource DLL Wizard) and the Translation Manager,
too technical, “An unexpected error occurred in module xyz while where translations can be entered. See “Integrated Translation
attempting to spawn a thread;” not informative enough, “Post the Environment” in Delphi Help for the specifics.
changes before proceeding;” and/or a little testy, “Error! You can’t
[whatever] until you [whatever].” Besides the ITE included with Delphi, there are third-party offerings
relative to internationalization and subsequent localization of Delphi
Separating the message strings to a discrete file makes it easier to hand applications. I prefer to use something that comes “in the box.” As
them over to someone with the skills necessary to write user messages long as it works well, of course, as the ITE seems to. ∆
(consulting with the programmers as to what exactly each message is
meant to convey, of course). If you don’t want non-programmers muck- — Clay Shannon
ing about in your .pas file directly, you can copy and paste the existing
resource strings to a text file and then, after they’ve finished polishing Clay Shannon is a Delphi developer for eMake, Inc. in Post Falls, ID.
your prose, copy their changes over the resource string constants. Having visited 49 states (all but Hawaii) and lived in seven, he and his
family have finally settled in northern Idaho, near beautiful Lake Coeur
Last, but not least, it is far easier to internationalize (genericize) d’Alene. The only spuds he has seen in Idaho have been in the grocery, and
and then localize (specificize) your applications when the strings most of those are from Oregon and Washington. Clay has been working
the user will see are collected in one place. With Delphi’s ITE (almost) exclusively with Delphi since the release of version 1, and is the
(Integrated Translation Environment), the process of internation- author of Developer’s Guide to Delphi Troubleshooting (Wordware,
alizing and then localizing your application’s strings is semi- 1999). You can reach him at [email protected].

37 July 2000 Delphi Informant Magazine


File | New
Directions / Commentary

An Interview with Robert A. DelRossi

R obert A. DelRossi is president of TurboPower Software Co., one of the oldest and most successful third-party
producers of Delphi tools. In the most recent Delphi Informant Magazine Readers Choice Awards (published in the
April, 2000 issue), TurboPower scored big — even bigger than last year. In 1999, TurboPower won four awards: Async
Professional won the Best Connectivity Tool, Memory Sleuth won Best Utility, Orpheus 3 won Best VCL, and SysTools won
Best Add-in Library. This year, TurboPower came in first place in five categories and placed second in two others. Memory
Sleuth’s new incarnation, Sleuth QA Suite, won the award for Best Testing/Debugging Tool, with each of the other
three 1999 winners repeating. Abbrevia won the award for Best VCL Component, and two other products — FlashFiler
and LockBox — came in second in their respective categories. Especially remarkable was Delphi Informant Magazine’s
decision to give a new award this year, Company of the Year, won by — you guessed it — TurboPower. You can find more
information about TurboPower products at its Web site, http://www.turbopower.com.

DelRossi has not always been the president of this company. He became president I imagined that the biggest challenges would be
began many years ago as a TurboPower customer. After he joined financial, but it’s really finding the right people to maintain your
the company as its first marketing director, he became vice president, company’s growth, and then keeping them engaged.
and, finally, its third president.
DI: You must be gratified with the great success that TurboPower
DI: You’ve had a varied and impressive career at TurboPower. Could has enjoyed in recent years, particularly the unprecedented show-
you elaborate a bit more about your experiences at TurboPower? ing in this year’s Delphi Informant Magazine Readers Choice
What advice do you have for the developer who aspires to embark Awards. To what do you attribute this success? Is there one factor
upon a similar career path? — the quality of the software, the documentation, the support —
that stands above all the others as the key to their
DelRossi: For starters, TurboPower is a great place popularity with users?
to work. That’s always been the case, and our man-
agement team has dedicated itself to keeping it that DelRossi: You probably won’t be surprised to hear
way. Many companies lose the positive corporate me say that I believe our success comes from several
culture they start with, especially as they grow. At factors. I think our customers see us as providing
TurboPower we regularly review not just how well more than just good code. When they buy one of
the products are doing, but also how the staff is our products, they’re really making us a partner in
doing, and we make adjustments to meet the chal- their projects. If they succeed, we succeed — plain
lenges of a growing company. Good communication and simple. And if they fail, they’ll look to us and
among team members — the engineers and our ask why — particularly if they feel that our products
terrific operations group — is a key to our success. let them down. So, we try to produce a unified set
of services: great code, great documentation, great
For me personally, and this is especially true having Robert A. DelRossi support, and a willingness to learn, accept criticism,
come from a different kind of business, I’ve learned that and help our customers achieve their very best work.
managing highly intelligent people requires a certain amount of flex-
ibility. Software development is still not 100 percent science. There’s a DI: I’d like to explore one of your newer products, one that I am
large dose of art involved too. Managing risk, practicing sound business currently in the process of reviewing: Sleuth QA Suite. Many in the
fundamentals, and applying proven project management strategies is a Delphi community have been encouraging you for some time to fill
big part of successful software company management, I believe. the gap that used to exist and create a solid Delphi profiler. What
were some of the challenges in developing this tool that might have
DI: What do you feel was your biggest challenge as a developer; contributed to it taking longer than you expected?
as a manager?
DelRossi: It seemed like everyone was asking us to develop a profes-
DelRossi: As a developer, keeping up with the incredible pace of sional profiling tool for Delphi and C++Builder! For years, the big tool-
change in our industry is the biggest challenge. For most of us, the makers have focused solely on Microsoft developers. The key tools, like
older you get, the harder it seems to keep up with the amazing degree NuMega BoundsChecker, really ignored the fact that real-world devel-
of change. That’s why TurboPower is successful, I think. We make opment was being done with Borland compilers. Over time, Bounds-
it easier to get the latest technology advances into your programs Checker, and some similar products were ported to work with Borland
without having to learn everything about them first. compilers, but in our opinion, they never quite made the grade.

Without question, the biggest challenge any software executive faces Like a lot of our products, Sleuth QA Suite was born out of a very
today is finding and retaining qualified engineers. When I first real need we had internally. We needed a bug detection tool and

38 July 2000 Delphi Informant Magazine


File | New

profiler that was an expert when it came to the VCL. The trouble ogy available for Web development. In fact, the COM object in
is, getting all that VCL knowledge into Sleuth QA Suite took a lot SysTools 3 is used extensively on our own Web site.
longer than we expected. Plus, we kept coming up with additional
capabilities that we felt were imperative to Sleuth QA Suite’s success. DI: In previous interviews with developers I have always included a
question on operating systems. Windows is very popular right now
In the end, I think we came up with a great product, and to tell the as a computing environment, but Linux is becoming such a strong
truth, though it was a lot later than we would have liked, rushing competitor that Inprise/Borland has decided to develop tools for
it out the door wouldn’t have been the answer. Building quality this environment. I noticed that you included an article on this
products sometimes takes a little longer than any of us expect. topic in the February, 2000 issue of your newsletter, Powerlines.
Please share some of your thoughts on the future growth of Linux
DI: I’d like to change the topic a bit and talk about some of the and how you see this impacting TurboPower.
other folks who work at TurboPower. For example, I’ve known
Julian Bucknall for several years and have been very impressed DelRossi: We’re incredibly excited about the potential for Linux,
with his articles and book chapters. I understand he is currently particularly with the advent of Borland’s Kylix project. For some
in the process of finishing a book on algorithms. Of course, he time now, I’ve regarded the Linux marketplace as the Windows
is just one of several of your engineers active as Delphi writers. market was back before Visual Basic. In those days, creating
Talk a bit about some of these active writers and some of the Windows programs was essentially a labor of love. You had to
reasons you not only allow this but encourage such “extra- learn all those API calls and bring out the old command-line C
curricular” activities. compiler. Then Visual Basic arrived and millions of developers
turned to it as an easy way to build Windows applications.
DelRossi: I’ve been at TurboPower four years now and I’m still awed Love it or hate it, Visual Basic changed everything for Windows.
by our programmers. I was a programmer too, once upon a time, but Even programmers who wouldn’t have considered programming
I was never anywhere near the caliber of our engineering staff. in Basic again started using Visual Basic, because it was so much
easier to build Windows programs with it.
One reason I think we’ve been so fortunate at attracting such
high-end developers is that we give our developers a chance to The Linux development world is also waiting for its Visual Basic and,
participate in every level of the product development cycle, from quite frankly, we think Kylix may be it. As it was back then, there
design to marketing. Not everyone likes every part of the process, are probably many C and C++ programmers that think their Pascal
of course, but there’s much to be learned and at the end of the day, days are behind them. But Kylix may represent such a fundamental
a greater appreciation for how each phase of the cycle develops. improvement in Linux-targeted programming that Pascal’s glory days
could be coming back. In a big way!
Another area, as you say, is to encourage our engineering staff to
write and give lectures. Again, not all of our engineers want to do DI: I’d like to conclude by talking a bit about Inprise. We’ve wit-
these things, but for those that enjoy it, this allows for a sharing nessed a lot of changes in recent years — certainly some ups and
and exchange of knowledge; it’s great for them, as well as for the downs. Among other remarkable developments, there is now a signifi-
whole company. cant merger with Corel Corporation on the horizon. What is your
perspective on some of the new developments at Inprise? How will
DI: By all reports, the latest version of Delphi seems to be one of this affect the future of Delphi and your company?
the most solid ones to appear in several years. Does this seem to be
an accurate assessment, and if so, do you feel it might help Delphi’s DelRossi: Inprise has always had great development talent and terrific
market share begin to expand? developer products. Of course, my crystal ball is no better than
anyone else’s. But from where I stand, Inprise has a better outlook
DelRossi: We’re very satisfied with this version of Delphi, although now than at any time in its recent history. Naturally, much of that
there’s lot more that could be done. Of course, all those gaps give will depend on the rate of Linux adoption.
us plenty to do!
DI: Thank you very much, and best of luck with all your future
DI: Some component-producing companies also produce versions for endeavors. ∆
other tools like Visual Basic. Have you ever considered this? What do
you see as the advantages and disadvantages? — Alan C. Moore, Ph.D

DelRossi: We’ve certainly considered expanding our product line to Alan Moore is a Professor of Music at Kentucky State University,
support Visual Basic, and COM developers generally. Many of our specializing in music composition and music theory. He has been
customers have for one reason or another — perhaps at the request of developing education-related applications with the Borland languages
a key client — moved away from Delphi to embrace COM, and they for more than 10 years. He has published a number of articles in
tell us they’d like to use our products too. various technical journals. Using Delphi, he specializes in writing
custom components and implementing multimedia capabilities in
Expanding our products to support all kinds of developers is an applications, particularly sound and music. You can reach Alan on
important part of my vision for TurboPower. As a matter of fact, the Internet at [email protected].
we’ve already begun including COM implementations in some of
our products, including Abbrevia and SysTools 3. In my view,
COM support is important for a number of reasons. Naturally, it
exposes TurboPower products to a wider audience of Visual Basic
and Visual C++ developers, but it also makes some of our technol-

39 July 2000 Delphi Informant Magazine

You might also like