Delphi Informant 95 2001

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

April 1996 — Volume 2, Number 4

Sharing
Components
Techniques for Delphi 1.0 and 2.0

A Word from the Editor


You may have noticed I did not write the editorial
(or “Symposium”) column of this or the March
issue of Delphi Informant. Instead, the work of
some prominent “guests” has been presented. Last
month’s “Symposium” was used to kick off Richard
Wagner’s “File | New” column which now appears
on the last page. In this month’s “Symposium,”
Borland’s Zack Urlocker reveals the origin of the
name “Delphi”. I hasten to note that Informant
Communications Group is not affiliated with
Borland International and that Borland has no con-
trol of the editorial content of any of our maga-
zines. Conspiracy buffs should instead revisit the
Zapruder film. — Jerry Coffey, Editor-in-Chief

Cover Art By: Tom McKeith

ON THE COVER 32 Dynamic Delphi — Andrew Wozniewicz


Mr Wozniewicz continues his DLL series by rounding out the
20 Sharing Components — Cary Jensen, Ph.D. collection of string-handling functions begun last month. He
Our DBNavigator column takes the cover this month to show how also discusses exporting these custom functions for use across
multiple forms can share a common component. It’s easy with your Windows applications.
Delphi 1.0, and even easier with Delphi 2.0. Dr Jensen presents
a step-by-step tutorial and provides us with a look at Delphi 36 Visual Programming — Walker Lipscomb
2.0’s data module feature. There are myriad ways to enhance the usability of Delphi
applications, and Mr Lipscomb shares many of them with his
FEATURES Who Owes Whom? application. It’s full of tips, from
9 OP Tech — Dana Scott Kaufman TTabbedNotebook to the lowly TLabel.
Streaming is a valuable Delphi programming technique, but is 40 The API Calls — Karl Thompson
unfortunately nearly undocumented. Thankfully, Mr Kaufman Mr Thompson presents his Walker utility which provides
gives us a clear and elegant implementation of streams for information about currently loaded modules, running tasks,
writing and reading data to and from memo objects. memory allocations, and many other under-the-hood
12 In Development — Craig L. Jones aspects of Windows programming.
Returning for Part II of his quality assurance discussion, Mr REVIEW
Jones shows us how to encapsulate test drivers and understand
the impact of QA on system design. His example, techniques 46 Light Lib VCLs — Product reviews by Douglas Horn
for handling “vague” dates, is also of interest. Mr Horn reports on two new tools, DFL Software’s Light Lib
Images and Light Lib Business.
24 Delphi C/S — Bill Todd
Sooner or later, you’ll upsize your desktop database Delphi DEPARTMENTS
application to client/server. To prepare you for the move, Mr Todd 2 Editorial
discusses some problems you’ll encounter, and their solutions. 3 Delphi Tools
27 Informant Spotlight — James Hofmann 5 Newsline
The votes are in and you have spoken. Here are the results 49 File | New
of the Delphi Informant Reader’s Choice Awards for 1996.
Our own Mr Hofmann tabulates the results and announces
the winners.

Delphi Informant April 1996 1


Symposium
Giving Birth
ne of the most fun things I’ve done in my career is to help build the “1.0” version of Delphi.
O Okay, 2.0 was pretty fun too, but, as they say, there’s nothing quite like the first time.
To be honest, when we started building tomers. There were two reasons for that. had came up with the original code name
Delphi 1.0, it was hard slogging. A lot of First of all, Delphi was an underground “Delphi” since it was to be a client/server
the tools out there, like PowerBuilder, project that was truly secret. Heck, for the tool connecting to the likes of Oracle
SQL Windows, and Visual Basic, were first year we had more code names than among others. We had to come up with a
pretty good. Long before we even had a beta testers! Secondly, and perhaps more code name for our first beta test and
prototype up and running, I was on the importantly, I wanted to make sure we everyone felt that Delphi was acceptable.
road talking to developers to understand understood customers problems rather We had many later code names for inter-
the problems they were facing. To be hon- than simply gauging a reaction to a nal use, external use, different countries,
est, most of them were pretty happy. I demonstration. That way we could ensure and at one point, I must admit, I random-
remember particularly thinking that the we were building the right product, rather ly made up a new code name for every
VB corporate users by and large were just than fine tuning the wrong one. presentation, so that if there ever was a
the happiest bunch of developers I’d ever When we finally started showing leak, we’d know from where it came.
met. Heck, they were even having fun! Delphi to customers, the reaction was Delphi 2.0 has come a long way since
I remember one particular meeting dramatic. After all, we were solving the then. Our original goal was to address a
with about a dozen developers from a problems they had told us about. Our few of the usability issues and also migrate
major airline. They told a not uncommon mantra: “Performance, Reuse, RAD, and to a 32-bit compiler and take advantage of
story of how a senior VP had decreed that Scalability.” This helped us to not only platform features like OLE automation,
they would use Visual Basic after he pro- define the product, but to communicate OCXes, etc. Along the way, we introduced
totyped an application on a weekend. The the benefits to the customer. several major innovations like Data
developer told me that he didn’t want to Oddly enough, one of the problems we Module Objects and Visual Form
have any association with — ugh — faced was how to name the product. As Inheritance that increased code reuse.
Basic, but after trying it out for a while, Jerry Coffey pointed out in this column The 32-bit compiler itself was actually
he changed his mind. [“Symposium,” Delphi Informant, January started way back when the original 16-bit
Another time, I was meeting with a 1996], we were experimenting with all version of Delphi was started. At the time
development team at a Wall Street foreign kinds of names. Although we had early on it seemed like a safe bet that “Chicago”
exchange. They showed me this tremen- decided that “Pascal” should not be includ- would slip out of 1994 and that Windows
dously impressive application for monitor- ed in the name since it wasn’t really mean- 3.1 would still be a viable development
ing currencies — written in SQL ingful except to long-time fans, we really platform for Delphi 1.0. We were able to
Windows. It was beautiful. I thought, “Oh hadn’t made much progress on the name have most of the VCL ported to 32-bits
no, another satisfied customer. Time to until a few months before its release. and running with the new compiler prior
move on.” So after a demonstration I asked Leading contenders included “Visual to the release of Delphi 1.0. So we were
how they liked the application. His answer: AppBuilder” (luckily, it was taken) quite confident that the architecture
“It’s a dog.” The response time was simply “Application Architect” (too much like a would ensure compatibility with most
too long to even be considered for produc- CASE tool), “Client Builder” (sounds like a code from Delphi 1.0, assuming it wasn’t
tion use. After all, Wall Street practically sales prospecting package), “Object Vision” dependent on 16-bit data assembler, data
defines the phrase “Time is money.” (ahh, we used that already, didn’t we?) and structures, or unsupported API functions.
I saw these scenarios repeated in meet- just about every combination of the words Although it’s a bit too early to
ing after meeting, city after city. On the “Visual”, “Power”, “SQL”, “Application”, announce plans for the next version of
surface, customers seemed pleased with “Object”, and “Builder” (“Visual Power Delphi, we’re certainly working on a num-
the productivity gains of “Rapid SQL Application Object Builder” anyone?) ber of fronts to further reduce the amount
Application Development” tools. But as I As we were in the late stages of select- of code folks need to write, and make it
delved further, I found the love affair ing a name, whenever I’d do a presenta- easier to support very large projects.
often came to a bitter end when they tried tion, whether to potential customers, sales
to move from prototype to production. I reps, or third party vendors, I’d always ask Zack Urlocker
found lots of spaghetti code out there, and them what they thought of the proposed
DLLs written in C to make up for perfor- names like “AppBuilder.” Invariably, the Zack Urlocker is Director of Delphi Product
mance bottlenecks in applications written response was lukewarm. Then they’d ask, Management at Borland International. The
in PowerBuilder, SQL Windows, and VB. “Why don’t you just call it Delphi?” So in views expressed here are his own.
It was some nine months into the two the end, we did. He can be reached on CompuServe
years of the development of Delphi 1.0 Danny Thorpe, then on the QA team, at 76217,1053.
before we showed it to potential cus- now currently part of the R&D group,
Delphi Informant April 1996 2
Delphi Component Building Tool for Delphi Updated
Potomac Document
T O O L S Software, Inc., of
Washington, DC, has
New Products released Component Create
and Solutions 2.0, a design tool and code
generator that enables Delphi
developers to produce new
components that can be
added to Delphi’s
Component Palette, and
dropped onto forms.
With version 2.0, develop-
ers can import container
components from form files
and create components that
“wrap around” forms, such generated Object Pascal code. Component Create 2.0 is
as a Windows common dia- Component Create users expected by press time and
New Delphi Book
log box. can develop and register will be available to registered
Developing Custom Delphi Components Developers can also make custom property editors users at no charge.
Ray Konopka
Edited by Jeff Duntemann component properties based on drop-down lists or
The Coriolis Group on a developer’s custom Price: US$179
(including inherited proper-
ties), invisible in Delphi’s Delphi forms. In addition,
Object Inspector, generate version 2.0 features an Contact: Potomac Document Software,
palette bitmaps, view Delphi improved code editor that Inc., PO Box 33146, Washington, DC
form (.DFM) files converted can undo errors and add 20033-0146
into text, and select detailed smart tabbing. Phone: (800) 628-5524
or normal commenting in the A 32-bit version of Fax: (202) 244-9065

SCT Associates Releases New Delphi Reporting Component


SCT Associates, Inc. of Oak there could be a button that etc.) will need to be tested
ISBN: 1-883577-47-0
Price: US$39.99,
Lawn, IL has released ACE prints the current customer, after building the application.
Canada $54.99 Reporter version 1.0 for another button that lists all ACE Reporter comes with
(500 pages; CD-ROM)
Phone: (800) 410-0192
Delphi, a VCL report compo- customers, and a third but- an online help file and a
nent. With Ace Reporter you ton that prints an open Delphi .KWF file (for inte-
can create and compile order report for that cus- grating it into Delphi’s
reports into an .EXE file. tomer. Ace Reporter also IDE), and a free upgrade to
Ace Reporter works as a allows you to minimize each the Delphi 2.0-compatible
container for report compo- report and modify the form version and source code.
nents (not data compo- at design time. A free trial version of ACE
nents), allowing you to place Ace Reporter features a Reporter is available for
multiple reports that share Fast button for selecting downloading from the Delphi
open tables on one form. multiple fields from tables and Informant CompuServe
For example, on a data entry on a form. Once selected, forums, and SCT’s and
screen for a customer table, Ace Reporter generates Informant’s Web Sites, file
default headings that can name: ACETRIAL.EXE.
be edited, allows you to
select bands for the head- Price: US$245, including a 30-day
ings and fields, and places money-back guarantee.
them on the report.
Using the Run button in Contact: SCT Associates, Inc.,
design mode, Ace Reporter 9221 S. Kilpatrick Ave., Oak Lawn,
tests the non-code portion of IL 60453-1813
the report without building Phone: (708) 425-0205
the application. You can run Fax: (708) 422-3877
the report from design mode, E-Mail: CIS: 73766,1224
but features relying on code Web Site: http://ourworld.compu-
(expression variables, events, serve.com/homepages/sct

Delphi Informant April 1996 3


Delphi Visual Components Updates to Include OCX Custom Controls
Visual Components, Inc.
T O O L S of Lenexa, KS has upgraded
Formula One, First
New Products Impression, and
and Solutions VisualSpeller, as OLE
Custom Controls, and
announced plans to release a
new version of
VisualWriter, also as an
OLE custom control.
These Visual Components
upgrades include 16- and
32-bit versions, enabling
developers to build applica-
tions for Windows 95,
Windows NT, as well as
Windows 3.1.
Starfish Releases EarthTime
Starfish Software, Inc. has posted Being OLE-enabled, Suite Subscription Plan, Gold (includes the Suite, enrollment in
EarthTime, a plug-in for Netscape Formula One, First developers can update and the Suite Subscription Plan, and a one-
Navigator 2.0, for developers to
download and evaluate. With the Impression, VisualSpeller, and maintain their Suite soft- year Gold Support contract), US$499.
EarthTime plug-in, Netscape VisualWriter can be used in ware for an annual fee. The Developers that own the Visual
Navigator users can check the time
anywhere in the world without leav- Delphi 1.0, Delphi 2.0, plan features quarterly Developer Suite Deal of OCX compo-
ing their browsers. EarthTime is a Paradox 7, Visual C++ 4.0, updates to all software in nents may enroll in the Suite
component of Sidekick 95, and runs
on Windows 95 and Windows NT. Access 95, Visual FoxPro, the Suite, and includes any Subscription Plan for US$179.
The EarthTime plug-in enables Visual Basic 4.0, and other bug fixes and online releas-
users to schedule appointments,
conference calls, and chat sessions environments that support the es. The update CDs will be Contact: Visual Components, Inc.
with participants in multiple time OCX component standard. mailed to subscription 15721 College Blvd.,
zones. It also allows users to simul-
taneously monitor the time and Visual Components also holders automatically. Lenexa, KS 66219
date in eight cities of their choice. plans to release a new edi- Phone: (800) 884-8665, or
For more information contact
Starfish Software at (800) 765- tion of the Visual Price: Visual Developers Suite Deal, (913) 599-6500
7839, or visit their Web site at Developers Suite Deal. It US$299 (including online documenta- Fax: (913) 599-6597
http://www.starfishsoftware.com.
will include new versions of tion); printed documentation for the BBS: (913) 599-6713
Formula One, First Suite, US$75; Visual Developers Suite E-Mail: Internet: sales@-
Impression, VisualWriter, Deal Plus (includes the Suite and enroll- visualcomp.com
and VisualSpeller. ment in the Suite Subscription Plan), Web Site: http://www.-
By subscribing to the new US$429; Visual Developers Suite Deal visualcomp.com

New VCL/DLL Provides a Financial Calculator


Odyssey Technologies, Inc. bility like a desk calculator,
of Cincinnati, OH has and may print the tape and
released VCaLc, a native finan- calculations. Standard calcula-
cial calculator VCL or DLL. tor features, such as addition,
Developers can drop this subtraction, and trigonometric
component into their functions are also provided.
applications and allow their
users to do the financial Price: US$49
calculations used in most
companies. Contact: Odyssey Technologies, Inc.,
VCaLc can calculate annuity PO Box 62733,
calculations, internal rates of Cincinnati, OH 45262-0733
return and net present value Phone: (800) 293-7893
calculations, simple loans, Fax: (513) 777-8026
interest conversions (effective E-Mail: Internet: [email protected]
to APR, APR to effective), Web Site: http://www2.eos.net/-
and currency conversions. odyssey/
Users also have a “tape” capa- CompuServe: GO DELPHI

Delphi Informant April 1996 4


News Borland Outlines Phased Internet and Intranet Strategies
Scotts Valley, CA — Borland
International Inc. has out-
nents for Borland C++, and
will result in Delphi-like visual
tion server for remote Java
and database access.
L I N E lined its short- and long-term tools for Java. Code-named Nexus, this
strategies for producing devel- Latte will be an integrated product will feature simple
April 1996 opment tools for building visual development toolset for clients, low-maintenance
Internet and intranet applica- Java programmers developing client configuration, central-
tions. Borland recently out- applications for the Internet ized business rules and vali-
lined its three-phase plan for and intranets. In addition to dation, and database connec-
addressing the evolving needs compilers and tools, the tivity. Anticipated delivery
of PC LAN and intranet development suite will offer dates were not made public,
developers, and displayed new class libraries, RAD function- although beta versions are
tools for building Java appli- ality, native database connec- expected in the second half
cations. The new tools are the tivity, data aware controls, of 1996.
first components of Latte, and X-platform RDBMS In addition to building
Borland’s native Java visual connections. Formal delivery client/server and Internet
development environment. dates for Latte were not avail- tools around Java, Borland
The current phase of able at press time. sees long-term growth oppor-
Borland’s strategy calls for In the next phase, Borland tunities in the client/server
Visual Query Builder Internet-enabled versions of will introduce the InterBase marketplace by developing
Now Available
Borland C++, Delphi, Visual InterClient, connectivity soft- tools for building three-tier,
dBASE, and Paradox that will ware for InterBase written in distributed applications using
provide customers with the Java for networked InterBase the Web and intranets as the
ability to develop new Web- databases. It will contain both operating platforms.
based applications and to client and server components, Borland also plans to sup-
extend existing applications and will also eliminate the port Microsoft’s Internet ini-
with Web interfaces. complexity of remote database tiatives, including the forth-
Borland’s Latte is expected to access for Java developers. coming Sweeper SDK, which
accelerate the growth of the In the last phase of this strat- will be supported in Borland’s
Internet and Web as a plat- egy, Borland will migrate existing Windows develop-
Borland is now selling the Visual
Query Builder separately for
form for corporate computing intranet developers to a three- ment tools, such as Borland
US$79.95 plus tax and shipping. solutions. An incremental tier environment that takes C++ and Delphi.
Also sold as part of Delphi 1.0
delivery of Latte is planned. It advantage of cross-platform
Client/Server Edition, the tool enables
developers to generate SQL state- began with the previously and emerging protocol stan- Visigenic to Develop
ments using a visual metaphor. The
Visual Query Builder cannot be
announced add-on compo- dards with a Borland applica- ODBC Driver for
deployed without royalties. For details Borland’s InterBase
call Borland at (800) 453-3375.
Borland Focuses on Client/Server Market Scotts Valley, CA — Borland
Scotts Valley, CA — Borland cent to more than 15 percent International Inc. has
International Inc. recently of the company’s total rev- announced that Visigenic
announced its strategy for enues. In addition, the com- Software will develop a set of
expanding in the client/serv- pany estimates that nine out ODBC drivers for Borland’s
er market. Borland President of 10 Delphi client/server InterBase. This agreement will
and CEO Gary Wetsel said customers are new Borland allow ODBC-enabled appli-
they plan to strengthen their customers. cations and development tools
position in the PC LAN According to the company, to access InterBase 4.
market by delivering desktop Borland will continue to focus InterBase 4 is designed for
tools that enhance productiv- on the departmental/division- workgroup and departmental
ity and shorten the develop- al segment of the client/server computing environments, and
ment cycle. market where Delphi is posi- is available for Windows and
According to Paul Gross, tioned as a database-neutral most UNIX platforms. The
Borland’s senior vice presi- tool, co-existing with previous Visigenic ODBC Drivers for
dent of Research and enterprise database standards. InterBase will support
Development, the success of To support its client/server Windows 95, Windows NT,
Delphi Client/Server and its business, Borland also plans to Sun Solaris, HP-UX, and
InterBase server in the last enhance its service, sales, and other UNIX platforms.
year has propelled the com- support organizations to For more company informa-
pany’s client/server revenue address the needs of these tion, visit Visigenic’s Web site
from approximately two per- customers. at http://www.visigenic.com.

Delphi Informant April 1996 5


News Computer Systems Advisers Bring Data Modeling to Delphi 2.0
Woodcliff, NJ — Borland
and Computer Systems
L I N E Advisers (CSA) have inte-
grated CSA’s Silverrun busi-
April 1996 ness modeling software with
Delphi Client/Server Suite
2.0. The integration will
enable developers to gener-
ate data models supporting
heterogeneous database envi-
ronments.
Silverrun’s Relational Data
Modeler (RDM) module —
one of four tools in the
Silverrun Professional Series
— is incorporated into
Delphi Client/Server Suite RDBMSs. The models will Entity Relationship Expert,
2.0 as an import/export facil- then become incorporated Relational Data Modeler,
Spring Internet World 96 ity. It enables Delphi devel- into a Delphi data dictionary. Business Process Modeler, and
Keynote Line-Up
Includes Kahn
opers to access RDBMSs Silverrun Professional is an Workgroup Repository
The Mecklermedia Corp. has such as Oracle, InterBase, integrated business process Manager.
announced that Philippe Kahn, Informix, and Sybase. and data modeling workbench For more information visit
co-founder and chairman of Starfish
Software, will be a keynote speaker Silverrun-RDM provides the for client/server development Computer Systems Advisers’
at their Spring Internet World 96, architecture for launching on the Windows, Solaris, Web site at http://www.silver-
scheduled for April 29-May 3,
1996 at the San Jose Convention client/server applications based OS/2, and Macintosh plat- run.com, or contact them by
Center in San Jose, CA. on graphical models designed forms. Silverrun Professional phone at (201) 391-6500, or
Additional keynote speakers include
Larry Ellison, chairman and CEO of with information stored in the Series includes four modules: e-mail: [email protected].
Oracle; Bill Gates, chairman and
CEO of Microsoft; Bill Joy, founder
and vice-president of research at Borland’s New C++ 5.0 for Windows 95, Windows NT, and Java
Sun Microsystems; and Tim
Krauskopf, co-founder and vice Scotts Valley, CA — Borland logo-certified product, programmers to develop
president of research at Spyglass. has released Borland C++ Borland C++ 5.0 is compati- cross-platform code, which
For more information visit
http://www.iworld.com/. 5.0, an update to its object- ble with most Windows 95 can run on many popular
oriented C and C++ product. user-interface standards and operating systems, including
Borland C++ 5.0 provides features, including OLE, reg- Windows 95, Sun Solaris,
developers with the tools istry, and long filenames. Macintosh, and others.
necessary to migrate to 32- Borland C++ 5.0 also includes In addition, Borland C++
bit operating systems, the complete 16-bit hosted 5.0 includes the Borland
including: support for both Borland C++ 4.52, for pro- Debugger for Java (the only
16- and 32-bit platforms; a grammers using Windows 3.1. GUI debugger for Java writ-
new version of Object- Borland C++ 5.0 has a 32- ten in Java), as well as
Windows Library; and sup- bit debugger with integrated AppExpert for Java-specific
port for 16- and 32-bit VBX resource editing, including applications and applets.
controls. new Windows 95-based con- Borland also announced
Borland C++ 5.0 includes a trols, multi-threaded and the release of Borland C++
new native 32-bit hosted multi-process debugging sup- Development Suite, which
development environment, port, and an expanded dialog includes Borland C++ 5.0;
which lets developers target editor that provides support CodeGuard 32/16, a new
multiple platforms, including for Windows 95-based com- version of Borland’s auto-
Windows 95, Windows NT, mon controls. mated bug detection and
Windows 3.1, and DOS, For Internet development, diagnosis tool; PVCS Version
from a single integrated Borland C++ 5.0 includes Manager; InstallShield
development environment. integrated development tools Express; and the new
Version 5.0 also includes a for Java, including Sun’s Java AppAccelerator for Java, a
multi-target project manager, Development Kit (JDK). The compiler that increases the
which lets developers build JDK works within Borland’s
16- and 32-bit applications integrated development envi- “Borland’s New C++ 5.0 for Windows 95,
Windows NT, and Java”
concurrently. A Windows 95 ronment (IDE), and allows continued on page 7

Delphi Informant April 1996 6


News Delphi 2.0 Information on the Internet
Scotts Valley, CA — Borland
International has released sev-
describing the client/server
database architecture of
guage/delphi/gen/. You will
also find articles comparing
L I N E eral technical documents that Delphi Client/Server Suite Delphi 1.0 with Visual Basic
outline Delphi 2.0’s new fea- 2.0; and DLP2BKS.ZIP, a list and PowerBuilder, and more.
April 1996 tures. These include of the Delphi 2.0 books cur- Borland also has a Web site
D2_Q&A.ZIP, a question rently in production. at http://www.borland.com,
and answer document that All these documents are and a BBS at (408) 431-5096.
addresses general questions; available online. On For Delphi-specific technical
D2_COMP.ZIP, a detailed CompuServe type “GO DEL- information visit the
look at the inner workings of PHI”, or visit the Borland http://www.borland.com/tech-
the Delphi 2.0 compiler; FTP site at ftp.borland.com/- info/delphi/index.html page of
D2_DB.ZIP, a white paper pub/techinfo/techdocs/lan- Borland Online.

Borland Ships New Paradox 7 Runtime, Client/Server, and Developer Tools


Scotts Valley, CA — Borland client/server-enabled version the source code for the
International Inc. has of Paradox 7 Runtime, unlim- Experts in Paradox 7, and a
announced three new prod- ited deployment licenses for new Library Prototyping
ucts in the Paradox 7 family Borland’s new 32-bit SQL Expert. For more informa-
The Coriolis Group
Joins ITP Media Group of database products: Links, a single-user Local tion about Paradox
International Thomson Paradox 7 Runtime, Paradox InterBase Server, and Borland’s Developer Tools and the
Publishing (ITP) announced
The Coriolis Group has 7 Client/Server, and Paradox Data Pump Expert. Paradox 7 Paradox Developer
joined their ITP Media Group. Developer Tools. Client/Server costs US$1,495. Connections Program, call
The ITP Media group now
includes Course Technology, Paradox 7 Runtime allows For more information about Borland at (800) 353-2211.
boyd & fraser, Ventana database developers to dis- the Paradox 7 Runtime or
Communications Group,
and The Coriolis Group. tribute their single-user or Paradox 7 Client/Server, call Borland’s New C++
The Coriolis Group will PC LAN Paradox applica- Borland at (800) 233-2444. 5.0 for Windows 95,
remain based in Scottsdale,
tions without requiring end- Available at no charge to
AZ and will function as an
autonomous publishing users to have a copy of members of Borland’s
Windows NT, and
operation under The
Coriolis Group imprint. The Paradox 7 installed. Paradox Developer Java (cont.)
Coriolis Group’s Web site Additionally, Paradox 7 Connections Program, performance for Java appli-
can be accessed at
http://www.coriolis.com. Runtime protects a develop- Paradox Developer Tools is cations and applets by up to
er’s source-code, and doesn’t a series of add-on utilities 10 times. AppAccelerator
require royalty or license fees that make Paradox applica- works with all Java applica-
for distribution. Paradox 7 tion development easier and tions and applets regardless
Runtime is priced at US$299 more productive. Many of of the development tools
and bundles Stirling these tools have been devel- used to create them.
Technologies’ InstallShield oped in ObjectPAL by the Borland C++ Development
product. Owners of previous Borland Paradox develop- Suite 5.0 is priced at
Runtime versions of Paradox ment team. The first of US$499.95, and Borland
can upgrade for US$249. these utilities to be available C++ 5.0 is priced at
Paradox 7 Client/Server fea- is ObjectSpy, an object- US$349.95. Current owners
tures development tools for based documentation add- of other Borland products
creating front-ends to existing in that documents the com- and owners of Microsoft
Oracle, Sybase, SQL Server, ponents of a Paradox appli- Visual Basic, Microsoft
InterBase, and Informix data- cation. Future additions to Visual C++, or Watcom or
base servers. The product the Paradox Developer Symantec C or C++ prod-
includes Paradox 7, a Tools program will include ucts, can buy Borland C++
5.0 for US$249.95.
Delphi Developers Conference Set for May Upgrades will be provided
Orange, CA — The Orange ner through advanced on CD-ROM and include
County Delphi Users Group Delphi developers. complete online documen-
will be sponsoring the The conference costs tation. Diskettes and print-
Southern California Delphi US$99. For more informa- ed documentation are avail-
Developers Conference May tion or to register, call (714) able separately at an addi-
4, 1996 at Chapman College 855-9789, fax (714) 457- tional charge. For more
in Orange, CA. The event 9641, or e-mail drdelphi@- information, call Borland at
will include tracks for begin- mannatech.com. (800) 645-4559.

Delphi Informant April 1996 7


News Borland Reports a Profit in Third Quarter Fiscal Results
Scotts Valley, CA — Borland
International Inc. announced
Included in the previous year’s
results is a US$109.9 million
US$16.2 million related to
the company’s acquisition of
L I N E a net income of US$849 non-operating gain on the ReportSmith. Excluding these
thousand, or US$.03 per sale of Quattro Pro, revenue transactions, Borland would
April 1996 share, on revenues of of US$24.5 million from the have reported a pre-tax loss of
US$47.3 million for its third sale of Paradox licenses to US$68.6 million in the nine
fiscal quarter ending Novell, and a one-time charge month period of the previous
December 31, 1995. These for purchased technology of fiscal year.
results reflect the third con-
secutive quarter of profitabili- Java 1.0 Available for Download
ty since Borland restructured Palo Alto, CA — JavaSoft, of JavaSoft. For example,
its operations in January, the newly-formed operating IBM has announced plans to
1995, and focused its strategy company of Sun Micro- build ports for Microsoft
on software developers. The systems, Inc., has made the Windows 3.1 and OS/2, and
net income for the nine Java 1.0 programming envi- OSF has announced plans to
months ending December 31, ronment available for down- build ports for additional ver-
1995 was US$6.3 million, or load at http://java.sun.com. sions of UNIX.
US$.20 per share, on rev- This release incorporates Java-based applications are
Borland Announces enues of US$152.3 million. the Java Applet Viewer for platform-independent; only
Premier Value Added
Partner Program
The company incurred a running and testing applets, the Java Virtual Machine
Borland has announced the net loss of US$22.9 mil- the Java Compiler, a proto- needs to be ported to each
Premier Value Added Partner lion, or US$.80 per share, type debugger, and the Java platform. It acts as an inter-
Program, a new program target-
ing client/server system integrators in the same quarter of the Virtual Machine to run Java- preter between an end-user’s
and consultants that provide previous fiscal year, on rev- based programs. Also includ- computer and the Java-based
corporations and government
clients with applications, consult- enues of US$48.1 million. ed are class libraries for application. An application
ing, and training services for Included in this result is a graphics, audio, animation, written in the Java environ-
Borland’s client/server software
products (Delphi Client/Server, US$10 million gain associ- and networking. ment can run anywhere, end-
InterBase, and ReportSmith).The ated with the sale of Java 1.0 is available for ing the need for porting appli-
Premier Value Added Partner
Program’s annual fee includes Borland’s Quattro Pro Windows 95 and Windows cations to multiple platforms.
software, rebates, technical sup- spreadsheet product line to NT on Intel, Solaris, and The Java Virtual Machine is
port, and marketing tools and
programs. Each partner must also Novell, Inc. SPARC platforms. Java 1.0 currently available through
complete Borland sales and prod- In the nine months ended for Mac OS 7.5 is expected JavaSoft’s HotJava and
uct training. For more
information, contact Borland December 31, 1994, net by the end of the first quarter Netscape Navigator 2.0 Web
at (408) 431-5117. income was US$38.8 million, of 1996. In addition, ports to browsers, and will be available
or US$1.21 per share, on rev- other significant operating in Oracle’s PowerBrowser and
enues of US$198.6 million. systems are underway outside Spyglass’ Mosaic browsers.

InstallShield Provides Software Deployment Toolkit for Borland C++


Schaumburg, IL — Suite 5.0. It allows program- third-party software com-
InstallShield Corp. and mers to create installations ponents.
Borland have created a soft- for Windows 95 and InstallShield Express
ware deployment toolkit, Windows NT. Borland C++ Development
InstallShield Express, for the The new product features Suite 5.0 Edition assists in
Borland C++ Development InstallShield Objects, which creating installation programs
Suite. automate the installation of that know how to install
Borland will also work with ObjectWindows Library shared dynamic link libraries,
InstallShield to provide soft- 5.0, Visual Database Tools, OCX controls, and the
ware deployment solutions and other technologies used Borland Database Engine on
for other Borland products, by the Borland C++ the target system.
such as Delphi 2.0, Paradox Development Suite. Borland C++ Development
7, and Visual dBASE. It also enables developers Suite users may purchase
InstallShield Express to specify product compo- InstallShield Express
Borland C++ Development nents and files, set up pro- Professional from InstallShield
Suite 5.0 Edition is a custom gram folders and icons, Corp.
version of InstallShield make system file and reg- For more information visit
Express that integrates with istry changes, and select Borland’s Web site at
Borland C++ Development InstallShield Objects to add http://www.borland.com.

Delphi Informant April 1996 8


OP Tech
Delphi / Object Pascal

By Dana Scott Kaufman

Working in Streams
(without Getting Wet)
Using Streams to Read and Write Memo Field Data

emo fields are common in applications that need to store an indefinite


M amount of character-based information in individual records in a data-
base. Although memo fields are widely used, Delphi contains no easy way
to import and export data from these fields. The DBMemo component is the
only ready-made control that can access a memo.
But what if you need to store and manage large It may help to think of a stream as you
amounts of text, but don’t include a TDBMemo would magnetic audio tape. Sound is record-
object on any of your Delphi forms? For exam- ed sequentially on tape in much the same
ple, I regularly store program configuration way that data is stored in a stream. Con-
data in string lists for easy manipulation at run tinuing this metaphor, a tape counter is simi-
time. The program must be able to load or lar to the stream position property because it
store this information at any time. indicates the current position on the tape. To
hear the sounds, or obtain data from the
Borland provides the means to perform these stream, you must ensure you “rewind” the
tasks, but they are poorly documented. This position property back to where you want to
article will explain how to use streams to start, usually position 0 for the beginning.
import and export data from fields in a data-
base. Later, we’ll address using these tech- BLOb Streams to the Rescue
niques with data stored on a SQL server, and The key to manipulating memo fields in
the benefits of using these methods. Code Object Pascal is the TBlobStream class. BLOb
examples are included that show how to read streams provide an easy way to access or mod-
and write a string list to a memo field. ify a memo field by allowing you to read or
write to the field as if it were a file or stream.
Into the Stream
To accomplish these data import/export The Create constructor is used to link the
feats, we must first become familiar with the field to the BLOb stream. Here is its syntax:
concept of streams. Streams allow applica-
tions to read and write data sequentially to constructor Create(Field : TBlobField;
and from a medium that can store binary Mode : TBlobStreamMode );
data. The stream can store the data in memo-
ry, on disk, or on other devices. Create has two parameters. The first is a ref-
erence that points to the memo field to be
The main strength of streams is that the actu- manipulated. The second specifies the read-
al medium used is irrelevant. All streams work write mode with one of the TBlobStreamMode
the same, in that the same methods work on constants: bmRead, bmWrite, or
memory blocks, individual files, etc. Streams bmReadWrite. As Delphi’s online documenta-
have two properties, size and position, that tion states: use bmRead to access an existing
denote the stream’s size in bytes and the cur- memo field, bmWrite to clear the contents of
rent position within the bytes. Streams also the field and assign a new value, and
provide methods for reading, writing, and bmReadWrite to modify an existing value.
copying bytes into and out of the stream. After the BLOb stream is created, the data

Delphi Informant April 1996 9


Op Tech
can be accessed. To do this, we use the stream methods avail- procedure ListToMemo(DestTable: TTable;
able to all classes derived from the TPersistent class. Note that DestField: string;
many of these objects contain stream calls that are not docu- SourceList: TStringList);
var
mented in Delphi’s online help. BlobStream1: TBlobStream;
begin
MemoToList: Reading Memo Fields into Memory BlobStream1 :=
TBlobStream.Create(TMemoField(
First, we’ll discuss how to read data from a database into DestTable.FieldByName(DestField)),bmWrite);
memory where it can be readily used. Then we’ll go over the
steps needed to write data to a memo field in the database. try
BlobStream1.Write(SourceList.GetText^,
StrLen(SourceList.GetText));
To read data into memory, we use the LoadFromStream pro- finally
cedure. Here is its syntax: BlobStream1.Free;
end;

procedure LoadFromStream( Stream: TStream ); end;

Figure 1: The ListToMemo procedure.


This will load data from the specified stream into the object.
The MemoToList procedure creates a BlobStream in read mode parameter, we attach a caret character ( ^ ) to the GetText call.
and then pulls the contents of a memo field into a TStringList: This instructs Delphi to use the actual string that GetText is
pointing to.
procedure MemoToList( SourceTable: TTable;
SourceField: string;
DestList: TStringList); We also need to pass the size of the string. We do this by
var using the StrLen function. StrLen takes a null-terminated
BlobStream1: TBlobStream;
string as a parameter and returns the number of characters
begin
BlobStream := TBlobStream.Create(TMemoField( contained in the string. So, we pass the text from the
SourceTable.FieldByName(SourceField)),bmRead); TStringList we retrieve to the StrLen function by using the
DestList.LoadFromStream(BlobStream1);
GetText function again. The call to Write resembles this:
end;

BlobStream1.Write(SourceList.GetText^,
As you can see, MemoToList takes three parameters. The first StrLen(SourceList.GetText));
is a TTable that should be attached to the table and posi-
tioned on the record you want to read. The second is a That’s it. You now have the code to read and write text to
String containing the field name from the table to read, and and from memo fields in a database. For convenience, I
the third is the TStringList that will contain the text read placed the MemoToList and ListToMemo procedures in a unit
back from the memo field. In the Create constructor, we use called MemoList. When I need their functionality in a Delphi
the TMemoField to inform the compiler that SourceField is form, I include the MemoList unit in the form’s uses clause.
actually a memo field.
This article is accompanied by the sample Memo I/O appli-
ListToMemo: Writing Memo Fields to a Table cation (see Figure 2) that uses these two routines. The form
Now that we can read data from the memo field, we must be contains two TMemo components (Memo1 and Memo2), two
able to put data into it. As in the previous example, we have TButtons (one to save and the other to load a button), and a
to create a TBlobStream that references the field we want to TTable.
manipulate. This time, we’ll use bmWrite as the mode. This
will clear the field before the data is inserted. We then save
the text from the string list to the BLOb stream. The syntax
of the Write method is:

function Write( const Buffer ; Count : Longint ) : Longint;

Write requires two parameters: Buffer, which is a block of memo-


ry that contains the string you want to write, and Count, which
is the number of characters (bytes) to be written. Write is a func-
tion and returns the number of characters that were written.

Figure 1 shows the source code for the ListToMemo proce-


dure. In our example, the text we want to write to the memo
is contained in a list. We can get a pointer to the memory
that contains the text by using the GetText function. This will
return the address in memory where the string starts.
Because the Write function requires the actual text as the Buffer Figure 2: The sample Memo I/O application.

Delphi Informant April 1996 10


Op Tech
The Save Memo button writes the contents of Memo1 to a these routines on InterBase, Microsoft SQL Server, and
memo field in a sample Paradox table using the ListToMemo Sybase tables. There is no reason it shouldn’t work on other
function. Memo controls have a Lines property that is a kind of database server types that are available through the Borland
string list, allowing me to cast the Lines property to a TStringList Database Engine.
so I can properly call the ListToMemo method. The OnClick
method for the Save Memo button is similar to this: At first glance, streams can be a complex and confusing issue. To
make matters worse, there’s little to no documentation on this
procedure TForm1.SaveButtonClick(Sender: TObject); extremely useful type of object. Once you understand their func-
begin
Table1.Open; tionality however, you will likely use them all the time. By using
Table1.Edit; the methods described above, you can add memo fields to all
ListToMemo(Table1,'MemoField',TStringList(Memo1.Lines)); your databases and not have to worry about how to fill them.
Table1.Post;
end;
ListToMemo and MemoToList are extremely handy routines that
Also, notice we need to put the table into edit state before calling I use on most of my development projects. The same tech-
the ListToMemo function. We can do this by using the table’s niques can be used to store and retrieve graphics, data points,
Edit, Append, or Insert method before calling ListToMemo. Final- and even other objects — but that’s for another article. ∆
ly, we must post the changed record after the ListToMemo call.
The call to MemoToList, the Load Memo button’s Click method, The Memotest project is available on the Delphi Informant
looks similar to the ListToMemo call above. Remember the Works CD located in INFORM\96\APR\DI9604DK.
TTable must be on the appropriate record that you want to read.

Conclusion
We have discussed getting data into and out of tables. Dana Kaufman is a Senior Consultant with Apogee Information Systems, Inc., a
However, we haven’t addressed the types of tables that can be Massachusetts-based consulting and development firm specializing exclusively in
used. The sample Memo I/O program uses a Paradox table to Delphi and Paradox applications. He is a contributing technical editor for QUE on
store the data. The ListToMemo and MemoToList methods can their Delphi 2 products. Dana can be reached at (508) 481-1400 or via the
also be used on SQL server tables. I have successfully used Internet at [email protected].

Delphi Informant April 1996 11


In Development
Delphi / Object Pascal

By Craig L. Jones

PQA: Part II
Practical Quality Assurance Techniques for Delphi

s every programmer knows, much can go wrong with even the simplest
A of programs. Worse yet, the number of possible problems grows geo-
metrically with the size of an application. The good news is that learning just
a little quality assurance (QA) savvy can go a long way towards heading off
disaster. There’s nothing mysterious about QA — just think of everything
that can go wrong and test for it. The trick is to eliminate the tedious aspects
by writing test drivers that automate as much of the testing as possible.
This is the second of three articles on assuring In addition, a tool kit was established for
the quality of Delphi programming projects. performing unit testing — this was probably
This series is primarily directed towards Delphi the most important of the eight.
programmers and assumes no prior knowledge
on the subject of QA. Hopefully, programmers Unit testing is a matter of focusing on the sub-
who previously gave QA little thought will dis- processes contained within a program and test-
cover some enthusiasm for applying the tech- ing each individually. To introduce the subject,
niques presented here. These techniques are a test driver was written to exercise a simple
quick to implement, easy to maintain, and function, showing how to consider four differ-
thereafter automatically reusable. ent areas of concern: path coverage, boundary
conditions, performance, and regression.
This installment will expand the QA tool kit
(introduced last month) to cover the testing The example function tested took one string
of more complicated procedures and object argument, a book title, and returned another
methods. Facilities will be added for running string that represented the title in a sortable
multiple test drivers consecutively (unattend- form. For example:
ed) and using a comparison program to
check the results against an established base- “A 3 Tier Solution”
line. [For an introduction to the QA tool kit,
see Craig Jones’ article “PQA: Part I” in the would be converted to:
March 1996 Delphi Informant.]
“THREE TIER SOLUTION, A”
A Quick Review
Last month, some general QA theory was The test driver called the function multiple
presented, along with how it applies to the times, passing it a series of different titles and
different stages of program development. checking the results with an “assertion tool.”
Eight types of testing were outlined:
1) requirements verification Once an automated test driver is written, it
2) design validation can be executed quickly at any time to ensure
3) unit that the unit (still) works properly.
4) integration
5) user acceptance Expanding and Encapsulating the Tool Kit
6) alpha Before proceeding, it would be prudent to bet-
7) beta ter organize the tool kit. For the sake of simplic-
8) gamma ity, the tools were initially presented as a collec-

Delphi Informant April 1996 12


Procedure Description Encapsulating the Test Driver
Figure 1: When writing test driver code for testing an object’s methods,
QAStart Procedure called to start a new Summary
test run, passing it an 8-character of the QA a convenient place to store that test driver is within the object
string to identify the test. itself as another method. Figure 3 shows the class definition
tool kit as
QALog Procedure called to directly presented for a sample object, called TVagueDate (its code, Listing Two,
record an entry in the log. in the first begins on page 17). This class definition includes a method,
part of SelfTest, that uses the new QA tool kit to exercise
QAsAssert Procedure called to assert an
equality between two string values. this series. TVagueDate’s other methods.

Object Element Description The TVagueDate object handles storing and processing
TQA fileQALog File handle for writing the test incomplete or non-specific dates. Such an object may be
results to disk (as an ASCII .TXT found as part of a scheduling or contact management appli-
file).
cation. TVagueDate allows dates to be specified with
sTestID 8-character test ID, used as the
name of the .TXT file. unknown portions. For example, you may know when a per-
FilePath Specifies the drive and/or sub- son celebrates a birthday (i.e. month and day), but not know
directory to contain the test result that person’s year of birth. Likewise, you may know the
log files.
month and year an event occurred, but not the specific day.
UseForm Set to True if the results will be
displayed on-screen using the
TFormQALog object. Figure 4 is a simplified form that is the front-end to a
UseFile Set to True if the results will be contact management database using TVagueDate. The
written out to disk according to birthday is stored in the database using a raw byte storage
the FilePath and sTestID fields
shown above. field that is 7 bytes long (the size of the combined data
Start Procedure called to start a new fields of the TVagueDate class). The form uses a display-
test run, passing it an 8-character only calculated field to display a text representation of the
string to identify the test.
VagueDate birthday that is stored in the byte field (via
Stop Procedure called to end a test run.
TVagueDate’s AsString property). In addition, a Specify
Log Procedure called to directly record
an entry in the log. button allows the birthday to be entered or changed, via
sAssert Procedure called to assert an the Vague Date Entry dialog box (see Figure 5).
equality between two string values.
bAssert Procedure called to assert an Another database field of type Date is automatically filled
equality between two Boolean
values. with a fully-specified approximation of the birthday using the
iAssert Procedure called to assert an AsDateTime property of TVagueDate. The Birthday field is set
equality between two integer to read-only on the form so that the user is forced to properly
values.
go through the Vague Date Entry dialog box to change it.
nAssert Procedure called to assert an
equality between two floating point
values. By entering records into the database and specifying vari-
TFormQALog memoQALog Memo component to display the ous birthdays, we can put the TVagueDate object through
results of a test run.
its paces. Such manual testing, however, is tedious, incon-
btnQAOK OK button to clear the window
(hide the form). sistent, and error prone. Thus, the form also features a
SelfTest button that calls TVagueDate’s SelfTest method. The
Figure 2: Summary of the new QA tool kit that is organized button is invisible when the application is not in test mode
around two objects: TQA (based on TObject) and TFormQALog
(based on TForm). (i.e. the compiler directive token QA_Mode is not defined,
as described in our first article).
tion of stand-alone functions (see Figure 1). A better way would
be to redefine those functions as the methods of an object. For The code in Figure 6 shows how TVagueDate’s SelfTest method
one thing, this allows for defining some data fields that the newer uses the AsString and AsDateTime properties (specifically their
methods need in common. Figure 2 shows this new organization. read methods, GetString and GetDateTime). Figure 7 shows
The associated code is shown in Listing One on page 16. the on-screen test results. So far, the only real difference
between this test driver and the one in last month’s article, is
A secondary object, a general-purpose form, has been defined that there are lines of code before each Assert call that are
for displaying the results of any test run. The form simply needed to set up for the assertion check. Primarily, though, we
consists of a Memo component that displays test results, and still are only verifying a function’s returned value.
an OK button that closes it. With this generic form, the tester
no longer needs to create a specific form for the task (as in last Testing More Complicated Functions
month’s article). Also, the various Assert methods have been Let’s move on to testing some more complicated functions where
written to report their findings through the Log method. Log, the function’s result value is not the only thing affected. For
in turn, has been modified to optionally handle writing those example, let’s say that the code changes the value of an object’s
findings to a disk file in addition to, or instead of, the screen. property, the value of a global variable, or the contents of a data-

Delphi Informant April 1996 13


In Development

TVagueness = (vdOn, vdAbout, vdBefore, vdAfter); {$IFDEF QA_MODE}


TVagueDate = class(TObject) procedure TVagueDate.SelfTest;
private var
iVagueness: TVagueness; QA: TQA;
iMonth,iDay,iYear: Word; begin
public QA := TQA.Create;
procedure InitBlank; with QA do
private begin
constructor Create; UseForm := TRUE;
function GetDT: TDateTime; FilePath := 'C:\QALOGS\';
function GetString: string; UseFile := TRUE;
procedure SetDT(dtFrom: TDateTime); Start('VAGUEDT');
procedure SetMonth(iValue: Word); Log('','TVagueDate Self-Test');
procedure SetDay(iValue: Word); Log('','--------------------');
procedure SetYear(iValue: Word);
procedure SetVagueness(iValue: TVagueness); InitBlank;
public Year:=1996; Month:=4; Day:=1;
property AsString: string read GetString; sAssert(AsString,'Apr 1st 1996');
property AsDateTime: TDateTime read GetDT write SetDT; nAssert(AsDateTime, EncodeDate(1996,4,1));
property Month: Word read iMonth write SetMonth; { Unknown Day }
property Day: Word read iDay write SetDay; Year:=1996; Month:=4; Day:=0;
property Year: Word read iYear write SetYear; sAssert(AsString,'Apr 1996');
property Vagueness: TVagueness read nAssert(AsDateTime, EncodeDate(1996,4,1));
iVagueness write SetVagueness;
procedure UpdateDlg; { Unknown Month & Day }
procedure GetData(var Buff: TVDBuff); Year:=1996; Month:=0; Day:=0;
procedure SetData(const Buff: TVDBuff); sAssert(AsString,'1996');
function AdjustDay: Boolean; nAssert(AsDateTime, EncodeDate(1996,1,1));
{$IFDEF QA_MODE}
procedure SelfTest; { Unknown Year }
{$ENDIF} Year:=0; Month:=4; Day:=2;
end; sAssert(AsString,'Apr 2nd');
nAssert(AsDateTime, EncodeDate(1996,4,2));
{ Additional tests here... }
Figure 3: The class definition for TVagueDate. Note the SelfTest Stop;
method that is used as a test driver to exercise the other methods end;
of the object. QA.Free;
Figure 4: A simplified end;
contact management {$ENDIF}
system that uses the
TVagueDate object for
tracking birthdays. The Figure 6 (Top): Excerpted
SelfTest button exe- code from
cutes the test driver TVagueDate.SelfTest.
associated with the
TVagueDate object. Figure 7 (Left): Screen
The button only output of a successful test
appears if the applica- driver execution. If any of
tion is compiled with the equality assertion tests
QA_Mode defined.
failed, then an X would
have appeared in place
Figure 5: A pop-up of the equal sign ( = ).
form that is provided
when calling the
UpdateDlg method of
TVagueDate. It allows
the user to specify a object and adjust them if necessary. For example, if the day is set
date with unknown
parameters.
to 31, but the month is set to February, AdjustDay will change
the day to either 28 or 29, depending if the year is a leap year.
base table or a disk file. Such code may be a function that only AdjustDay is a function that returns a Boolean value: True if the
returns a status flag, or a procedure that returns nothing at all. day had to be adjusted, False if not.

If a status flag is returned, it can certainly be tested using As before, to test this routine we’ll call it several times, setting it
bAssert, for example, which is used to test a Boolean value. As up with a series of sample data designed to exercise all the various
with sAssert, the first argument passed to bAssert is the actual combinations. We can check the Boolean result flag for an expect-
result of the function being tested, and the second argument is ed value, but that hardly tells us anything. How do we know that
the expected result (True or False). Another routine, iAssert, the routine actually adjusted anything (or properly refrained from
can be used to test functions that return an integer status flag. modifying anything)? The answer lies in using our tool kit to
make some secondary assertions. In this case, we’ll simply check
One of TVagueDate’s methods, AdjustDay, is used to check the the value of the day field directly to see if it’s correct. A portion of
combination of values currently defined by an instance of the the SelfTest method (see Figure 8) illustrates such test code.

Delphi Informant April 1996 14


In Development

Comparing Log Files


{ Month with 30 days }
InitBlank;
As the size of an application grows, the number and size of the
Year:=0; Month:=4; Day:=30; associated test drivers should also grow. After a while, running
bAssert(AdjustDay,FALSE); the test drivers one at a time and visually inspecting the results
Day:=31;
bAssert(AdjustDay,TRUE);
on the screen will become tiresome. If TQA.UseFile is set to
iAssert(Day,30); True, then the results of the various assertion checks will be
written to a disk file, according to the specified file path and
{ Leap year day }
Year:=1996; Month:=2; Day:=10;
test name. This makes it possible to run all the test drivers for
bAssert(AdjustDay,FALSE); an entire system consecutively and then inspect the results after-
Day:=31; wards, simultaneously.
bAssert(AdjustDay,TRUE);
iAssert(Day,29);
Furthermore, once the results are verified, the files can be
{ Non-leap year } copied to another subdirectory for future reference as a base-
Year:=1995;
bAssert(AdjustDay,TRUE);
line. Thereafter, visually inspecting the results of subsequent
iAssert(Day,28); test runs would be unnecessary. Instead, the newly generated
set of files could be compared to the baseline copies using an
Figure 8: Additional test code to be included in ASCII file comparison utility program. A simple comparison
TVagueDate.SelfTest. This code exercises the AdjustDay method. utility, FC.EXE, is provided with MS-DOS (including
Since the Day property is changed as a ramification of the Windows 95). Some comparison utilities are bundled with ver-
AdjustDay method, the value of the property is checked in addition
to checking the result code returned by the method.
sion control software packages, and others are available from
programmer catalogs, as well as through many online services.

Similarly, if the function being tested involves adding records Conclusion


to a table, then an appropriate secondary assertion might be With the proper tools and planning, assuring software quality
to check the table’s size. Directly searching the table for a spe- can be relatively painless. We’ve seen how to systematically
cific record — to ensure that it does or does not exist — may develop unit test drivers that are machine-executable, and
be a better method. If the function causes a global variable to therefore easily repeatable, and we built a QA tool kit to
change, or otherwise changes the state of the system, then accommodate the task.
that too could be checked directly (or indirectly).
The final installment in this series will discuss other commercially
QA Impact on System Design available testing software that can be used with Delphi programs. ∆
As previously stated, a good software engineer knows how a
system will be tested before a single line of code is written. The demonstration project referenced in this article is available
This is because there are times when testing issues can have on the Delphi Informant Works CD located in
direct bearing on the system’s design. Let’s say that a test dri- INFORM\96\APR\DI9604Q2.
ver must be written for a function that uses the current system
date as a factor (e.g. for computing a person’s age or calculat-
ing a depreciation). We need to call this function several times
Craig Jones is a contract software engineer in Southern California, with over 14
within the driver and be able to declare the expected results. years of programming and consulting experience. He is the programming standards
However, this is impossible if the test driver is to be reusable SIG leader for the Orange County Delphi Users Group. He is also a member of Team
in the future when the current date will differ. Furthermore, Borland, supporting Paradox and Delphi on the GEnie network. Mr Jones can be
we really ought to test for significant upcoming dates such as reached at [email protected] or on CompuServe at 71333,3515.
month-end, year-end, and the turning of the millennium.

One solution is to define a global variable that represents the


current system date (e.g. SysCtrls.Today), initializing it once at
startup, and then always referring to it within the application
instead of directly calling the Date function. During normal
operation, this variable is set only once at startup and then left
alone, but during testing we are free to change it at will. Some
other system factors that could be handled in this fashion
include: user information (full name, login ID, initials), work-
station information (node address, local vs. remote), and print-
er connection information.

Delphi Informant April 1996 15


In Development

Begin Listing One — UT1QA.PAS var


{ Project: UT1 - General Application Utilities sLine: string;
Function: Quality Assurance Tool Kit } begin
unit Ut1qa; if (sActual = sExpected) then
{$I UT1Incl.PAS} Log('= ',sActual);
else begin
interface Log('X ',sActual);
Log(' ',sExpected);
uses end;
SysUtils, WinTypes, WinProcs, Messages, Classes, end;
Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
{ Assert that two Boolean values are equal. bActual =
type value to test. bExpected = what tested value should be. }
TformQALog = class(TForm) procedure TQA.bAssert(bActual, bExpected: Boolean);
memoQALog: TMemo; var
btnQAOK: TButton; sLine, sActual, sExpected: string;
procedure btnQAOKClick(Sender: TObject); begin
end; if bActual then
sActual := 'true'
TQA = class else
private sActual := 'false';
fileQALog: TextFile; if bExpected then
sTestID: string[8]; sExpected := 'true'
public else
FilePath: string[79]; sExpected := 'false';
UseForm, UseFile: Boolean; if (bActual = bExpected) then
procedure Start(sTestName: string); Log('= ',sActual);
procedure Stop; else
procedure Log(sPrefix, sMessage: string); Log('X ',sActual+' -- expected: '+sExpected);
procedure sAssert(sActual, sExpected: string); end;
procedure bAssert(bActual, bExpected: Boolean); end;
procedure iAssert(iActual, iExpected: Integer);
procedure nAssert(nActual, nExpected: Double); { Assert that two integers are equal. iActual = the value
end; to test. iExpected = what the tested value should be. }
procedure TQA.iAssert(iActual, iExpected: Integer);
var var
formQALog: TformQALog; sLine: string;
QA: TQA; begin
if (iActual = iExpected) then
implementation Log('= ',IntToStr(iActual));
else
{$R *.DFM} Log('X ',IntToStr(iActual) +
' -- expected: ' + IntToStr(iExpected));
{ Start test run. sTestName = Identifier for the test. } end;
procedure TQA.Start(sTestName: string); end;
begin
Screen.Cursor := crHourglass; { Assert that two real numbers are equal. nActual = the
sTestID := sTestName; value to test. nExpected = what tested value should be. }
if UseForm then begin procedure TQA.nAssert(nActual, nExpected: Double);
formQALog.Caption := sTestID+' Test Sequence'; var
formQALog.Show; sLine: string;
with formQALog.memoQALog do begin begin
Lines.Clear; if (nActual = nExpected) then
Font.Color := clNavy; Log('= ',Format('%n',[nActual]))
Font.Name := 'Courier New'; else
Font.Size := 8; Log('X ',Format('%n',[nActual])+' -- expected: ' +
end; Format('%n',[nExpected]));
end; end;
if UseFile then begin
AssignFile(fileQALog, FilePath + sTestID + '.TXT'); procedure TQA.Stop; { End a test run }
{ Erase the file, if not already empty. } begin
Rewrite(fileQALog); if UseForm then
formQALog.Caption:=
sTestID+' Test Sequence Completed';
end;
end; if UseFile then
Close(fileQALog);
{ Log message during test run. sMessage = message to log. } Screen.Cursor := crDefault;
procedure TQA.Log(sPrefix, sMessage: string); end;
begin
if UseForm then { Hide the QA results form }
formQALog.memoQALog.Lines.Add( procedure TformQALog.btnQAOKClick(Sender: TObject);
Format('%-2.2s',[sPrefix]) + sMessage); begin
if UseFile then {$IFDEF QA_MODE}
WriteLn(fileQALog, sPrefix+sMessage) formQALog.Hide;
end; {$ENDIF}
end;
{ Assert that two strings are equal. sActual = the value
to test. sExpected = what the tested value should be. } end.
procedure TQA.sAssert(sActual, sExpected: string); End Listing One

Delphi Informant April 1996 16


In Development

Begin Listing Two — CM1Vague.PAS { Copy data to an external buffer. Parameter: 7-byte
{ Project: CM1 - Contact Manager Example Application buffer in which to place the data. }
Function: Object to represent vaguely specified dates procedure TVagueDate.GetData(var Buff: TVDBuff);
(e.g. "After Apr 1996") } begin
unit Cm1vague; Move(iVagueness,Buff,SizeOf(TVDBuff));
{$I UT1Incl.PAS} end;
interface
{ Load the data from an external buffer. Parameter:
uses 7-byte buffer with the source data. }
SysUtils, WinTypes, WinProcs, Messages, Classes, procedure TVagueDate.SetData(const Buff: TVDBuff);
Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, begin
UT1App, UT1QA;
Move(Buff,iVagueness,SizeOf(TVDBuff));
const end;
MonthList = 'JanFebMarAprMayJunJulAugSepOctNovDec';
{ Build a string representation of the date }
type function TVagueDate.GetString: string;
{ Holding buffer for TVagueDate data } var
TVDBuff = array[0..6] of Byte; sVagueness,sMonth,sDay,sYear: string;
TVagueness = (vdOn, vdAbout, vdBefore, vdAfter); begin
sVagueness := ''; { Initialize }
sMonth := '';
TVagueDate = class(TObject)
private sDay := '';
iVagueness: TVagueness; sYear := '';
iMonth,iDay,iYear: Word; case iVagueness of { Vagueness prefix }
public vdAbout: sVagueness := 'About ';
procedure InitBlank; vdBefore: sVagueness := 'Before ';
private vdAfter: sVagueness := 'After ';
function GetDT: TDateTime; end;
function GetString: string; { Month, e.g. Oct }
procedure SetDT(dtFrom: TDateTime); if (iMonth >= 1) and (iMonth <= 12) then
procedure SetMonth(iValue: Word); sMonth := copy(MonthList,iMonth*3-2,3)+' ';
procedure SetDay(iValue: Word); case iDay of { Day, e.g. 31st }
procedure SetYear(iValue: Word); 0: sDay := '';
procedure SetVagueness(iValue: TVagueness); 1,21,31: sDay := inttostr(iDay)+'st ';
public 2,22: sDay := inttostr(iDay)+'nd ';
property AsString: string read GetString; 3,23: sDay := inttostr(iDay)+'rd ';
property AsDateTime: TDateTime read GetDT write SetDT; else sDay := inttostr(iDay)+'th ';
property Month: Word read iMonth write SetMonth; end;
property Day: Word read iDay write SetDay; if iYear>0 then { Year, 4 digits }
property Year: Word read iYear write SetYear; fmtstr(sYear,'%4.4d ',[iYear]);
property Vagueness: TVagueness read iVagueness write { Combine them }
SetVagueness; result := sVagueness + sMonth + sDay + sYear;
procedure UpdateDlg; while (length(result)>0) and
procedure GetData(var Buff: TVDBuff); (copy(result,length(result),1) = ' ') do
procedure SetData(const Buff: TVDBuff); delete(result,length(result),1);
function AdjustDay: Boolean; end;
{$IFDEF QA_MODE}
procedure SelfTest; { Build a specific approximation of the date }
{$ENDIF} function TVagueDate.GetDT: TDateTime;
end; var
iM, iD, iY: Word; { Working copies }
TformVagueDate = class(TForm) iThisMonth, iThisDay, iThisYear: Word; { Today }
radioVagueness: TRadioGroup; bConverted: Boolean; { Conversion success flag }
btnOK: TButton; begin { -- Initialize -- }
boxMDY: TGroupBox; iM := iMonth;
editMonth: TEdit; iD := iDay;
editDay: TEdit; iY := iYear;
editYear: TEdit; if (iM<1) then begin { Month }
btnCancel: TButton; case iVagueness of
procedure btnOKClick(Sender: TObject); vdBefore: iM := 1;
procedure btnCancelClick(Sender: TObject); vdAfter: iM := 12;
end; else iM := 1; { Use July 1st? }
end;
var end;
formVagueDate: TformVagueDate; if (iD<1) then begin { Day }
case iVagueness of
implementation vdBefore: iD := 1;
vdAfter: iD := 31;
{$R *.DFM} else iD := 1; { Use the 15th? }
{ Initialize a blank vague date } end;
procedure TVagueDate.InitBlank; end;
begin if (iY<1) then begin { Year }
iMonth := 0; DecodeDate(AppCtrl.dtToday,iThisYear,
iDay := 0; iThisMonth,iThisDay);
iYear := 0; iY := iThisYear;
iVagueness := vdOn; end;
end; { Combine them }
bConverted := FALSE;

Delphi Informant April 1996 17


In Development

while (not bConverted) do begin end;


try end;
Result := EncodeDate(iY,iM,iD); end;
bConverted := TRUE;
except on EConvertError do begin { Directly set the year }
if iD > 28 then begin procedure TVagueDate.SetYear(iValue: Word);
iD := iD -1; begin
Continue; if (iValue>=1) and (iValue<=9999) then
end; iYear := iValue
Result := EncodeDate(1,1,1); else
bConverted := TRUE; iYear := 0;
end; { EConvertError } end;
end; { try }
end; { while } { Directly set the vagueness factor }
end; procedure TVagueDate.SetVagueness(iValue: TVagueness);
begin
{ Load the date from a TDateTime record } iVagueness := iValue;
procedure TVagueDate.SetDT(dtFrom: TDateTime); end;
begin
DecodeDate(dtFrom,iYear,iMonth,iDay); { Pop up a dialog box to enter vague date info }
iVagueness := vdOn; procedure TVagueDate.UpdateDlg;
end;
var
iTemp: Word;
{ Directly set the month } begin
procedure TVagueDate.SetMonth(iValue: Word); with formVagueDate do begin
begin { Initialize the dialog box fields: Month }
if (iValue>=1) and (iValue<=12) then if (iMonth > 0) then
iMonth := iValue editMonth.Text := IntToStr(iMonth)
else else
iMonth := 0; editMonth.Text := '';
end; if iDay > 0 then { Day }
editDay.Text := IntToStr(iDay)
{ Directly set the day } else
procedure TVagueDate.SetDay(iValue: Word); editDay.Text := '';
begin if iYear > 0 then { Year }
if (iValue>=1) and (iValue<=31) then editYear.Text := IntToStr(iYear)
iDay := iValue else
else editYear.Text := '';
iDay := 0; { Vagueness }
end; radioVagueness.ItemIndex := Ord(iVagueness);
ShowModal;
{ Adjust the day to agree with the month and/or year } { -- Save the changes (if Okayed) -- }
function TVagueDate.AdjustDay: Boolean; if ModalResult = mrOK then begin
var try { Save the month }
bEncoded: Boolean; iTemp := StrToInt(editMonth.Text);
iMaxDay: Word; except on EConvertError do iTemp := 0;
begin end;
Result := FALSE; { Assume no adjustment necessary } if (iTemp<1) or (iTemp>12) then
if (iDay>28) and (iMonth>0) then begin iTemp := 0;
if (iYear>0) then begin iMonth := iTemp;
{ Encode full date and adjust until it works } try { Save the day }
bEncoded := FALSE; iTemp := StrToInt(editDay.Text);
while (not bEncoded) do begin except on EConvertError do iTemp := 0;
try end;
EncodeDate(iYear,iMonth,iDay); if (iTemp<1) or (iTemp>31) then
{ If we get this far, the encode worked } iTemp := 0;
bEncoded := TRUE; iDay := iTemp;
except on EConvertError do begin try { Save the year }
if iDay > 28 then begin iTemp := StrToInt(editYear.Text);
iDay := iDay-1; except on EConvertError do iTemp := 0;
Result:=TRUE; end;
if (iTemp<1) or (iTemp>9999) then
end; iTemp := 0;
if iDay > 28 then iYear := iTemp;
Continue; { Try again } { Save the vagueness }
bEncoded := TRUE; iVagueness := TVagueness(radioVagueness.ItemIndex);
end; { EConvertError } end; { Changes okayed }
end; { try } end; { with }
end; { while } end;
end else begin
case iMonth of { Unknown year, so go by month } {$IFDEF QA_MODE}
2: iMaxDay := 29; procedure TVagueDate.SelfTest; { Test driver }
4,6,9,11: iMaxDay := 30; var
else iMaxDay := 31; dtTemp: TDateTime;
end; begin
if iDay > iMaxDay then begin QA := TQA.Create;
iDay := iMaxDay; with QA do begin
Result := TRUE; { Adjustment made } UseForm := TRUE;
end; FilePath := 'C:\QALOG\';

Delphi Informant April 1996 18


In Development

UseFile := FALSE; nAssert(AsDateTime,EncodeDate(1801,4,1));


Start('VAGUEDT'); Year:=2000; Month:=4; Day:=1;
Log('','TVagueDate Self-Test'); sAssert(AsString,'About Apr 1st 2000');
Log('','--------------------'); nAssert(AsDateTime,EncodeDate(2000,4,1));
{ -- Vagueness = "On" -- } Year:=2001; Month:=4; Day:=1;
Log('','----- On -----'); sAssert(AsString,'About Apr 1st 2001');
InitBlank; Vagueness:=vdOn; nAssert(AsDateTime,EncodeDate(2001,4,1));
Year:=1996; Month:=4; Day:=1; { -- Adjust Day Function -- }
sAssert(AsString,'Apr 1st 1996'); Log('','----- AdjustDay Function -----');
nAssert(AsDateTime,EncodeDate(1996,4,1)); { Month with 30 days }
{ Unknown Day } InitBlank;
Year:=1996; Month:=4; Day:=0; Year:=0; Month:=4; Day:=30;
sAssert(AsString,'Apr 1996'); bAssert(AdjustDay,FALSE);
nAssert(AsDateTime,EncodeDate(1996,4,1)); Day:=31;
{ Unknown Month & Day } bAssert(AdjustDay,TRUE);
Year:=1996; Month:=0; Day:=0; iAssert(Day,30);
sAssert(AsString,'1996'); { Leap year day }
nAssert(AsDateTime,EncodeDate(1996,1,1)); Year:=1996; Month:=2; Day:=10;
{ Unknown Year } bAssert(AdjustDay,FALSE);
Year:=0; Month:=4; Day:=2; Day:=31;
sAssert(AsString,'Apr 2nd'); bAssert(AdjustDay,TRUE);
nAssert(AsDateTime,EncodeDate(1996,4,2)); iAssert(Day,29);
{ -- Vagueness = "After" -- } { Non-leap year }
Log('','----- After -----'); Year:=1995;
InitBlank; Vagueness:=vdAfter; bAssert(AdjustDay,TRUE);
Year:=1996; Month:=4; Day:=1; iAssert(Day,28);
sAssert(AsString,'After Apr 1st 1996'); Stop;
nAssert(AsDateTime,EncodeDate(1996,4,1)); end;
{ Unknown Day } QA.Free;
Year:=1996; Month:=4; Day:=0;
sAssert(AsString,'After Apr 1996'); end;
nAssert(AsDateTime,EncodeDate(1996,4,30)); {$ENDIF}
{ Unknown Month & Day }
Year:=1996; Month:=0; Day:=0; { Close (OK) the dialog box }
sAssert(AsString,'After 1996'); procedure TformVagueDate.btnOKClick(Sender: TObject);
nAssert(AsDateTime,EncodeDate(1996,12,31)); begin
{ Unknown Year } ModalResult := mrOK;
Year:=0; Month:=4; Day:=2; end;
sAssert(AsString,'After Apr 2nd');
nAssert(AsDateTime,EncodeDate(1996,4,2)); { Cancel the dialog box }
{ -- Unusual Years -- } procedure TformVagueDate.btnCancelClick(Sender:
Log('','----- Unusual Years -----'); TObject);
InitBlank; Vagueness:=vdAbout; begin
Year:=1; Month:=4; Day:=1; ModalResult := mrCancel;
sAssert(AsString,'About Apr 1st 0001'); end;
nAssert(AsDateTime,EncodeDate(1,4,1));
Year:=101; Month:=4; Day:=1; end.
sAssert(AsString,'About Apr 1st 0101'); End Listing Two
nAssert(AsDateTime,EncodeDate(101,4,1));
Year:=1801; Month:=4; Day:=1;
sAssert(AsString,'About Apr 1st 1801');

Delphi Informant April 1996 19


On the Cover
Delphi 1.0 / Delphi 2.0 / Object Pascal

By Cary Jensen, Ph.D.

Sharing Components
Sharing Objects Between Forms:
Techniques for Delphi 1.0 and 2.0

T he properties of an object are sometimes objects themselves. For exam-


ple, the DataSet property of a DataSource component is a property of
the type TDataSet. This means that you can assign an object of type
TDataSet (or one of its descendants) to this property.
In most cases, Delphi makes it easy to assign Sharing Objects between
a value to an object property. In the Object Forms in Delphi 1.0
Inspector, when you click the down arrow of A DataSet descendant (a Table, Query, or
an object property, Delphi displays a list of StoredProc component) defined on one form
all objects defined for that form that are of will never appear in the drop-down list for
the specified type (or a descendant of it). the DataSet property of a DataSource defined
With the DataSet property of a DataSource on another form. This merely means that this
component, for example, the drop-down property cannot be set at design time.
menu for the property contains all DataSet
descendants defined for the form. Fortunately, however, it can be assigned at run
time. Doing so allows you to share DataSet
While this list is convenient, it does not nec- and DataSource components across multiple
essarily contain all objects that can be forms, permitting the forms to also share the
assigned to that property. Actually, there may same Table component (or other DataSet), and
be objects defined within a project that are of consequently, a common cursor to the table.
the appropriate type, but they do not appear
on the displayed list for a particular property. This valuable technique is simply a specific
application of the more general capability of
Specifically, an object that is of the appropriate assigning to properties, objects and methods
type, but is declared as part of another form in contained in another unit. In other words,
the project, will not appear in the property’s although this technique is demonstrated here to
drop-down list, even though it’s a valid object show two forms sharing a common cursor, it
for that property. For example, if Form1 con- could just as easily be used to let two forms
tains a Table component, the DataSet property share any other type of object, or even event
of a DataSource defined on Form2 can be handlers. Now that we’ve got the basic concepts
assigned this Table from Form1, even though outlined, it’s time to build an interactive exam-
the Table will never appear in the drop-down ple. Carry out the following instructions to cre-
list for the DataSource’s DataSet property. ate two forms that share a common DataSet.

This article describes how to assign an object Building Form1


defined on one form (or unit) to the object Begin by creating a new project. On the
property of an object defined on another displayed form, add the following compo-
form. For Delphi 1.0, this technique requires nents: a DataSource, a Table, a MainMenu,
that you add Object Pascal code. Performing and a DBGrid. Set the DataSet property of
this task is even easier in Delphi 2.0. Let’s take the DataSource to Table1. For the Table
them in order. (And please keep in mind that component, set the DatabaseName property
the Delphi 2.0 information in this article is to DBDEMOS, the TableName property to
based on a pre-release version of that product. CUSTOMER.DB, and the Active property to
Behavior in the shipping version may differ.) True. Set the DBGrid’s DataSource property

Delphi Informant April 1996 20


On the Cover

Figure 2 (Top):
Figure 1: A form under construction. A menu for
Form1.
to DataSource1 and its Align property to alClient. Your form
should now resemble Figure 1.
Figure 3 (Left):
A single-record
Now double-click the MainMenu component and create two form created
main menu items, &File and &View. Add an E&xit option using the
under the &File menu, and add an &Single Record item Database Form
under the &View menu. Your menu should resemble Figure 2. Expert.
Close the Menu Designer and enter the following statement
in the OnClick event handler for the MenuItem Exit1:

Close;
On the fourth page, set the field layout to Vertical and
click Next to advance.
Then enter the following statement in the OnClick event
On the fifth page, select Left to display field labels to the
handler for SingleRecord1:
left of the fields, and then click Next.
Form2.Show;
Finally, on the sixth page, remove the check from the
Generate a main form check box. Click the Create button
Now, while we’re still working with Unit1, we’ll need to add to build the form shown in Figure 3.
Unit2 to a uses statement in Unit1 (we’ll create the second
form shortly). In the implementation section of Unit1, add Since the purpose of this demonstration is to use Table1
the following uses clause: from Form1 in Form2, remove the Table component from
Form2.
uses
Unit2; The final two steps require code to be placed in Unit2 to
assign Table1 from Form1 to the DataSet property of
The completed Unit1 is shown in Listing Three on page 23. DataSource1 on Form2. (If you’re working in Delphi 2.0,
replace these final two steps with the ones under the section
Building Form2 “Sharing Objects Between Forms in Delphi 2.0.”) Begin by
Now, we’ll create Form2 that will be a single record form in selecting Form2 in the Object Inspector and displaying the
this project. A single record form — where individual fields Events page. Then, double-click the OnCreate event proper-
are displayed using DBEdit components — is easier to create ty to display Form2’s OnCreate event handler. It already
with the Database Form Expert. contains this statement:

Select Help | Database Form Expert from Delphi’s menu to Table1.Open;


access this tool. Then follow these steps:
On the first page, set Form Options to Create a simple Delete this code and replace it with the following line:
form, and DataSet Options to Create a form using TTable
objects. Click the Next button. DataSource1.DataSet := Form1.Table1;

On the second page, set the Drive or Alias Name combo


box to DBDEMOS, choose CUSTOMER.DB from the Table Finally, add a uses clause to Unit2 so that it can refer to the
Name list, and click Next. objects declared in Form1. Within Unit2’s implementation
On the third page, click on the double right arrow ( >> ) section, add:
to move all fields from the Available Fields list to the uses
Ordered Selected Fields list, and click Next. Unit1;

Delphi Informant April 1996 21


On the Cover

Figure 5: The Use Unit dia-


log box permits you to choose
a unit whose interface
declarations are available
to the current form.

With Form2 selected, select File | Use Unit to display the


Use Unit dialog box (see Figure 5). Select Unit1 and then
click OK.
Figure 4: The project Share1.DPR. Form1 and Form2 both use
Select DataSource1 on Form2 and then display the Object
the Table component defined in Form1. As a result, both forms Inspector. Open the drop-down list for the DataSet proper-
share a common cursor, which causes them to remain synchro- ty of DataSource1. As shown in Figure 6, Table1, a DataSet
nized, regardless of which form you use to navigate the table. descendant defined on Form1, is displayed in this list.
Select Form1.Table1 to assign Table1 from Form1 to the
The completed Unit2 should now resemble Listing Four on DataSet property of the DataSource1 object on Form2. This
page 23. project can now be run to produce synchronized forms.

You’re done! Run this project and select View | Single The only limitation to the Use Unit feature in Delphi 2.0
Record to display Form2. With both forms displayed, notice is that it does not allow you to select event handlers
that as you navigate one form, the other automatically defined in another unit at design time. This is, however,
remains synchronized, with both forms always showing the much less of a common need than assigning objects to
same record (see Figure 4). This is because the forms share object properties.
the same DataSet, Form1.Table. Thus, they share a cursor.
Using Data Modules
Sharing Event Handlers In addition to a form, which can hold visual as well as non-
We’ve demonstrated that it’s possible to assign a value defined visual objects, Delphi 2.0 supports a non-visual version of a
in another unit to a component’s property. As mentioned, this form, called a data module. One of the primary uses for a
same technique can be used to assign an event handler defined data module is to hold DataSource and DataSet objects that
in another unit to the event property of an object. For exam- can be used by multiple forms. The advantage of a data mod-
ple, if you create a button named Button1 on Form2, you can ule over a regular form is that it takes fewer resources because
add the following statement to the OnCreate event handler for it does not have a visual representation.
Form2 to call the OnClick event handler for Exit1 from Form1
when Button1 is clicked: The use of a data module is demonstrated in the project
Sharedm.DPR. This project is similar to the one shown in
Button1.OnClick := Form1.Exit1Click; Figure 4, with one important exception — neither Form1 nor
Form2 contains any DataSource or DataSet components.
Instead of assigning the procedure name defined in Unit1 to the Instead, a data module was created by selecting File | New
Button’s OnClick event handler, you can alternatively assign the Data Module. This data module, named DataModule1, is
OnClick property of the MenuItem, Exit1, to this property. The shown in Figure 7.
following statement is functionally identical to the preceding one:
Both Form1 and Form2 must use the unit associated with
Button1.OnClick := Form1.Exit1.OnClick; the data module to use the components placed in the data
module. This is accomplished by selecting Form1, selecting
With this button added to Form2, clicking it causes Form1 to File | Use Unit, and then selecting the data module’s unit
close. As a result, Form2 closes as well.

Sharing Objects between Forms in Delphi 2.0


While it’s not difficult to share objects between forms in
Delphi 1.0, it’s even easier with Delphi 2.0. This is
because Delphi 2.0 includes a feature that permits one
unit to use another unit’s declarations. Importantly, this
feature in Delphi 2.0 permits you to set object properties
at run time.
Figure 6 (Left): Because Unit2 uses Unit1, a Table component
declared in Unit1 can be assigned to the DataSet property of
You can demonstrate how easy this is in Delphi 2.0. Create a
DataSource1 on Form2 at design time. Figure 7 (Right): A data
new project by following all but the last two steps of the pre- module holding the DataSource and DataSet components used by
ceding example. Then, continue as follows: the Sharedm.DPR project.

Delphi Informant April 1996 22


On the Cover
from the Use Unit dialog box. This process must be repeat-
procedure TForm1.SingleRecord1Click(Sender: TObject);
ed for Form2. begin
Form2.Show;
Conclusion end;

Using Delphi 1.0, the objects and event handlers defined end.
with one unit can be used by objects on other forms by End Listing Three
adding a small amount of code that makes the necessary
property assignments at run time. Begin Listing Four — Unit2.PAS
unit Unit2;

With Delphi 2.0, the Use Unit dialog box enables you to interface
assign object properties at design time as well, greatly simpli-
uses
fying the process of using objects across forms. Finally, the SysUtils, WinTypes, WinProcs, Messages, Classes,
ability to define data modules — non-visual, form-like Graphics, Controls, StdCtrls, Forms, DBCtrls,
DB, DBTables, Mask, ExtCtrls;
objects — permits database developers to separate data asso-
ciations from the forms used to display the data. ∆ type
TForm2 = class(TForm)
ScrollBox: TScrollBox;
The demonstration forms referenced in this article are available Label1: TLabel;
on the Delphi Informant Works CD located in EditCustNo: TDBEdit;
Label2: TLabel;
INFORM\96\APR\DI9604CJ. EditCompany: TDBEdit;
Label3: TLabel;
EditAddr: TDBEdit;
Label4: TLabel;
Cary Jensen is President of Jensen Data Systems, Inc., a Houston-based database EditAddr2: TDBEdit;
development company. He is author of more than a dozen books, including the Label5: TLabel;
EditCity: TDBEdit;
upcoming Delphi in Depth [Osborne, MacGraw-Hill, 1996]. He is also Contributing Label6: TLabel;
Editor of Paradox Informant and Delphi Informant, and this year’s Chairperson of the EditState: TDBEdit;
Paradox Advisory Board for the upcoming Borland Developers Conference. You can Label7: TLabel;
reach Jensen Data Systems at (713) 359-3311, or on CompuServe at 76307,1533. EditZip: TDBEdit;
Label8: TLabel;
EditCountry: TDBEdit;
Label9: TLabel;
Begin Listing Three — Unit1.PAS EditPhone: TDBEdit;
unit Unit1;
Label10: TLabel;
EditFAX: TDBEdit;
interface
Label11: TLabel;
EditTaxRate: TDBEdit;
uses
Label12: TLabel;
SysUtils, WinTypes, WinProcs, Messages, Classes,
EditContact: TDBEdit;
Graphics, Controls, Forms, Dialogs, Grids, DBGrids, DB;
Label13: TLabel;
EditLastInvoiceDate: TDBEdit;
type
DBNavigator: TDBNavigator;
TForm1 = class(TForm)
Panel1: TPanel;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
DataSource1: TDataSource;
Panel2: TPanel;
Table1: TTable;
Table1: TTable;
MainMenu1: TMainMenu;
procedure FormCreate(Sender: TObject);
File1: TMenuItem;
private
Exit1: TMenuItem;
{ Private declarations }
View1: TMenuItem;
public
SingleRecord1: TMenuItem;
{ Public declarations }
procedure Exit1Click(Sender: TObject);
end;
procedure SingleRecord1Click(Sender: TObject);
private
var
{ Private declarations }
Form2: TForm2;
public
{ Public declarations }
implementation
end;
{$R *.DFM}
var
Form1: TForm1;
uses
Unit1;
implementation
procedure TForm2.FormCreate(Sender: TObject);
{$R *.DFM}
begin
DataSource1.DataSet := Form1.Table1;
uses
end;
Unit2;
end.
procedure TForm1.Exit1Click(Sender: TObject);
begin
End Listing Four
Close;
end;

Delphi Informant April 1996 23


Delphi C/S
Delphi / Object Pascal / SQL servers / Paradox

By Bill Todd

Design for Upsizing


Making It Easier to Move Delphi Database Applications
from Paradox to Client/Server

s more organizations begin the move to client/server computing, the


A odds are increasing that your Delphi applications that use local Paradox
and dBASE tables for data storage will have to be changed to work with
data stored on a SQL database server. There are a number of things that
you can do when you write a local table application to make the move to
client/server easier.
Some of the first problems you’ll encounter You can add this unit to the uses clause of
are that the rules for naming tables, indexes, every unit where you need to use a table name
and columns are different on SQL servers. or index name in your code and use the con-
Here are some incompatibilities you may face. stant instead of a literal. This means that if the
names of tables or indexes change when you
Table names. Servers will not accept a table move to a server, you need only change the
name that contains a period. For example, if constant declaration in one place, recompile,
you have a Paradox table named CUS- and you’re ready to go.
TOMER.DB you won’t be able to create a
table with the same name on a server. The Using a Globals unit is good practice for any
closest you could get would be a table named fixed value. By defining a value in a single place
“Customer”. and referencing it by the constant name, you
not only make your code more readable, but
There are several ways to deal with table you make changing any constant value easy.
name incompatibilities. The first is to add a
unit to your project that’s sole purpose is to Index names. All indexes on a SQL server
hold constants that are used throughout your must have a name. However, the primary
program. Define a constant for each table index of a Paradox table does not have a name.
name and index name. The unit would then To get around this, specify the primary index
resemble the Globals unit shown in Figure 1. of a Paradox table in Delphi as a null string:
unit Globals;
CustTbl.IndexName := ''

interface
Then, when the transition to a SQL server is
const
made, you can modify the constant without hav-
{ Tables }
tnCustomer = 'customer.db'; ing to revisit every reference to the primary key.
tnOrders = 'orders.db';
tnInvoice = 'invoice.db';
Column names. Column names in Paradox
tnItems = 'items.db';
{ Indices } tables can include spaces and punctuation
inCustCustNo = ''; characters. RDBMS column names are typi-
inCustName = 'LastFirst';
cally restricted to letters, numbers, and the
inOrderOrderNo = '' ;
inOrderCustNo = 'CustNo'; underscore character.

implementation
The only way to avoid column name incom-
end. patibilities is to use column names in your
Figure 1: The Globals unit.
local tables that will be acceptable to most

Delphi Informant April 1996 24


Delphi C/S
servers. This means that column names should start with a let- TDatabase: Easing Migration to C/S
ter, include only the letters A through Z, the digits 0 through Using a TDatabase component in your application will not only
9, and the underscore character. make conversion to client/server easier, but also offers several
benefits while you are using local tables. When moving your
A Property Value Problem data to a server, you’ll need a Database component to maintain
Using constants solves the problem when table or index names your connection to the server, provide explicit transaction con-
are used in code. However, you still need to deal with table trol through its StartTransaction, Commit, and Rollback meth-
and index names that are used as property values for TTables ods, and specify the transaction isolation level on the server.
and other components. One technique that works in all situa-
tions is to assign the values of all table and index properties in To use a Database component:
the form’s OnCreate event handler using the constants from 1) Add it to your main form.
the Globals unit described earlier. 2) Set the AliasName property to the alias that contains your
data.
However, there’s one problem with assigning table names in your 3) Set the DatabaseName property to the name of the tem-
form’s OnCreate event handler. If the TableName property is not porary alias you will use in your project.
assigned at design time, you won’t be able to see live data or 4) Set the transaction isolation level if you want a level other
open the Fields editor to instantiate field objects for your tables. than the default of tiReadCommitted.
5) Set the Connected property to True either at design time
Here’s another alternative that you can use with TTable com- or in your main form’s OnCreate event handler.
ponents for the table name. Use the name of the table with 6) Use the temporary alias you assigned to the
no extension as the value for the component’s TableName DatabaseName property everywhere else in your project
property. Normally, the TableType property is set to ttDefault where an alias is required.
and Delphi determines the table’s type from the file extension
of the TableName property. With the Database component already in place, you can move
to data on a server by simply running the BDE Configuration
If you omit the extension from the TableName property you Utility, and changing the definition of the alias used in the
must also set the TableType property to either ttParadox or Database component’s AliasName property. You must do this
ttDbase so Delphi will know the table’s type. This will make the so that the alias points to the database on the server instead of
move to a database server easy, provided you can create the to a subdirectory on your hard disk or file server.
tables on the server with the same names you use for your local
tables. You do not have to change the TableType property back While using local tables you can specify the path to the direc-
to ttDefault when moving your data to a server. Delphi only tory that contains your tables instead of setting the AliasName
uses the TableType for local tables. property. To use a path, set the DriverName property to
Standard and add the path parameter to the Database com-
Note, however, that this only works for table names. The only ponent’s Params property.
solution for the primary index of a Paradox table is to assign the
value in the form’s OnCreate event handler from a constant. The The path parameter takes the form:
index name must change when you move to a server; no server
will let you create an index with a null name. PATH=C:\DIR1\DIR2

A TQuery Problem and can be in either upper- or lower-case. Figure 2 shows an


TQuery components also present a problem if the SQL state- example of setting the path parameter at design time using
ment contains a table name that includes a file extension. the String list editor. This is a particularly useful technique
Fortunately, in queries you can omit the file extension and not for a program that you will distribute to many sites because it
worry about the table type. The query will first search for the avoids having to define a permanent alias as part of the instal-
table with a .DB extension. If the query does not find a lation procedure.
Paradox table, it will then search for a file with a .DBF exten-
sion (i.e. a dBASE table). You can obtain the path from the command line, or an .INI
file. If you put the .EXE file in the same directory as the
Alternatively, you can also assign those lines of the SQL state- tables, you can obtain the path from the Application.ExeName
ment that include table names in the form’s OnCreate event property. Just remember to remove the .EXE file’s name from
handler by using the constants from the Globals unit. For the end of the string so you are left with just the path to the
example, the following statement will create the FROM clause directory containing the .EXE file.
of a SQL query from a constant by assigning a new string to
the fourth line of the query (remember that the first line is 0): Passing either the path to the data files, or the value to assign
to the Database component’s AliasName property on the
CustQry.SQL[3] := 'FROM ' + tnCustomer; command line, is also a handy technique because it lets you
run the program against more than one database easily.

Delphi Informant April 1996 25


Delphi C/S

Figure 2: Using the String list editor to set the path parameter Figure 3: A Windows 95 shortcut — starting PHONE.EXE and
at design time. passing the PhoneNet alias on the command line.

For example, if you have a production database and a test Figure 3 shows an example of a Windows 95 shortcut that
database, you can provide two icons on the Windows desk- starts an application named PHONE.EXE and passes an alias
top. One will include the name of the production database name, PhoneNet, on the command line.
on the command line, and the other will include the name
of the test database. Conclusion
You should write your Delphi programs to make conversion to
Another situation where this technique is useful is with a a client/server architecture as easy as possible. It takes little effort
traveler who uses a notebook computer while on the road. and can save many hours of conversion and debugging time
By passing the database alias or path on the command later on. Even if you do not convert the program to client/serv-
line, you enable the user to employ a copy of the database er, using a Globals unit (instead of sprinkling literal values
on the local hard drive while traveling, and the production throughout your code), will pay dividends in both readability
database on the network when he or she is in the office. and maintainability. In addition, using a Database component
To get a path from the command line, use the following code in enables you to change your data’s location at run time. ∆
your main form’s OnCreate handler.
with Database1 do
begin Bill Todd is President of The Database Group, Inc., a Phoenix area consulting and
Connected := False; development company. He is co-author of Delphi: A Developer’s Guide [M&T
Params.Clear; Books, 1995], Creating Paradox for Windows Applications [New Riders
Params.Add('Path=' + ParamStr(1));
Connected := True;
Publishing, 1994], and Paradox for Windows Power Programming; Technical
end;
Editor of Paradox Informant; a member of Team Borland; and a speaker at every
Borland database conference. He can be reached at (602) 802-0178, or on
CompuServe at 71333,2146.
To pass an alias name on the command line, change the code
as follows:

with Database1 do
begin
Connected := False;
AliasName := ParamStr(1);
Connected := True;
end;

Delphi Informant April 1996 26


Informant Spotlight
By James Hofmann

The Readers Speak


1996 Delphi Informant Reader’s Choice Awards

ince its premiere in April of 1995, Delphi Informant has endeavored to


S bring you current information about the products and services available
to the Delphi developer community. Each month’s issue features Delphi-
centric product announcements and industry news in our “Delphi Tools”
and “Newsline” columns. DI also regularly features book reviews and
product reviews in our respective “TextFile” and “New & Used” columns.

This month, however, we turn the tables. It’s time to voice your opinion and this is the
result: The First Annual Delphi Informant Reader’s Choice awards.

We asked you to pick your favorites from nearly 100 products in 12 categories. And you
responded, sending ballots by fax, e-mail, the World Wide Web, and even snail mail. As
expected, some categories were highly competitive, with winners determined by few
votes. In others, precedents have been set by establishing clear leaders in the Delphi add-
on market.

But enough preamble; let’s cut to the chase.

Product of the Year


With the most votes overall, this year’s Product of the Year is Woll2Woll Software’s
InfoPower. Woll2Woll Software began by producing Paradox-specific tools, and leapt into
the Delphi tools market early, releasing InfoPower just months after Delphi 1.0 shipped.
For more information about InfoPower, see “Best VCL” below, and the sidebar
“InfoPower Selected as Product of the Year” on page 29.

The Reader’s Choice Awards proved to be highly competitive. In fact, the vote was so close
for Product of the Year that we decided to give the second place finisher — Borland’s
InterBase server — an Honorable Mention award.

Best Delphi Book


Two books clearly dominated
Book
the Best Book category. Both Teach Yourself Delphi Delphi Programming
in 21 Days — 3% for Dummies — 4%
from SAMS Publishing,
Delphi Developers Guide, by Delphi How-To — 3% Delphi Programming
EXplorer — 5%
Xavier Pacheco and Steve Delphi 33%
Teixeira, took first, narrowly Developer’s 11% Delphi: A
Guide Developer’s
beating Charles Calvert’s 12% Guide
Delphi Unleashed.
29%

“If you’re serious about Mastering


Delphi
Delphi, you need this Delphi Unleashed

Delphi Informant April 1996 27


Informant Spotlight
book,” said Tim Feldman in his review of Delphi A close second, MicroHelp’s Communications Library 3.0
Developers Guide, in the November 1995 DI. “In 22 chap- acquired 26 percent of the vote, while Media Architects’
ters, it covers every major area of Windows ... program- ImageKnife/VBX took third with 20 percent.
ming using Delphi.”
Best Database Server
And while perhaps not on the top shelf, two other Delphi In addition to receiving Honorable Mention for its close sec-
books made a very good showing. From Sybex, Marco ond-place finish as Product of the Year, Borland
Cantu’s Mastering Delphi, and from M&T Books, Delphi: A International’s InterBase secured 52 percent of the vote in the
Developer’s Guide by Bill Todd and Vince Kellen, finished Best Database Server category.
third and fourth respectively.
Database Server
Best VCL
Besides being Product of the Year, Woll2Woll Software’s Informix — 4%
InfoPower got the nod as Best VCL. A collection of data- Sybase
aware components that enhance Delphi’s existing VCL 8%
components, InfoPower includes an enhanced data grid, 17% Microsoft SQL
InterBase 52%
database filtering, lookup combo boxes, expanding memo
dialog boxes, incremental search components, and more. 19%

VCL Oracle

VisualPROs — 4%
Borland’s InterBase 4.0 Workgroup Server has had a great
Light Lib Images VCL — 5%
Other — 4% year. In its biggest sale ever, Borland closed a deal with the US
Power Controls — 5% Army to use InterBase in its Advanced Field Artillery Tactical
InfoPower 47% Crystal
Data System (AFATDS). The Army selected InterBase because
7%
Reports VCL of its platform-independence, robustness, and other unique
12%
16% features. For more information about InterBase, see the side-
bar “InterBase Emerges from the Shadows” on page 30.
Light Lib
Business VCL
Best Database CASE Tool
Orpheus
There’s no doubt about your choice as Best Database CASE
Blow the dust off your August 1995 DI for a detailed look Tool. Half of you agreed that Asymetrix Corporation’s
at InfoPower’s unique features. In his review, Joseph Fung InfoModeler is the best way to automate database creation
called InfoPower’s components “complete and well thought and maintenance. Asymetrix shipped InfoModeler 1.5 in
out, significantly enhancing the development process.” It October of 1994, adding connectivity to several databases
appears you agree. In one of the more populated categories, including Informix, Ingres, Sybase, and Visual dBASE.
Woll2Woll carried a whopping 47 percent of the votes. Version 1.5 also incorporates model import/export, and two
new tools: Fact Assistant and Verbalizer.
In the second tier of popularity, TurboPower’s Orpheus and
DFL Software’s Light Lib Business VCL, took second and Database CASE Tool
third places respectively.
Chen ER-Modeler — 7%
Best VBX
Given the variety of VBXes, it’s no surprise the competi- 16% System
tion was stiff in this category. Yet when all was said and InfoModeler 50% Architect
done, Visual Components’ Visual Developers Tool Suite 27%
edged out its opponents with 30 percent of the total votes.
VBX S-Designor

GigaSoft ProEssentials — 4%
ImageBASIC S-Designor from SDP Technologies came in second place,
for Delphi — 5% followed by Popkin Software’s System Architect, and Chen &
Visual Associates’ Chen ER-Modeler.
30% 15% Media
Developers Developer 2.0
Tool Suite Best Installation Software
20%
26% To the finish, the Best Installation Software category was one
of the tightest races. Three products dominated the category:
ImageKnife/VBX
InstallSHIELD from InstallShield Corporation finished first,
Communications Library 3.0

Delphi Informant April 1996 28


Informant Spotlight

Installation Software dialog box editor, multimedia support, Windows 95 support,


and notably, specific Borland Database Engine (BDE) sup-
port, among others.
Sax Setup Wizard
4%
You obviously consider Eschalon Setup Pro a worthy installation
14% PC-Install
InstallSHIELD 30% tool. So do AT&T, DOW, Kodak, Electronic Arts, Macromedia,
Polaris, Sprint, Xerox, and the Army Corps of Engineers, all of
26%
26% whom have made it their standard installation tool.
Eschalon Setup Pro
Wise Installation Best Training
System Established in 1988, Softbite International has become known
not only as a leader in software training, but in consulting as well.
narrowly besting Great Lakes Business Solutions’ The Wise
You chose Softbite as Best Training organization, giving it 44 per-
Installation System, and Eschalon Development’s Eschalon
cent of your votes. Oregon-based Grumpfish Incorporated made
Setup Pro.
a strong second place showing with 25 percent of the votes.
Recently, InstallShield Corporation and Borland Training
International, have collaborated to create a software deploy- The 4GL Consulting
ment toolkit, InstallShield Express, for Borland’s C++ Optimax — 3% Group Ltd. — 3%
Development Suite. Borland is also working exclusively with Others — 3% GenoTechs — 7%
InstallShield Corporation to provide software deployment The DSW
solutions for all other Borland products, including Delphi Softbite 44% Group Ltd — 7%
2.0, Visual dBASE, and Paradox 7. International
25% InfoCan
Reviewed by Micah Bleecher in the February 1996 DI, The Management — 8%
Wise Installation System features full system access, a custom Grumpfish

InfoPower Selected as Product of the Year Still expanding, Softbite recently added accounting services.
Regarding their 1996 agenda, Softbite founder Kevin Smith
Delphi Informant readers selected Woll2Woll Software’s
InfoPower as their favorite Delphi add-in product by casting said, “We’ll increase support for Delphi and stay on the
more votes for it than any other product in any category. developer side of Paradox 7.”

Woll2Woll reviewed a beta version of Delphi in late 1994 Best Reporting Tool
with hopes of creating a Delphi tool. They wanted to help All questions have been answered about the Best
database developers familiar with products like Paradox for
Windows feel more productive with Delphi. In their market Reporting Tool — Crystal Reports from Crystal
research, Woll2Woll found database developers didn’t want to Services/Seagate Software is the clear winner. The recently
compromise the quickness or functionality of their applica- released version, Crystal Reports 4.5, includes a Delphi
tions, but wanted the speed of true .EXE files. VCL, a full-featured OLE (OCX) control, a 32-bit Report
Engine DLL, the ability to drill down on graphs, new
The InfoPower suite includes 15 visual and non-visual data-
aware Delphi components, designed for 16- and 32-bit Lotus Notes and Excel 5.0 export formats, and the ability
Windows database applications. The suite features an to save report options with the report.
enhanced data-aware grid that supports fixed columns, check-
boxes, embedded multi-field lookup combo boxes, dynamic Reporting Tools
cell coloring, multi-line titles, memo display, and editing. It
also includes customizable dialog boxes for locating field val- Quick Reports — 4%
ues in a table, query, or query-by-example (QBE), where R&R Report Writer — 5%
searches can be case-sensitive, pattern based, or incremental.

InfoPower also provides database lookup facilities and customiz- Crystal 44% 16%
able dialog boxes that can be attached to any event. Additionally, Reports
InfoPower includes a QBE component that operates just as its ReportPrinter
31%
Paradox counterpart, as well as a Table component that provides
data filtering capabilities.
ReportSmith
Woll2Woll is currently working on InfoPower 2.0 which they
expect to ship in May, 1996. New functionality will include These are some of the features that keep Crystal Reports a
field validation, visual filtering and querying, and the ability
to print the contents of a grid. The new version will also fea- leader in the reporting tool market, and the winner of the
ture enhancements to existing InfoPower components, such as Best Reporting Tool with 44 percent of the votes. Borland
multi-record selection and bitmap support in the grid compo- International’s ReportSmith finished a healthy second, and
nent, and other developer-requested features and functions. Nevrona Designs’ ReportPrinter took third place.

Delphi Informant April 1996 29


Informant Spotlight

Best Version Control Windows DLL


An established software configuration management tool, GigaSoft ProEssentials — 4% WYSIWYG Form and
INTERSOLV’s PVCS Version Manager commands the Best Report Designer — 5%
Other — 4% ProtoView Interface
Version Control category with 67 percent of the ballots.
Component
PVCS Version Manager enables a team of programmers to 26% Set — 7%
DynaZIP
track file changes during application development. 9% CrystalCOMM
Developers can check out and modify source code, executa- for Windows
14%
bles, utilities, and documentation files. 19%
Communications 16%
Version Control Library 3.0
Restruct.DLL
Microsoft Visual Source Safe SilverWare Windows
Communications Tool Kit
Versions
4%
13% Released during the last quarter of 1995, DynaZip features
PVCS 67% 16% 16- and 32-bit versions for Windows, Windows 95, and
MKS Source Windows NT, plus a new DZ_EASY interface. DynaZip also
Integrity offers OCX and VBX support.

The third and fourth place finishers — SilverWare’s


INTERSOLV has recently announced that Borland will Windows Communications Tool Kit, and TrayMar Software’s
incorporate PVCS Version Manager into the Borland C++ Restruct.DLL — made strong showings as well.
5.0 Development Suite.
Best Windows Help Authoring Tool
Best Delphi Add-In The Best Help Authoring Tool category had two clear front-
In another hotly-contested category, SuccessWare runners, but Blue Sky Software’s RoboHELP came in first
International and Woll2Woll Software battled to the end for with 46 percent of the votes. For more information about
Best Delphi Add-In. With an onslaught of last minute votes, RoboHELP, pull out the January 1996 issue of DI and help
SuccessWare International’s Apollo Rock-E-T emerged as yourself to Gary Entsminger’s review.
your favorite with 26 percent of the votes.

Delphi Add-In
Codewright — 3% InterBase Emerges from the Shadows
Other — 8% VB2D Translator — 4%
InterBase’s strong showing in the Delphi Informant Reader’s
Graphics Server — 4% Choice Awards is just another example of its recent, rapid rise
Apollo 26% Conversion into prominence. Considered the “Crown Jewel” of the Ashton-
Rock-E-T Assistant — 7%
Tate acquisition, InterBase nevertheless remained obscure and
HyperTerp/Pro isolated from the rest of the Borland product line. Its bundling
16% — 7% with the phenomenally successful Delphi changed all that.
10%
15%
ezDialogs Delphi/Link for Borland has finally begun a tighter integration of InterBase with
for Delphi Multi-Edit Lotus Notes the rest of their products, propelling InterBase forward in sales
for Windows and market penetration. Delphi 2.0 Client/Server Suite is the
latest effort in Borland’s “Trojan horse” strategy to entice Delphi
Woll2Woll Software’s ezDialogs for Delphi received 16 per- developers to embrace InterBase. The new Suite includes not
cent of the votes. And in a near-tie for second place, only the new Windows 95/NT Local InterBase server, but also
American Cybernetics’ Multi-Edit for Windows took third the multi-user InterBase NT Server for prototyping and devel-
oping multi-user applications. In addition, a new Event Alerter
with 15 percent of the votes. component has been added to the VCL, allowing developers
much tighter integration to the InterBase engine.
Of the 12 categories, the race for Best Delphi Add-In was the
most crowded with over 13 products receiving votes. Other Borland isn’t content with Delphi/InterBase synergies, how-
notables included: Borland/Brainstorm Technologies’ ever, and has rumored that future versions of their Java tools,
code named “Latte,” may include InterBase or InterBase con-
Delphi/Link for Lotus Notes, HyperAct’s HyperTerp/Pro, nectivity as well. The endorsement by Borland’s InterBase of
and EarthTrek’s Conversion Assistant. the new JDBC standard by Sun Microsystems, Inc. is further
evidence that something formidable is brewing.
Best DLL
Giving it 22 percent of the votes, you named Inner Media’s With growth of InterBase sales close to 40% in 1996, and
new versions available almost every month, it looks like
DynaZip as Best DLL. Balloting was tight however, and Borland’s evolution from desktop tools provider to
MicroHelp’s Communications Library 3.0 came in a close client/server provider is succeeding.
second with 19 percent of the votes.

Delphi Informant April 1996 30


Informant Spotlight

Contacting the Winners Help Authoring Tool


DocToHelp — 5%
Other — 4% Windows Help
Best Book Best Training Magician Pro — 6%
Delphi Developer’s Guide Softbite International
Xavier Pacheco & Steve 33 N Addison Road, #206
Teixeira Addison, IL 60101-8401 RoboHELP 46%
SAMS Publishing Phone: (708) 833-0006
ISBN: 0-672-30704-9 Fax: (708) 833-0584 39% ForeHelp
Phone: (800) 428-5331 Web Site: http://www.soft-
Web Site: http://www.mcp.com bite.com

Best VCL Best Reporting Tool


InfoPower Crystal Reports ForeFront’s ForeHelp dominated the other contenders, plac-
Woll2Woll Software Crystal Services/Seagate ing second with 39 percent.
2217 Rhone Drive Software
Livermore, CA 94550 1095 West Pender St.,
Phone: (800) 965-2965 or 4th Floor Thank You
(510) 371-1663 Vancouver, BC Canada In the final analysis, third-party Delphi products are only pro-
Fax: (510) 371-1664 V6E 2M6
CompuServe: GO Phone: (800) 663-1244 or ductive when they enable developers to “get the job done.”
WOLL2WOLL (604) 681-3435 This makes you — the greater Delphi community — the only
Web Site: http://woll2woll.com Fax: (604) 681-2934 true arbiter. Thanks to you, the Delphi community now has a
CompuServe: GO REPORTS
Best VBX Web Site: http://www.sea- good idea of the products available — and which are most
Visual Developers Tool Suite gate.com/software/crystal popular.
Visual Components, Inc.
15721 College Boulevard Best Version Control
Lenexa, KS 66219 PVCS We would also like to thank the vendors for spending the
Phone: (913) 599-6500 INTERSOLV time and money to develop such excellent Delphi products.
Fax: (913) 599-6597 1700 NW 167th Place The Delphi third-party marketplace is buzzing, and the
Web Site: http://visualcomp.com Beaverton, OR 97006
Phone: (800) 547-7827 or excitement will only increase when Delphi 2.0 hits the
Best Database Server (503) 645-1150 shelves. We’re looking for many new Delphi products in the
InterBase Fax: (503) 645-4576 coming year, including OCXes, Web-enabling components,
Borland International Inc. E-mail: Internet: pvcsinfo@-
100 Borland Way intersolv.com and many more we cannot even envision. ∆
Scotts Valley, CA 95066-3249 Web Site: http://www.inter-
Phone: (408) 431-1000 solv.com
Web Site: http://www.bor- James Hofmann is Assistant Editor for Delphi Informant.
land.com Best Delphi Add-In
Apollo Rock-E-T
Best Database CASE Tool SuccessWare
InfoModeler 27349 Jefferson Avenue,
Asymetrix Corporation Suite 111
110 110th Ave. NE, Suite 700 Temecula, CA 92590
Bellevue, WA 98004-5840 Phone: (800) 683-1657 or
Phone: (800) 448-6543 or (909) 699-9657
(206) 462-0501 Fax: (909) 695-5679
Fax: (206) 454-7696 CompuServe: GO SWARE
Web Site: http://www.-
asymetrix.com Best DLL
DynaZIP
Best Installation Software InnerMedia, Inc.
InstallSHIELD 60 Plain Road
InstallShield Corporation Hollis, NH 03049
1100 Woodfield Road Phone: (800) 962-2949 or
Suite 108 (603) 465-3216
Shaumburg, IL 60173 Fax: (603) 465-7195
Phone: (800) 374-4353 or E-Mail: CIS: 70444,31
(847) 240-9111
Fax: (847) 240-9120 Best Help Authoring Tool
E-Mail: Internet: info@- RoboHelp
installshield.com Blue Sky Software Corp.
Web Site: http://www.- 7777 Fay Ave., Suite 201
installshield.com La Jolla, CA 92037
Phone: (800) 677-4946 or
(619) 551-2485
Fax: (619) 551-2486
Web Site: http://www.blue-
sky.com

Delphi Informant April 1996 31


Dynamic Delphi
Delphi 1.0 / Object Pascal

By Andrew J. Wozniewicz

DLLs: Part II
Completing the Example Dynamic Link Library

ast month, we introduced dynamic link libraries (DLLs) and how to cre-
L ate them using Delphi. As part of that interactive discussion, we created
the sample unit, XSTRING.PAS, to contain five string-handling functions:
FillStr, UpCaseFirstStr, LTrimStr, RTrimStr, and StripStr.
In this installment, we’ll create the last three Here’s how LTrimStr is implemented in the
functions needed to fill XSTRING.PAS, implementation section of the XString unit:
then learn to export DLLs. [If you haven’t
built XSTRING.PAS, refer to “DLLs: Part I” function LTrimStr(const S: string):
string;
in the March 1996 Delphi Informant.] var
Index, MaxIndex: Integer;
The LTrimStr Function begin
Index := 1;
Often, you’ll need to ensure that the string MaxIndex := Length(S);
you’re working with begins with a mean- while (Index <= MaxIndex) and
ingful (i.e. non-blank) character. If there (S[Index] = ' ') do
Inc(Index);
are any leading blanks, you may need to Result := Copy(S,Index,
remove them. MaxIndex-Index+1);
end;

For example, the standard procedure Val


converts strings of digits into their numeric The LTrimStr function relies on a while loop
representations and requires that the first to determine the position of the first non-
character of the argument string is a valid, blank character in the string. The loop termi-
non-blank digit. Before you can pass a nates when a non-blank character is found or
string of digits to Val for conversion, you when the loop reaches the end of the string.
must trim the string’s leading blanks.
This last consideration is especially impor-
The LTrimStr function is designed to per- tant to handle if the function is to be a
form the “trimming” of the leading blanks robust, production-quality subroutine. You
on the left side of the passed string argu- must ensure proper behavior with a passed
ment. Enter the following declaration inside argument string that contains no leading
the interface section of the XString unit: blanks, or is entirely blanks. In the former
case, the string is returned unchanged. In the
function LTrimStr(const S: string): latter case, the result of trimming all leading
string; export; blanks from a string consisting of only
blanks is returned: a null (empty) string.
The only parameter passed to LTrimStr, S, is
the string to be trimmed. The result returned The loop counter, Index, is initialized to
is the same string, but without leading point to the beginning of the argument
blanks. If there are no leading blanks in S, string. To ensure that the loop doesn’t run
then LTrimStr returns the argument string past the end of the string, a guardian value,
unchanged.

Delphi Informant April 1996 32


Dynamic Delphi
MaxIndex, is established. MaxIndex is the index of the string’s argument, runs “backward” from the end of the string, and
last character, and is equal to its original length. proceeds toward the beginning.

After the index of the first non-blank character of the argu- When the loop counter Index — initialized to mark the argu-
ment string is determined, the portion of the argument ment string’s last character — indicates a position of a non-
string S containing the leading blanks is removed, and the blank character in the argument string, the loop has counted
remainder is returned as the function’s result. all trailing blanks and terminates.

The standard string function Copy is used to select only the In case the argument string consists of blanks only, the loop
meaningful, non-blank portion of the original argument condition checks if the iteration has reached the beginning of
string S, omitting the leading blanks from the function’s the string, indicated by an Index value of less than 1. This
result, as required. condition also terminates the while loop.

Here’s how LTrimStr is called in an application: After the while loop is finished, the Index variable holds the
position of the argument string’s last non-blank character.
var The Copy function is then used to extract the non-blank lead-
S1, S2, S3 : string;
begin ing portion of the string and return it to the caller as the
... function result.
S1 := LTrimStr(' Teach Yourself Delphi');
S2 := LTrimStr(' 123');
S3 := LTrimStr(' 456 '); The RTrimStr function can be used as follows:
...
end; var
S1, S2, S3: string;
begin
After the assignment statements are executed: ...
S1 contains, “Teach Yourself Delphi” S1 := RTrimStr('Teach Yourself Delphi ');
S2 contains, “123” S2 := RTrimStr('123 ');
S3 := RTrimStr(' 456 ');
S3 contains, “456 ” ...
end;
Note that LTrimStr has no effect on trailing blanks. This is
the job for the next trimming function, RTrimStr. After the assignment statements execute:
S1 contains “Teach Yourself Delphi”
The RTrimStr Function S2 contains “123”
In concept, RTrimStr is similar to LTrimStr. Instead of S3 contains “ 456”
removing the leading blanks, however, RTrimStr removes
any trailing blanks, ensuring that the resulting string is as RTrimStr removes the trailing blanks from the argument, but
short as possible. A typical use of the RTrimStr function is the leading blanks remain.
to prepare a string for conversion to a number using the
Val procedure. The StripStr Function
The last string-handling subroutine you’ll implement inside your
Enter the following declaration inside the interface section of DLLFirst library is StripStr. It removes all blanks — whether they
the XString unit: are leading, trailing, or embedded within the argument string —
and returns a result of all non-blank characters. The declaration
function RTrimStr(const S: string): string; export; of the StripStr function is similar to that of the others:

As you can see, the declaration of RTrimStr is nearly identical function StripStr(const S: string): string; export;
to that of LTrimStr. RTrimStr is implemented as follows in
the implementation section of the XString unit: Since the number of iterations is known, StripStr uses a for
loop for top performance. The loop must run through all the
function RTrimStr(const S: string) : string; characters in the argument string, removing any blanks as it
var
Index: Integer; visits the consecutive character locations. Here’s the imple-
begin mentation of the StripStr function:
Index := Length(S);
while (Index > 0) and (S[Index] = ' ') do function StripStr(const s: string): string;
Dec(Index); var
Result := Copy(S,1,Index) Index : Integer;
end; begin
Result := '';
The trick to implementing RTrimStr that makes it even sim- for Index := 1 to Length(S) do
if S[Index] <> ' ' then
pler than LTrimStr, lies in the while loop. This code deter- Result := Result + S[Index];
mines the position of the last non-blank character of the string end;

Delphi Informant April 1996 33


Dynamic Delphi

The for loop visits every character position of the argu- library DLLFirst;

ment string S, and determines if the character is a blank. uses


If it is, the current character is appended from the argu- WinTypes,
ment string to the function’s Result string. Thus, the result XString in 'XSTRING.PAS';

string is accumulated one character at a time, omitting exports


blank characters in the process. FillStr,
UpCaseFirstStr,
LTrimStr,
It’s important to note that Result is an implicit variable of RTrimStr,
the same type as the function’s return type — string in StripStr;
this case — and is automatically available for every Object
begin
Pascal function. Delphi automatically initializes Result to end.
an empty string. This ensures that the result is defined
even if the argument string is empty and the for loop is This code shows you how to make the routines implement-
never executed. ed in the XString unit visible to applications using DLL-
FIRST.DLL. The XString unit is listed in the uses clause of
Here’s an example of StripStr in use: the library module to make its interface visible inside the
var
S1, S2, S3, S4 : string; library file so you can reference the names of the subroutines
begin you want to export.
...
S1 := StripStr(' Teach Yourself Delphi ');
S2 := StripStr('123 '); Because our five string-handling routines are listed in the
S3 := StripStr(' 456 '); exports clause, their names will be available so that applica-
S4 := StripStr(' 789');
tions can use the library’s services. Make sure you recompile
...
end; the DLLFirst project so the executable DLL reflects all your
most recent changes.
Following execution of the assignment statements:
S1 contains “TeachYourselfDelphi” Looking Ahead
S2 contains “123” Now that we’ve successfully implemented a DLL with
S3 contains “456” Delphi, we’ll next turn to the application side of the
S4 contains “789” dynamic-linking equation.

Using the XString Unit The key to accessing externally implemented subroutines
And that does it! You’ve implementing the XString unit (e.g. a DLL function) is to create import units. Any appli-
which now contains five useful string-handling functions. To cation that wants to use the DLL’s services must have the
help you verify the code you entered is complete and valid, appropriate import reference in its uses clause. The
Listing Five (beginning on page 35) shows the entire unit. WinProcs unit, for example, imports the subroutines
Comments were added to make the code more readable and defined in three Windows DLLs that comprise the
understandable. Windows API: USER, KERNEL, and GDI. To use subrou-
tines implemented in these DLLs, you must include
Each of the functions implemented in the unit’s imple- WinProcs in the uses clause of your application.
mentation section — FillStr, UpCaseFirstStr, LTrimStr,
RTrimStr, and StripStr — is listed in the interface section. In Part III of this series, we’ll create a corresponding import unit
In turn, each function is made exportable by placing the for the custom DLLFirst library. We’ll also discuss the external
export directive after the function declaration. This makes directive, interface with DLLFirst, import subroutines by ordinal
it possible to include the subroutines in an exports clause number, and build a sample application. See you then. ∆
and make them available outside the DLL.
This article was adapted from material for Teach Yourself
With the functions implemented, now it’s time to com- Delphi in 21 Days [SAMS, 1995], by Andrew Wozniewicz.
plete the DLL project by actually exporting the functions
so they are visible and accessible outside DLLFIRST.DLL.

Exporting String Functions


To reiterate, you must list the functions you want to make Andrew J. Wozniewicz is president and founder of Optimax Development
available to client applications inside an exports clause of Corporation (http://www.webcom.com/~optimax), a Chicago-based consultancy
specializing in Delphi and Windows custom application development, object-ori-
your library source code file. ented analysis, and design. He has been a consultant since 1987, developing pri-
marily in Pascal, C, and C++. A speaker at international conferences, and an
First, enter the following code (select View | Project Source early and vocal advocate of component-based development, he has contributed
from Delphi’s menu, if necessary, to return the main articles to major computer industry publications. Andrew can be contacted on
library file): CompuServe at 75020,3617 and on the Internet at [email protected].

Delphi Informant April 1996 34


Dynamic Delphi

Begin Listing Five — The XString Unit


unit XString;
{ A collection of string-handling routines to comple-
ment
those from the SysUtils unit }

interface
function FillStr(C : Char; N : Byte): string; export;
{ Returns a string with N characters of value C }
function UpCaseFirstStr(const s: string): string; export;
{ Capitalizes the first letter of every word }
function LTrimStr(const S: string) : string; export;
{ Trims the leading blanks }
function RTrimStr(const S: string) : string; export;
{ Trims the trailing blanks }
function StripStr(const s: string): string; export;
{ Strips all blanks }

implementation

function FillStr(C : Char; N : Byte): string;


{ Returns a string with N characters of value C }
begin
FillChar(Result[1],N,C);
Result[0] := Chr(N);
end;

function UpCaseFirstStr(const s: string): string;


{ Capitalizes the first letter of every word }
var
Index: Byte;
First: Boolean;
begin
Result := S;
First := True;
for Index := 1 to Length(s) do
begin
if First then
Result[Index] := UpCase(Result[Index]);
if Result[Index] = ' ' then
First := True
else
First := False;
end;
end;

function LTrimStr(const S: string): string;


{ Trims the leading blanks }
var
Index, MaxIndex: Integer;
begin
Index := 1;
MaxIndex := Length(S);
while (Index <= MaxIndex) and (S[Index] = ' ') do
Inc(Index);
Result := Copy(S,Index,MaxIndex-Index+1);
end;

function RTrimStr(const S: string) : string;


{ Trims the trailing blanks }
var
Index: Integer;
begin
Index := Length(S);
while (Index > 0) and (S[Index] = ' ') do
Dec(Index);
Result := Copy(S,1,Index)
end;

function StripStr(const s: string): string;


{ Strips all blanks }
var
Index : Integer;
begin
Result := '';
for Index := 1 to Length(S) do
if S[Index] <> ' ' then
Result := Result + S[Index];
end;

end.
End Listing Five

Delphi Informant April 1996 35


Visual Programming
Delphi 1.0 / Object Pascal

By Walker Lipscomb

Who Owes Whom?


Creating an Attractive and Intuitive User Interface

ave you ever spent a relaxing vacation at the beach with several
H friends, only to have the idyllic experience spoiled at the end by a heat-
ed argument over who owes whom?
This article provides a software solution to this Thursday, some of the group charters a
problem. Along the way, the sample program fishing trip and others rent a ski boat. At
illustrates several examples of Delphi compo- the end of the week, there is a minor argu-
nents and features — what they are, and how ment about whether to count the kids at
they can be used to construct a user interface “full fare.” The family with the most kids
that is both attractive and intuitive to use. As points out that the kids slept four to a
Marshal McLuhan wrote, “The Medium is the room, and did not have any of the drinks
Message,” and in this case, the medium is a (or so they think). The family without
program, which by its nature, will be used children notes that they weren’t the ones
only occasionally. However, this program will who left the refrigerator door open
be indispensable on those occasions. overnight with US$150 worth of shrimp
in it (or made that mess on the carpet). A
The Scenario compromise is reached: children are to be
Four families of varying sizes rent a large beach counted at 50 percent for rent and 75 per-
house for a week. One couple pays the US$800 cent for food. Now — who owes whom?
deposit, and another pays the remaining
US$2600 due on arrival. One family picks up Who Owes Whom (WOW) is a Delphi
US$125 worth of drinks at the discount liquor program designed to simplify the job of
store. During the week, each family buys gro- allocating shared expenses and determining
ceries several times (and keeps the receipts). who should pay whom, and how much.
Information is arranged on four notebook
As the week progresses, other friends and pages. The Summary page (see Figure 1) is
relatives show up to stay a few days. On the primary mechanism for entering fami-
lies and charges. Use of the Details by
Family page (see Figure 2), the Rent
Calculations page (see Figure 3), and the
Food Calculations page is not required,
but they will help in the inevitable and
unenviable task of explaining to people
why they owe so much. You move to each
page by pressing the appropriate notebook
tab with the mouse, or by holding down
A and pressing the underlined letter.

Operation
Follow these steps to enter a simple scenario
(Rent and Food only). Operational details
are provided in the usual Windows Help file:
If the Summary Page is not displayed,
Figure 1: The Summary page. press AS to display it.

Delphi Informant April 1996 36


Visual Programming

Design Considerations
By its nature, WOW is not likely to be used on a huge
database or by multiple simultaneous users. Therefore, lit-
tle consideration was given to efficiency or multi-user
access. Due to Delphi’s inherent speed, WOW does run
quite nicely with up to a couple of hundred families
entered — surely larger than any conceivable beach party
— even if you invited the Kennedys.

Since this program will probably be running on a laptop at


the beach or in the mountains, printing capability was not
deemed a requirement. (It’s bad enough that you’re carrying
your laptop on vacation, much less a printer!) On the other
hand, WOW will probably be run only once or twice a year,
and without a manual handy. Thus, the screens should pro-
vide sufficient visual clues for easy operation. This also makes
having adequate context-sensitive help a requirement. Since
laptop pointing devices are often less than optimal, it must
be easy to perform all operations from the keyboard.

Now, we’ll discuss the implementation of WOW in Delphi,


and how that implementation satisfies the design goals.

Implementation
TTabbedNotebook. Delphi offers myriad visual components
“out of the box” and a virtually unlimited number of add-in
VBX controls are available. Many additional Delphi compo-
nents are already on the market, and the number is growing.
The trick is to choose the right mix of components that will
maximize usability.
Figure 2 (Top): The Details by Family page.
The TabbedNotebook component (TTabbedNotebook) is the
Figure 3 (Bottom): The third notebook page is for calculating rent.
primary “motif ” for WOW. TabbedNotebooks offer the neo-
phyte user a familiar real-world visual metaphor, and can
Enter the first family’s name in the Family Name column immediately show the user what major application areas are
at the left of the grid. available — in this case the Summary, Details by Family,
Under Rent Nights/Adult, enter the number of adults multi- Rent, and Food calculations. A TabbedNotebook effectively
plied by the number of nights they should be charged rent provides sub-forms that can be used to change the type of
(e.g. 2 adults for 7 nights equals 14 nights). information shown in an area of the main screen. In the
Similarly, enter the appropriate information into Rent design phase, the TabbedNotebook will be typically sized to
Nights/Child, Food Nights/Adult, and Food Nights/Child. occupy a large portion of the form — a quarter to a half or
Using the b to move to each subsequent row, enter the more. Then, to create the multiple sub-forms, you add two
remaining families. or more Page names to the Pages property of the component.
Press the Payments button (or
AP) to display the Payments As many other visual components (e.g. labels, grids, edit
form (see Figure 4). boxes, etc.) as desired can be placed on each page. At run
For each payment, use the time, when the user or the program changes the notebook
drop-down edit boxes to select page, the components on the selected page are displayed and
the appropriate family who the components on the other pages are hidden.
made the payment, and the
correct category (Rent or Note that while “page” is the correct programming terminol-
Food). Enter the amount and Figure 4: Calculating
ogy, the Help file uses the term “sheets.” If the user thinks in
food expenses per family.
press OK or J. terms of pages, he or she will naturally expect to move from
Press the Cancel button or E to one to the other by pressing z and x. (Wouldn’t
return to the Summary page. you?) The Windows standard use for these keys is to “scroll
Note that a status box at the bottom of the screen suggests grid” to a new set of records, and we happen to need that
re-calculation. Press the Calculate button (or AC or functionality. Therefore, WOW provides the 6 / V6
9) to calculate who owes whom. keys to rotate among Notebook “sheets,” and accelerator keys

Delphi Informant April 1996 37


Visual Programming

procedure TMainForm.DepartNotebookPage;
procedure TMainForm.ArriveNotebookPage;
begin
begin
case TabbedNotebook1.PageIndex of
case TabbedNotebook1.PageIndex of
0: begin
0: begin
CurrentFamilyName:=
SyncToFamily(FamilyTable,CurrentFamilyName);
FamilyTableFamilyName.AsString;
FamilyGrid.SetFocus;
if FamilyTable.State<>dsBrowse then
end;
FamilyTable.Post;
1: begin
end;
LoadFamilyNames(FamilyTabSet.Tabs);
1: begin
FamilyTabSet.TabIndex:=
CurrentFamilyName:=
FamilyTabSet.Tabs.IndexOf(CurrentFamilyName);
FamilyTable2FamilyName.AsString;
AdultNightsRentBox.setfocus;
if FamilyTable2.State<>dsBrowse then
end;
FamilyTable2.Post;
2: begin
if PaidTable.State<>dsBrowse then
SyncToFamily(RentTable,CurrentFamilyName);
PaidTable.Post;
RentGrid.SetFocus;
end;
end;
2: begin
3: begin
CurrentFamilyName:=
SyncToFamily(FoodTable,CurrentFamilyName);
RentTableFamilyName.AsString;
FoodGrid.SetFocus;
if RentTable.State<>dsBrowse then
end
RentTable.Post;
end;
end;
end;
3: begin
CurrentFamilyName:=
FoodTableFamilyName.AsString; Figure 6: The OnClick event works with the
if FoodTable.State<>dsBrowse then ArriveNotebookPage procedure to synchronize each new page
FoodTable.Post; to the same specified family.
end;
end;
end; get no respect. And, actually they are smarter than they
seem. They provide accelerator keys to set the focus to
Figure 5: The DepartNotebookPage procedure is called by the another control. These accelerator keys are indicated by
OnChange event.
putting an ampersand (&) in the Label’s Caption. Herein lies
to select a page directly. Clicking the appropriate tab with the the problem: Sometimes you may not want an ampersand in
mouse works as well, of course. the Caption to create an accelerator — you want it to be a
plain ol’ ampersand. To display ampersands normally (and
Two TTabbedNotebook events are particularly important. The not as accelerators) in a Label component, simply set its
OnChange event occurs before leaving a page, and the OnClick ShowAccelChar property to False. Or if that is unworkable,
event occurs after arriving on the new page. In WOW, each simply enter && to let Delphi know you simply want the
page displays data from the Family table, but each references a ampersand character.
different TTable component. The OnChange event calls the
procedure DepartNotebookPage (see Figure 5) which stores the TPopup. The Details by Family page provides a method of
Family Name from the “old” page, and posts any outstanding changing payments once they have been entered. Direct
edits. The OnClick event calls the ArriveNotebookPage proce- entry works fine for the date and amount, but to change a
dure that synchronizes the table on the “new” page to the same payment category by typing it in, the user would have to
family (see Figure 6). remember the exact spelling and punctuation of the desired
category.
TTabSet. On the Details By Family page, you will see a differ-
ent kind of tab, the TabSet. Unlike the TTabbedNoteBook, the A PopupMenu component provides an easy method for
TTabSet has no “built-in” functionality other than changing entering the correct category name. Since PopupMenus are
the highlighted tab. You must write code to affect any other normally accessed by right-clicking, a keyboard alternative is
controls. The TabSet could be used to change any value, for required to meet our design goals. By providing the Change
example the color of another component, or to modify the Category button, we create the AT accelerator key and
field’s value in a record. WOW uses TabSet for perhaps its document it at the same time. Similarly the Prior and Next
most obvious use — changing position within a table. buttons are included primarily to provide a keyboard
method of accessing the details of other families.
When the table referenced contains a small number of
records, the TabSet is great because the user can immediately When the PopupMenu is initiated with a right-click, it
see the available choices. For larger data, however, TabSet appears next to the mouse pointer. When you display it
would be a poor choice. Delphi provides scroll keys if all using the Popup method, you must include the screen coor-
choices cannot be seen, but scrolling through more than two dinates where the menu should appear. On the other hand,
page-widths of tabs would be inconvenient to say the least. the menu should appear near the Payments grid on the
form, and the user may move the form around on the
TLabel. Labels? Why write about labels in Delphi Informant? screen. The ClientToScreen method (see Figure 7) provides
Labels are the Rodney Dangerfield of components — they the needed translation.
Delphi Informant April 1996 38
Visual Programming

procedure TMainForm.ChangeCategoryButtonClick(
Sender: TObject);
var
PopupPoint: TPoint;
begin
PopupPoint :=
{ Get SCREEN coordinates }
FamilyDetailPanel.ClientToScreen(
Point (PaymentsPanel.Left+trunc(
{ 2/3 accross panel }
PaymentsPanel.Width*0.66),
{ Mid-way down }
PaymentsPanel.Top+trunc(PaymentsPanel.Height/2)));
CategoryPopup.Popup(PopupPoint.X,PopupPoint.Y);
end;

Figure 7: The ClientToScreen method.

Conclusion
Designing a pleasing and functional user interface is a matter
of picking the right mix of components for the job and cus-
tomizing those components as needed. Delphi’s extensive set of
user-interface components and infinite capacity for expansion
can provide any component required. Delphi’s strong object
orientation and well-organized programming environment
makes customization of these components easy, and its inher-
ent speed makes the resulting program nimble and efficient. ∆

The Who Owes Whom project is available on the Delphi


Informant Works CD located in INFORM\96\APR\DI9604WL.

Walker Lipscomb is an independent developer in the Washington, DC area,


working with both commercial and governmental organizations. Mr Lipscomb
can be reached at (703) 524-9232, or on CompuServe at 74132,2225.

Delphi Informant April 1996 39


The API Calls
Delphi 1.0 / Object Pascal / Windows API / Perseus VCL

By Karl Thompson

A Walk on the Wild Side


Getting at Windows Secrets Using Windows API
Functions and Perseus

elphi takes Windows programming to a higher plateau, for the most part
D because it hides the complexity of Windows programming. With Delphi’s
component architecture, the Object Pascal developer can easily create an
application by dropping components on a form, setting the initial values of
some properties, and adding code to selected event handlers. Delphi also
makes pointers obsolete for the great majority of applications — no small
feat for a sophisticated optimizing compiler. Undoubtedly however, there will
be times when some coders need to dig in and get into the guts of Windows.

One of Delphi’s strengths is that it allows istered Windows classes, and how memory is
programmers easy access to Windows API being allocated on the Windows global heap.
functions and data structures. The API func- Walker will also do a memory dump of any
tion calls are encapsulated in various Delphi global memory block. Both a hex and ASCII
units. (For more details, click on Delphi’s dump can be done for any block up to 64K.
Help menu and select Windows API.) Walker will also provide additional resource
statistics including:
This article will explore some of the API available GDI and user heap,
functions and data structures of the total available memory and largest avail-
ToolHelp unit. These API functions enable able contiguous memory block,
you to query Windows about available sys- the number of free items and least-
tem resources and how those resources are recently used (LRU) items,
being used. The sample Walker project (see and the number of virtual pages and free
Figure 1) demonstrates how to use the virtual pages.
ToolHelp unit to get information about the
currently loaded modules, running tasks, reg- Note that the Walker program is definitely a
work in progress. Time permitting, we’ll add
additional functionality and robustness in
future articles. And naturally, you are free to
take on the task yourself.

Some Terms Defined


Before covering the program in detail, let’s
review some Windows terminology that
often causes confusion:
A module handle (or more specifically, an
entry in the module table) contains infor-
mation that can be shared by multiple
instances of an application.
Figure 1: Walker displays information about the A task, or entry in the task database
currently loaded modules, running tasks, the reg-
istered Windows classes, and how memory is
(TDB), contains information specific to
being allocated on the Windows global heap. each execution (instance) of an application.

Delphi Informant April 1996 40


The API Calls
The module table will contain information about program of article for details). For now, suffice it to say that an atom is
resources (bitmaps, etc.) while the TDB will contain such a 16-bit word and that the Windows atom manager provides
information as the current working directory for a specific a means for translating an ASCII string into an atom.
task — thus two executions of a program can have different,
current directories. The real work for this unit, and the others, occurs in the
OnShow event handler.
To understand the next term, it’s important to first under-
stand a basic Windows concept. Anyone who has pro- procedure TClassesFrm.FormShow(Sender: TObject);
var
grammed in Windows for long realizes that nearly every visi- OK: Bool;
ble interface item is a window. From the perspective of a begin
Delphi program, a window is (obviously) a window. What pOurClassEntry := InitClassEntry(pOurClassEntry);
OK := ClassFirst(pOurClassEntry);
might not be so obvious, however, is that a scrollbar, button, while OK do begin
listbox, checkbox — and about every other display item — is ListBox1.Items.Add(Format(' %-11s %5.4x %-s',
a window too, as far as Windows itself is concerned. [GetModuleNameFromHandle(pOurClassEntry^.hInst),
pOurClassEntry^.hInst,
pOurClassEntry^.szClassName]));
Why is this important? Windows must be able to send mes- OK := ClassNext(pOurClassEntry);
sages to any of these and other window types (classes). Each end;
GroupBox1.Caption := ' Module Name hInst Class Name ';
window is created based upon a window class, and the class ClassesFrm.Caption := ' ' +
tells Windows what procedure is going to process the message. IntToStr(ListBox1.Items.Count) + ' Classes Found ';
end;

These are enough terms to get started. Other terms are cov-
ered in the online help included with Walker. For now, let’s First, a pointer called pOurClassEntry is initialized with a call
move on and take a look at Walker’s organization. to InitClassEntry (located in uGlobal). The pOurClassEntry is
of type pClassEntry and is a pointer to a TClassEntry struc-
Walker’s Structure ture. pOurClassEntry must be initialized before the Class
Walker’s files are organized so that if you need to use the walk can begin.
functionality of the ToolHelp unit, you’ll be able to easily
locate the appropriate initialization code. There is one global The call to InitClassEntry not only initializes a pointer but
unit, uGlobal.PAS, that initializes all the pointers and also allocates memory on the heap to hold one TClassEntry
ToolHelp data structures used by Walker. Normally you structure. One of the requirements for using many of the
would not want to write a function just to initialize a pointer, ToolHelp data structures is that the structure’s dwSize field
particularly if it’s initialized only once. In this case, however, must be initialized to the size of the structure. The Walker
it was done to clarify how the pointers are initialized. InitXXXX functions also handle this requirement. Notice
that all we have to do is call the Object Pascal function
Even if you don’t understand pointers, but do understand SizeOf while passing it the data type of the Windows API
that the functions in uGlobal return properly initialized structure.
pointers, then you can easily use the ToolHelp unit. Other
files include: the main program module, Walker; the Task If Walker were to be a truly robust application (i.e. suitable
Walker, uTasks; the Class Walker, uClasses; the Module for commercial distribution), the code should handle the
Walker, uModule; and the Global Heap Walker, uGlobalW. run-time error that’s generated if there isn’t enough memory
The unit that handles the display of the global data dump is available to allocate the structure. For more information, see
wGlobalD. [The source code is too extensive to list in its Delphi’s help topic “RTL Exceptions.” For now however,
entirety. However, it is available on diskette and for down- we’ll overlook this shortcoming so that we can proceed with
load. See end of article for details.] learning about these Windows data structures.

Module, Task, and Class Walkers Start Walking


I use the term Walkers because each one iterates, or “walks,” Basically, all the Walkers work in the same manner — once a
over the appropriate linked list of data structures maintained by pointer is initialized, the walk begins. To begin the walk, we
Windows. Let’s examine the Class Walker first (see Figure 2). must get the first structure in the list. In this case, the call to
Note that we call the routines that list the classes, Tasks, ClassFirst accomplishes this. If the call is successful, we enter
Modules, etc. The list of the class structures is maintained in the a while loop that first adds an entry to a TListBox and then
user segment’s default local heap. tries to again initialize the Class data structure (pointed to by
pOurClassEntry) with a call to ClassNext.
When you run the Class Walker, you may see classes with
names such as #34XXX. These are integer atoms. Borland’s When the call to ClassNext returns False, the walk is over.
WinSight program will provide a translation of these atoms The title row above the list box is the value of the Caption
to string names. Atoms are discussed in the book, property of the TGroupBox that is the parent of the TListBox.
Undocumented Windows, by Andrew Schulman, et al. (see end Finally, the memory allocated for the TClassEntry structure is

Delphi Informant April 1996 41


The API Calls

Figure 2: The Class Walker.

freed in the OnDestroy event handler (the FormDestroy


method). This is accomplished with the call to Destroy.

When you review the code in the other units, you’ll see that the
Class, Task (see Figure 3), and Module (see Figure 4) Walkers
work the same way. The Global Walker, however, is different.

The Global Walker


The Global Walker (see Figure 5) presents us with some inter-
esting challenges. As Charles Petzold points out in his book,
Programming Windows 3.1, “global memory ranges from the
spot where MS-DOS first loads Windows to the top of avail-
able memory.” This means that Windows is itself running in
its global memory (as the Global Walker demonstrates).

Windows manages blocks of memory on the global heap known


as segments or items. As the name implies, the Global Walker will
walk the global heap to document these segments. The difficulty
comes from trying to walk the heap without modifying it.

As you may know, Delphi’s TListBox allocates memory to store


strings on the global heap. Thus, each call to TListBox.Add
allocates some additional memory from the global heap. This
means as we try to walk the global heap and add a formatted
string to a listbox (as we did with the other Walkers), the heap
itself is changing. Particularly since segments can be discard-
able, moveable, etc., allocating memory during the walk ren-
ders the results of a global heap walk useless.
Figure 3 (Top): The Task Walker. Figure 4 (Middle): The
Module Walker. Figure 5 (Bottom): The Global Walker.
Perseus to the Rescue
This problem was overcome by using Perseus, a virtual listbox And it does so superbly. While developing Walker on a com-
shareware component written by Sebastian Modersohn. [A trial puter with 20MB of memory, I constantly exhausted available
version of Perseus is included in the .ZIP file that contains the memory trying to use a TListBox component while working on
Walker program. See end of article for details.] Perseus’ installa- the Global Walker module. This was never a problem when
tion instructions are in its help file. Essentially, however, it using Perseus, as it can handle up to 2,147,483,647 strings.
(VListBox.DCU) is installed just as any other component.
Picking the Right Event
Perseus is not a descendent of Delphi’s TListBox, and there- All memory allocation is completed in the FormShow method
fore does not use a TString variable to manage the strings it (see Listing Six beginning on page 45). Since we want to walk
displays. In fact, it’s the programmer’s responsibility to supply the global heap, we want to grab the items on the heap at the
the strings to Perseus, whose job in turn, is to efficiently dis- last possible moment after all allocation has been completed.
play those strings. Since Delphi allocates memory for forms and child controls in

Delphi Informant April 1996 42


The API Calls

the FormCreate method, we should perform the Global walk in


the FormShow method after the form that is used to show the
contents of the heap has itself been allocated.

The sequence of the allocation is important. First, a


TStringList is created and initialized. GlobalItemString is used
to manage the strings that Perseus displays. Memory is allo-
cated by calling TStringList’s Add method in a for loop that
iterates MaxGlobalItems times.

Next, a TGlobalEntry structure and a pointer are initialized,


and finally, a TGlobalInfo structure and pointer are initial-
ized. (Remember, this memory must also be deallocated. This
is done in the FormDestroy method.) The TGlobalInfo struc- Figure 6 (Top):
ture is initialized last because we’ll need a count of all items Double-click an item to
view the segment’s
on the global heap. Since the strings being managed by
memory dump.
TStringList are also allocated in global memory, the items Figure 7 (Left): The
they occupy must also be included in the count. Resources page of the
TTabbedNotebook.
Now that all the memory needed for this module has been
allocated, the global walk can begin. We use the same while
loop construct to walk the global heap as we have used in
the other Walkers. As the loop is executing, one string is
being formatted with information about each item on the The Resources Page
heap. Notice that the Add method is not being called. Since The Resources page (see Figure 7) of the TTabbedNotebook
memory was previously allocated, we can use a simple component shows the currently available or used amount of
assignment statement. As before, when a call to GlobalNext some of the more interesting Windows resources. Each time
fails, the walk is complete. the user clicks on the Resources tab, the values are
refreshed.
Finally, Perseus must display the strings formatted during the
procedure TMainForm.TabbedNotebook1Click(Sender: TObject);
walk and initialize a couple of title strings. For Perseus to begin
handle the display of the strings managed by the if TabbedNotebook1.PageIndex = 1 then
GlobalItemString object, Perseus’ RowCount property must be GetResources;
end;
set to the number of strings it will need to display. Since that
number is equal to the number of items on the global heap,
we use the value in TGlobalInfo.wcItems. With Walker, the If a user is running Walker with the Resources page displayed
TStringList.Count property could not have been used to pro- and wants to see updated statistics, a simple click on the R
vide this number. Remember we added MaxGlobalItems to button does the job. Since it only makes sense to refresh the
the string list, which of course, is not the same as the number values when the Resources page is displayed, the R button is
of actual items on the heap. only enabled while this page has focus. The R button’s
enabled state is handled by the OnChange event handler.
When Perseus needs a new string to display, the OnGetItem
event is fired and the string is assigned in the The code that actually gets the values is located in the
VListBox1GetItem event handler method. GetResources method in the uTlHelp unit (see Listing Seven
beginning on page 45). Two new ToolHelp data structures are
procedure TGlobalW.VListBox1GetItem(Sender: TVListBox; initialized here: TMemManInfo and TSysHeapInfo.
Index: Longint; Col: Integer; var Strg: OpenString; TSysHeapInfo provides the free percentage of GDI and User
State: TOwnerDrawState; var Font: TFont; resources, while TMemManInfo provides the number of virtual
var BGColor: TColor);
begin
pages and free virtual pages. TGlobalInfo, which was introduced
Strg := GlobalItemString.Strings[Index]; in the Global Walker module, provides information about the
end; total items and free items on the global heap. The “Free Mem”
and “Max Mem” values are returned by run-time Object Pascal
When browsing the list of items, the user can double-click on functions. (See Delphi online help for more details.)
an item to see a memory dump of the segment (see Figure 6).
Again, because of the efficiency with which Perseus displays Odds and Ends
strings, it’s used to display the memory dump. The size of A common practice among component vendors is to ship
any memory dump is limited to 64K. Note that an item on completely working copies of their components for trial pur-
the global heap can be larger than 64K, but a segment of poses. Such trial components give the developer the opportu-
such a size would be quite rare. nity to fully evaluate the component. There is just one “catch”

Delphi Informant April 1996 43


The API Calls
— trial components will only work while Delphi is running. form will be disposed differently than what the examples illus-
This is true for Perseus, the virtual listbox used by Walker. trate). When ShowModal returns, free the memory allocated.

So how does the component know that Delphi is running? Why Walker, and Where to Now?
There is no IsDelphiRunning function. At least one way to Walker came about because of an interest in learning more
find out if Delphi is loaded is to use the ToolHelp function, about how a developing application was working in Windows.
ModuleFindName. Walker uses this technique in the Hopefully, if you spend some time with Walker, you’ll gain
MainForm.FormShow method (see Figure 8). This is because some insight into the way that Windows works as well.
if a form that is using the trial version of Perseus is loaded
when Delphi is not running, Perseus will display a polite There is still a lot that can be done with this program. For
message saying it cannot be used outside Delphi. It will then one thing, the User and GDI Walkers need to be written.
close the application. A hex dump routine could be written for all the local
heaps, and a tool could be developed for monitoring stack
procedure TMainForm.FormShow(Sender: TObject); space. Beyond that, Walker could be made application
begin specific. That is, Walker would monitor just the resources
GDI1.Enabled := False;
User1.Enabled := False; used by an application (that is part of the purpose of the
SpeedButton4.Enabled := False; Options page). With further development, Walker could
BitBtn10.HelpContext := 0; show the developer resources that haven’t been properly
{ If Delphi isn’t running, disable Global Button
and menu item. disposed after the application terminates. If anyone makes
Note: Global Walk depends upon Perseus, a virtual any of these enhancements, please send them my way!
list box component that must be registered
separately to run when the Delphi IDE is not running. }
pOurModuleEntry := InitModuleEntry(pOurModuleEntry); References and Acknowledgements
if (ModuleFindName(pOurModuleEntry,'DELPHI') = 0) then A great deal of the material this article is based upon came from
begin the book, Undocumented Windows by Andrew Schulman, David
SpeedButton7.Enabled := False;
Global1.Enabled := False; Maxey, and Matt Pietrek [Addison-Wesley, 1992]. I would like
end; to thank Yon Barrenechea who recommended this text to me,
Dispose(pOurModuleEntry); and Peter Below for seconding the recommendation. They were
end;
right on the money with their advice. Additional information
Figure 8: The MainForm.FormShow method detects if Delphi is
came from Charles Petzold’s Programming Windows 3.1
running. [Microsoft Press, 1992].

In the case of Walker, we did not want to prevent anyone I’ve known about these books for a long time, but I never both-
from using the program when Delphi was not loaded. In ered to read them because I’m a Pascal/Delphi programmer at
the FormShow method, the code checks if Delphi is run- heart (I’ve not written a line of C in 11 years) and I was put off
ning. If not, then the Global walk button and Global menu by their C orientation. I figured that I would just buy the
item are grayed. This prevents the user from selecting Delphi or Pascal books that interested me. This was a mistake.
Global Walk and then receiving a message saying that
Perseus is not registered, and having Walker close down. Unfortunately, the topic of Windows architecture is not cov-
Naturally, you can use the same technique for any trial ered in-depth in any Pascal-specific book that I’ve encoun-
components that you want to ship. tered. So if you have even a passing interest in how Windows
works “under the hood,” pick up a copy of one or both of
If you happen to select Options | Project from the Delphi these books. And don’t worry about the C code if you don’t
menu, you’ll notice that the only form that is automatically know C. It’s not that tough to follow. ∆
created is MainForm. All other forms are created as Walker
needs them. This saves from having to allocate memory for The demonstration Walker program and trial version of Perseus are
the forms before they are used. It also allows Walker to available on the Delphi Informant Works CD located in
load faster. To see how a form is created/allocated manually INFORM\96\APR\DI9604KT.
as needed, look at the code in each of the XXXXWalker
methods in uTlHelp.
Karl Thompson is an independent Paradox and Delphi developer serving clients
The technique is very simple. Call Application.Create, passing it from New York City to Philadelphia. He has been writing applications using some
the type of form and the variable instance of the form. Call a vintage of Borland’s Pascal since 1984. He can be reached at (800) 242-9192,
procedure that will eventually call ShowModal (or Show if the or on the Internet at [email protected].

Delphi Informant April 1996 44


The API Calls

Begin Listing Seven — TMainForm.GetResources


Begin Listing Six — TGlobalW.FormShow
procedure TMainForm.GetResources;
procedure TGlobalW.FormShow(Sender: TObject);
var
var
OK: Bool;
OK: Bool;
begin
Counter: Longint;
Label7.Caption := format('%4.1nmb',[MemAvail/OneMb]);
begin
Label8.Caption := format('%4.1nmb',[MaxAvail/OneMb]);
{ Show hourglass mouse pointer }
pOurSysHeapInfo := InitSysHeapInfo(pOurSysHeapInfo);
Screen.Cursor := crHourglass;
pOurMemManInfo := InitMemManInfo(pOurMemManInfo);
try
pOurGlobalInfo := InitGlobalInfo(pOurGlobalInfo);
GlobalItemString := TStringList.Create;
if SystemHeapInfo(pOurSysHeapInfo) then begin
{ Allocate the strings before! walking Global Heap }
Label5.Caption :=
for Counter := 0 to MaxGlobalItems do
Format('%d%%',[pOurSysHeapInfo^.wGDIFreePercent]);
GlobalItemString.Add('');
Label6.Caption :=
pOurGlobalEntry := InitGlobalEntry(pOurGlobalEntry);
Format('%d%%',[pOurSysHeapInfo^.wUserFreePercent]);
pOurGlobalInfo := IniTGlobalInfo(pOurGlobalInfo);
end
GlobalInfo(pOurGlobalInfo);
else begin
OK := GlobalFirst(pOurGlobalEntry, Global_All);
Label5.Caption := 'N/A';
if not (OK) then
Label6.Caption := 'N/A';
MessageDlg('Error getting Global information...',
end;
mtWarning, [mbOK], 0);
if MemManInfo(pOurMemManInfo) then begin
Counter := 0;
Label14.Caption :=
while OK do begin
Format('%d',[pOurMemManInfo^.dwTotalPages]);
GlobalItemString.Strings[Counter] :=
Label16.Caption :=
Format(' %4.4x %5x %-14s %-5d %-14s',
Format('%d',[pOurMemManInfo^.dwFreePages]);
[pOurGlobalEntry^.hBlock,
end
pOurGlobalEntry^.dwBlockSize,
else begin
GetModuleNameFromHandle
Label14.Caption := 'N/A';
(pOurGlobalEntry^.hOwner),
Label16.Caption := 'N/A';
ord(pOurGlobalEntry^.wHeapPresent),
end;
GetGlobalBlockType(pOurGlobalEntry^.wType,
if GlobalInfo(pOurGlobalInfo) then begin
pOurGlobalEntry^.wData)]);
Label12.Caption :=
inc(Counter);
Format('%d',[pOurGlobalInfo^.wcItemsFree]);
OK := GlobalNext(pOurGlobalEntry, Global_All);
Label13.Caption :=
end;
Format('%d',[pOurGlobalInfo^.wcItemsLRU]);
finally
end
Screen.Cursor := crDefault; { Show default pointer }
else begin
end;
Label12.Caption := 'N/A';
VListBox1.RowCount := pOurGlobalInfo^.wcItems-1;
Label13.Caption := 'N/A';
GroupBox1.Caption :=
end;
' Handle Size Owner LHeap Type ';
Dispose(pOurGlobalInfo);
GlobalW.Caption := ' ' +
Dispose(pOurMemManInfo);
IntToStr(VListBox1.RowCount)+' Global Items Found ';
Dispose(pOurSysHeapInfo);
end;
end;
End Listing Six
End Listing Seven

Delphi Informant April 1996 45


New & Used
By Douglas Horn

DFL’s Light Lib Series


Two Delphi VCLs That Know What to Do with Data

s Delphi’s popularity among developers skyrockets, many third-party soft-


A ware developers are taking notice. These developers of programming tools
are now releasing VCL controls designed for, and written in, Delphi. Such
native VCLs are generally more compatible and easier to implement in Delphi
applications than VBX and DLL controls that are designed to be used by any
number of development platforms.
Two new Delphi-specific controls are now Whether installing the Business or Images
available from DFL Software, Inc. These package, when the installation routine begins,
controls, Light Lib Images and Light Lib it unexpectedly informs the user that, “The
Business, promise powerful, nearly effortless following Light Lib components will be
implementation of data-aware image han- installed: Light Lib Objects, Light Lib Images,
dling and business graphing, respectively. Light Lib Business, Light Lib Multimedia.”
Although each has a few minor drawbacks,
the components provide interesting and The program then asks for the serial number
needed solutions to problems that many and hard disk location for installation, then
Delphi developers may be experiencing. asks what versions to install: Delphi,
Microsoft Foundation Classes, Microsoft
DFL’s tools also support other programming Visual Basic, and/or CA-Visual Objects.
environments, such as C/C++, Visual Basic,
and CA-Visual Objects. However, unlike The peculiarity begins after the installation,
many other products that try to cover all the when not only the Business (or Images)
bases, DFL has actually provided language- tool is installed, as the user intended, but
specific components and class libraries for also an evaluation version of the other
each environment, and the standard and pro- VCL. If the user tries to install the other
fessional editions include the source code to VCL into the same directory in order to
the VCL components. The professional edi- delete the evaluation version and save some
tions are reviewed here. disk space, he or she is informed that the
existing directory will be deleted. Actually,
Installation the program will only overwrite the evalua-
While the people at DFL tried to make the tion versions, but without some sort of
Light Lib components as easy as possible to documentation, the user is left wondering.
install and use, the installation is in fact
rather puzzling, especially for users who want Apart from this quirk, the installation rou-
to use both of the separately sold compo- tine gets most of the job done (albeit slow-
nents. No printed documentation comes ly). After the necessary files are installed,
with the components. This does not hurt the the user is instructed how to merge the
product because the online help files are very Light Lib and Delphi help files, and how
strong, and they’re tied directly into Delphi’s to add the new controls to Delphi’s
help system. Although this review is very Component Palette. The Light Lib tools
much in favor of this trend in documenta- are added to their own page of the
tion, its Achilles’ heel presents itself when the Component Palette, but using standard
installation routine becomes unclear and Delphi commands, they can be moved
there is no documentation to reference. anywhere the user chooses.

Delphi Informant April 1996 46


New & Used

Light Lib Images This flaw aside, the VCL’s native BLOb (Binary Large
The Images VCL consists of a single component, Object) handling is very useful for storing large numbers of
TImageWindow. It contains an area for displaying images, images. TImageWindow offers three choices for BLOb com-
and a toolbar that allows users to access and manipulate those pression: none, speed-optimized, and size-optimized.
images. As Delphi developers would expect, this toolbar can
be configured or concealed altogether. The component’s inte- Unfortunately, because of the proprietary format and the
grated scrollbars and progress gauge can also be hidden. need to use Binary fields rather than Graphic fields, the Light
Lib BLObs are not compatible with existing image BLObs in
TImageWindow allows a wide range of image operations, includ- Paradox tables. TImageWindow can write and retrieve its own
ing: scanning or opening existing images; zooming; scaling; BLObs from Paradox tables, but cannot read those that have
rotating and flipping; dithering; and printing. The component been input using TDBImage components. The reverse is also
also handles conversion between any of its supported image for- true of TDBImage components and Light Lib BLOb images.
mats. The Standard Edition supports BMP, PCX, TGA, TIF, Although DFL says this will be fixed in future releases, users
and PNG. (The public domain PNG format replaces the wide- of existing databases must re-enter all their database images if
spread but proprietary GIF format.) The Professional Edition they use the Light Lib component rather than Delphi’s.
adds GIF, JPG, and Light Lib’s own BLOb format.
Light Lib Images includes a few other features that add to its
Using TImageWindow as a standard image viewing compo- usability. The first is a stripping algorithm that divides images
nent is useful, but not particularly ground-breaking. Much into 32K memory blocks before processing. The result is faster
more interesting is the fact that TImageWindow is a data- loading and display than is available in many other image view-
aware component. Using its DataSource and DataField prop- ers. Even large JPEG images load quickly. The component also
erties, developers can attach TImageWindow to a database just takes advantage of what it calls “intelligent dithering” to
as they would Delphi’s native DBImage component. improve image quality. This allows users to save hi-color images
However, by using the TImageWindow, developers can access as 16-color images with surprisingly little image degradation.
images from any of the supported formats, or from TWAIN
(Technology Without An Interesting Name — no kidding!) Although Light Lib Images is a Delphi-specific VCL, in some
compliant scanners. This is an excellent feature for databases areas it strays from some of Delphi’s established norms, to the
that require a large number of images (see Figure 1). chagrin of Delphi users. For example, the ShowToolbar prop-
erty would best be implemented as a nested property, much
The traditional method of entering images into a TDBImage as TDBNavigator’s VisibleButtons property is. Instead, double-
component uses the Windows Clipboard to cut and paste clicking the ShowToolbar property calls a dialog box with
them into a database. The TImageWindow component elimi- numerous check boxes (see Figure 2).
nates this requirement and makes image-intensive databases
easier to use and implement in Delphi. While functional, this variance from the norm does stand
out. More troublesome is the fact that the ImageName and
It does have a shortcoming, however, in that images must be ImagePath properties used to set the image (when not in data-
saved to a Binary type field rather than a Graphic type field aware mode) do not call Open File dialog boxes. Rather than
(in Paradox for Windows tables). Also, to save an image to browsing for the desired file, the developer must manually
the database, the TImageWindow’s Save button must be add the path and file name.
pressed. The more Delphi-esque way of handling this would
be to automatically update the image when the record is post- Light Lib Business
ed. In fact, this can be added programmatically by trapping As with Light Lib Images, Light Lib Business consists of a
the Post procedure and calling TImageWindow’s Save method. single data-aware component. The component,
However, the documentation does not mention this possibili- TGraphWindow, has one claim to fame — it turns live data
ty, so developers are on their own. into live graphs (see Figure 3). For example, developers can
connect a table or query to a DataSource with a DBNavigator
component. Adding a TGraphWindow component will then
allow them to scroll through their database, viewing graphs of
Figure 1: their data in real time.
Light Lib Images’
TImageWindow
component allows Delphi
TGraphWindow has many strengths, but a few weaknesses
developers as well. The graphing is surprisingly quick — fast enough
to scan and import to render complex graphs in the time it takes to do a basic
images into a screen redraw. In addition, the graphs are fully end-user
database. configurable by default. When a user clicks on a section of
the graph, the component is intelligent enough to deter-
mine what section has been selected (vertical or horizontal
axes background, bar, etc.), and open the dialog box con-
trolling that section.
Delphi Informant April 1996 47
New & Used

types it offers. The standard


version includes bar, line,
pie, and stacked graphs,
which may be either two-
or three-dimensional, filled
Light Lib Images is a data-aware image scanning,
or non filled. The viewing, and conversion VCL component. This ster-
Professional Edition allows ling tool is only slightly tarnished by its rather non-
Delphi way of doing things. It’s a vast improvement
mixed line and bar graphs, over Delphi’s own TDBImage component, making
image-intensive database development a real pos-
and stock tracking graphs. sibility in Delphi.
Still, this variety is rather Price: VCL only (no source code) US$149;
sparse compared to many Standard Edition US$295; Professional Edition
US$495.
packages on the market.
Light Lib Business offers data-aware graphing in
Delphi database applications. This Delphi VCL
The graphs supplied do offers fast graphing, and can be connected to
tables and queries via the DataSource property.
cover a wide range of situa- The current version could be improved with a wider
Figure 2: The Light Lib VCLs behave in a non-Delphi-esque way. variety of graph types.
tions, but simply are not
This dialog box, for example, would be more appropriate as a Price: VCL only (no source code) US$149;
numerous enough to satisfy Standard Edition US$249; Professional Edition
nested property on the Object Inspector.
the sophisticated graphing US$449.
needs of many end-users. DFL Software, Inc.
55 Eglinton Avenue East, Suite 208
Toronto, Ontario, Canada M4P 1G8
The Rest Voice: (416) 487-2660
One real advantage the Fax: (416) 487-3656
BBS: (416) 487-4041
Light Lib tools offer is that E-Mail: Internet: [email protected]
they include complete CompuServe: GO DFLSW
Web Site: http://www.dfl.com
source code to the VCL
components. In point of
fact, these components are actually very complex Pascal
wrappers that interface with Light Lib’s object-oriented
DLL, the source code for which is not provided.

However, the source code that developers really need is avail-


able. The primary PAS file for Light Lib Business, for example,
contains over 6,500 lines of code. As the source code is fairly
well documented, many of these lines are program comments.

DFL’s technical support line is a toll call, but is not likely


to leave customers stuck in voice mail, and the technical
support personnel do a good job of solving problems.
Technical support is also available via fax, CompuServe,
Figure 3: Light Lib Business (Professional Edition) allows 2D and
DFL’s BBS, and the World Wide Web. DFL issues online
3D line and bar graphs to be combined, but would be much
improved with more graph types. upgrades free of charge to users. (According to one DFL
representative, many of the concerns addressed in this
The TGraphWindow component includes a toolbar, which review will be resolved in upcoming online releases.)
the developer can configure or hide. Using the
DialogAccess property, the six graph-configuration dialog Light Lib Images could be a real godsend to anyone develop-
boxes can be set as either accessible or inaccessible. Other ing image-intensive databases in Delphi. The product’s data-
nice features include auto-scaling and auto-sizing of aware and BLOb compression capabilities make it a natural
graphs, the ability to add and change graph legends, and choice for such applications. As a graphing tool, Light Lib
the ability to modify graph scale. Business is short on graph types. However, its ability to tie in
seamlessly with Delphi’s DataSource property make it a
One excellent feature of TGraphWindow is its ability to save strong contender in the Delphi graphics world. ∆
graphs as BLObs that can then be saved to disk or to a data-
base. These BLObs encapsulate the attributes of the graph so
that future users can see the data exactly as it was first pre-
sented, without having to reformat the graph. Douglas Horn is a free lance writer and computer consultant in
Seattle, WA. He specializes in multilingual applications, particu-
Light Lib Business shares some minor flaws with Light Lib larly those using Japanese and other Asian languages. He can be
Images, but its major drawback is the number of graph reached via e-mail at [email protected].

Delphi Informant April 1996 48


File | New
Directions / Commentary

It Doesn’t Cure the Common Cold


on’t believe everything you read. Not only will the Web not cure the common cold, it is not about to replace most
D client/server applications either. Listening to some industry prognosticators, however, you would think that we might as
well trade in our Pentiums for Internet terminals. The argument is that you will soon forget about running applications local-
ly, opting instead for programs that arrive courtesy of the Web. I beg to differ. A maturing PC marketplace is moving towards
“enabling” technologies, not “replacing” technologies. As with the local area network in the 1980s, the Web should be consid-
ered an extension of your local operating system, not as its successor. After all, the whole PC revolution brought decentraliza-
tion to the computer world; I find it a rather cheeky notion that we would ever tolerate a return to a centralized framework.
What then does the Web mean to you Providing an alternative advertising medi- sophisticated user interfaces that Delphi
as a Delphi developer? If it will not um. Over the past six months, you have and other visual development tools can
replace your desktop, is it important to probably begun to see ads pop up every- provide. Any HTML-based application has
pay attention to? Definitely. The Web where on the Web. Just how effective the a meager supply of UI controls available to
holds an opportunity to reach domains Web is as an advertising medium remains to them. Even future enhancements to the
that mainstream desktop and client/serv- be determined, but I suspect it will do well HTML language will not anytime soon
er applications would never work in. at reaching targeted audiences. While Web catch up to a Windows 95 Treeview or
Therefore, rather than making your advertising is often static HTML pages, Listview control. Perhaps as Java visual
Delphi programming skills obsolete, the there will be an ever increasing number of tools like Borland’s Latte or Symantec’s
Web offers an occasion for you to use advertising-oriented applications on the way. Espresso begin to perk, you will begin to
those talents in new areas. Savvy devel- A second area in which the Web excels as see better UIs available across the Web. But
opers will embrace the Web, carefully an application environment is in Intranets. even if they can provide the presentation
deciding when and how to use it as a Intranets use essentially the same plumbing depth and richness that Windows users are
development environment. as the Internet, but they are private — now demanding, such a solution would
Intoxicating Connectivity. While its restricted to an organization or groups with- come at a cost: the greater the complexity
global connectivity is intoxicating, the in a company. These systems are ideal for of a Java application, the longer it will take
Web is suited for a relatively narrow genre Fortune 500 or multinational companies to download onto the client.
of applications. But within these camps, that want to improve corporate communi- Further, Web applications are ultimately
no other technology gives you more cations within the face of geographical and dependent on the speed of the transmis-
“bang for the buck.” Perhaps the most multi-platform challenges. Not only can sion line. A direct or ISDN connection to
striking case is an application that needs Intranets serve as a corporate mouth organ, the Internet may be becoming ubiquitous
to reach an external and diverse user base. they also provide a fertile environment for for Fortune 100 companies, but most
Some examples include: groupware applications (à la Lotus Notes). small- to medium-size businesses continue
Disseminating information. FedEx illus- Without the Web as a backdrop, you to use 14.4 or 28.8 baud access to the
trates the Web at its finest with its really have no practical way of using Delphi Web. And even if you have a blazingly fast
Package Tracking system (http://www.- to develop solutions in these realms. connection, heavy network traffic can
fedex.com/track_it.html). No matter who Strings Attached. The use of the Web as plague application performance.
you are, if you have access to the Web, an application environment is convincing Therefore, when designing Web applica-
you can find out the status of a package when you need to appeal to a wide or tions, you are often forced to think in
you sent. diverse audience, but it is not without a terms of simplicity, compromising usabili-
Gathering information. Head hunters price. Quite literally, Web applications have ty and high performance to maximize your
and placement firms provide an example strings attached to them: they rely on a syn- use of limited bandwidth.
of how the Web can be used to allow chronous means of communication in order Shed the hype. Look at the Web realisti-
potential employees or contractors to sub- to work. Without a connection, you cannot cally. The Web offers newfound power, but
mit a resume or employment application run the application. However, not only are it is not our panacea. Just like other revolu-
via the Web. most business PCs still not connected to the tionary technology advances that came
Providing a direct sales line to consumers. Web, but there are many contexts in which before it, the Web augments, not replaces,
Web applications provide companies a that scenario just does not work. For exam- our development environment.
direct sales line to its customers. For intan- ple, an application for a remote salesperson’s
gible goods like software, the Web serves as notebook is likely to involve distributed Richard Wagner
a store front as well as the delivery channel. data. But without mature and affordable
For tangible goods like Godiva chocolates wireless technology, the best solution Richard Wagner is the Chief Technical
(http://www.godiva.com), a Web applica- remains asynchronous replication with the Officer of Acadia Software in Boston,
tion offers a convenient alternative to visit- home office, not the Web. MA. He welcomes your comments at
ing a retail store or ordering by phone. Web applications also cannot match the [email protected].

Delphi Informant April 1996 49

You might also like