You are on page 1of 34

October

2003

Volume 9 Number 10

INSIDE
ON THE COVER
In Development

Totally Random

Producing truly random numbers via software


is not straightforward. Fortunately, we have
Fernando Vicaria to explain the difficulties and
share comprehensive solutions to the challenge,
with four demonstration programs and an impressive
collection of resources.

FEATURES
On Language

Regular Expressions and .NET

Regular expressions have been around in UNIX


forever, but most Windows developers arent familiar
with their powerful text-handling capabilities. The .NET
Framework changes all that, explains Bill Todd, who
introduces regular expressions, then examines the
.NET classes that implement them.

Cover Art by Arthur A. Dugoni Jr.

Greater Delphi

14

Going Native: Part II

Last month, Keith Wood introduced the Java Native


Interface and showed us how to call Delphi routines
from Java apps. This month he turns the tables with a
demonstration program that provides a Delphi GUI to
the JavaD tool, so you can generate Delphi skeleton
files for JNI from Java classes.

Delphi at Work

17

For the Record

Simon Murrell shares his Event Viewer Demo


application, demonstrating use of the EventLog class
that lets you access the Windows NT/2000 event
logs, which are in turn very useful for application
development, maintenance, and reporting.

REVIEWS
29 VMware Workstation 4.0
Product Review by Bill Todd

Special Preview Edition

2 C#Builder for Delphi

Developers

Its okay! Just because youre a Delphi


developer doesnt mean you cant check
out C#Builder. So go ahead and read
along as Borland R&D engineer Corbin
Dunn introduces C#Builder from a Delphi
developers perspective, explaining the
similarities and differences of the two tools.

6 Whats New in C# 2

D E PA R T M E N T S
2 Toolbox
33 File | New by Alan C. Moore, Ph.D.

DELPHI INFORMANT MAGAZINE | October 2003

When the next version of Visual Studio .NET


(code-named Whidbey) is released, youll get
a new, upgraded C# language. Bill Wagner
explains how generics and iterators will help
you write less code that does more.

T O O L B O X
TestComplete 3.0 Available

AutomatedQA announced the launch


of TestComplete 3.0, an automated
testing toolset with full support for
unit testing, functional testing, and
regression testing. TestComplete
offers systematic, automated, and
structured testing, with native
support for Delphi, C++ (Visual
C++ and C++Builder), Visual
Basic, VS .NET, Java, and Web
applications.
TestComplete gives external test
scripts access to all onscreen VCL
elements, with their properties and
even their methods. Optionally, it
gives access to all published elements
of the Delphi application under test

and gives scripts practically the same


access to application internals as the
Delphi IDE.
Scripts can be recorded or coded
in DelphiScript, VBScript, JScript,
or C++Script for replay from
TestComplete. Scripts in DelphiScript
can also be used for insertion into
Delphi source code and they can
be coded directly into a Delphi
application. TestComplete is an OLE
server and can be run from a Delphi
application; a Delphi application can
also be run from TestComplete.
TestComplete 3.0 offers support
for automation of unit, functional,
and regression testing. All results
are stored in a
customizable,
persisted log.
TestComplete
supports Web
application
testing, datadriven testing,
and team
testing. It
also offers an
extensive plug-in
API architecture.
TestComplete
3.0 introduces
several new
modules,

Gnostice Announces eDocEngine


Gnostice announced eDocEngine, a
100% pure VCL electronic document
creation component suite for Delphi
and C++Builder.
eDocEngine provides VCL components for the creation of more than 20
electronic document formats, enabling
developers to deliver information
straight from the applications they
develop. Rich content can be formatted
to the precise look and feel required
and delivered directly to users Web
browsers, sent as e-mail attachments,
or saved to disk. eDocEngine does not
use any external software or require
any DLLs to be deployed for the creation of documents.
The developer can place the components on a Form or DataModule, set
properties to customize the output,
then call methods to insert text; draw
shapes, charts, and images; render
2

DELPHI INFORMANT MAGAZINE | October 2003

metafiles; and place electronic form


elements. The final output can be
received in a memory stream or a disk
file. Built on an open, extensible architecture, eDocEngine integrates smoothly with reporting tools and other Web
application development tools.
All document engines of eDocEngine support a vast set of common
properties and methods and the
classes are layered in such a way
that efficient OOP techniques can be
applied to create documents in multiple formats with a single code base.
Advanced features unique to each
document format are supported by
additional properties and methods of
that engine.
Gnostice Information Technologies
Price: Check the Gnostice Web site.
Contact: info@gnostice.com
Web Site: www.gnostice.com

including HTTP Load Testing (which


allows for load, stress, and scalability
testing of Web servers) and Network
Suites (which allow for the ability
to run test projects on multiple
computers simultaneously). Version
3.0 also offers improved unit testing
support, testing of CORBA objects,
and improved support for .NETconnected applications.
AutomatedQA Corp.
Price: TestComplete 3.0 Professional, US$349;
TestComplete 3.0 Enterprise, US$599;
upgrades and multi-user discounts are available.
Contact: sales@automatedqa.com
Web Site: www.automatedqa.com

Developer Express
Upgrades
ExpressBars Suite
ExpressBars Suite 5 is now available
from Developer Express. A
toolbar, menu, and docking system
for Delphi and C++Builder,
ExpressBars Suite 5 allows you to
bring to your applications the user
interface elements and application
customization features introduced
in Microsoft Office (including
the yet unreleased Office 11) and
Windows XP.
ExpressBars Suite 5 features an
advanced toolbar/menu system, as
well as Developer Express newest
component library, ExpressDocking.
The ExpressDocking Library gives
you the ability to create flexible
docking applications with only a
few mouse clicks. You can create
complex interfaces like those in
the Delphi IDE, Visual Studio .NET
IDE, and others; simply place a
dock site on the form and add dock
panels populated with your choice
of VCL controls. The rest is done
automatically. Simply drag and
drop to dock panels to sites, float
them, or dock them to each other
to form complex dock controls,
including tab containers and side
containers.
Developer Express
Price: US$179.99 (single developer license,
includes source); US$129.99 (single developer
license, without source).
Contact: info@devexpress.com
Web Site: www.devexpress.com

T O O L B O X
DScriptVCL Provides Winamp Look and Feel

DelphiScript announced DScriptVCL,


a set of visual components for Delphi
5, 6, and 7 that allows developers to
build applications with a Winamp
look and feel.
DScriptVCL features more than 20
visual components, including menus,
panels, LEDs, numeric displays, progress bars, buttons, timers, checkboxes, and other graphical components.
With these components you can
incorporate snap to grid, minimize
and maximize windows, LED viewers,

bar graphs, LCD displays, and more


into your Delphi applications.
DScriptVCL is completely free for any
usage, including personal and professional developments. DScriptVCL ships with
a Getting Started tutorial; documentation
in English, French, and German; and
a downloadable program that demonstrates how to use all the components.
DelphiScript
Price: Free
Contact: dscriptvcl@delphiscript.com
Web Site: www.dscriptvcl.com

QuickUML 1.1 for Windows and Linux


Excel Software is shipping
QuickUML 1.1 for Windows
and Linux. QuickUML is an
object-oriented design tool that
provides integration and synchronization of a core set of
UML models. A tabbed window
provides access to the entire
project, including use cases,
class models, object models,
dictionary, and code.
Use cases capture the essence
of user-visible functions the system must perform. Class models
describe objects in the system
and various kinds of static relationships. Sequence diagrams illustrate
how objects interact and emphasize
the order in which things occur. The
project is saved as a platform-independent XML file that can be edited from
Windows or Linux.
QuickUML 1.1 for Windows adds
Windows XP enhancements, a new
online help system, and an image
export feature to save class and object
diagrams as bitmap, JPEG, or GIF files.
QuickHelp replaces WinHelp for context-sensitive QuickUML application
help and provides users with a consistent, familiar help interface across all
Windows and Linux platforms. Other
enhancements in QuickUML 1.1 for
Windows include an improved contents view for class and object models,
support for UNC file names, and a
toolbar to access commonly used code
manager commands.
QuickUML 1.1 for Linux adds
improved font handling, an enhanced
contents view for class and object

DELPHI INFORMANT MAGAZINE | October 2003

RemObjects SDK 2.0


and Client SDK for .NET
RemObjects Software announced the
immediate availability of RemObjects
SDK 2.0 for Delphi and Kylix.
This version of the RemObjects SDK
remoting framework includes enhancements and new features, such as session
management, a proxy registry, a UDP
channel/server, support for asynchronous channels, and more, as well as
new Enterprise-level features, such as
advanced new channels, database session management, server discovery,
asynchronous communication, and more.
It also features improved documentation
and an IDE-integrated reference.
RemObjects SDK 2.0 is available in two
editions, Standard and Enterprise (available as part of the Enterprise SDK).
RemObjects also announced RemObjects Client SDK for .NET, available
for C#Builder and Visual Studio .NET.
RemObjects Client SDK for .NET signals the first step toward providing a
seamless cross-platform solution for
both Delphi/Kylix and .NET, client- and
server-side.
RemObjects Software, Inc.
Price: See the RemObjects Web site for details.
Contact: info@remobjects.com
Web Site: www.remobjects.com

models, and a toolbar to access code


manager commands. QuickUML 1.1
for Linux has the same features as
the Windows edition and also uses
QuickHelp to provide context-sensitive
application help.
Both versions add enhancements for
multiple platforms and programming
languages. The QuickUML project file
stores platform-independent UML data
plus platform-specific text fonts, sizes,
and file paths for Windows and Linux.
QuickUML supports flexible text-handling features on each platform to
accommodate line-ending differences
on Windows (CRLF) and Linux (LF).
During design, language-specific details
like attribute data types and operation argument lists can be defined for
Delphi, C++, Java, Ada, and Visual
Basic, and then listed to text as a programming specification.
Excel Software
Price: US$495; upgrade pricing is available.
Contact: info@excelsoftware.com
Web Site: www.excelsoftware.com

List & Label Supports


C#Builder
combit has introduced support for Borland C#Builder to its List & Label 9.0.
List & Label is a database-independent
development tool for report, label, and
form printing functions, as well as Web
reporting. Using List & Label, applications can be equipped with functions for
reports, forms, lists, labels, and statistics.
And the integrated .NET component
incorporates complete List & Label functionality for .NET.
Among other upgrades, new in List &
Label 9.0 is the choice of dockable or
floating tool bars and tool windows. It
also now sports an optional XP-style look
and feel. Other improvements include an
RTF editor, native Excel export, encryptable project files, and more.
List & Label 9.0 ships in Standard and
Professional editions; both can be used
with all programming languages.
combit GmbH
Price: Check the combit Web site.
Contact: pr@combit.net
Web Site: www.combit.net

I N

D E V E L O P M E N T

RANDOM NUMBER GENERATORS


TESTS FOR RANDOMNESS
MICROSOFT CRYPTOGRAPHY API

By Fernando Vicaria

Totally Random
Getting a Truly Random Number

ecurity and scientific-related applications


often need to use high quality, randomnumber generators for statistical analyses,

mathematical simulations, modeling, and even


obscure, hard-to-break encryption algorithms.

A few months ago I started working on an article on


genetic algorithms, and found myself getting strange
results that didnt seem to fit the theory. In particular,
the results werent converging to an optimum solution,
or in the best case they werent doing so as quickly
as I expected. I started looking for the culprit in my own
code, but couldnt find anything that could affect the
results enough.

To make a long story short, I found that the chromosomes,


represented by series of random numbers, or genes, werent
as random as I expected when compared to one another,
even though the numbers inside them were. We just had to
update the Random function to cater to this specific case,
and all was well again. But it got me started on random
numbers, so...
In this article I will introduce you to a bit of the theory
behind random numbers, show you how to generate them
in Windows, and demonstrate how to test them to ensure
theyre really as random as possible. I hope you find the
information useful, but I must stress that any use of the
code provided in this article is at your own risk.

What Is a Random Number?


A random number cannot be predicted in advance. RanTo show you how desperate I was, I even decided to code
dom numbers can be produced by physical processes,
a similar example in Java. And it worked! This gave me
such as throwing a dice, flipping a coin, or counting
the clue that whatever was
intervals between radioaccausing my Delphi applicative decay events. By itself,
By itself, software cant
tion to misbehave had to
software cant generate truly
generate truly random numbers;
be in the Delphi RTL (runrandom numbers; instead, it
instead, it creates what are called creates what are called pseutime library). It took some
pseudo-random numbers, starting do-random numbers, starting
time, but I eventually traced
the problem to calls to the
from a single random seed.
from a single random seed.
Random function. That was
when I got scared; after all, Random has been in the RTL
To generate a random sequence, we start with a cerfor ages, and I and the rest of the team for that matter
tain seed, and then iteratively apply some mathematical
had never noticed a problem. Later, I found that the
transformation to it (see Figure 1), progressively extractproblem wasnt as bad as I had thought, and was only
ing a random sequence. A sequence is considered ranapparent in fast machines (basically, new machines are
dom when no prediction can be made about it, and no
getting faster than the resolution of some timing APIs),
simple description can be found. Because we generate
and with the specific kind of calculation I was doing for
this sequence from a definite transformation, however, a
my genetic-algorithms article.
description of it will always exist.

DELPHI INFORMANT MAGAZINE | October 2003

In

Development

Totally Random
function Random [ (Range: Integer) ];

var
Seed: Longword = a;
function _Random: Longword;
begin
Seed := Seed*b + c;
Result := Seed shr d;
end;

Note that the Range parameter is optional. The Random


function will return a random integer number, x, between 0
and Range. If Range isnt specified, the result is a Real random
number within the range 0 = x < 1.

Figure 1: A simple implementation for the LCRNG algorithm.

Function

Unit

Resolution

Now, Time, Timer

seconds

1 second

GetTickCount

milliseconds

10 ms

TimeGetTime

milliseconds

10 ms

QueryPerformanceCounter

milliseconds

< 1 ms

Figure 2: Different time accuracy offered by Windows.

Random Number Generators


The recent proliferation of security-related applications has
dramatically increased the need for good Random Number
Generators (or RNGs), especially for Web-related applications
that require passwords, or session keys used in SSL/TLSenabled Web browsers. Other good examples are the many
forms of online gambling.

Calls to the Random function using the Integer parameter


are redirected by the compiler to the _RandInt function,
while parameterless calls are redirected to _RandExt.
(_RandInt and _RandExt are internal functions and should
not be called directly.)
Before we go any further, Id like to make one point very clear.
Because the implementation of the Random function or
that of Randomize for that matter may change between
compiler versions (as it did recently), Borland explicitly advises
users not to use Random for encryption or other purposes that
require reproducible sequences of pseudo-random numbers.
Bad Seeds
Generally, a bad seed is one that can be guessed after a
relatively small number of tries, or in a short time. If a hacker
(or adversary to use a cryptography term) can predict the
value of the constants used in your random algorithm, for
instance the values of a, b, c, and d from Figure 1, the seed
can be computed, and the whole scheme is compromised.

Uniform RNGs are deterministic algorithms that produce


numbers with certain distribution properties. Roughly
speaking, these numbers
Typical examples of
should behave similarly to
this problem can be
Generally, a bad seed is one that
realizations of independent,
found in some earlier
can be guessed after a relatively small implementations of SSL
identically distributed,
random variables. Every
key generation in Web
number of tries, or in a short time.
RNG has its deficiencies,
browsers, which encrypted
however, and no RNG is appropriate for all tasks. For example,
messages with a large sequence of random numbers. Some of
several good RNGs from the toolbox of stochastic simulations
those seed generators would use the time of the day to obtain
are unsuited for crypto-graphical applications, because they
values for the constants used in their algorithms.
produce predictable output streams. A good RNG has also been
thoroughly analyzed theoretically, and is backed by convincing
Such basic algorithms can usually be reversed-engineered
practical evidence such as extensive statistical testing.
using a disassembler, and the time of the day can be known to
within a certain precision often better than a second. This
In Delphi, as in most commercial compilers, random numbers
means that there are only one million possible choices for the
use an algorithm known as Linear Congruential Random
microsecond part (often a lot less because the clocks on most
Number Generator, or LCRNG for short. The algorithm consists computer systems dont have true microsecond resolution). See
of three or more fixed constants, usually large prime numbers,
Figure 2 for the resolution of some Windows API functions.
which have certain mathematical properties.
Note that the values shown in Figure 2 dont take into account
One of the possible implementations for the LCRNG
the overheads of calling those functions. For example, it takes
algorithm could look something like the code in Figure 1.
5 to 10us (microseconds) to call QueryPerformanceCounter,
The values of a, b, c, and d are fixed constants. The formula
because it has to do several port I/O instructions to
is simple and deterministic, and will always result in the
read the computers clock counter. (If you decide to use
same sequence of numbers, as long as you dont modify
QueryPerformanceCounter, remember that it doesnt run at the
the values of the constants. The second line in the body of
same rate on all machines.)
the function is optional, and will only shift the result by a
specified number of bits to narrow the result to a fixed size.
Another good example of a flawed seed generator algorithm
For instance, you could shift the result by 24 bits to force
is one that uses the Process ID (or PID) of an application. You
the values returned to always be between 0 and 255.
should never consider PIDs a secret. PIDs are usually present
in message packets from the system.
Delphi programmers have a Random function thats a bit more
flexible, and will allow you to specify the range within which
You can see that generating a good random number
all the numbers should fall. The actual function declaration in
is difficult. As Donald Knuth said: A random number
Delphis RTL looks like this:
generator should not be chosen at random. A good random

DELPHI INFORMANT MAGAZINE | October 2003

In

Development

Totally Random

Figure 3: GUI for our Runs test implementation.

process isnt just


a complicated,
or obscure,
procedure.
Another quote
(by Auguste
Kerckhoff) states
that Compromise
of the system
should not
inconvenience the
correspondents.
This means a
system should
remain secure,
even if all the
details about its
inner workings
down to the actual
source code are
known to all.

Entropy = Randomness
In general, the importance of generating a good sequence of
random numbers is a bit more relaxed when were working
with mathematical or scientific applications, given that we only
need to fool our simulations or modeling programs into thinking that true random numbers have been used. This approach
isnt possible or is at least unwise when writing security
applications; to fool a strong-willed hacker, or the NSA, you
need to do a lot better than simply shuffling your numbers.
As the name implies, pseudo-random numbers arent really
random; they just seem random. As we discussed earlier,
they are knowable because they come from a mathematical
function, and are therefore not truly random. To avoid this,
we need to introduce genuine entropy; anything derived by
external factors can and should be used as input, e.g.
time between keystrokes, timing of disk interrupts, number
of network packages arrived, etc. The list goes on and on;
on a multi-user system you can use the number of page
faults, the number of disk read/writes, the time to wait until
eligible to get the next time slice any number that is difficult to predict or control.
Typically a cryptographically sound message digest
such as MD5 or SHA (the Secure Hash Algorithm) is computed over an entropy pool and used as your next random
number or seed. Its a good idea to have a tenth to a fifth
as much entropy as the length of the key. For example,
if youre generating a 1024-bit key, you should have
between 100 and 200 bits of entropy. Of course, more is
always better, but its also slower (as you would expect).
Sources of entropy usually involve significant overhead.
(By one bit of entropy I mean the equivalent of a true
random event with two, and only two, possible outcomes,
e.g. a coin toss.)
There are many ways to get true random numbers. Some
methods include making hardware devices that generate
noise, observing cosmic ray flux, and observing light
emissions from trapped mercury atoms. Theyre great
6

DELPHI INFORMANT MAGAZINE | October 2003

in theory, and there are some very high-quality random


generator chips out there, but most of the time we have to
work with existing equipment.
Here is a list of ways to get entropic numbers:
User input: A few examples are time between keystrokes,
mouse movements, etc. Users are among the most entropic
(meaning unpredictable) things there are. The obvious
drawbacks are that it takes time to get entropic responses
from the user, and if you have no user (in the case of a
server), this doesnt help. Nonetheless, if you have a user,
you should use user input, because these are the hardest
for the adversary to acquire or spoof.
Hardware: If you have no user, you have to use your
hardware for a stream of entropy. This is annoying,
but not impossible. Some methods you might use are:
clock, hard disk, video, etc. Most of these devices will
give you a source of nearly true random data, if you
know how/where to get them.
Network: There are all sorts of things on a network that are
unpredictable. The downside is that these methods are as
accessible to other people as they are to you.
Microsoft Cryptography API
Starting with PIII processors, Intel has added support for a
Security Driver that provides software applications the ability to
access the Firmware Hubs hardware Random Number Generator. This RNG is based on sampling the thermal noise in resistors. It uses SHA-1 as a mixing function for the outputs. It also
runs some FIPS 140-1 autotests.
A number of Microsoft Operating Systems are currently
supported by Intel, and you can take advantage of this feature
using Microsofts Cryptography API (or CAPI). To start using
this RNG in your programs, you must first download the
driver from Intel, then read the directions presented in the
CryptoAPI documentation in the Platform SDK. You can find
the necessary drivers from one of the links in the Resources
section at the end of this article. You can also find some good
examples on how to use the CryptoAPI in the SDK.
Some people consider this the ultimate solution. Others
dislike it because all the entropy comes from one source,
albeit a good one.
Testing for Randomness
There are some classic tests that can help you determine
if a sequence of numbers represents a good or bad
approximation of a random sequence. I will present a
few of them so you can test your RNG. Although they may
not be as comprehensive as we might need, theyre easy to
implement and interpret, and will be ideal for sanity checks.
Runs test. The Runs test is a statistical technique used to
determine the degree of aggregation of items ordered in
a sequence, for example, a sequence of eight zeroes and
ones such as 11110000, or 10101010, or any other one of the
255 possible combinations (called a Run). Under the null
hypothesis of randomness, each run has a certain probability
of occurrence that can be computed from the theory of runs.
Basically, a sequence is a random sequence if it is neither
over-mixed nor under-mixed.

In

Development

Totally Random

Figure 4: ENT output.

If a purportedly random sample fails the Runs test, this


indicates that there are unusual, non-random periodicities in
the order of the sample inconsistent with random sampling.
Figure 3 shows a screen shot of our implementation of the
Runs test. In this example we use a confidence level of 95%.
In this simple application we have the option to test the
RTLs Random function or to supply a file with the data
(as long as its a string of zeros and ones). You can easily
modify it to accept any type of data.
Chi-square test. The Chi-square test is the most commonly
used test for the randomness of data, and is extremely
sensitive to errors in pseudo-random sequence generators.
The Chi-square distribution is calculated for the stream of
bytes in the file, and expressed as an absolute number and a
percentage, which indicates how frequently a truly random
sequence would exceed the value calculated. We interpret
the percentage as the degree to which the sequence tested is
suspected of being non-random.
If the percentage is greater than 99% or less than 1%, the
sequence is almost certainly not random. If the percentage
is between 99% and 95% or between 1% and 5%, the
sequence is suspect. Percentages between 90% and 95%
and 5% and 10% indicate the sequence
is almost suspect.

Long Run test.


Over a 20,000bit sample, the
test is considered
successful if
there are no runs
of length 32 or
greater, of either
zeros or ones.

Figure 5 shows a
simple GUI application that executes the Monobit and the Long
Run test on a sample generated with the Random function.
Continuous RNG test. Look for two consecutive outputs
that have the same value. Depending on how the generator
is implemented, you might have repetitions of 8- and 16-bit
values, but definitely not of 32- or 64-bit values.
You can run this test via the command line, passing a file
as a parameter. Figure 6 show the output when running
the test on the file test.txt. The output in the figure shows
a certain degree of predictability, since it shows a few
repeated sequences of 32 bits, i.e. pairs of four elements in
our file that are repeated.

An ENT program, which I bundled


with the source code for this article,
implements this test. To use ENT, simply
pass a file containing the data via the
command-line. Figure 4 shows a typical
output of ENT. For this test, I passed a
file containing a sequence of zeros and
ones generated by the Monobit test.
Monobit test. Over a 20,000-bit sample
(2,500 bytes), the total number of 1-bit
accumulated must be between 9,654 and
10,346. This test will check the number
of occurrences of zeros and ones in a
sample to determine if theyre properly
balanced.
7

DELPHI INFORMANT MAGAZINE | October 2003

Figure 5: The Monobit and Long Run tests.

Figure 6: Continuous RNG test output for test.txt.

In

Development

Totally Random

Another canonical mathematical definition


for randomness is: A string is random
if there is no shorter description for the
string. In plain English, this means we
cant compress the string without loosing
information.
Can we find a shorter description for the
contents of test.txt? Sure we can; just zip
it! Figure 7 shows the result of the same
test, only this time run on the resulting zip
file, test.zip. Now weve got a sequence
of zeros and ones that can be considered
more random than the first. Does that
ring any bells?
We have several programs at our disposal
Figure 7: Continuous RNG test output for test.zip.
for performing more rigorous tests. A
few examples are: ENT (which has been
packed with the code for this article) and the NIST Test Suite.
random.org, www.random.org/essay.html
You can find more tests in the NIST FIPS 140-1 U.S. Federal
Generating a Truly Random Number by Leif Svalgaard,
Standard at: http://csrc.nist.gov/publications/fips/fips140-1/
http://cobolreport.com/columnists/leif
fips1401.htm.
Use QueryPerformanceCounter to Time Code,
http://support.microsoft.com/default.aspx?scid=
Conclusion
http://support.microsoft.com:80/support/kb/articles/
As weve seen, generating truly random numbers is
Q172/3/38.asp&NoWebContent=1
not a trivial task, and trying to do so can cause a lot of
The Cryptography API, or How to Keep a Secret by
headaches. However, there are some guidelines you can
Robert Coleridge, http://msdn.microsoft.com/library/
follow to make sure your RNG is at least as good as you
default.asp?url=/library/en-us/dncapi/html/
need it to be. Its advisable that you run at least one or
msdn_cryptapi.asp
two of the tests mentioned here or any others you
Delphi Cryptography Libraries from Torrys Delphi,
might have as sanity checks.
www.torry.net/cryptstrong.htm
If you want an easy way out you can always use Microsofts
CAPI. Its quite secure and you can always enhance it
by collecting your own entropy and feeding it into the
CryptoGenRandom API function.

Four demonstration projects associated with this article are


available for download to members of the Delphi Informant
Magazine Complete Works CD located in INFORM\2003\OCT\
DI200310FV.

For the low-budget solution, there are a few open source


cryptography libraries for Delphi users. A simple search using
your favorite search engine will return at least a dozen of them.
Resources
Intel Security Driver, www.intel.com/design/software/
drivers/platform/security.htm
Using and Creating Cryptographic-Quality Random
Numbers by Jon Callas, www.merrymeet.com/jon/
usingrandom.html

DELPHI INFORMANT MAGAZINE | October 2003

Fernando Vicaria is a QA Engineer at Borland Software Corporation in Scotts


Valley, CA. He is also a freelance technical author for Delphi and C++Builder
issues. Fernando specializes in VCL and CLX frameworks. When hes not at
work hes probably surfing at some secret spot in Northern California. He can be
reached via e-mail at fvicaria@borland.com.

O N
.NET

L A N G U A G E

REGULAR EXPRESSIONS

By Bill Todd

Regular Expressions
and .NET
Powerful Text-handling Tool Not Just for UNIX Anymore

egular expressions are a powerful text


manipulation tool. Using them, for example, you can validate data by confirming

Regular expressions use the \d metacharacter to represent


a single numeric digit, so a regular expression for a valid
nine-digit ZIP code would be:
\d\d\d\d\d-\d\d\d\d

that a text string matches a pattern. You can


determine if a string contains a substring, and, if
so, how many times and where each occurrence
is located. You can search a string for all substrings that match a pattern and replace each of
the substrings with something else. You can even
parse a string and extract the parts. The magic is
that you can do these things in one or two lines
of code instead of one or two hundred.

Regular expressions are old news in the UNIX world, but they
arent widely implemented in the Windows environment.
.NET changes that by including a set of classes that provide
a very powerful implementation of regular expressions. This
article starts with an introduction to regular expressions, then
examines the .NET classes that implement them.
Atoms, Repetition, and Groups
Regular expressions consist of atoms. An atom is a literal
character, a special character (usually called a metacharacter), or a group of one or more literal and/or metacharacters enclosed in parentheses. Suppose users must enter
a ZIP code. You want to verify that the string of characters the user typed has the form of a valid US ZIP code.
9

DELPHI INFORMANT MAGAZINE | October 2003

To make life easier, regular expressions let you specify a


repeat count for a character or group of characters. Using a
repeat specification you can rewrite the regular expression
for a nine-digit ZIP code as:
\d{5}-\d{4}

The number 5 in braces means that the preceding atom


must occur exactly five times.
You can also specify a repetition range. Suppose you want
to verify that a string has the form of a valid credit card
number that consists of four groups of either three or
four digits with a space between each group. The regular
expression would be:
\d{3,4} \d{3,4} \d{3,4} \d{3,4}

The repeat specification includes both a minimum and a


maximum number of occurrences in this regular expression. Zero is assumed if you omit the minimum value.
Character

Shortcut For

{0,}

Repeats zero or more times

{1,}

Repeats one or more times

{0,1}

Repeats zero or one time

Figure 1: Repeat shortcut characters.

Description

On

Language

Option

Regular Expressions and .NET

Character

Ignore Case

Multiline

ExplicitCapture

Singleline

Description
Specifies case-insensitive matching.
Specifies multiline mode. Changes the meaning of
^ and $ so that they match at the beginning and
end, respectively, of any line, not just the beginning and end of the whole string.
Specifies that the only valid captures are explicitly named or numbered groups of the form
(?<name>...). This allows parentheses to act as
noncapturing groups without the syntactic clumsiness of (?:...).

counts that are so common that the


regular expression language includes
shortcuts for them (see Figure 1).
Lets return to the regular expression
for a US ZIP code, and change it to
allow the user to enter either a five- or
nine-digit code. You need to make the
dash and the last four digits optional by
changing the regular expression to:
\d{5}(-\d{4})?

Specifies single-line mode. Changes the meaning


of the period character (.) so that it matches every
character (instead of every character except \n).

In this example, the -\d{4} is


enclosed in parentheses to make it a
group, and the group is followed by
IgnorePatternWhitespace
x
Specifies that unescaped whitespace is excluded
a question mark (?) to indicate that
from the pattern, and enables comments followthe preceding group repeats zero or
ing a number sign (#). Note that whitespace is
one times. The effect of placing a ?
never eliminated from within a character class.
after a character or group is to make
that character or group optional. Later
Figure 2: Options you can use to change the matching behavior of regular expressions.
in this article, well address regular
expressions that refer back to groups defined earlier in
Character Description
the regular expression. Groups are identified by number,
^
Beginning of the string or beginning of a line,
from one through nine (from left to right).
depending on the Multiline option

End of the string or end of the line, depending on


the Multiline option

\A

Beginning of the string (ignores the Multiline option)

\Z

End of the string or before the \n and the end of


the string (ignores the Multiline option)

\z

End of the string (ignores the Multiline option)

\G

The point at which the current search started

\b

A word boundary

\B

Not on a word boundary

Figure 3: Boundary metacharacters.

Infinity is assumed if you omit the maximum value,


so {,5} means zero through five occurrences, and {3,}
means three or more occurrences. Before continuing, its
important to note that the spaces in this regular expression are literal characters. In other words, if you include
a space in a regular expression, there must be a space in
that location in the string for the regular expression to
match the string.
Theres one problem with the regular expression for the
credit card number. Its wasteful to have to repeat \d{3,4}
three times. The solution is to create a group of characters by enclosing them in parentheses. Using a group and
a repeat count changes the regular expression to:

You can also give a group a name. The regular expression:


\d{5}(?<plusfour>-\d{4})?

also matches a five- or nine-digit ZIP code, but the group


that defines the dash and last four digits of the ZIP code
has been named plusfour by adding ?<plusfour> at the
beginning of the group.
Options and Boundaries
There is still one problem with the ZIP code regular
expression. If you apply this regular expression to the
string 1234567-890987 youll find that it succeeds
because the string contains five digits followed by a dash
followed by four more digits. To correct this problem, add
a ^ (carat) at the beginning and a $ (dollar sign) at the
end, so the regular expression becomes:
^\d{5}(-\d{4})?$

The ^ stands for the beginning of the string and the $


stands for the end of the string. Now the regular expression requires the beginning of the string to be immediately
followed by five digits, which are optionally followed by a
dash and four more digits, followed immediately by the end
of the string. For the same reason, the regular expression for
validating a credit card number should also be changed to:
^\d{3,4}( \d{3,4}){3}$

\d{3,4}( \d{3,4}){3}

This regular expression matches a digit repeated either


three or four times, followed by three groups of characters
where each group consists of a space followed by three or
four digits. In this case, the repeat count, {3}, applies to
the preceding group of characters. There are some repeat
10

DELPHI INFORMANT MAGAZINE | October 2003

There are other boundary characters besides ^ and $.


Before looking at them, however, we need to examine the
options you can use to change the matching behavior of
regular expressions, because some of these options affect
the behavior of some boundary characters. The options
are shown in Figure 2, and you include them in a regular

On

Language

Regular Expressions and .NET

Character

Shortcut For

\a

\x07

Description
A bell character

\d

[0-9]

All decimal digits

\D

[^0-9]

\e

\x1b

An escape character

\f

\x0c

A form feed character

\n

\x0a

A newline character

\r

\x0d

A carriage return character

\s

[ \f\n\r\t\v]

\S

[^ \f\n\r\t\v]

All non-digit characters

Any whitespace character


Any non-whitespace character

\t

\x09

A tab character

\v

\x0b

A vertical tab character

\w

[A-Za-z0-9_]

\W

[^A-Za-z0-9_]

Any word character (letters, numbers, and the underscore)


Any non-word character
The period represents any character (except \n in Singleline mode)

Figure 4: Character class shortcuts.

expression as a group with the form (?imnsx). Placing a


dash in front of any option turns off that option. The group
(?im-nsx) turns on ignore case and multiline mode, and
turns off all other options. Figure 3 shows the boundary
metacharacters.
Character Classes and Alternation
The \d metacharacter, which matches a decimal digit, is
also a shortcut. Regular expressions let you use classes,
or sets of characters, enclosed in brackets. For example,
[abc] will match either the letter a or the letter b or
the letter c; [0-9] will match the digits zero through 9,
and is identical to \d; and [a-zA-Z] will match all lowercase and uppercase letters.
You can also use the ^ in a character class to get its
complement. For example, [^a-z] matches all characters
that are not lowercase letters. You can also represent any
character using its hexadecimal value using \xnn, where
nn is two hexadecimal digits. Figure 4 shows the common
shortcuts for characters and character classes.

procedure TestRegularExpression;
var
ARegex:
Regex;
RegexMatch,AMatch: Match;
Matches:
MatchCollection;
Groups:
GroupCollection;
AGroup:
Group;
Captures:
CaptureCollection;
ACapture:
Capture;
Im, Ig, Ic:
Integer;
begin
// Create a regular expression object.
ARegex := Regex.Create(RegexString);
// Get a match object and check its Success property.
RegexMatch := ARegex.Match(searchString);
if (RegexMatch.Success) then
Console.Writeline('Match Succeeded
')
else
begin
Console.Writeline('Match Failed');
Exit;
end;
// Get the matches collection.
Matches := ARegex.Matches(searchString);
// Loop through the matches collection and display the
// matched text and its starting position.
for im := 0 to Matches.Count - 1 do begin
AMatch := matches[im];
Console.Writeline('Match ' + IntToStr(im) + ' at ' +
IntToStr(AMatch.Index) + ' = |' + AMatch.Value +
'|
');
// Get the groups collection for each match.
Groups := AMatch.Groups;
// Loop through the groups collection and display
// the text and position for each group.
for ig := 0 to Groups.Count - 1 do begin
AGroup := Groups[ig];
Console.Writeline(#9'Group ' + IntToStr(ig) +
' at ' + IntToStr(AGroup.Index) + ' = |' +
AGroup.Value + '|
');
// Get the captures collection for each group.
Captures := AGroup.Captures;
// Loop through the captures collection and display
// the text and starting position for each capture.
for ic := 0 to Captures.Count - 1 do begin
Acapture := Captures[ic];
Console.Writeline(#9#9'Capture ' + IntToStr(ic) +
' at ' + IntToStr(ACapture.Index) + ' = |' +
ACapture.Value + '|
');
end;
end;
end;
end

Figure 5: The TestRegularExpression procedure.

A common need is to identify strings that match any of


two or more patterns. The alternation operator, the vertical bar character (|), meets this need. Suppose you want
to find all the words that start with either the letter f
or the letters ph. You could use this regular expression
(f|ph)\w+ to specify the letter f or the letters ph followed by one or more word characters.

The problem is how do you refer to the matched word so


you can construct a regular expression that says the same
word twice? First, create a group and give it a name:

Backreferences
Suppose you need to scan some text to find each case
where the same word appears twice in succession. Constructing a regular expression that matches a word is
easy. A word is just a word boundary followed by one or
more word characters followed by another word boundary, e.g. \b\w+\b.

(?<aword>\b\w+\b)\k<aword>

11

DELPHI INFORMANT MAGAZINE | October 2003

(?<aword>\b\w+\b)

Then use the \k metacharacter to add a reference to the group:

The \k<aword> sequence is called a backreference. It equates to


the currently matched value of the group whose name is aword.
The same regular expression without using a named group is:
(\b\w+\b)\1

On

Language

Regular Expressions and .NET


ters with special meanings, such as
*+?[]{} and others. If you need to
include one of these characters in a
regular expression literal, simply precede the character with a backslash.
For example, the regular expression
\(602\) matches (602). Because they
are preceded by the escape character,
\, the parentheses are included in the
expression as literal characters instead
of enclosing a group. To include
the backslash character precede it
with another backslash. Then the
expression c:\\foo would match the
string c:\foo.

Figure 6: Output produced by the test command.

Figure 7: Output from the parse command.


procedure ParseOnRegularExpression;
var
ARegex: Regex;
Parts: array of string;
I: Integer;
begin
ARegex := Regex.Create(RegexString);
// Pase the search string on each match to the
// regular expression.
Parts := ARegex.Split(searchString);
// Display the parts parsed from the string.
for I := 0 to Length(Parts) - 1 do
Console.Writeline(Parts[I]);
end;

Figure 8: Parsing a string.

where \1 is a backreference to the first group in the regular expression.


Using the Escape Character
As you can see, regular expressions include many charac12

DELPHI INFORMANT MAGAZINE | October 2003

Using Regular
Expressions in .NET
The key to using regular expressions
in your .NET applications is the
Regex class. The sample program that
accompanies this article is a console
application that prompts you for a
command letter, a regular expression,
some text to search with the regular
expression, and, optionally, a replace
expression (see end of article for
download details). The command
letters are t to test the regular
expression, p to parse the string
on character groups that match the
regular expression, and r to replace
the characters that match the regular
expression using the replace string.
The program evaluates the regular
expression and displays the results.
Figure 5 shows the
TestRegularExpression procedure,
which begins by creating an instance
of the Regex object named ARegex.
Note that the regular expression the user entered is
passed as a parameter to the Regex objects constructor.
Next, the code gets a Match object by calling the Regex
objects Match method. The Match object has a Success
property that shows whether the string passed as a
parameter to the Match method contains a character or
characters that match the regular expression. This is all
you need for data validation.
When a regular expression is applied to a text string, the
expression may match characters at many locations in
the string. The Regex objects Matches method returns a
MatchCollection that contains all the matches. The code in
Figure 5 loops through the Matches collection and writes the
index of the Match object, its starting position in the string,
and its value to the console.
Each Match object in the Matches collection may match one
or more groups of characters. The Groups method of the
Match object returns a GroupsCollection that contains all the

On

Language

Regular Expressions and .NET


characters, and the output shows the
words that the string is parsed into.
Figure 8 contains the code for the
ParseOnRegularExpression procedure.
This procedure also begins by creating
an instance of the Regex object. Next
it calls the Regex.Split method, and
passes the string to be parsed as the
parameter. The Split method returns
a dynamic array of strings that
contains the substrings created by the
parse operation. This is a very useful
technique for parsing any kind of
delimited text.

Figure 9: Using the Replace method.


procedure ReplaceRegularExpression;
var
ARegex: Regex;
NewString: string;
begin
ARegex := Regex.Create(RegexString);
// Replace each match to the regular expression with the
// search expression.
NewString := ARegex.Replace(SearchString, ReplaceString);
// Display the result.
Console.Writeline(NewString);
end;

Figure 10: The ReplaceRegularExpression method.

groups for the match. The code in Figure 5 loops through


the Groups collection and displays the index number of the
group, its starting position in the string, and its value.
One or more strings that match the regular expression may
be captured for each group. The Group objects Captures
method returns a CaptureCollection that contains all the
Capture objects for the Group. Again, the code loops through
the collection and displays the index, starting position, and
value for each Capture object.
Figure 6 shows the output of the sample program when
the regular expression (ab)*c is applied to the string
xabcxabxcxabcx. This regular expression will match
zero or more occurrences of the group (ab) followed
by the letter c. You can see that the MatchCollection
object contains three matches at positions 1, 8, and 10
(the positions are zero-based). The first Match contains
two groups in its GroupsCollection. Both groups start at
position 1, and the first matches the letters abc; the
second matches the letters ab. Each group contains a
single Capture object.
Figure 7 shows the results when you use the parse
command by entering the letter p with the regular
expression \s+ and the string 111 Shade Tree Lane.
The regular expression matches one or more whitespace
13

DELPHI INFORMANT MAGAZINE | October 2003

Figure 9 shows the result of the


Replace command. Here, all groups
of whitespace characters are replaced
with two vertical bars. Figure 10 shows
the code that creates a Regex object,
and then calls its Replace method, passing the string to be
searched as the first parameter, and the replacement string
as the second parameter.
The Replace method is much more powerful than this
example might lead you to believe, because the second
parameter can be the name of a method that takes a
Match object as its parameter and returns a string. Within
this method you can use your own code to perform the
replacement using any logic you wish.
The Delphi for .NET sample application described in this
article is a console application. You can also download a
Windows version with a GUI. However, I wrote the Windows
version in C#, because writing Windows forms applications
with a command-line compiler is just too time consuming.
Conclusion
This is an introduction to the features of regular expressions
in the .NET environment. You should search for regular
expressions in the index of the .NET SDK documentation,
and read the listed topics to gain a more complete
understanding of their features. The classes presented in this
article have other methods and properties, and the methods
have other overloaded versions that you can also explore in
the online help. Understanding regular expressions can save
a lot of development time whenever you need to manipulate
text. A little time learning to use them is time well spent.
The demonstration apps referenced in this article are
available for download on the Delphi Informant Magazine
Complete Works CD located in INFORM\2003\OCT\
DI200310BT.
Bill Todd is president of The Database Group, Inc., a database consulting
and development firm based near Phoenix. He is co-author of four database
programming books, author of more than 100 articles, a contributing editor to
Delphi Informant Magazine, and a member of Team B, which provides technical
support on the Borland Internet newsgroups. Bill is also an internationally known
trainer and frequent speaker at Borland Developer Conferences in the United
States and Europe. Readers may reach him at bill@dbginc.com.

G R E A T E R
JAVA NATIVE INTERFACE

JAVA

D E L P H I

JBUILDER

DELPHI 6, 7

By Keith Wood

Going Native
Part 2: Calling Java Code from a Delphi App

ast month, in Part 1, you saw how Delphi


code could be called from a Java application
using the Java Native Interface (JNI). By fol-

lowing certain naming conventions, and by using


specific data types and structures within your
native code, your Java classes can easily invoke
these routines, passing parameters and results
between the two. Exceptions can also be generated
and handled on either side of the interface. This
months article will demonstrate how Java code

Before you can call any Java code from Delphi, you
must first load the Java virtual machine (JVM). Add the
JNI unit to your application, then start by preparing the
parameters for the JVM. You have the option of using a
VM that conforms to the JNI 1.1 or 1.2 specifications. The
differences are mainly concerned with versioning of JNI,
reflection support, and enhancements of existing capabilities. This choice affects the format of parameters passed
to the initial call: JDK1_1InitArgs or JavaVMInitArgs.
In this case, use JNI 1.2 and the latter type. The arguments are the normal parameters you would pass to the
JVM if you were starting it from the command line. Only
the classpath option is handled here, after having been
entered by the user:

can be called from a Delphi application, fulfilling


the bi-directional promise of JNI.
The ease-of-use of Delphi and Java interactions is due to the
efforts of Matthew Mead, who has produced a Pascal translation of the JNI suitable for use in Delphi. If you havent
already, you can download the package from Matthews Web
site (see References at the end of this article).
Loading the Java VM
To illustrate how to invoke a Java class from Delphi, you can
use the JavaD utility described in Part 1 and provide a Delphi
GUI for it (see Figure 1). As youll recall, this tool generates a
Pascal skeleton based on a given Java class native methods to
simplify the use of JNI with Delphi. Written in Java, the utility
makes use of Javas reflection abilities to discover the native
methods and their signatures.
The generateDelphiWrapper method in this class takes three
parameters: the full name of the Java class to examine, the output directory for the Pascal file, and a flag indicating whether
any existing file can be overwritten.
14

DELPHI INFORMANT MAGAZINE | October 2003

Figure 1: Delphi GUI for the JavaD tool.

Greater

Delphi

Going Native

{ Load the JVM }


procedure TfrmJavaD.btnLoadClick(Sender: TObject);
var
ClassPath: string;
Errcode: Integer;
VM_args: JavaVMInitArgs;
Options: array [0..10] of JavaVMOption;
begin
ShowStatus('Loading Java VM...');
// Confirm the classpath.
ClassPath := InputBox('JavaD','Enter the Java classpath',
'C:\Projects\DJNI\classes');
// Set up the options for the VM
FillChar(Options, SizeOf(Options), #0);
Options[0].optionString :=
PChar('-Djava.class.path=' + ClassPath);
VM_args.version := JNI_VERSION_1_2;
VM_args.options := @Options;
VM_args.nOptions := 1;
// Create the wrapper for the VM
FJavaVM := TJavaVM.Create;
// Load the VM
Errcode := FJavaVM.LoadVM(VM_args);
if Errcode < 0 then begin
// Loading the VM more than once will cause this error.
if Errcode = JNI_EEXIST then
raise Exception.Create('Java VM has already ' +
'been loaded. Only one JVM can be loaded.')
else
raise Exception.Create(Format(
'Error creating JavaVM, code = %d', [Errcode]));
end;
// Create the Env class
FJNIEnv := TJNIEnv.Create(FJavaVM.Env);
ShowStatus(Format('Java VM %d.%d loaded',
[FJNIEnv.MajorVersion, FJNIEnv.MinorVersion]));
// Adjust buttons
btnLoad.Enabled
:= False;
btnLoad.Default
:= False;
btnGenerate.Default := True;
edtChange(nil);
end;

Figure 2: Loading the Java VM.


-Djava.class.path=<user-defined class path>

Next, create a TJavaVM object, then use it to load the


actual JVM with the arguments set above, as shown in
Figure 2. Check for and report on any error condition
during the load. If no problems arise, initialize a TJNIEnv
object based on the loaded JVM. You are then ready to
continue and invoke the Java code itself. Note that its
impossible to unload the JVM, so be sure your parameters
are correct before loading it.
Calling Java Code
To call a method on a Java class or object, you must
first obtain a reference to that class or object. You locate
the class through the FindClass function of the Delphi
TJNIEnv class. If successful, it returns a reference to the
class. Otherwise, it returns nil.
Having obtained the class reference, you must then locate
the required method through its name and signature using
GetMethodID or GetStaticMethodID from TJNIEnv. Check
that the returned value is valid (not nil) and report an
error if not. Then call the appropriate invocation routine,
depending on the methods return type and whether the
method is static. In this case, because the return value
15

DELPHI INFORMANT MAGAZINE | October 2003

is a String (an object), and the method is static, you use


CallStaticObjectMethod. Convert the result into a Delphi
string through the JStringToString method.
Because this process involves several steps and the
actual Delphi function called depends on both the return
type and its static status, the JNIUtils unit includes two
calls that make it much simpler. If the return type is a
primitive type, a String, or void, then invoke CallMethod.
Use the CallObjectMethod function if the return type is of
any other object (including an array). The former routine
returns a Variant value, encapsulating all the basic types
(including void as Null), and the latter returns a generic
JObject reference.
Both routines take a reference to the JNI environment,
the class (for static methods) or object reference, the
name of the method, its signature (as Java types), an
array of parameter values that match the signature, and
an optional Boolean to indicate a static (True) or nonstatic method (False, the default) method. You can see
both versions in the demonstration code (available for
download; see end of article for details). Which one is
used depends on the presence or absence of the JNIUTILS
conditional definition.
Following a method call, you should check whether any
exceptions arose in the Java code. Start by calling the
ExceptionOccurred method of the JNI environment, which
returns nil if no exception was thrown, or a reference to
the exception that cropped up. If not nil, your first step
should be to clear the JNI flag indicating that the exception
occurred using ExceptionClear (otherwise some further calls
will also fail). You can then proceed through the normal
course of obtaining further details about the exception by
calling its methods. In this case, find out its class name
and message, and display these to the user. As before, both
direct and JNIUtils versions of the code are included.
If the Pascal skeleton generation succeeded, the
generateDelphiWrapper method returns the name of the
created file. You can use this to load its contents into a
memo and display them to the user. The code for all of
this is shown in Listing One (on page 16).
Run the application to test it. Load the JVM by clicking
the left button. Confirm or update the classpath to use.
Remember that the path must include the directories at
the top of the class file hierarchies of both the JavaD
class and the class that you wish to generate from.
Separate multiple path definitions with semi-colons ( ; ).
After the JVM is loaded, the status bar shows the version
of JNI in use.
Enter the name of the class to examine, your output
directory, and whether you will allow existing files to
be overwritten. Click Generate to start the process. If
successful, the output is loaded into the memo for your
perusal. If a Java exception occurs, its reported to you
as a class name and message. To observe some of the
possible Java errors that may arise, try entering an invalid
class name or clearing the overwrite flag.

Greater

Delphi

Going Native

Conclusion
The Java Native Interface lets you call Java code from native
code, or vice versa. Through the efforts of Matthew Mead, a
Delphi version of the JNI is available, making it simple to use.
To call Java code you must first load the Java virtual
machine, setting appropriate parameters. Then locate the
classes required, find the methods on them to invoke, and
call these with the appropriate parameters. The demonstration program described here provides a Delphi GUI to the
JavaD tool, allowing you to easily generate Delphi skeleton
files for JNI from your Java classes.
Using the facilities described in this article, you can now
mix and match between Delphi and Java as the need arises.
See the documentation that comes with Matthews Delphi/
JNI package for more details and examples.
References
JNI Specification: http://java.sun.com/j2se/1.4.1/docs/guide/
jni/index.html
JNI FAQ: http://java.sun.com/products/jdk/faq/jnifaq.html
Delphi/JNI Home: http://home.pacifier.com/~mmead/jni/
delphi/index.html
The demonstration programs referenced in this article are
available for download on the Delphi Informant Magazine Complete Works CD located in INFORM\2003\OCT\
DI200310KW.

Keith Wood hails from Australia, where he is a consultant working with


Java and Delphi, and a freelance technical writer. He started using Borlands
products with Turbo Pascal on a CP/M machine. His book, Delphi Developers
Guide to XML from Wordware, covers many aspects of XML from a Delphi
point of view. You can reach him via e-mail at kbwood@iprimus.com.au.

Begin Listing One Calling the


Java class
{ Generate a Pascal skeleton for JNI. }
procedure TfrmJavaD.btnGenerateClick(Sender: TObject);
var
Cls: JClass;
{ $IFNDEF JNIUTILS }
MID: JMethodID;
{ $ENDIF }
Exc: JThrowable;
FileName: string;
ErrMsg: string;
begin
try
memOutput.Lines.Clear;
ShowStatus('Calling generator method...');
// Find JavaD class
Cls := FJNIEnv.FindClass(
'wood/keith/opentools/javad/JavaD');
if Cls = nil then
raise Exception.Create('Can't find class: ' +
'wood.keith.opentools.javad.JavaD');
// Run it
{ $IFDEF JNIUTILS }
FileName := JNIUtils.CallMethod(FJNIEnv, Cls,
'generateDelphiWrapper',
'String (String, String, boolean)',
[edtClassName.Text, edtDirectory.Text,

16

DELPHI INFORMANT MAGAZINE | October 2003

chkOverwrite.Checked], True);
{ $ELSE }
MID := FJNIEnv.GetStaticMethodID(Cls,
'generateDelphiWrapper',
'(Ljava/lang/String;Ljava/lang/String;Z)' +
'Ljava/lang/String;');
if MID = nil then
raise Exception.Create('Can't find method: ' +
'generateDelphiWrapper');
FileName := FJNIEnv.JStringToString(
FJNIEnv.CallStaticObjectMethod(Cls, MID,
[edtClassName.Text, edtDirectory.Text,
chkOverwrite.Checked]));
{ $ENDIF }
// Check for exception.
Exc := FJNIEnv.ExceptionOccurred;
if Exc <> nil then
begin
// Clear the exception so we
// can call other methods.
FJNIEnv.ExceptionClear;
// Find out about the exception // its class and message.
{ $IFDEF JNIUTILS }
Cls := JNIUtils.CallObjectMethod(
FJNIEnv, Exc, 'getClass', 'Class()', []);
ErrMsg := JNIUtils.CallMethod(
FJNIEnv, Cls, 'getName', 'String()', []) +
#13 + JNIUtils.CallMethod(
FJNIEnv, Exc, 'getMessage, 'String()', []);
{ $ELSE }
MID := FJNIEnv.GetMethodID(
FJNIEnv.GetObjectClass(Exc),
'getClass', '()Ljava/lang/Class;');
if MID = nil then
raise Exception.Create(
'Can't find method: getClass');
Cls := FJNIEnv.CallObjectMethod(Exc, MID, []);
MID := FJNIEnv.GetMethodID(
FJNIEnv.GetObjectClass(Cls),
'getName', '()Ljava/lang/String;');
if MID = nil then
raise Exception.Create(
'Can't find method: getName');
ErrMsg := FJNIEnv.JStringToString(
FJNIEnv.CallObjectMethod(Cls, MID, []));
MID := FJNIEnv.GetMethodID(
FJNIEnv.GetObjectClass(Exc),
'getMessage', '()Ljava/lang/String;');
if MID = nil then
raise Exception.Create(
'Can't find method: getMessage');
ErrMsg := ErrMsg + #13 +
FJNIEnv.JStringToString(
FJNIEnv.CallObjectMethod(Exc, MID, []));
{ $ENDIF }
raise Exception.Create(
'A Java exception occurred'#13 + ErrMsg);
end;
// Load the generated file.
memOutput.Lines.LoadFromFile(FileName);
ShowStatus('Done');
except
on E: Exception do begin
ShowStatus('Error');
MessageDlg('Error: ' + E.Message,
mtError, [mbOK], 0);
end;
end;
end;

End Listing One

D E L P H I
EVENT LOGGING

A T

NT SERVICES

W O R K

WINDOWS NT/2000

DELPHI 2-7

By Simon Murrell

For the Record


Reading/Writing to the Windows NT/2000 Event Log

eading and writing to event logs can be


a useful part of application development,
maintenance, and reporting. This is espe-

cially true of NT services, because logging can


help you process messages and errors that occur
within your service.

Each of these events calls a method named ReloadLog which


is responsible for reading the event log entries from the
event log. The method first instantiates the EventLog class.
It then assigns a value to the Log member of the EventLog
class based on the specific log from which the user wants
to read the entries. By default, the EventLog class will read
entries from the local machine. If you want to read entries
from another machine, you must alter the MachineName
member of the EventLog class to the relevant machine name.

This article presents the Event Viewer


Demo application (see Figure 1) which
demonstrates how to read from, and
write to, event logs, as well as how to
clear separate event logs. In other words,
the Event Viewer Demo application
demonstrates how to use the EventLog
class. This class lets you access the
Windows NT/2000 event logs. Using
the EventLog class, you can read from
existing event logs, write new entries to
the logs, and clear and delete event logs.
Reading from Event Logs
I created a menu item, Event Log Type, in
the Event Viewer Demo application,
with three sub-menu items: Application,
System, and Security (the three default logs
found in Windows NT/2000). The user
can select one and the relevant event
log will be read using the EventLog
class. The information will also be
displayed in the ListView control. I
created three events for the three submenu items, shown in Figure 2.
17

Figure 1: The Event Viewer Demo.

DELPHI INFORMANT MAGAZINE | October 2003

Delphi

at

Work

For the Record

procedure TEventViewer.OnApplicationLog(Sender: TObject;


E: EventArgs);
begin
ReloadLog('Application'); // Reload application log.
ApplicationLogMenu.Checked := True;
SecurityLogMenu.Checked := False;
SystemLogMenu.Checked := False;
end;
procedure TEventViewer.OnSecurityLog(Sender: TObject;
E: EventArgs);
begin
ReloadLog('Security'); // Reload security log.
ApplicationLogMenu.Checked := False;
SecurityLogMenu.Checked := True;
SystemLogMenu.Checked := False;
end;
procedure TEventViewer.OnSystemLog(Sender: TObject;
E: EventArgs);
begin
ReloadLog('System'); // Reload system log.
ApplicationLogMenu.Checked := False;
SecurityLogMenu.Checked := False;
SystemLogMenu.Checked := True;
end;

Figure 2: Reading events from the event log.

After the Log and/or MachineName properties have been


assigned, the Entries member is automatically populated
with the entries for the relevant event log. The method
then loops through the Entries (because the member is of
type EventLogEntryCollection) and populates the ListView
control with each entrys information. Each item of the
EventLogEntryCollection class is of type EventLogEntry.
The EventLogEntry class has many members, of which
I have used EntryType, TimeWritten, Source, Category,
EventID, Username, MachineName, and Message. The
BeginUpdate and EndUpdate methods of the ListView class
were used to populate the ListView control. The code for the
ReloadLog method is shown in Listing One (on page 19).
Writing to Event Logs
I created two TextBox controls in the Event Viewer Demo
application. These controls store the Message and Source
members of the EventLogEntry class. You must specify an
event Source if you want to write to the event log. The
Source makes your application distinct from any other application or source already found in the event logs. The value
of Source is usually the name of the relevant application.
Note that the Source member must have a value; otherwise,
an exception will be thrown. Also note that this log is readonly; you cannot write to the Security event log.
When writing to the event log, you can also specify what
type of Event Log Entry you are writing, i.e. whether the
entry is an Error, Information, or Warning entry type. You
can also use the EventID and Category members of the
EventLogEntry, which allow you to specify applicationdefined event ids and categories.
I created an OnAddEventLogEntry event in the Event Viewer
Demo application, which will add a new event log entry to
the event log. This event instantiates the EventLog class,
18

DELPHI INFORMANT MAGAZINE | October 2003

procedure TEventViewer.OnAddEventLogEntry(Sender: TObject;


E: EventArgs);
var
Log: EventLog;
begin
// Check values.
if (DescriptionTextBox.Text = '') then begin
// Show dialog box.
MessageBox.Show('Please enter in a Description Value!',
'Error', MessageBoxButtons.Ok, MessageBoxIcon.Error);
Exit;
end;
if (SourceTextBox.Text = '') then begin
// Show dialog box.
MessageBox.Show('Please enter in a Source Value!',
'Error', MessageBoxButtons.Ok, MessageBoxIcon.Error);
Exit;
end;
Log := EventLog.Create; // Initialize class.
Log.Log := 'Application';
// Add Log Entry
Log.WriteEntry(SourceTextBox.Text,
DescriptionTextBox.Text);
end;

Figure 3: How to write entries to event logs.

procedure TEventViewer.OnClearEventLog(Sender: TObject;


E: EventArgs);
var
Log: EventLog;
begin
// Query user.
if (MessageBox.Show('Clear ' + EventLogComboBox.Text +
' Event Log?', 'Clear Event Log',
MessageBoxButtons.YesNo, MessageBoxIcon.Question) =
DialogResult.Yes) then
begin
Log := EventLog.Create; // Initialize class.
Log.Log := EventLogComboBox.Text;
Log.Clear; // Clear log.
end;
end;

Figure 4: The OnClearEventLog event.

and then assigns a value to the Log member of the EventLog


class based on the specific log to which the user wants to
write the entries. The method finally calls the WriteEntry
method to write a new event log entry to the relevant event
log. The code for this event is shown in Figure 3.
Clearing the Event Log
I created a ComboBox control in the Event Viewer Demo
application to store the three default event log types. Use
the Clear method of the EventLog class if you want to clear
the event log. I created an OnClearEventLog event in the
Event Viewer Demo application that will clear the event log
of whatever event log is specified in the ComboBox control.
The code is shown in Figure 4.
Conclusion
Event logs can be very useful, especially when writing NT services, because they help you report information messages and
errors that occur within your service. However, event logging
consumes processor time, and other system resources such as
disk space. You should only log important information, such as
errors, because you can affect your applications performance. I

Delphi

at

Work

For the Record

normally store a Level setting in the applications I write so I can


enable or disable specific information for logging.
The demonstration project referenced in this article is available for download on the Delphi Informant Magazine Complete Works CD located in INFORM\2003\OCT\DI200310SM.

Simon Murrell is the director of ByteForest, a software development and


consulting firm based in Johannesburg, South Africa. Simon has used such
languages as Delphi, Visual Basic, Java, and C# in his years of development.
You can contact Simon at simonm@byteforest.co.za.

Begin Listing One The ReloadLog


method
procedure TEventViewer.ReloadLog(LogName: string);
var
Log: EventLog;
Counter: Integer;
ListItem: ListViewItem;
begin
Log := EventLog.Create;
// Initialize class.
Log.Log := LogName;
// Assign log name.
LogListView.Items.Clear; // Clear items.
LogListView.BeginUpdate; // Begin update.
// Search through log.
for Counter := 0 to Log.Entries.Count - 1 do begin
// Initialize class.
ListItem := ListViewItem.Create;
// Assign values.
if (Log.Entries.Item[Counter].EntryType =
EventLogEntryType.Error) then
ListItem.Text := 'Error';
if (Log.Entries.Item[Counter].EntryType =
EventLogEntryType.FailureAudit) then
ListItem.Text := 'Failure Audit';
if (Log.Entries.Item[Counter].EntryType =
EventLogEntryType.Information) then
ListItem.Text := 'Information';
if (Log.Entries.Item[Counter].EntryType =
EventLogEntryType.SuccessAudit) then
ListItem.Text := 'Success Audit';
if (Log.Entries.Item[Counter].EntryType =
EventLogEntryType.Warning) then
ListItem.Text := 'Warning';
ListItem.SubItems.Add(
Log.Entries.Item[Counter].TimeWritten.ToString);
ListItem.SubItems.Add(
Log.Entries.Item[Counter].Source);
if (Log.Entries.Item[Counter].Category = '(0)') then
ListItem.SubItems.Add('None')
else
ListItem.SubItems.Add(
Log.Entries.Item[Counter].Category);
ListItem.SubItems.Add(
IntToStr(Log.Entries.Item[Counter].EventID));
if (Log.Entries.Item[Counter].Username = '') then
ListItem.SubItems.Add('N/A')
else
ListItem.SubItems.Add(
Log.Entries.Item[Counter].Username);
ListItem.SubItems.Add(
Log.Entries.Item[Counter].MachineName);
ListItem.SubItems.Add(
Log.Entries.Item[Counter].Message);
LogListView.Items.Add(ListItem); // Add to ListView.
end;
LogListView.EndUpdate; // End update.
end;

End Listing One


19

DELPHI INFORMANT MAGAZINE | October 2003

Whats New in C# 2?

www.C-SharpPRO.com

C#

Solutions & Strategies for .NET Developers


October 2003 Special Preview Edition

C#Builder
for Delphi
Developers
Whats Different?
Whats the Same?

FEATURE

Whats New
in C# 2?
Find out what
new features
will impact your
development.

Cover Story

By Corbin Dunn

C#Builder for Delphi


Developers
Whats Different? Whats the Same?

ORLAND C#BUILDER IS THE FIRST COMPLETE DEVELOPment solution for the .NET Framework from Borland.
C#Builder combines a first-rate IDE for both Windows
Forms and Web Forms development, with links to Java
and a broad range of enterprise databases. For Delphi
developers who are accustomed to writing native Windows applications theres much to like in the C# language. Although the language superficially resembles C,
its been designed to be accessible to new programmers.
Similarly, Borland has designed C#Builder to be easy to
learn, and easy to use. Many of the features from Delphi
will be familiar, including the approaches to application
design, the Code Editor, and the debugger. Other features
are appropriate for C# and the .NET Framework, such as
the streamlined Tool Palette and the
new Model View.

to development with C#Builder is similar. When a Windows


Forms application is created with File | New, these similarities
become obvious. A blank user interface is presented, with
the properties for the form displayed in the Object Inspector to the left. The Tool Palette now at bottom right by
default displays visual components that can be placed on
the form using the familiar drag-and-drop or double-click.
At the bottom of the central pane are tabs to switch
between Design view and Code for the form. Like Delphi,
each form has a unit of source code associated with it.
Unlike Delphi, the initial properties for the form and its
components are set through this same unit of source code;
theres no equivalent to Delphis .dfm form file. Developers
familiar with Borland JBuilder will recognize this approach.

The User Interface


When C#Builder is first loaded it
looks quite different from Delphi.
Theres no blank form. The long list
of components across the top of the
screen is missing. The Project Manager initially blank is displayed
at top right with a new Tool Palette
below it, also blank.
Initially, C#Builder displays a
Welcome page in the center of the
screen which gives one-click access
to resources such as the Borland
Developer Network, newsgroups, and
sample applications. It also includes
a full C# tutorial and Getting Started
guide for developers new to the language and/or C#Builder.
Creating a new application. While
the initial impressions may be quite different from Delphi, the approach taken

Figure 1. Like Delphi, the Borland C#Builder IDE includes live tools, capable of displaying data
from databases, ADO.NET connections, and other data sources during design time. And its IDE
arrangement can be extensively customized and saved in various configurations.
www.C-SharpPRO.com

Cover Story

C#Builder for Delphi Developers

At top right the Project Manager shows that a new


to place them in a
application consists of the source code unit for the Winmore convenient
dows Form and an AssemblyInfo file. Windows Forms C#
order. Category
applications dont have an executable project source file.
colors can be indiRather, a static Main method is included in the form desvidually customized,
ignated as the start-up. The AssemblyInfo.cs file contains
and for expert users
copyright information, version information, a code signabutton captions can
ture, and other general information about the application.
be turned off to save
Like Delphi, an Object Repository is available to create stanspace and time.
dard forms and applications. If we choose File | New to insert
A new Search
a new form, C#Builder creates a new unit of source code and
option at the top
adds it to the Project Manager in the same way as Delphi.
of the Tool Palette
With two forms in the application, another difference
incrementally filbetween Delphi and C#Builder becomes apparent. By
ters the list of comdefault, C#Builder uses a single window divided into
ponents displayed.
panes. In contrast, Delphi has a separate window for
Knowing the name
each form in the application. C#Builder uses tabs at the
of a component
top of the central pane to switch between each form.
makes it much easThis approach makes it immediately obvious which unit
ier to narrow down
of source code belongs to which form. It also makes the
a search.
screen less cluttered (see Figure 1).
A further sigModel View. An important new innovation is the Model nificant benefit
View (see Figure 2). This is located in a tab underneath
of using the .NET
the Project Manager at top right, and gives a symbolic
Framework comview of the logical structure of the application, based on
ponents is that the
the C# class hierarchy. For large applications with many
Microsoft help for
source code units, this view can be easier to navigate
the class library is Figure 2. Model View provides a logical view of
an application.
than one based on file names.
available directly
The Model View is the launch pad for the Code Visualfrom the C#Builder
ization capabilities. This looks further into the code and
user interface. This includes extensive C# code samples and
creates a UML-style model of part or all of the application cross-references, each of which is as appropriate to C#Builder
(see Figure 3). This greatly assists documentation and
as it is to any other .NET Framework SDK development tool.
understanding of the code, and can serve
to be a useful check on the structure of
the application. The model is displayed in
a central pane, and its possible to navigate through the source by right clicking
on individual elements in the view.
The Tool Palette. By default, the Tool
Palette appears below the Project Manager
and Model View. This has been substantially
reworked from Delphi, and is now much easier to use especially when large numbers
of components are installed. C#Builder uses
the standard .NET Framework, so any .NET
components can be used, no matter what
language theyre designed in. This means
that C#Builder developers can use any thirdparty component thats compatible with the
.NET Framework SDK version 1.1.
The Palette is extremely configurable, allowing the individual developer to customize it to
suit their needs (see Figure 4). Palette buttons
Figure 3. Code Visualization creates a UML model view of the application.
and categories can easily be dragged around

www.C-SharpPRO.com

Cover Story
Object Inspector. This improved help extends to the
Object Inspector as well (refer to Figure 1). This broadly
resembles the Delphi version, with items grouped in categories and those changed from their defaults marked in bold.
However, each property and event now displays a one-line
explanation at the bottom of the Object Inspector, which is
especially useful when using a new component for the first
time. While Delphi has always been able to include Component Editors on right-click context menus, C#Builder also
includes links to these at the bottom of the Object Inspector.
This makes it clearer to see what editors are available.
Code Editor. One area that will be immediately familiar is
the Code Editor, but its also been changed to make it even
easier to use. All the familiar features and keystrokes (including 9 for Run with Debugging) are available, together with the
standard Borland syntax highlighting, now updated for C#.
Code Insight remains, although it has naturally been
updated to understand the C# language and provide the same
abilities previously seen for Delphi, C++, and Java. And like
Delphi 7, it also supports HTML so its especially useful when
combined with the new visual Web Forms designer.
An important new feature is code folding; the developer
can now collapse a portion of code, temporarily hiding it.
This provides a general outline, similar to the interface
section of a Delphi unit, and provides the developer with a
clearer view of the source.
Other changes are subtle but no less important. Rather than
the begin and end keywords of Delphi, C# uses C-like braces;
the editor automatically highlights opening and closing pairs,
making it easy to check for mismatched pairs. The same feature is used to identify opening and closing braces, quotes, and
parentheses. Syntax errors are denoted by red wavy underlines,
as in Microsoft Word. Because C# is case-sensitive, this can
provide immediate feedback to prevent simple mistakes!
The new Tool Palette changes depending on context;
when code is displayed, for example, a set of Code Snippets are available. These are simple code extracts that can
quickly be selected and dropped into the program.

Database Development
The approach to creating applications, then, is broadly similar
to that used for Delphi. Applications are built of units of code,
which may optionally be used for Windows forms. The significant differences are in the component library, and in particular the way that the components are put together for database access and Web applications. Rather than the different
Borland database technologies, database access from C#Builder
uses the standard ADO.NET technology, with optionally
additional .NET components provided by Borland.
The first major difference encountered by Delphi database
application developers is that there is no equivalent to the
DataModule. Rather, non-visual components that are used
for data access are placed in the bottom margin of the visual
designer. Here they appear as separate icons, so their proper-

C#Builder for Delphi Developers

ties can be set as if


they were located
on the form.
In another major
difference from
Delphi, the components on a form are
marked as private
by default, and are
therefore not accessible by objects
on other forms.
This suggests that
database handling
is best done in a
set of centralized
public methods on
one single form,
which are then
made available to
other units of code
as necessary.
Figure 4. Two views of the Tool Palette; its
Borland
C#Builder includes extremely configurable.
all of the standard
ADO.NET database components. However, one drawback to
these is that they are closely tied to Microsoft SQL Server and
do not make it easy to link to other databases such as Oracle,
DB2, or InterBase; there is no simple dialog box to configure
the connection string for these links. A second drawback is that
live data is not available at design time; instead, the developer
works blind.
To make life a little easier, the Borland Data Provider (BDP)
comprises four components (bdpConnection, bdpDataAdapter,
bdpCommand, and bdpCommandBuilder) that substitute for
ADO.NET equivalents. When used with Windows Forms components they deliver live data to the designer, so the developer can see exactly whats going on (see Figure 1). A full set
of dialog boxes is provided to configure links to databases,
including connections to any ADO.NET data source.
The Data Explorer. The BDP uses connections that are
defined through the Data Explorer. This pane, under the Project
Manager at top right of the interface, gives an easy to use interface for browsing data on different servers (see Figure 1). The
browser can drill down to tables, individual fields and indices,
and also lists stored procedures and views. An especially convenient feature is the ability to drag and drop from the Data
Explorer to a Windows Form or Web Form. This automatically
places BDP components on the form and configures them to
point to each other.

ASP.NET Applications
Perhaps the biggest difference in the user interface of
C#Builder is the approach taken to Web-based applications.
www.C-SharpPRO.com

Cover Story

C#Builder for Delphi Developers

In place of the different Internet server


technologies seen in recent versions of
Delphi, C#Builder uses the Microsoft
ASP.NET classes in the .NET Framework.
The ASP.NET system manages a great
deal of the interaction between the Web
browser and the server. A URL is translated to a Web form, which is defined
by an .aspx HTML file that is pre-processed and delivered to the Web browser.
Additional custom program code can be
included in a code-behind C# language
file, which is run at the server when the
Web form is delivered.
C#Builder makes it easy to program
using ASP.NET (see Figure 5). A full
graphical Web form designer can be
used to lay out Web pages using either
a grid layout, or the more standard
HTML document-style layout. The
HTML thats generated is standard,
with the addition of extra tags for Web Figure 5. Developing an ASP.NET application.
Forms HTML components that are preprocessed at the server.
been designed to take advantage of the unique features of
When designing a Web Form, C#Builder makes these
the C# language. It works very closely with the Microsoft
HTML components available in the Tool Palette as if they
.NET Framework SDK technologies such as ADO.NET and
were regular .NET Framework components. The properties
especially ASP.NET.
of these components (and the other HTML elements) can
Unlike other development solutions, Borland C#Builder
be set using the Object Inspector in the familiar way.
helps developers create .NET Framework applications that
Alternatively, the Code Editor can be used to directly
work closely alongside technologies such as Java, and with
edit the HTML mark-up. The Code Editor includes Code
databases like IBM DB2 and Oracle. It also includes links
Insight for HTML, helping prompt for the more obscure
with modeling tools, requirements management solutions,
tags and options. Pairs of tags (such as <p> and </p>) source code management utilities and application profilers.
are highlighted in the same way as pairs of braces in C#,
To Delphi developers, the result is a powerful combihelping to track down syntax issues.
nation: a programming environment that is familiar, but
The two-way nature of the HTML designer makes it poswhich works with the latest Microsoft standards for prosible to switch between layout and HTML mark-up seamgramming, can include third-party components, and has
lessly and quickly. All the latest Internet Explorer browser
links to Java and beyond. #
extensions (including CSS2 layout) are supported in the
visual designer, making C#Builder a very effective Web page
This is an abridged version of this article. The fulldesigner in its own right.
length article (available at www.C-SharpPRO.com)
Like other .NET components, HTML components have
describes other important features of C#Builder, including
properties and events that can be defined in the Object
its debugger, integration with Java via Borland Janeva,
Inspector. Program code can be defined against the events
and integration with other solutions such as Microsoft
in the code-behind C# source code. The Web Form shows
Visual Studio, and Borlands CaliberRM, StarTeam, and
three tabs on the bottom of the editor, so C#Builder makes
Optimizeit for .NET products.
it very easy to switch between the form, the HTML, and
the code-behind file.

Summary
Borland Delphi developers will quickly find themselves
at home with Borland C#Builder. Many of the features of
the two solutions are the same, from editor keystrokes to
the approach to development. However, C#Builder has

Corbin Dunn is a Research and Development software engineer for the .NET
group at Borland Software Corporation. He is currently working on the IDE.
When not speaking at conferences, writing for magazines, or posting to the
newsgroups, he can be found scaling steep rocks in the Santa Cruz Mountains,
riding motorcycles, or hanging out in his tree house.

www.C-SharpPRO.com

Feature

By Bill Wagner

Whats New in C# 2?

Find out what new features will impact your development.

HEN THE NEXT VERSION OF VISUAL STUDIO .NET


(code-named Whidbey) is released, you will get a
new, upgraded C# language. The additions to the language are sure to make you a more productive developer
youll be able to write more reusable code and higherlevel constructs in fewer lines of source. All in all, youll
get more done faster.
There are four new features announced already for C#
2.0: generics, iterators, anonymous methods, and partial
types. In this article, Ill show you the impact each of
these features will have on your daily activities, focusing
primarily on generics and iterators. The focus of these
new features is to make C# a tool that will make you
more productive. Its all about writing less code that does
more work smarter, not harder.

The Big One: Generics


Generics will have more impact on how you develop solutions than any of the other new features in C# 2. In fact,
the generics feature is not specific to C#. To implement
C# generics, Microsoft is changing the CLR and Microsoft Intermediate Language (MSIL) as well. In time, you
might see other languages add generics support. C# will
be, however, the first to
I like the design decisions implement generics.
Generics provide parain C# 2.0s generics.
metric polymorphism,
which is a fancy way of saying you create a series of
similar classes from a single source. The multiple versions
are created by the compiler when you provide a specific
type for a generic parameter. You use generics to build
algorithms that are, at least in part, independent of the

structures they act upon. You can find typical examples of


generics in the .NET Collections namespace: HashTables,
ArrayLists, Queue, and Stack all can store different object
types without affecting their implementation. C# 1.0
stores reference to the System.Object type. Although this
design is reusable for all types, it has many deficiencies.
For one, the current design is not truly type-safe. Consider this code:
ArrayList myIntList = new List ();
myIntList.Add (new int (32));
myIntList.Add (new double (98.6));
myIntList.Add (new CSharpGuru ("Bill Wagner"));

This code compiles just fine, but it almost certainly is not


the intent. I have seen few designs that call for a container
to hold totally disparate items. When you remove items
from the collection, you need to add extra code to determine
what kind of objects were put on the list in the first place.
In all cases, you need to cast items from System.Object to
the particular kind of object you placed on the list.
But thats not all. Value Types pay a particular penalty
when they are placed in the boxing and unboxing collections. When you put a value type in a collection, you
must store it in a box. You pay again to remove the item
from the box when you access an element in the collection. This penalty is small, but with large collections of
thousands of items it adds up quickly. Generics remove
this penalty.
Those of you familiar with C++ templates will have
no trouble working with C# generics because the syntax
for the two is almost identical. The inner workings for
www.C-SharpPRO.com

Feature
generics are, however, quite different. Lets look at one
simple example to see how generics work and how they
are implemented. Consider this portion of a list class:
public class List
{
internal class Node
{
object val;
Node next;
}
private Node first;
public void AddHead (object t)
{ // ...
}
public object Head ()
{
return first.val;
}

Whats New in C# 2?

generic implementations you can create today. For one,


the C# compiler will report errors if you attempt anything
but an integer in the collection; today, you need to catch
those errors by testing the code at run time.
In C# 1.0, you pay the boxing and unboxing penalty
whenever you move a value type into or out of the collection. With generics, the JIT compiler creates a specific
instance of the collection that stores a particular value
type; you dont need to box or unbox the items. But
theres more to it. The C# designers want to avoid the
code bloat often associated with C++ templates. To save
space, the JIT compiler generates only one version of the
template for all reference types. This provides a size/
speed trade-off whereby value types get a specific version
of each type, and reference types share a single runtime
version using object as a base type.
For all this to work, the CLR and MSIL will undergo
some changes. When you compile a generic class, MSIL
contains placeholders for each parameterized type. For
example, consider these two method declarations in MSIL:

}
.method public AddHead (!0 t) {

The previous code stores System.Object references in its


collection. To reuse it, you change the casts on the objects
being removed from the collection. But using C# generics,
you instead could define the same class like this:
public class List<ItemType>
{
private class Node<ItemType>

}
.method public !0 Head () {
}

!0 is a placeholder for a type to be created when a particular


instantiation is declared and created. Heres one possible
replacement:

{
ItemType val;

.method public AddHead (System.Int32 t) {

Node<ItemType> next;

}
private Node<ItemType> first;

.method public System.Int32 Head () {


}

public void AddHead (ItemType t)


{ // ...
}
public ItemType Head ()

Similarly, variable instantiations contain the specific


type. The previous declaration for a list of integers
becomes this:

{
return first.val;
}

.locals (class List<int>)


newobj void List<int>::.ctor ()

Notice that I replace object with ItemType the parameter type in the class definition. The C# compiler stores
the parameter for ItemType as the proper type when you
instantiate the list. For example, take a look at this code:
List<int> intList = new List<int> ();

The MSIL generated specifies that intList stores integers,


and only integers. This has several advantages over the

This simple example illustrates the way the C# compiler


and the JIT compiler work together for generics. The C#
compiler generates MSIL that contains placeholders for
each type parameter. The JIT compiler turns these placeholders into specific types either System.Object for
all reference types, or specific value types for each type.
Each variable instantiation of a generic type includes type
information so the C# compiler can enforce type safety.
But were not done yet. Anyone who has used C++
templates is familiar with the painful task of tracking
www.C-SharpPRO.com

Feature

c:\Program Files\Microsoft Visual Studio .NET


2003\Vc7\include\algorithm(1719) : error C2784: 'bool
std::operator <(const std::vector<_Ty,_Alloc> &,const
std::vector<_Ty,_Alloc> &)' : could not deduce template
argument for 'const std::vector<_Ty,_Ax> &' from
'std::allocator<_Ty>::value_type'
with
[
_Ty=Test
]
c:\Program Files\Microsoft Visual Studio .NET
2003\Vc7\include\vector(915) : see declaration of
'std::operator'<''
c:\Program Files\Microsoft Visual Studio .NET
2003\Vc7\include\algorithm(1774) : see reference to
function template instantiation 'std::pair<_Ty1,_Ty2>
std::_Unguarded_partition<_RanIt>(_RanIt,_RanIt)' being
compiled
with
[
_Ty1=std::vector<Test>::iterator,
_Ty2=std::vector<Test>::iterator,
_RanIt=std::vector<Test>::iterator
]

Figure 1. This error, edited for brevity, fails because the Test class does
not support copy construction or assignment, even though the specific
messages dont tell you that. C# 2 should, with constraints, give you
better indications about problems when you have compilation trouble.

down compiler errors. This simple example yields the set


of errors shown in Figure 1:
class Test
{
public:
Test () {}
private:
Test (const Test& t);
const Test& operator = (const Test& r);
};
int _tmain(int argc, _TCHAR* argv[])
{
vector <Test> vect;
sort (vect.begin (), vect.end ());
}

Simple, right? Well, the C# designers want to do a much


better job of communicating potential compilation problems. Remember that a specific instantiation of a generic
class does not get created until the CLR loads and creates
the class at run time. This means your users would see
those cryptic messages. The C# solution for this problem
is constraints, which describe the capabilities of any class
that can be used for a parameterized type. As an example,
consider a generic implementation of a binary tree. Binary
trees store objects in sorted order; you only want to store

Whats New in C# 2?

types that implement IComparable. You can specify this


requirement using constraints:
public class BinaryTree <ValType> where
ValType : IComparable
{
}

Using this definition, any instantiation of BinaryTree using a


class that does not support the IComparable interface wont
compile. You can create multiple constraints easily. Suppose
you want to limit your BinaryTree to objects that support
ICloneable. You simply add more constraints:
public class BinaryTree <ValType> where
ValType : IComparable <ValType>,
ValType : ICloneable
{
}

You can specify one base class and any number of interfaces as a set of constraints for each parameterized type. In
addition, you can specify that a type must have a constructor
without parameters (but this syntax is still being discussed).
Constraints also provide one more advantage: The compiler assumes the objects in your generic class support any
interfaces (or base-class methods) specified in the constraint
list. In the absence of any constraints, the compiler assumes
only the methods defined in System.Object. You would need
to add casts to use any other method. In general, whenever
you use a method not defined in System.Object, you should
specify that requirement in a set of constraints.
I like the design decisions in C# 2.0s generics. Its a nice
mix between run-time power and a powerful development tool.
You can specify your requirements on parameterized types
better than you can in C++, and Microsofts commitment to
reflection in generics will be an important advantage over the
Java Tiger project. Generics are important enough that Microsoft will release a new namespace containing generic versions
of the existing .NET collections: System.Collections.Generic.
This namespace will contain generic versions of ArrayList,
HashTable, and all the other classes in this namespace.
Generics are big news and require you to make big changes.
Some changes are still in discussion. For example, consider
generics and reflection. The C# designers have stated a goal
that you will be able to use reflection to find both a generic
class and a specific instantiation of a generic class. These
features also will have some impact on both MSIL and the
CLR. In addition, it might introduce new features in the
System.Reflection namespace.
Because generics require changes to the CLR and MSIL,
other .NET languages can add generics in time. No official statements have been made on when or if other languages will include generics.
www.C-SharpPRO.com

Feature
Explore Iterators
Iterators are a new syntax to create a common idiom
using much less code. Suppose you create some nifty
new container class (many general-purpose containers are
implemented in the .NET Framework library already). To
support your users, you need to create methods that support traversing this collection and returning the objects in
the collection.
Today, you would do this by creating a class that implements IEnumerator. IEnumerator contains two methods
Reset and MoveNext and one property Current. In
addition, you would add IEnumerable to the list of implemented interfaces on your binary tree, and its GetEnumerator
method would return an IEnumerator for your collection. By
the time youre done, you have written an extra class with at
least three functions as well as some state management and
another method in your main class. To illustrate this, consider
the code in Figure 2 its almost a full page of code, simply
to handle list enumeration.
C# 2.0 adds a new semantic to the foreach keyword
that lets you write these iterators much more concisely.
Here is the C# 2.0 version of the code in Figure 2:
public class List
{
public object foreach ()
{
int i=0;
while (i < theList.Length ())
yield theList[i++];
}
// Other methods removed.
}

The foreach operator, combined with the yield statement, lets you replace roughly 30 lines of code with only
six. This means fewer bugs, less development time, and
less source code to maintain all good things.
Internally, the compiler generates the MSIL that corresponds to those 30 lines of code in todays version. The
difference is that the compiler does it so you dont have
to. The compiler generates a class that implements the
IEnumerator interface and adds it to your list of supported
interfaces; youll have no control over the compiler-generated class name. The interaction between the source,
the generated MSIL, and the reflection APIs are all being
debated, and it will change before the final version of C#
2 is released. But a few requirements should stay true.
Youll be able to use reflection to find the GetEnumerator
method of the outer class, and youll be able to use the
inner enumerator class. But you might not necessarily be
able to use reflection to get at the inner enumerator class.
I always get this question when I talk about these new
features, and though it seems obvious, Ill say it anyway:
You can combine iterators with generic classes. In addition,
some syntax changes are planned for iterators. In the final

Whats New in C# 2?

public class List : IEnumerable


{
internal class ListEnumerator : IEnumerator
{
List theList;
int pos = -1;
ListEnumerator (List l)
{
theList = l;
}
public object Current
{
get
{
return theList[pos]);
}
}
public bool MoveNext
{
pos++;
return pos < theList.Length;
}
public void Reset ()
{
pos = -1;
}
}
public IEnumerator GetEnumerator ()
{
return new ListEnumerator (this);
}
// Other methods removed.
}

Figure 2. You need all this code simply to support iterating a collection
based on an array. You would need even more code to support more
complicated internal data structures. C# 2.0 lets you write these iterators
much more concisely using the foreach keyword.

version, you might be able to give the function a name of


your choosing instead of being forced to use foreach. But
the yield statement should remain the same.
The new features in C# 2.0 address the areas in the language where you need to do repetitive work, such as reimplementing common algorithms for new types, casting
objects between types, writing enumerations, and merging
your changes with a tools changes. Using these new tools
means you can get more done faster. Im excited to know
that my favorite tool is going to continue to improve and
will become even more powerful. I cant wait to get my
hands on this version of the language. #

Bill Wagner has been an independent software consultant for more than nine
years, and he co-founded SRT Solutions in 2001. He is a nationally recognized
expert on Microsoft .NET, developing in .NET, C#, and C++, as well as
ASP.NET and ASP. His publications include The C# Core Language Little Black
Book (Coriolis). E-mail him at wwagner@SRTSolutions.com.

www.C-SharpPRO.com

N E W

&

U S E D

By Bill Todd

VMware Workstation 4.0


Virtual Machines Provide Powerful and Flexible Development
and Test Environment

hat do you do when you need to test the


installation of your new application several times, starting with a clean machine

each time? How about when you need to test your


new application on a different operating system than
the one you develop under? Or test your application
on a machine with less memory or a smaller hard
drive than you have on your development machine?
Maybe you want to demonstrate the multi-user features of your application in a network environment
at a client site, or work on your application, which
must connect to a Linux server, while you are on
the road and the only computer you have is your
Windows notebook (or vice versa).
If you spend a lot of time setting up spare machines
and installing operating systems or you cant or dont
do these things when you need to because it takes too
much time and too much hardware let me assure you
there is an easier way.

you can make the virtual hard disk on any virtual machine
non-persistent. This means that each time you shut down the
virtual machine you will be asked if you want to save all the
information that was written to the virtual hard drive. If you
decline, all the information written to the hard drive will be
discarded. The next time you start the virtual machine it will
be in exactly the same state it was before you used it. Instead
of using a non-persistent virtual disk you can take a snapshot
at any time while a virtual machine is running, and later revert
back to the snapshot. Snapshots and non-persistent disks make
repetitive testing using a known environment very easy.
What Is VMware Workstation?
VMware Workstation is a program that enables you to run
multiple operating systems simultaneously in fully networked,
portable virtual machines on one physical machine. You install
it just like any other piece of software. It does not modify your
operating system and it does not require any special hardware.
What you do need is fairly powerful hardware on the host
machine. For adequate performance you need a 500 MHz
P6-compatible processor and enough memory to run the
host operating system, the guest operating systems, and
any applications that you need to run. You will also need
enough disk space to accommodate the virtual disks for

VMware Workstation lets you set up any number of virtual


machines on your computer. You can install any version of
Windows (including Microsoft Windows Server 2003), as
well as Linux, Free BSD and Netware 5 and 6. If you want
to revisit the good old days, you can even create a DOS virtual machine. You can connect your virtual machines to the
network to which the host machine is connected or you can
create a virtual network that connects your virtual machines
and the host machine. You can choose which hardware is
installed on a virtual machine and control the size of the hard
drive (or drives) and the amount of installed memory.
All the files that comprise a virtual machine are stored in a single folder. There are no registry entries or other external information. That means that you can back up a virtual machine
to a zip file, a CD-ROM, a tape drive, a file server, or any other
device. You can delete the virtual machine from your computer
and put it back later simply by restoring the folder. Better still,
29

DELPHI INFORMANT MAGAZINE | October 2003

Figure 1: The new virtual machine wizard.

New

&

Used

VMware Workstation 4.0


Workstation does impose a performance
penalty because you are adding another
layer of software between the operating
system and the hardware, but in most
cases the performance difference is not
noticeable and, considering what you get
in return, it is a very small price to pay.

Figure 2: The VMware Workstation window.

Figure 3: Two virtual machines running.

your virtual machines. By default, the file that contains each


virtual disk grows dynamically as you use space on the virtual disk. This means that if you have a 1 GB virtual disk
but it only contains 300 MB of data, a little over 300 MB will
be used on the host machines hard drive.
I run VMware Workstation on a 1 GHz Pentium III notebook
with 512 MB of RAM and the performance is great. The virtual
machines user interface is as responsive as a real PC. VMware
30

DELPHI INFORMANT MAGAZINE | October 2003

Creating a Virtual
Machine
To create a new virtual machine start
VMware Workstation and choose File |
New | Virtual machine from the menu to
start the virtual machine wizard shown
in Figure 1. You can choose a standard
configuration or create a custom configuration. Next, the wizard lets you choose
from a drop-down list of operating systems. The list includes an Other choice if
you want to try an O/S that is not in the
list. If you select a custom installation
you will be asked to choose the amount
of memory in your virtual machine.
Next, you can choose the type of networking support you want. VMware
supports three types of networking.
Bridged networking gives the virtual
machine direct access to the network to
which the host machine is connected.
The virtual machine will appear as
another machine on the network with
its own IP address and machine name.
Network address translation (NAT) gives
the virtual machine access to the hosts
network using the hosts IP address.
This works exactly like having two or
more machines connected to a network
through a router that supports NAT, and
can be used to connect to non-TCP/IP
networks, such as Token Ring. The third
choice is Host Only networking. This
provides a virtual network to which
only the host and the virtual machines
can connect. The great thing about Host
Only networking is that, unlike Bridged
and NAT, it works on a machine that is
not connected to an external network.
The only disadvantage to Host Only
networking is that if your machine is
connected to an external network, the
virtual machines cannot access it.
The next page of the wizard asks what kind of hard disk your
virtual machine will use. You can choose to create a new virtual disk, use an existing virtual disk, or use a physical disk.
If you elect to create a new virtual disk, the next page of the
wizard lets you choose the size of the disk. You also have the
option to allocate all the disk space at this time. Allocating all
of the disk space when you create the virtual disk means better performance when you use the disk because VMware does
not have to expand the file that contains the virtual disk as you

New

&

Used

VMware Workstation 4.0

Feature

Description

Snapshots

Take a snapshot of a virtual machine at


any time and revert to that snapshot at
a later time.

Tabbed user interface

Run multiple virtual machines in the


same window and switch by clicking the
tab for the virtual machine you want.

Shared folders

Share files between guest and host operating systems using shared folders.

Drag and drop


between systems

Drag and drop files between the guest


and host machines on Windows hosts.

New Linux UI

If you run VMware Workstation on a


Linux host you will find a new user
interface that is almost identical to the
Windows UI.

Favorites list

A browser-like Favorites list provides an


easy way to organize virtual machines.

Full debugging support

Full support for native program debugging, including user- and kernel-level
debuggers.

APIC hardware

Virtual machines are now based on an


Intel 440BX motherboard with NS338
SIO chip and 82093AA IOAPIC.

VESA BIOS

Provides better graphics support before


VMware Tools is installed.

DOS support

Support for EMM386 provides better


support for legacy DOS applications.

New sound card

Virtual machines now use a SoundBlaster-compatible PCI sound card for


improved audio input and output performance.

DVD and CD R/RW

Improved performance for optical drives


lets you burn CDs in a virtual machine.

Direct Draw and


improved graphics

Virtual machines now support Direct


Draw and provide improved graphics
performance for multimedia, including
streaming video.

USB 2.0

USB 2.0 host devices are now supported by mapping to USB 1.1 in virtual
machines.

Figure 4: New features in version 4.0.

add data to the disk. The disadvantages of allocating the disk


space at this time are that it will take longer to create the new
virtual disk and the disk will consume its full capacity on the
host systems hard drive. This means that if you create a 2 GB
virtual disk and allocate the space at the time of creation you
will immediately lose 2 GB of space on the host systems hard
drive. You also have the option to have VMware split the virtual disk into two 1 GB files. This lets you create large virtual
disks on a host operating system with a maximum file size of
2 GB. Finally you will be asked to specify the path for the file
that contains the virtual disk.
After youve created the virtual machine, you are, for all practical purposes, staring at a new computer with a new unformatted hard disk. Figure 2 shows the VMware Workstation win31

DELPHI INFORMANT MAGAZINE | October 2003

Just the Facts


VMware Workstation lets you set up any number of virtual
machines on your computer. That is, it enables you to run
multiple operating systems simultaneously in fully networked, portable virtual machines on one physical machine.
Installed like any other piece of software, it doesnt modify
your operating system and it doesnt require any special
hardware. If you need to perform repetitive testing starting
with a known machine configuration, test on multiple operating systems, develop on multiple operating systems, run legacy applications, or develop and test network applications,
VMware Workstation is a great solution.
VMware, Inc.
3145 Porter Drive
Palo Alto, CA 94304 USA
Phone: (877) 486-9273
E-Mail: sales@vmware.com
Web Site: www.vmware.com
Price: US$299 via download, US$329 packaged.
dow. The toolbar includes Power Off and Power On buttons. To
install an operating system on your new virtual machine put
the appropriate diskette or CD-ROM in the host machines drive
and turn the virtual machines power on just as you would
on a physical machine. After youve installed the operating
system and your virtual machine is running you need to install
VMware tools. The VMware tools package provides enhanced
video driver and mouse support within your virtual machine.
The VMware Workstation main window contains a link
that lets you edit the virtual machine settings. This lets
you switch the virtual disk between persistent and nonpersistent modes, enable or disable hardware devices, and
change networking settings.
Figure 3 shows the VMware Workstation window with Windows 98 and Red Hat Linux virtual machines running. You can
switch between the virtual machines by clicking the tabs at
the top of the window. If you prefer, you can open a separate
VMware Workstation window for each virtual machine.
Whats New in Version 4.0
If you are using an earlier version of VMware Workstation, the table in Figure 4 contains a summary of the new
features in version 4.0. All the new hardware for virtual
machines makes upgrading virtual machines created in
prior versions of VMware workstation more difficult than it
has been in the past.
When you attempt to open a virtual machine created with
a prior version, VMware Workstation asks if you want to
upgrade the hardware. If you say yes, your guest operating
system will behave exactly as it would if you had opened
the case on a physical computer and replaced the motherboard, sound card, and other hardware. How smoothly this
process goes depends on how well the operating system
handles hardware changes and how skilled you are at dealing with hardware changes on an existing machine.

New

&

Used

VMware Workstation 4.0

Conclusion
If you need to perform repetitive testing starting with a known
machine configuration, test on multiple operating systems,
develop on multiple operating systems, run legacy applications,
or develop and test network applications, VMware Workstation
is a great solution. You get all the power and flexibility of having multiple machines connected on a network with advantages that you cannot get with physical hardware. You can change
the hardware configuration of a virtual machine simply by
editing the virtual machine configuration. You can restore the
machine to a known state using either a non-persistent virtual
disk or snapshots. VMware Workstation provides a development and test environment that is more flexible than hardware
at a fraction of the cost.

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


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

32

DELPHI INFORMANT MAGAZINE | October 2003

F I L E

N E W

Documentation: More Than an Add-on

By Alan C. Moore, Ph.D.

o you consider the


documentation that
accompanies your software
whether its a help file, a tutorial,
or a demo an essential element?
Or is documentation just an add-on,
something you append when youre
finished with the real work of
creating a killer app?
My view is that as the complexity of
our applications increases, the need
for documentation increases with it.
This became particularly clear to me
in the process of completing several
recent reviews of complex libraries and
tools (CDK, ExpressQuantumGrid, and
RoboHelp). I found the documentation and tutorials essential in getting
up to speed quickly. I will use these
three products as examples of how to
provide appropriate learning tools for
users, concentrating especially on their
tutorials. Lets begin with a brief discussion of help files.
Help! I need help! There are a number
of ways to provide help to users, including general help, contextual help, and
hints. Most readers are familiar with the
basic constituents of general help-file
topics browsing sequences, indexes,
and tables of contents so I wont
dwell on this. There are many places
where you can find information, including Delphi itself (see HCW.hlp located
in the \DelphiX\Help\Tools folder).
Context-sensitive help is another matter altogether. Ideally, when the user
33

presses 1, the topic invoked will


be related to the part of the program
in which the person is working. One
developer friend who has worked with
both Delphi and Visual Studio .NET
praised the contextual understanding of
the latter, noting that Delphis help system seems unaware of whether you are
developing a CLX or VCL application.
When accessing help for components,
you are often shown both sets of topics
from which you must then choose.
Regarding contextual help, Mark
Miller recommended the following
questions for the help writer: Is the
presented information clear, or is it
littered with noise that users have
to wade through? If its the latter,
then could a stronger understanding
of [the triggering] context help to
reduce the noise that end users must
endure each time they need some
assistance? I would add some additional suggestions: Always adopt the
mind set of the user and concentrate
on the applications purpose, not its
programming details. Dont assume
anything, except that your users are
going to be far less familiar with your
application or component than you
are. And if you want your help file to
rise above the average, put a lot of
thought into context-related help.
Tutorials. Besides help files, tutorials
are becoming increasingly important
as a means of quickly getting users
up to speed. Tutorials can take a
variety of forms; applications may

DELPHI INFORMANT MAGAZINE | October 2003

require a different approach than that


used for component libraries. Here
Ill describe three tutorials I encountered in the process of writing product reviews (two applications and
one component library). Ill begin
with the tutorials that accompany the
two applications, CDK and RoboHelp,
and end with the component library,
ExpressQuantumGrid.
My delightful experience with the CDK
tutorial provided the original inspiration to write this column. That tutorial
is a stand-alone application with a
simple and attractive user interface. On
the left are two tabs, one providing a
list of tutorials, the other a list of the
topics in the selected tutorial. On the
right is the viewer itself. The topics are
organized logically a necessity for
an effective learning system.
Although each tutorial covers a discrete topic and can stand on its own
to some extent, they are arranged
sequentially and build on the previous lessons. Each tutorial begins with
a description, an outline of its specific goals, and what you need to complete it. The various tutorials provide
a good mix of theory and practice,
first providing the necessary information about Delphi and its component
structure, and then taking the user
step by step through the process of
building and/or modifying a particular component. Tasks common to
building any component (packaging,
registering, testing, and installing)

File

New

are repeated in each tutorial. Each


tutorial concludes with a summary of
what the user should have learned.
Because its based on an independent
application instead of a browser or
help engine, a tutorial can incorporate all the functionality of a wizard.
Specifically, some tutorials provide
a portal to the very task they discuss, accomplishing code creation
behind the scenes. For example, with
a single mouse-click you can drop a
visual component with specificallyset properties onto a form to create a
new composite component. Because
of its intelligent design and powerful
capabilities, I consider the CDK tutorial to be an excellent model for nextgeneration training tools.
Like CDK, RoboHelp is an application to help developers produce a
certain kind of output; in this case
not component code, but a help file.
It features the same thoroughly logical
design, but takes a slightly different
approach. Although the CDK tutorial
integrates information about component structure into its various tutorials,
RoboHelp deals with topics such as
getting started and help authoring in
its printed manual. The tutorial, which
mainly covers the use of RoboHelp
HTML, exists as both a chapter of the
manual and as an application, similar
in some respects to the CDK manual.
As with the earlier tutorial, it includes
clear instructions, logical organization,
and helpful screen shots.
Components dont usually need tutorials to the same extent as applications;
usually a good help file describing the
properties, methods, and events is sufficient. But in the case of a complex component or library one that includes
property or component editors and
sub-components a demo or tutorial
showing how to use it can be very helpful. ExpressQuantumGrid qualifies as
such a set of components. Its demos and
tutorials are perfect for demonstrating
its capabilities, and helping developers
to take advantage of those capabilities.
Lets examine some of the approaches
used by ExpressQuantumGrid; theyre
certainly worth emulating.

except the user must complete certain tasks in the tutorial for it to be
identical to its corresponding demo.
While the demos are perfect for getting a quick idea of capabilities, the
tutorials are ideal for learning the
essential skills and architecture. Most
of the grunt work is done for you.
Although this approach is obvious
and logical, the developers at Developer Express have used some innovative techniques to set the demos
apart from the tutorials.
When you open a tutorial for the
first time and try to run it, you get
a warning message alerting you that
you have some tasks to complete.
Those tasks involve manipulating the
component/property editors, setting
properties, and yes relating to
code. Its in the latter task that we
find some truly clever approaches.
Instead of having you type a lot of
code, or even pasting code from
somewhere else, youre simply asked
to activate the code by relating to
comment characters. A block of code
to be activated would begin with:
{ remove/add the closing brace on
this line to disable/enable the
following code }

and end with:


// }

By adding a brace to the first line, the


entire block becomes visible; by removing it, the block is hidden again. By
having the closing brace preceded by Cstyle comment characters, that brace is
only relevant if there is an earlier opening brace that has not been terminated.
Clever, dont you think? I was very
impressed with all of the demos and felt
that they added even more power to an
already excellent product.
At the beginning I opined as the
complexity of our applications
increases, the need for documenta-

ExpressQuantumGrid includes a
demo and a tutorial for every major
topic. They are essentially the same,
34

DELPHI INFORMANT MAGAZINE | October 2003

tion increases with it. Mark Miller


suggested, As the actual complexity of our applications increases, the
perceived complexity of our applications must decrease. Specifically, he
suggested that we (a) decrease the
points of entry into all functionality,
(b) increase the clarity of information, (c) reduce the noise caused by
unneeded information, (d) reduce
barriers between assistance (tutorials,
help, etc.) and the application, and
(e) create self-documenting solutions
instantly absorbed by users.
On the final point Miller explained,
Self-documenting solutions are built
into the application [and] are instantly
absorbed by users because they require
no documentation. As an example, he
described the behavior of the scrollbar:
As a user navigates through a large
document or image, the thumb follows
the view. When a user pushes on the
thumb with the mouse, the view into
the document or image adjusts. Some
applications provide additional feedback indicating exactly where you are
in the view (e.g. Page 3 appears in a
popup hint). Furthermore, the size of
the thumb is proportional to the percentage of the document represented
by the view. A scrollbar used in this
manner is self-documenting. Effective
self-documentation can actually reduce
the need for external help and makes
working with an application much
more intuitive.
This month we explored various
aspects of documenting software, especially help files and tutorials. But there
are areas we have not touched on that
we will explore in the future, including interactive and multimedia demos.
Until next time...
Mark Miller reviewed an early draft
of this article and contributed excellent observations, many of which I
included. I would be remiss not to
gratefully acknowledge his important
contributions.

Alan Moore is a professor at Kentucky State University, where he teaches music theory and humanities. He
was named Distinguished Professor for 2001-2002. He has been named the Project JEDI Director for 2002-2004.
He has developed education-related applications with the Borland languages for more than 15 years. Hes the
author of The Tomes of Delphi: Win32 Multimedia API (Wordware Publishing, 2000) and co-author (with
John C. Penman) of The Tomes of Delphi: Basic 32-Bit Communications Programming (Wordware Publishing,
2003). He also has published a number of articles in various technical journals. Using Delphi, he specializes in
writing custom components and implementing multimedia capabilities in applications, particularly sound and
music. You can reach Alan at acmdoc@aol.com.

You might also like