Anda di halaman 1dari 36

April

2004

Volume 10 Number 4

INSIDE
ON THE COVER
.NET Developer

Product Reviews: PE Explorer and ImageLib Corporate Suite 7

Moving to Delphi 8

Okay; so youve decided to make the move to .NET. Thats when the
questions really start. Like, Should I use VCL.NET? What about porting
VCL code to VCL.NET? Should I use the new Framework Class Library
(FCL) for .NET? Fortunately, Glenn Stephens provides answers to these
questions and many more.

Delphi Informant
www.DelphiZine.com

The Complete Monthly Guide to Delphi Development

April 2004 vol.10, no. 4

FEATURES
Columns & Rows

Should You Use VCL.NET?


the FCL? or Both?

ADO.NET Data Access Components

Bill Todd turns to the disconnected side of ADO.NET, focusing on DataSet


as the key object. Since it contains a collection of DataTable objects, and
each DataTable object contains collections of DataRow, DataColumn, and
Constraint objects, you can model a complete relational database in memory.

ADO.NET
Everyday

Form Techniques
Special C#PRO Insert

Everyday Delphi

14

Disconnected

Cover Art by Arthur A. Dugoni Jr.

Fun with Forms

Many Delphi developers spend a lot of time getting forms just so for clients. Thats why Rick Spence
thought it would be a good idea to share some user-pleasing-yet-generic UI techniques, from
changing a fields color when it receives focus (or highlighting it with a rectangle), to disabling
data-aware controls using RTTI, and more.

SPECIAL C#PRO INSERT


2
Manage Your Memory

Just because .NET is a managed environment doesnt mean youre suddenly absolved from all your
resource management responsibilities, warns Bill Wagner. You still need to clean up some resources
in a timely fashion, rather than waiting for the garbage collector to be called in to tidy up after you.

Ask the C# PRO: Return a Value From a Thread

The question is: Should I use a .resx or a .res file in my .NET application? The simple answer, replies
Richard Grimes, is that if you want to add resources to a managed application, you should add managed
resources. You do this through a .resx file. However, the simple answer only tells part of the story...

Protect Your Intellectual Property With Code Access Security

Reflection is a feature that gives developers good reason to think about securing their
components, cautions Michle Leroux Bustamante. She then goes on to discuss what can,
and cannot, be done to protect intellectual property, and even provides us with best practice
techniques for protecting components.

REVIEWS
29 PE Explorer

Product Review by Mike Riley

31
1

ImageLib Corporate Suite 7.0

Product Review by Bill Todd

DELPHI INFORMANT MAGAZINE | April 2004

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

T O O L B O X
ProDelphi.Net Available for .NET Versions of Delphi
ProDelphi.Net from Helmuth J.H.
Adolph measures the run time of
programs written in Delphi for .NET.
This source code profiler gives you
the CPU-time consumed by the procedures of your program. The measurement is done based on CPU-cycles,
and works with a Pentium CPU or
Pentium-compatible CPU.
The principle of source instrumenting, the sophisticated measurement
correction algorithm, and the granularity of 1 CPU-cycle guarantee a high
measurement accuracy. Even small
or multiple nested functions are measured precisely. Source instrumenting
also ensures that idle times caused by
certain Delphi- or Windows-API functions (e.g. Sleep, MessageBox, WaitForSingleObject, etc.) are automatically
excluded from measurement.
Measurement results can be viewed
with a built-in viewer or exported to
a database. The user-friendly human
interface makes the profiling process
fast and easy; in particular, the sorting viewer enables the user to find the
bottlenecks at first glance. With one
mouse click the file with the measured
method is opened in Delphi and the
cursor is positioned at the beginning of
the measured method.
An optional call graph shows who
called a procedure and who is called
Web Tech Announces
New Delphi 8 for
.NET Training
Web Tech Training and Development announced a new five-day
training course: Delphi 8 for .NET.
Targeting existing Delphi developers, this course covers porting existing Delphi applications to Delphi 8,
ADO.NET, and ASP.NET.
The Delphi 8 course joins Web
Techs other VB .NET and C#
.NET courses to provide full coverage of the .NET platform. Courses
are held in St. Augustine, FL, and
Painswick, Gloucestershire, UK.
Courses are also available at client
sites throughout the world. For full
course details, schedules, and pricing visit www.WebTechCorp.co.uk or
www.WebTechCorp.com.

DELPHI INFORMANT MAGAZINE | April 2004

by it. Also, how often


this happened and what
time was consumed when
processing this path is
displayed.
Results are either displayed in CPU-cycles or in
a variable format in micro
seconds (minimum 0.001
micro seconds), seconds,
minutes, or hours.
The viewer can also
display the procedures
that are never called. The
built-in history function
can compare the run time
of the actual run with the
data of previously stored
measurements.
All results can be recalculated for a faster or slower PC. So
the run-time measurement can be done
on the development PC and the results
for the customer PC can be emulated.
ProDelphi.Net integrates with the
Delphi 8 IDE. However, run-time mea-

surement on PCs without an installed


Delphi compiler is also possible.
Helmuth J.H. Adolph
Price: 59.50
Contact: hjh.adolph@prodelphi.de
Web Site: www.prodelphi.de

Gnostice Upgrades eDocEngine


Gnostice Information Technologies
released eDocEngine 1.03, an
improved version of its 100% pure
VCL electronic document creation
component suite for Delphi and
C++Builder. eDocEngine enables
developers to deliver information
straight from the applications they
develop in over 20 popular electronic
document formats. Rich content
can be formatted to the look and
feel required and delivered directly
to users Web browsers, sent as email attachments, or saved to disk.
Actionable and navigational elements
can be added to created documents to
provide interactivity and to enhance
the overall user experience.
eDocEngine supports the creation
of documents in PDF, RTF, HTML,
XHTML, Excel, text, CSV, Quattro Pro,
LOTUS 1-2-3, DIF, SYLK, TIFF, PNG,
SVG (XML-based vector graphics),
JPEG, GIF, BMP, EMF, and WMF
formats. Metafile, BMP, DIF, SYLK,
and text can be directly output to
the Clipboard. eDocEngine does
not use any external software or
require any DLLs to be deployed for
creation of documents in any of the

formats. eDocEngine also exports


from QuickReports, ReportBuilder,
FastReport, Rave Reports, AceReporter,
Express Printing System, TRichView,
ThtmlViewer, GmPrintSuite, and more.
Enhancements in version 1.03 include
a stamping feature implemented for
overlaying text and graphics on top
of page content; new text effects
introduced and implemented in PDF
Engine; a form submit feature to
submit data in HTML, FDF, and XML
formats; cell border styles and color
implemented in Excel; and more.
eDocEngine 1.03 also includes
several fixes to such areas as metafile
rendering; memory leaks; an entry
point error in Ace Export interface
corrected; text rotation corrected for
Rave 4.07 and up; image positioning
in RTF corrected; and internal
margins in HTML tables corrected.
Gnostice Information Technologies
Price: eDocEngine 1.03 is free to all registered users
of version 1.0. eDocEngine Professional, US$299;
eDocEngine Professional and PDFtoolkit Professional, US$449. Unlimited Server Deployment License,
US$999. A registered edition of eDocEngine includes
full source code at no additional cost.
Contact: info@gnostice.com
Web Site: www.gnostice.com

T O O L B O X
Gnostice Upgrades PDFtoolkit
Gnostice Information Technologies
or DataModule, set properties, then
released PDFtoolkit 1.02, an improved
call methods to fill or read form
version of its 100% VCL PDF document
values, compress, secure by setmanagement, manipulation, and PDF
ting password, append, or merge
eForms processing component set for
multiple PDF documents, rubber
Delphi and C++Builder. PDFtoolkit
stamp as Confidential, Draft,
supports filling and reading of PDF
etc., add bookmarks, and more.
forms; compressing, securing, appendThe final output can be received in
ing, and merging of multiple PDF docua memory stream or a disk file.
ments; stamping; setting bookmarks;
Enhancements in version 1.02
and many other functions that can be
include Unicode support for docuperformed on PDF documents. PDFtoolment information and bookmarks
kit operates on existing PDF documents
provided; embedding and sub-setand can create new ones by extracting
ting of True-Type fonts implementpages from existing documents. With
ed for text watermarks; form fields
the help of PDFtoolkit developers can
processing; merging of form fields;
embed PDF document management
modifying of appearance of filled form
functionality right into the business
fields implemented for viewing in Acroapplications they develop, making the
bat Reader and XPDFViewer; ENoAcroapplications more efficient and indepenFields exception introduced; removing of
dent of other external software.
form fields implemented; and more.
PDFtoolkit provides VCL components
PDFtoolkit 1.02 also includes
to link to the PDF documents that need
several fixes: the media box error
to be managed. The developer can place
for pages has been corrected; the
the PDFDocument component on a form
Invalid typecast error while insertUpdated Version of Visustin Released
Aivosto Oy has updated its flow chart
soft Visio 2002 for further editgenerator utility. Visustin 2 reverse engiing or to a word processor for
neers source code to flow charts. New
project documentation. Use the
features include Visio export, Web pubcharts in your project documenlication, print preview, and support for
tation in BMP, JPG, PNG, WMF,
T-SQL, PL/SQL, Ada, and Perl.
EMF, PS, or DOT image format.
Visustin creates an automatic flow
Other improvements include
chart layout by analyzing existing
multi-procedure visualization to
code. It works with the following landocument an entire module or the
guages: Pascal/Delphi, Visual Basic,
methods of a class, and options to
VB .NET, VBA, C/C++, C#, Java,
fit large charts into a small area.
JavaScript, COBOL, T-SQL, PL/SQL,
Visustin 2 supports Windows
Ada, and Perl. The flow diagrams can
95/98/ME/NT/2000/XP/2003. An evalube printed or published as Web pages. ation copy is available for free download
They can also be exported to Microat Aivostos Web site.

ing watermarks has been corrected;


exceptions related to form-fields has
been corrected; and more.
Gnostice Information Technologies
Price: PDFtoolkit 1.02 is free to all registered users
of version 1.0. PDFtoolkit Professional, US$299.
PDFtoolkit Professional and eDocEngine Professional, US$449. Unlimited Server Deployment License,
US$999. A registered edition of PDFtoolkit includes
full source code at no additional cost.
Contact: info@gnostice.com
Web Site: www.gnostice.com

Aivosto Oy
Price: Single-user license, US$249.
Contact: vbshop@aivosto.com
Web Site: www.aivosto.com

FIBPlus 5.2 Available


Devrace released FIBPlus 5.2, a
set of native Delphi, C++Builder,
and Kylix components that provide
access to all the features of the InterBase and Firebird database servers.
The FIBPlus components use direct
API calls for maximum performance
and the FIBDataSet component is a
TDataSet descendant for compatibility with Delphi, C++Builder, Kylix,
and third-party data aware controls.
Applications based on FIBPlus do
not require the BDE, ODBC, etc. You

DELPHI INFORMANT MAGAZINE | April 2004

can also port your InterBase Express


applications to FIBPlus.
FIBPlus components are completely
compatible with standard and thirdparty visual data-aware components
(TDBEdit, TDBGrid, TDBChart, InfoPower, DevExpress, EhLib, IntraWeb,
ASTA) and report generators (QuickReport, FastReport, Report Builder).
This new version allows you to
develop applications that can simultaneously work with different InterBase and Firebird versions using

separate client libraries (gds32.dll,


fbclient.dll, fbembed.dll) for each
connection.
Devrace is offering a special 15%
discount on FIBPlus for Delphi
Informant readers. Visit http://
delphizine.devrace.com for details; this
discount is valid until May 31st, 2004.
Devrace
Price: 235 per seat. Special volume discounts are
available.
Contact: sales@devrace.com
Web Site: www.fibplus.com

.N E T
VCL

VCL.NET

D E V E L O P E R

DELPHI 7, 8

By Glenn Stephens

Moving to Delphi 8
Should You Use VCL.NET? the FCL? or Both?

oftware development is all about decisions.


Which components and/or classes should
I use? Should I use one technology over

another? What are the performance tradeoffs? As


a developer, youll come across these questions
quite regularly. For the Delphi developers reading
this article, you have either chosen to move to
.NET, or are still evaluating the decision.

Should I Use VCL.NET?


This is the most common question asked regarding moving your VCL applications to .NET. As Delphi developers,
most of us have invested a significant portion of the last
eight years to working with the VCL and you most
likely have classes, components, and libraries of code that
all rely on the VCL.
The VCL has a proud history. While other Windows
programming languages were being used to write
applications by making calls to the Windows API, Delphi,
since its first release, had a powerful object-oriented
component-based framework called the Visual Component
Library (VCL). This meant that instead of writing
applications using Windows API calls, you would write
object-oriented code, or set properties of objects using
Delphis Form Designer. This is what
made Delphi such a great tool.

Even if youve made the decision to move to .NET, youll


need to know how to port your existing Delphi applications.
There are many questions, but once
youve read this article you should be
For the first
able to make an informed decision as to
which migration path is best for you.
in a while there

time
are
major changes to the
Delphi language.

VCL and VCL.NET


Throughout this article Ill be referring to
both the VCL and VCL.NET. Whenever
I refer to VCL, Im referring to the VCL classes that ship
with Delphi 1-7. When I refer to VCL.NET, Im referring to
Borlands implementation of the VCL classes that ship with
Delphi 8 for the Microsoft .NET Framework.
So lets start looking at the most common questions about
.NET and see how they affect you, the Delphi developer:
Should I use VCL.NET? What problems might I encounter
when migrating from the VCL to VCL.NET? What can be
ported? Should I use the new Framework Class Library
(FCL) for .NET?
4

DELPHI INFORMANT MAGAZINE | April 2004

With Delphi 8 released for the .NET


Framework, Microsoft introduced
the Framework Class Library (FCL),
which will be discussed later in the
article. Needless to say, the FCL is
an alternative framework to the VCL. Essentially, the FCL
does what the VCL has been doing for years.

Although the FCL is another framework, this doesnt mean


you have to lose all your old VCL code. Im sure youve
worked hard to develop your VCL applications and classes. Delphi 8 has done a fantastic job of creating a .NET
version of the VCL known as VCL.NET, which means you
can now re-use your VCL code on the .NET platform. This
affords you all the power youve come to know and use,
while still moving to the .NET platform.

.NET

Developer

Moving to Delphi 8

var
sl: TStringList;
procedure TfrmUnsafeMain.AddItemToStringList(
AText: string; Value: integer);
begin
// This is an unsafe typecast.
sl.AddObject(AText, TObject(Value));
end;

Figure 1: Unsafe casting in the .NET world.

// From the interface section.


function LookupAccountName(lpSystemName,
lpAccountName: string; Sid: PSID; var cbSid: DWORD;
ReferencedDomainName: StringBuilder;
var cbReferencedDomainName: DWORD;
out peUse: SID_NAME_USE): BOOL;
// From the implementation section.
[DllImport(advapi32, CharSet = CharSet.Auto,
SetLastError = True, EntryPoint = 'LookupAccountName')]
function LookupAccountName; external;

Figure 3: Using PInvoke to make Win32 API calls from .NET code.

can do. For example, in .NET most of the memory is


managed for you. As a result, working with pointers isnt
recommended. In fact, this code is referred to as unsafe
and goes against the goals of the .NET Framework (such
as minimizing security threats). Anything that uses any
kind of pointer or memory address function in your
Delphi 1-7 applications will cause your application to
not compile when your application moves to .NET. The
problems that can cause your application to not compile
unsafe types, unsafe code, and unsafe casts are
explained below.
Unsafe types are data types that work with pointers in
some way. The types to avoid here are any that refer
to a pointer, such as PChar, untyped pointers, file of
<datatype> and variant records, untyped out and ref
parameters, and the Real48 data type.

Figure 2: Using Delphi 7 to check that your code is ready for Delphi 8 for .NET.

Many of us are in a transition phase where were shifting


our applications toward the .NET platform. But bear in
mind however, that even Microsoft hasnt fully shifted to
.NET for all its applications. So if your applications target
Windows, you can use Delphi 7 and the VCL for Windows.
You can then move your application to use VCL.NET with
Delphi 8 and take advantage of the benefits of a .NET target
environment, with few if any code changes.
.NET is a big platform, and when it was designed it
needed a way to incorporate language features from all
languages. This means that the Delphi language had to be
expanded so that it could deal with the functionality of
all the diverse languages.
As a result, youll get some new features in the Delphi
language such as static methods, properties, and fields;
nested types within classes; the ability to seal classes
and methods; .NET attributes; operator overloading; and
more. So not only are you getting the benefits of moving
to .NET with a familiar development language, youre
getting additional Delphi features.
Migration Frustrations
For the first time in a while there are major changes
to the Delphi language, and because of the way .NET
works, there are further restrictions on what you
5

DELPHI INFORMANT MAGAZINE | April 2004

Unsafe code is that which accesses or works with


memory and cannot be verified to be safe. This includes
functions such as BlockRead, BlockWrite, Addr, Ptr, etc.
In .NET, each class should only try to access memory
contained within its code; otherwise, problems such as
buffer overruns are possible. The .NET runtime states
that if you cannot guarantee that the executing code will
not overwrite the memory of other classes, then the class
itself is assumed to not be safe.
Unsafe casts occur when you typecast an object to a type that
is not a subclass or superclass of the object instance. I have
seen this many times in applications where classes will use a
pointer to store an integer, and use typecasting to convert to/
from an integer to a pointer or object. (see Figure 1).
The easiest and simplest way to check that your code will be
ready for .NET is to turn the .NET compiler warnings on in
Delphi 7. With your project open, select Project | Options. Then
select Compiler Messages and check the Unsafe type, Unsafe code, and
Unsafe typecast options (see Figure 2). Now when you compile
your application in Delphi 7, you can see where the potential
problems will be in your code when you try to compile the
application in Delphi 8 for .NET.
What Can Be Ported?
Most applications in Windows already use COM, ActiveX,
or DLLs, so compatibility between Windows and .NET
applications is extremely high. So much so, in fact, that most
of the VCL objects that come standard with Delphi VCL are
available as part of VCL.NET with Delphi 8 for .NET.

.NET

Developer

Moving to Delphi 8

Youll still need to be sure that your components have .NET


versions, but calling out to Windows DLLs and ActiveX
controls is still available. Delphi 8 for .NET allows you to
reference an ActiveX control or COM Object from within your
.NET application. If you look at the Tool Palette in Delphi 8
for .NET you can see almost all the VCL components have
been ported to VCL.NET. This demonstrates how much easier
it is to port your VCL applications to VCL.NET.
Delphi 8 also has the ability to call Win32 API calls via a
technology in .NET called PInvoke. PInvoke allows you to
call DLLs from your .NET application (see Figure 3).
If you have VCL components that you need to port, you
must move them over to VCL.NET as well. Although moving
applications using the standard VCL controls is straightforward,
if you have custom components that youve written, these
components may need more attention to port them to Delphi 8
for .NET. Luckily, migrating these components isnt too much
of an issue. Besides ensuring that you minimize the use of
unsafe types, unsafe code, and unsafe casts, there are only a
handful of issues with which you need to work. When porting
some of my own components, I noticed the predominant
change has been in regard to working with Windows
messages. Under the VCL, you could hook a Windows message
by using the message keyword in your message declaration
and then defining the handler for the message:

TfrmMain = class(TForm)
protected
procedure HandleResizeMessage(var Msg: TMessage);
message WM_MOVE;
end;
...
procedure TfrmMain.HandleResizeMessage(var Msg:
TMessage);
var
MoveMessage: TWMMove;
begin
MoveMessage := TWMMove.Create(Msg);
Caption := Format('X: %d; Y: %d',
[MoveMessage.XPos, MoveMessage.YPos]);
end;

Figure 4: Using the VCL.NET message cracker classes.

procedure HandleResizeMessage(var Msg: TWMMove);


message WM_MOVE;

In the VCL the TMessage parameter was typecast to a


TWMMove. Because the message types dont share a
common ancestor, this is now classified as an unsafe cast.
Borland got around this issue by defining classes that
wrap the message functionality. So instead of dealing with
the TWMMove record, as you would do in the VCL, in
VCL.NET you create an instance of a message class cracker
(such as TWMMove) that reads in TMessage structure in its
constructor and provides you with information specific to
that Windows message (see Figure 4).

Figure 5: The Delphi 7 application getting ready to move to .NET.

a simple database application that contains a main form


and a data module, as well as the components required to
connect to an InterBase database using the Borland Database Engine (BDE). (This example application, and two
others, are available for download; see end of article for
details.) Well also include some data-aware controls for
display (see Figure 5).

One of the things that cannot be ported at the moment are


DataSnap/MIDAS application servers. This isnt a major problem, because you can still write the clients to your BizSnap
applications in Delphi 8 for .NET. However, at the time of this
writing, only the DCOM transport is supported, so if you rely
on the Socket or CORBA transports for DataSnap, you may
have to look at using the DCOM option.

Porting a Database Application to .NET


Because of the high compatibility between the VCL and
VCL.NET, many database applications can be ported easily.
Most of the database technologies, such as the BDE, dbExpress,
and IBExpress, have been ported to VCL.NET. ADO Express,
components, however, havent been ported to VCL.NET. This is
unfortunate if youre using ADO, but you can use the BDE or
dbExpress component set instead to get to Access or Microsoft
SQL Server data.

Also, if your middle-tier relies on using the AppServer property to execute custom methods on your application server,
youll have to make sure the interface is imported to the client
machine, because you cannot call methods on a variant in
.NET. Instead, once you obtain the AppServer, youll need to
typecast it to the correct interface so you can access the business logic of your application server.

Porting a database application to .NET is just like porting


a non-database VCL application. Save the Delphi 7 project,
open the project file in Delphi 8 for .NET (see Figure 6),
then recompile and run the application. Voil! You have a
.NET version of your app.

Porting a VCL Database


Application to VCL.NET
Lets start with a simple application written with Delphi
7 using the VCL. In our example application, well port
6

DELPHI INFORMANT MAGAZINE | April 2004

This is a huge accomplishment. Even developers using


other .NET languages, such as VB .NET, had major code
rewrites to do when moving to the .NET platform because
of the language changes. C# simply didnt exist before
.NET, so C++ developers had to do major rewrites. Del-

.NET

Developer

Moving to Delphi 8
Of course, not all platforms support
all of the functionality of the FCL. For
example, Windows CE smart phones
wont support all the display functionality of a WinForms application, and may
have limited support for other parts of
the FCL. This is because many of these
devices, such as the Windows CE-based
phones, dont implement support for
threaded classes. As a result, when you
develop an FCL application, youll need
to test that it will run on the platforms
youre targeting.

Figure 6: The VCL Form application shown in Delphi 8.

phi really has the advantage when it comes to moving


applications from Windows to .NET.
Benefits of the FCL
The .NET Framework has a new collection of classes
called the Framework Class Library (FCL). The FCL
includes a huge number of classes for most functions you
can think of, and is included in all .NET distributions.
The FCL includes a range of different technologies, such
as Windows Forms (WinForms) and Web Forms. It also
has support for such features as threading, security, network access, and more.
One of the technologies that well look at is WinForms,
the name of the Windows Control Library that the FCL
uses. Its an object-oriented class library for windowed
controls similar in concept to the VCL. Like WinForms,
the underlying technology of VCL.NET is still tied to the
Windows platform. This is no different to how WinForms
is implemented in .NET. Microsofts implementation of
WinForms still requires the Windows API just as much
as VCL.NET does. For Delphi programmers, the VCL has
proven itself to be a good investment for future development, and you can continue to use your VCL knowledge
with .NET using VCL.NET.
More operating systems for your application. Just a few
years ago, on the Borland newsgroups or at any Borland
conference, someone would always ask When are we
going to see Delphi for Windows CE? or Can we have
Delphi for the Mac? Now theres no need to ask that question, because you can compile your applications using Delphi 8 for .NET. Then as long as the platform youre deploying to supports the classes that your application uses, your
application will run. If you remember when Java came
out, it took some time for alternative Java VMs to become
available. This will also be the case with alternative implementations of the .NET Framework.
7

DELPHI INFORMANT MAGAZINE | April 2004

A single framework for every language. Apart from the FCL allowing you to use your code on new
platforms, there are other benefits
as well. One of the major benefits
of .NET is that the FCL allows many
programming languages to integrate
seamlessly. In the past, each programming language would have its own
framework. We had different compilers
with different frameworks, such as the
VCL for Delphi and C++Builder, and others such as the
Microsoft Foundation Classes (MFC) for Visual C++. With
.NET, there is only the one framework, the FCL, and every
programming language can use it.
A few years ago, a Visual C++ colleague might have
asked you how best to create a way of updating the caption every second. You might suggest the TTimer component, but because your colleague was using a different
framework (the MFC), that wasnt much help. Also, if
your project had both Delphi and Visual C++ modules,
youd have to deploy two frameworks that essentially do
the same thing. In the .NET world, everyone can use the
FCL, and because the .NET Framework will be deployed
with other .NET applications, theres less chance that
youll need to deploy the .NET runtime as well. In the
.NET world, with the FCL you could have just told your
colleague to use the System.Windows.Forms.Timer component. Then youd both be using the same classes and
could truly be working together.
This also means that you can integrate your Delphi apps
with the .NET language of your choice. If, for example,
youre developing an application that needs to integrate with
an expert system, you could develop that portion with a
Lisp or Prolog implementation of .NET, and develop the user
interface with Delphi 8 for .NET.
The Best of Both Worlds:
Mixing the VCL and the FCL
Once your application is running on .NET, you can mix
the VCL and FCL. If youve ported your VCL applications
to .NET, you may want to use some of the other technologies available in .NET, such as Web Services support,
encryption classes, or its remoting functionality. Because
youre programming in the .NET environment, youll have
access to all this functionality and can easily use these

.NET

Developer

Moving to Delphi 8
WinForm controls you want to use. The wizard then creates wrappers for the WinForm components so you can
use them in your VCL.NET applications. You can use this
wizard from inside Delphi 8 by selecting File | New | Other
and then selecting WinForm Controls Package.
Mixing frameworks is extremely handy, especially when
you have knowledge of how to do something in one
framework and want to use the best technique available.

Figure 7: Mixing the VCL and FCL in a .NET application.

classes in your applications. This is having the best of


both worlds.
All it will require is that you add a reference to the correct assemblies that your application needs. If you have
a VCL application, you could call out to a WinForm and
have it displayed in your application, simply by creating an instance of the WinForm and adding a reference
to System.Windows.Forms (see Figure 7). Just because
youve been using the VCL doesnt mean you should be
limited to the VCL.
You can also place .NET components onto VCL.NET
applications using the WinForm Control Import Wizard.
Using this wizard you select a .NET assembly and the

DELPHI INFORMANT MAGAZINE | April 2004

Conclusion
Moving to .NET is a big step, but because .NET is the
future of Windows, its a necessary step for Windows
developers. Moving your existing Delphi applications to
.NET, however, shouldnt be a headache, and it isnt with
Delphi 8 for .NET. Yes, there will be bumps along the
migration path, but if you follow the advice in this article
your transition to .NET will be much smoother.
The projects referenced in this article are available for
download on the Delphi Informant Magazine Complete
Works CD located in INFORM\2004\APR\DI200404GS.

Glenn Stephens designs and develops applications for various platforms,


and has been programming for more than 15 years. He is a Borland Certified
Consultant, a Microsoft Certified Solution Developer, and is the author of The
Tomes of Kylix: The Linux API (Wordware, 2001). Living in sunny Australia,
Glenn spends his spare time marathon swimming and playing the piano too loud
for his neighbors. Feel free to contact Glenn at glenn@glennstephens.com.au.

C O L U M N S
ADO.NET

&

R O W S

DELPHI 8 FOR THE MICROSOFT .NET FRAMEWORK

By Bill Todd

ADO.NET Data Access


Components
Part III: The DataAdapter Class

he first two parts of this series examined


connected ADO.NET objects; that is, objects
that connect to, and interact directly with,

a database server (see Part I and Part II). Now its


time to look at the disconnected side of ADO.NET
and the bridge between the two.

The DataSet is the key disconnected object. A DataSet


contains a collection of DataTable objects. Each DataTable
object contains collections of DataRow, DataColumn, and
Constraint objects. A DataSet also contains a collection of
DataRelation objects that define the relationship between
the DataTable objects. This architecture lets you model a
complete relational database in memory. If youve worked
with DataSnap or MIDAS, you can think of the ADO.NET
DataSet as a multi-table version of ClientDataSet.
Since the DataSet object has the ability to save all its data
to, and load its data from, an XML file, you can use the
DataSet object alone as a database. In other words, you
dont need to use a database server, or any of the ADO.NET
objects described in the first two parts of this series. The
only limitation is that all your data must fit in memory.
In most applications, you will select data from a database
and load it into a DataSet. Connecting user interface objects,
such as a text box or a grid, to the DataSet lets users insert,
delete, and update data. The first step in this process is to
select some data and load it into the DataSet. You could
do this with a Command object and a DataReader, but you
would have to write the code to loop through the records
returned by the Command object. For each record, your
code would create a new DataRow, assign each field value
to the corresponding field in the new DataRow, and then
add the DataRow to a DataTable. The DataAdapter automates both this process and the process of applying the
users changes to the database.
9

DELPHI INFORMANT MAGAZINE | April 2004

The DataAdapter
Figure 1 shows the most important properties of the
DataAdapter. The first four properties are Command objects.
The SelectCommand object contains the SQL SELECT statement
used to retrieve data from the database. The InsertCommand,
UpdateCommand, and DeleteCommand properties hold the
Command objects used to apply inserts, updates, and deletes to
the database. By performing inserts, updates, and deletes using
SQL statements that you can alter, the DataAdapter gives you
complete control. You can update tables directly, or you can
call stored procedures that perform the updates. If there are
fields that the user is allowed to enter in new records, but not
change in existing records, simply include those fields in the
InsertCommand objects INSERT statement and omit them from
the UpdateCommands UPDATE statement.
Since the SQL statement in the DataAdapters SelectSQL
Command object could select from a table, multiple tables,

Figure 1: DataAdapter properties.

Columns

&

Rows

ADO.NET Data Access Components

Figure 3: The sample application form.

Figure 2: The Employee connection in Data Explorer.

a view, or a stored procedure, the DataAdapter has no way


to know the name of the table from which the data comes.
The DataAdapter solves this problem by naming the table
Table. The TableMappings property, which is a collection
of DataTableMapping objects, lets you provide a more
meaningful name for the DataTable object in the DataSet.
How you do this depends on which ADO.NET data provider
youre using. If you use the SqlConnection and SqlDataAdapter
you can add a table mapping at design time using the Object
Inspector. If you use the Borland Data Provider (BDP), the
property editor for the TableMappings property doesnt work,
so you have to set the property in code.

procedure EmployeeForm.OpenBtn_Click(Sender: System.Object;


e: System.EventArgs);
begin
if (EmployeeAdapter.TableMappings.Count = 0) then begin
EmployeeAdapter.TableMappings.Add('Table', 'Employee');
EmployeeAdapter.TableMappings[0].ColumnMappings.Add(
'EMP_NO', 'EMPLOYEE_NUMBER');
end; // if
EmployeeAdapter.Fill(EmployeeDataSet);
EmployeeGrid.DataMember := 'Employee';
end;

Figure 4: The Open buttons OnClick event handler.

that ADO.NET providers developed by other database


vendors will be equally difficult to use. To save time, I will
use the BDP components for this example and I assume that
you already have a connection for the InterBase Employee
database defined in the Data Explorer. Figure 2 shows the
Data Explorer with the Employee connection expanded.

Each DataTableMapping object in the TableMappings collection


has a ColumnMappings property. The ColumnMappings
property is a collection of DataColumnMapping objects. Each
DataColumnMapping object maps the name of a column in
the result set returned by the SelectCommand to the name
of a column in the DataTable object in the DataSet. The
ColumnMappings property is like the TableMappings property
in that you cannot set it at design time if youre using the
BDP objects. Before going any further, lets build a sample
application so you can see how the objects described so far fit
together. (The sample app is available for download; see end
of article for details.)

Start by creating a new WinForms application. Drag the


Employee connection from the Data Explorer and drop it on
the form. A BdpConnection object will appear in the tray.
Change its name to EmployeeConn. Next, drag the Employee
table from the Data Explorer to the form. A BdpDataAdapter
will appear in the tray. Name it EmployeeAdapter. Drag
a DataSet from the Data Components group on the Tool
Palette and drop it on the form. Name it EmployeeDataSet.
Drag a DataGrid and a Button from the Tool Palette to the
form and position them as shown in Figure 3. Name the
DataGrid EmployeeGrid and name the Button OpenBtn.
Select the DataGrid and set its DataSource property
to EmployeeDataSet. You also need to set the grids
DataMember property to the DataTable in the DataSet that
will supply data to the grid. You cannot do that at design
time, because the DataTable object doesnt exist yet.

Using a DataAdapter to Fill a DataSet


How you build your application depends on which data
access objects you use. If you use the BDP objects, you
can do a lot of the work easily by dragging and dropping
from the Data Explorer. If you use the SqlConnection and
SqlDataAdapter, or the OleDbConnection and OleDbAdapter,
you have to set all of the properties manually; Borland
doesnt support the standard ADO.NET provider architecture
in the Data Explorer. That is unfortunate. Not only does it
make the Microsoft providers difficult to use, it also means

Double-click the button and add the code in Figure 4 to the


buttons OnClick event handler. The if statement ensures that
the TableMappings and ColumnMappings are only created
once. Without the if statement, you would get an exception
if you clicked the button twice. The first line within the if
statement (see Figure 4) adds a TableMappings object to the
DataAdapter that maps Table, the default table name for any
result set returned by the DataAdapter to Employee. This
means that the DataTable that will be created in the DataSet
will be named Employee.

10

DELPHI INFORMANT MAGAZINE | April 2004

Columns

&

Rows

ADO.NET Data Access Components

It may seem odd to use a collection for the TableMappings


property. When would you want to create more than one
DataTableMapping object? The answer is, if youre using
Microsoft SQL Server. SQL Server lets you execute multiple
SELECTs and return multiple result sets from a single query.
In the case of ADO.NET, the DataAdapters SelectCommand
object might contain multiple SELECT statements and return
a result set for each SELECT. Each result set will be placed
in a different DataTable in the DataSet, and each will require
its own table mapping to specify the DataTables name. As
an example, you could use the following lines in the SQL
property of the SelectCommand object:
SELECT * FROM EMPLOYEE;
SELECT * FROM SALARY_HISTORY;

When you call the DataAdapters Fill method, two


DataTable objects will be created in the DataSet, one for
the result set returned by each SELECT statement.
The next statement adds a new DataColumnMapping
object to the ColumnMappings property of TableMappings.
The column mapping specifies that the column
named EMP_NO in the query result set will be named
EMPLOYEE_NUMBER in the DataSet.
So what happens if you dont have an entry in the
TableMappings property for all the tables returned by
the DataAdapter, or if you dont have an entry in the
ColumnMappings property for all the columns? The answer
is determined by the DataAdapters MissingMappingAction
property. By default the property is set to Passthrough.
With this setting, any tables or columns not included in
the TableMappings or ColumnMappings are passed through
to the DataSet and created with the name supplied by the
DataAdapter. If you set MissingMappingAction to Ignore, any
missing tables or columns will be ignored and will not appear
in the DataSet. Setting MissingMappingAction to Error will raise
an exception if there are any tables or columns in the result set
that arent in the TableMappings or ColumnMappings.
The first line after the if statement in Figure 4 calls the
DataAdapters Fill method. This single call executes the
SelectCommands SQL statement, creates a DataTable named
Employee in EmployeeDataSet, and loads all of the data into
the Employee DataTable. Note that the code doesnt open the
connection. If the connection to the database isnt open when
Fill is called, the DataAdapter opens the connection, loads
the data into the DataSet, then closes the connection. If the
connection is already open when you call Fill, the DataAdapter
leaves it open. The last statement in Figure 4 sets the
DataGrids DataMember property to the Employee DataTable
in the DataSet, so the data appears in the grid.
You dont need to use the TableMappings or ColumnMappings
properties to get the table and column names you want. The
code in Figure 5 will also create a DataTable named Employee.
This code uses an overloaded version of the Fill method that
takes two parameters. The first is the DataSet, and the second
is the name for the DataTable. To get the column names that
you want, just use column aliases in your SELECT statement.
To rename the EMP_NO column to EMPLOYEE_NUMBER,
11

DELPHI INFORMANT MAGAZINE | April 2004

procedure EmployeeForm.OpenBtn_Click(Sender: System.Object;


e: System.EventArgs);
begin
EmployeeAdapter.Fill(EmployeeDataSet, 'Employee');
EmployeeGrid.DataMember := 'Employee';
end;

Figure 5: Another way to specify the DataTable name.

Action

Description

Add

If the data supplied by the DataAdapter contains


columns that arent in the DataTable, the columns
are automatically created.

AddWithKey

If the data supplied by the DataAdapter contains


columns that arent in the DataTable, the
columns are created, the primary key constraint
is created, and the MaxLength, AllowDbNull, and
AutoIncrement properties are set.

Error

An InvalidOperationException is raised if a column


is missing from the DataTable.

Ignore

Columns that are missing from the DataTable are


ignored.

Figure 6: MissingSchemaAction options.

change the SelectSql Command objects SQL property to


something like the following:
SELECT EMP_NO AS EMPLOYEE_NUMBER, ...
FROM EMPLOYEE

where the ellipsis represents the names of the other columns. You will see an example of this later in this article.
Getting Schema Information
The DataTable object can enforce many of the data
constraints that you defined in your database. You can tell the
DataAdapter to get the primary key, MaxLength, AllowDbNull,
and AutoIncrement properties from the database when you
call the Fill method. Note that although the AutoIncrement
property will be set (if appropriate), the AutoIncrementStep
and AutoIncrementSeed properties will not.
Because getting the schema information for the constraints
is a relatively expensive operation, the DataAdapter
doesnt get this information by default. The DataAdapters
MissingSchemaAction property controls what information
is fetched when there are no DataColumn objects in the
DataTable. Figure 6 shows the four possible values for
MissingSchemaAction and explains what each one does.
If you want the constraint information added to the
Employee DataTable when you click the Open button, set
MissingSchemaAction to AddWithKey.
Another way to get the schema information is to call the
DataAdapters FillSchema method. Add another button to
the sample application, set its Text property to Fill Schema
and its name to FillSchemaBtn. Double-click the button and
add the code in Figure 7 to the OnClick event handler.
This code is identical to the code for the Open buttons
OnClick event handler shown in Figure 4 with two

Columns

&

Rows

ADO.NET Data Access Components

procedure EmployeeForm.FillSchemaBtn_Click(
Sender: System.Object; e: System.EventArgs);
begin
if (EmployeeAdapter.TableMappings.Count = 0) then begin
EmployeeAdapter.TableMappings.Add('Table', 'Employee');
EmployeeAdapter.TableMappings[0].ColumnMappings.Add(
'EMP_NO', 'EMPLOYEE_NUMBER');
end; // if
EmployeeAdapter.FillSchema(EmployeeDataSet,
SchemaType.Mapped, 'Employee');
EmployeeAdapter.Fill(EmployeeDataSet, 'Employee');
EmployeeGrid.DataMember := 'Employee';
end;

Figure 7: The Fill Schema buttons OnClick event handler.

procedure EmployeeForm.EmployeeAdapter_FillError(
Sender: System.Object;
e: System.Data.FillErrorEventArgs);
begin
MessageBox.Show('Error Filling Data Table',
'Error filling the ' + e.DataTable.TableName +
' table. ' + e.Errors.Message,
MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Continue := False;
end;

Figure 8: The FillError event handler.

exceptions. The first is the call to the DataAdapters


FillSchema method in the first line after the if statement
block. FillSchema takes the same parameters as the Fill
method with one addition. The second parameter determines
how the constraints are mapped to the columns in the target
DataTable. Normally you will want this parameter to be
SchemaType.Mapped, as shown in Figure 7. This tells the
FillSchema method to assign the constraints to the columns
in the DataTable using the column names specified in the
TableMappings property. The alternative is SchemaType.Source
which creates the columns and assigns the constraints using
the column names in the DataAdapters result set.
The second difference between the code in Figures 4
and 7 is that the call to the Fill method has a second
parameter. This parameter is the name of the DataTable
object that is to be filled and it must match the DataTable
name in the FillSchema call.

DataAdapter Events
The DataAdapter has just three events, FillError,
RowUpdating, and RowUpdated. Figure 8 shows the
FillError event handler for the EmployeeDataAdapter in
the sample application. This event handler displays an
error message that includes the table name, and sets
the Continue property of the FillErrorEventArgs object
to False to stop any further processing of the data. The
FillErrorEventArgs object also has a Values property that
gives you access to the value in each field of the record
that caused the error. If you wanted to, you could write the
record that caused the error along with the error message
to a file or table, and set the Continue property to True to
continue processing the rest of the records.
The RowUpdating and RowUpdated events fire when you
call the DataAdapters Update method to submit changes
12

DELPHI INFORMANT MAGAZINE | April 2004

procedure EmployeeForm.OpenDeptBtn_Click(
Sender: System.Object; e: System.EventArgs);
begin
EmpDeptAdapter.GetFillParameters[0].Value := '671';
EmpDeptAdapter.Fill(EmployeeDataSet, 'Employee');
EmployeeGrid.DataMember := 'Employee';
end;

Figure 9: Using GetFillParameters.

procedure EmployeeForm.CloseBtn_Click(
Sender: System.Object; e: System.EventArgs);
begin
if (EmployeeDataSet.Tables.Count > 0) then
EmployeeDataSet.Tables[0].Clear;
end;

Figure 10: The Close buttons OnClick event handler.

made to data in a DataSet to the database. I will examine


these events in more detail in a future article that
discusses applying updates.
Parameters the Easy Way
The examples so far have been unrealistic in that they
select all the rows from the Employee table. In a real
application you would add a WHERE clause with one or
more parameters to the SELECT statement to limit the
number of returned rows. The sample application contains
another DataAdapter named EmpDeptAdapter that returns
the employees for a specified department.
You could set the parameter value through the Parameters
collection of the SelectSql Command object, but the
DataAdapter provides an easier way using its GetFillParameters
method. Figure 9 shows the code from the OnClick
event handler of the Open Dept. button. The first line uses
GetFillParameters to assign a department number to the first
parameter. The second line calls Fill and passes both the
DataSet object and the name for the DataTable as parameters,
so theres no need to use the TableMappings property. The
SQL statement also uses a column alias to change the name
of the EMP_NO column to EMPLOYEE_NUMBER, so no
ColumnMappings are needed.
Refreshing the Data
What happens if youre viewing data in a DataSet and you
want to see any changes made by other users since you
filled your DataTable? To refresh the data call the Fill method
again. Before you call Fill make sure you know what will
happen to your DataTable. If the DataTable doesnt have a
primary key constraint the new rows will be appended to
the DataTable. If the table has a primary key and a record in
the DataAdapters result set has the same primary key as the
record that is in the DataTable, the record in the DataTable
will be updated with the new values. If an incoming record
doesnt match an existing record it will be added to the
DataTable. The only problem is that records that were
deleted by other users wont be deleted from the DataTable.
The best solution to refreshing the data is to empty the
DataTable before you call Fill. The sample app also has a Close
button; the code from its OnClick event handler is shown in
Figure 10. This code checks the Count property of the DataSets
Tables collection to ensure the DataSet contains a DataTable. If

Columns

&

Rows

ADO.NET Data Access Components

the DataTable exists, a call to its Clear method removes all its
rows. After all its rows have been removed, you can call the
DataAdapters Fill method and get a new set of records.
Conclusion
The DataAdapter is the key to loading data into a DataSet
so users can work with the data. As you will see in future
articles, its also the key to applying the changes the user
makes back to the database. This article has only scratched
the surface of the DataSet object. The DataSet is the most
complex object in ADO.NET, and several articles in this series
will be devoted to getting the most from it.
The files referenced in this article are available for download
on the Delphi Informant Magazine Complete Works CD
located in INFORM\2004\APR\DI200404BT.

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 btarticle@dbginc.com.

13

DELPHI INFORMANT MAGAZINE | April 2004

E V E R Y D A Y
FORMS

TIPS

D E L P H I

DELPHI 1-8

By Rick Spence

Fun with Forms


Tips and Tricks for Delphi Forms

his article discusses a number of tips and


tricks for you to use with Delphi forms.
Sure, its basic stuff, but lets face it, many

developers day-to-day grind concerns creating


and tweaking forms, and so any help is welcome.
Most things youll want to do with forms are
quite simple; its making them generic that is
more awkward but thats where you save
development time.
Im a great fan of writing generic code, so this article will
present a few tips and techniques from applications Ive
developed over the last few years. Send me an e-mail if
you have any tips of your own youd like to see published
in a future issue of Delphi Informant (see the end of this
article for my contact info).

Figure 1: Change the focused controls color.

14

DELPHI INFORMANT MAGAZINE | April 2004

Changing a Fields Color on Entry


Lets start with an easy one. One of my clients wanted the
currently selected input field to be displayed with a different background color from the other fields, thus focusing
the users attention on the field (see Figure 1). Of course,
as the user moves from one field to another, the code
must restore the color of the control being left, and set
the color for the field receiving focus.
The easiest, albeit most tedious, way to do this is to
have each controls OnEnter event change its color to the
selected color, and have the OnExit event reset its color.
This is simple, but, as I said, tedious. At entry time we
need to remember each controls initial color, so we can
reset it upon exit (it might not be the default color). Well
save this in a new property of the form named oldColor:
// In the form class declaration...
protected
oldColor : TColor;

But accessing the color property of the selected control isnt


so simple. The selected control can be any TWinControl
descendant (any control that can receive focus), and to
access the Color property we need to determine the actual
class and perform a typecast. Figure 2 shows part of this
code (I hasten to add that well improve on this in a
moment).
So what are the problems with this implementation?
First of all, the code only handles Edit, RadioButton, and
CheckBox controls; in practice youd need to add conditions for other controls as well (and dont forget the dataaware versions). Another problem is that weve hijacked
all the controls OnEnter and OnExit events, preventing
the user from using them.

Everyday

Delphi

Fun with Forms

const
FOCUS_COLOR = clYellow;
// OnEnter event.
procedure TForm1.Edit1Enter(Sender: TObject);
begin
if Sender is TEdit then
begin
oldcolor := (Sender as TEdit).Color;
(Sender as TEdit).Color := FOCUS_COLOR;
end
else if (Sender is TRadioButton) then
begin
oldcolor := (Sender as TRadioButton).Color;
(Sender as TRadioButton).Color := FOCUS_COLOR;
end
else if (Sender is TCheckBox) then
begin
oldcolor := (Sender as TCheckBox).Color;
(Sender as TCheckBox).Color := FOCUS_COLOR;
end;
end;
// OnExit event.
procedure TForm1.Edit1Exit(Sender: TObject);
begin
if Sender is TEdit then
(Sender as TEdit).Color := oldcolor
else if (Sender is TRadioButton) then
(Sender as TRadioButton).Color := oldColor
else if (Sender is TCheckBox) then
(Sender as TCheckBox).Color := oldColor;
end;

Figure 2: Cumbersome code for OnEntry and OnExit events changing controls
background colors.

Fortunately, we can overcome both these problems quite


simply. Well solve the first problem the excessive class
checking and typecasting by using Delphis run-time type
information (RTTI). We can use RTTI to:
determine whether an object contains a particular property (color, in this case).
set a particular property without worrying about the class.
The last time I looked, the functions used to work with RTTI
(defined in TypInfo.pas) were undocumented. Thats not
to say they arent used heavily theyre just not defined
in the help file. The routines are used extensively by Delphi itself, and by component writers. Were interested in a
routine that tells us whether an object has a particular published property, so IsPublishedProp is our savior:
if (IsPublishedProp(Sender, 'Color')) then...

Note how the property name is passed as a string. We also


need to set the Color property of an object, regardless of its
class. This time SetOrdProp comes to the rescue:
if IsPublishedProp(Sender, 'Color') then
SetOrdProp(Sender, 'Color', oldColor);

Note how SetOrdProp allows us to set a property of a particular object indirectly. That is, we call a function passing
(in this order) the object, the property in the object we
want to set (as a string), and the new value of the property. This relieves us from having to check the objects class
and typecast the result as we did in Figure 2.
15

DELPHI INFORMANT MAGAZINE | April 2004

uses TypInfo;
procedure TForm1.FormCreate(Sender: TObject);
begin
Screen.OnActiveControlChange := ControlChange;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Screen.OnActiveControlChange := nil;
end;
// Called whenever focus switches to a new control.
procedure TForm1.ControlChange;
begin
oldControl := thisControl;
thisControl := Screen.ActiveControl;
ColorExit(oldControl);
ColorEnter(thisControl);
end;
procedure TForm1.ColorEnter(Sender: TObject);
begin
if Sender <> nil then
if IsPublishedProp(Sender, 'Color') then
begin
oldColor := GetOrdProp(Sender, 'Color');
SetOrdProp(Sender, 'Color', FOCUS_COLOR);
end;
end;
procedure TForm1.ColorExit(Sender: TObject);
begin
if Sender <> nil then
if IsPublishedProp(Sender, 'Color') then
SetOrdProp(Sender, 'Color', oldColor);
end;

Figure 3: Better code to highlight the active control.

Well overcome the second problem hijacking the


events by using the OnActiveControlChange event of the
TScreen class. As noted in the Delphi help file: Write an
OnActiveControlChange event handler to take specific action
when input focus changes to a new control. The change in
focus may be within the active form, or across forms to a
new form that then becomes the active form.
Using this event relieves us from having to set all the controls OnExit and OnEnter event handlers, and also leaves
these free for the developer to use for other things. To use
this TScreen event you need to set the handler manually in
your main forms OnCreate event, and reset it to nil in the
main forms OnDestroy event (there is no TScreen component you can drop onto a form). In addition, youll need
to remember the control that was currently active, because
once this event fires weve already switched focus to the
new control (and we need to perform our OnExit logic for
the control that was previously active). I implemented this
by adding two additional properties to the form:
protected
oldColor
: TColor;
oldControl : TWinControl;
thisControl : TWinControl;

// Previous control.
// Current control.

Figure 3 shows the complete code; this is available, along


with a sample project, for download (see end of article
for details).

Everyday

Delphi

Fun with Forms

procedure TForm1.SetEnabledState(
ParentControl: TWinControl; State: Boolean);
var
i : Integer;
begin
for i := 0 to ParentControl.ControlCount - 1 do begin
ParentControl.Controls[i].Enabled := State;
// Recurse nested containers.
if ParentControl.Controls[i] is TWinControl then
Self.SetEnabledState(
TWinControl(ParentControl.Controls[i]), State);
end;
end;

Figure 4: Recursive routine to disable child controls.

const
BORDER_GAP = 1;
BORDER_SIZE = 2;
FOCUS_COLOR = clGreen;
implementation
{$R *.dfm}
procedure TForm1.focusChanged(Sender: TObject);
begin
Rect.Parent := Screen.ActiveControl.Parent;
Rect.Top := Screen.ActiveControl.Top BORDER_GAP - BORDER_SIZE;
Rect.Height := Screen.ActiveControl.Height +
(BORDER_GAP * 2) + (BORDER_SIZE * 2);
Rect.Left := Screen.ActiveControl.Left BORDER_GAP - BORDER_SIZE;
Rect.Width := Screen.ActiveControl.Width +
(BORDER_GAP * 2) + (BORDER_SIZE * 2);
Rect.Visible := True;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Rect := TShape.Create(Self);
Rect.Shape := stRectangle;
Rect.Visible := False;
Rect.Pen.Color := FOCUS_COLOR;
Rect.Pen.Width := BORDER_SIZE;
Screen.OnActiveControlChange := focusChanged;
end;

Figure 5: Highlighting the selected control with a rectangle.

Disabling Data-aware Controls


As I just demonstrated, using RTTI can relieve you from excessive class checking and typecasting. To drive this point home,
consider a generic routine to disable the children of any parent
control (see Figure 4). This code loops through each element
of the parent controls Controls array (pointers to its children),
setting their Enabled property, then calling itself recursively for
controls that can have their own children, e.g. a group box on
a tab sheet where the group box has its own children.
But what if you wanted to set the state of just the dataaware controls? Without RTTI, you would have to check the
class of each control to determine whether its a TDBEdit,
TDBCheckBox, etc. A much easier way is to use RTTI to
determine whether the control in question has a DataSource
property a dead giveaway that its a data-aware control.
All you need to do is change the statement that sets the
Enabled property thus:
if IsPublishedProp(
ParentControl.Controls[i], 'DataSource') then
SetOrdProp(ParentControl.Controls[i],
'Enabled', Integer(State));

Again, this code, along with a sample project, is available for


download.
Highlighting Input Fields
with a Rectangle
If you really want to draw the users attention to the current
field, draw a rectangle around it, as shown in Figure 5.
This is surprisingly easy to do. Every time focus changes
you must draw a rectangle around the current control. The
easiest way to do this is to create a rectangle object in the
16

DELPHI INFORMANT MAGAZINE | April 2004

procedure TForm1.FormDestroy(Sender: TObject);


begin
Screen.OnActiveControlChange := nil;
end;

Figure 6: Using TScreen.ActiveControlChange to display a rectangle around the


active control.

forms OnCreate event, set its pen color and width (the
pen is used to display the border of the rectangle), but
make it invisible. Then, when the active control changes
(well do this in the TScreen.ActiveControlChange event we
used earlier), well set the dimensions of the rectangle to
slightly larger than the active control.
Figure 6 shows the entire form; again, this code is available for download.
Change the constants for BORDER_GAP (the space
between the rectangle and the control), BORDER_SIZE (the
pen width, or how wide the line is), and FOCUS_COLOR to
your preference. Note how the custom focusChanged event
uses TScreen.ActiveControl to access the current control,
and how we use properties of this control to change the
rectangles dimensions.
Encapsulation with Forms
This tip focuses more on how you provide access to components on your form, rather than any specific GUI tip. If youve
read any books on object-oriented programming, or the Delphi
manuals, youve read about the principle of encapsulation.
For the uninitiated, it refers to the process of hiding the implementation details of a class from the class user. For example, a
person using the TStringList class doesnt need to know how
the class works internally (how it represents the strings). All
the class user needs to know is that it supports methods to

Everyday

Delphi

Fun with Forms

add and delete strings from this list, and that the list will grow
or shrink as necessary. The advantage of encapsulation is that
it allows the class implementer (the programmer writing the
class) to change the way the class works internally without
impacting the other programmers using the class.
A lot of Delphi developers have never created their own
classes; they use either the classes that come with the VCL,
or third-party components they buy on the open market.
For a lot of these developers, articles and books touting the
principles of object-oriented programming fall on deaf ears.
There are a couple of places, however, where these developers can benefit from encapsulation, even if they never
write their own classes. These relate to the form classes that
Delphis form designer generates for you.
As you know, when you create a form, Delphis form designer
generates a class for you. The components you place on your
form become instance variables in the new class. An important
question to consider is the scope of these instance variables.
Delphis form designer creates them as public, which means
theyre visible outside the class. This allows, for example, event
handlers of one form to change the properties of components
on another form (a common task).
Im suggesting, however, that maybe this isnt such a good
thing. It means that one form (lets call it frm1) has to have
knowledge of the components on another form (call this one
frm2). What if frm2 needs to change its components? Youll
have to change the code in frm1 as well. This is poor encapsulation, but is forced on us by the Delphi form designer.
Dont agree with me? Well, I have Microsoft on my side!
The Visual Studio .NET form designer generates code for
forms much as Delphi does, but by default the instance
variables used to store the forms components are declared
with a private scope, meaning you cannot access them from
outside the forms event handlers.
So how do .NET developers change components on other
forms? And how would Delphi developers change components on other forms if you follow my suggestion and
not directly reference the component? You write public
methods in the form to perform the changes to its components. Its forced on .NET developers, and Im recommending Delphi developers do the same thing.

17

DELPHI INFORMANT MAGAZINE | April 2004

Heres a simple example of how poor encapsulation cost


me some time (granted, it was my fault its all my code).
I developed a wizard framework that allows me to move
between several forms in a modal manner. I have a basic
host form for the wizard, which contains a page control
in which I display other forms (each form being one step in
the wizard), and a panel with buttons labeled Next, Previous,
Cancel, etc. The forms displayed within the wizard needed
to set the Enabled state of the host forms Next button. No
problem; theyre public properties, so I just referenced the
host forms Next button directly.
This was all well and good until a couple of years down the
road when I decided to update the look of my host form and
removed the buttons. Of course, all the wizards I had written
immediately broke because they were referencing a component named btnNext which no longer existed. A better solution (Im older and wiser now) would have been to implement
a method on the host form named DisableNextIndicator. That
way, I could have simply changed the one routine to reference
the new component, and the existing wizards wouldnt have
needed modification.
Conclusion
This article presents a few form tips and tricks I use in my
day-to-day development work. As I wrote the examples I
realized Ive only just scratched the surface. Working with
forms is certainly not state-of-the-art software development;
we do it every day. But thats just the point. Sometimes its
worth re-examining the things you do every day to see if you
can make the tedious things you do generic. Thats when
you really start saving development time.
If you have any form tips and tricks of your own please e-mail
me and Ill include them in a future column.
The five demonstration projects referenced in this article
are available for download on the Delphi Informant Magazine Complete Works CD located in INFORM\2004\APR\
DI200404RS.

Rick Spence (RSpence@WebTechCorp.com) runs Web Tech Training and


Development (www.WebTechCorp.com), a development and training company
with offices in Florida and England. Web Tech specializes in Delphi and .NET
training and development.

Return a Value From a Thread

www.C-SharpPRO.com

C#

By
Richard Grimes

Solutions & Strategies for .NET Developers


Premiere Issue

By Bill Wagner

Protect Your
Intellectual Property
With Code

Access Security
By Michle Leroux Bustamante

Cover Story

By Bill Wagner

Manage Your Memory

The garbage collector isnt always up to the task of freeing up


your resources. Use IDisposable to free up the ones you need.

UST BECAUSE .NET IS A MANAGED ENVIRONMENT


doesnt mean you are suddenly absolved from all your
resource management responsibilities. You do need to clean
up some resources in a timely fashion, rather than waiting
for the garbage collector to be called in to tidy up after you.
The IDisposable interface signifies that an object needs a
cleanup of non-memory resources, and several classes in the
.NET Framework provide examples on how to implement
IDisposable in the context of a framework of classes. Simply
put: If you need to free resources and a protected Dispose
method is available in your base class, override it. If its not
available, youll need to implement the IDisposable interface, a finalizer, and a protected Dispose method for clients.
Its easier to understand how these three methods work
together if you see how to implement them when you need
to create all three. Ill explain adding these in the context of
a drawing program for the Compact Framework. For example, most of my meetings are to discuss software designs.
During these meetings, I listen to design discussions and jot
down block diagrams and other simple drawings. Given this,
I need an application that lets me quickly make line drawings that I can use later. Take a look at the application in
action (see Figure 1).
If you dont have a pocket PC, dont fret. Applications that target the Compact Framework can run on
the desktop. Simply double-click on the executable,
PocketDraw.exe, and it will run on your desktop. This
application is modeled after the Scribble sample, and
includes changes for the Compact Framework.

Implement the IDisposable Interface


The pocket drawing application creates and edits drawings. A drawing contains strokes, which contain points
and a pen. Pens implement the IDisposable interface,
which causes the Stroke and Drawing classes in this sample to handle disposing owned objects.
Lets start with the Stroke object. Ive included the
source for the class, abbreviated for space (see Figure 2).

To get the entire sample,


download the code Ive
provided (see end of article for download details).
The Stroke class could be
used as a base class for
more feature-rich drawing primitives. Because it
might have other classes
derived from it, you must
implement the IDisposable
interface in such a way
that derived classes can
also free their resources.
From Figure 2, you can see
that the Stroke class needs to
Figure 1. Heres an example of the
clean up the Pen it allocates.
pocket drawing application. You
Thats where the IDisposable
can jot down design diagrams on
interface comes in. Impleyour pocket PC.
menting IDisposable is the
standard way to inform users and the runtime system that your
objects hold resources (either managed or unmanaged) that
must be released in a timely manner.
The implementation of your IDisposable.Dispose method
is responsible for four tasks: First, it frees all unmanaged
resources. Second, it frees all managed resources. This might
include unhooking events. Third, it sets state to indicate that
the object has been disposed. Youll need to check this state
and throw ObjectDisposed exceptions in your public methods after an object has been disposed. See the AddPoint
method for an example of throwing the ObjectDisposed
method. And fourth, it suppresses finalization. You call
GC.SuppressFinalize (this) to accomplish this task.
By implementing the IDisposable interface, youve
accomplished two things: Youve provided the mechanism
to release any managed resources you hold, and youve
given clients a standard way to release all managed and
unmanaged resources.
Premiere 2004 Issue

Cover Story

public class Stroke : IDisposable


{
private Pen _drawPen;
private ArrayList _points = new ArrayList ();
private bool _isDisposed = false;

Manage Your Memory

public sealed class Drawing : IDisposable


{
private ArrayList _strokes = new ArrayList ();
private bool _isDisposed = false;
public void StartStroke (Color clr, Point pt)
{
if (_isDisposed)
throw new ObjectDisposedException ("Drawing",
"AlreadyDisposed drawing");
Stroke st = new Stroke (clr, pt);
_strokes.Add (st);
}

public Stroke(Color penColor, Point pt)


{
_drawPen = new Pen(penColor);
_points.Add (pt);
}
public virtual void AddPoint (Point pt, Graphics g)
{
if (_isDisposed)
throw new ObjectDisposedException
("Stroke", "AlreadyDisposed stroke");
Point ptLast = (Point)_points[_
points.Count- 1];
g.DrawLine (_drawPen, ptLast.X, ptLast.Y,
pt.X, pt.Y);
_points.Add (pt);
}
#region IDisposable Members
public void Dispose()
{
Dispose (true);
GC.SuppressFinalize (this);
}
~Stroke ()
{
Dispose (false);
}
protected virtual void Dispose (bool isDisposing)
{
if (_isDisposed)
return;
if (isDisposing)
_drawPen.Dispose();
else
{
// No unmanaged resources here.
}
_drawPen = null;
_isDisposed = true;
}
#endregion
}

Figure 2. This listing shows the implementation for the stroke class. The
code inside the IDisposable region shows the proper implementation of
the IDisposable interface in a base class.

Finalizers Free Resources


However, the Stroke class could be used as a base class. In
addition to freeing its own resources, it needs to provide any
possible derived classes with a mechanism to free their own
resources, yet still work with the Stroke class to ensure the
Pen owned by the Stroke class is released. Furthermore, any
derived class might introduce unmanaged resources. The
Garbage Collector will not clean up managed resources; a
finalizer is necessary to ensure these resources are freed.
In C#, the garbage collector calls a finalizer when it cleans
up objects that are no longer used. Yes, unlike Java, finalizers are always called by the .NET Framework in C#. (There
are caveats for extreme classes: another finalizer deadlocks
waiting on another thread, or the runtime terminating.

#region IDisposable Members


public void Dispose()
{
foreach (Stroke s in _strokes)
s.Dispose ();
_strokes.Clear ();
_isDisposed = true;
}
#endregion
}

Figure 3. Here is an implementation of the Drawing class. The Drawing


class shows the correct reference implementation of IDisposable in a
sealed class.

Well ignore those caveats because theres nothing you can


do about them anyway.) However, unlike C++, you cannot predict when finalizers will be executed. All you know
is that when an object is garbage, and it is being removed
from memory, the garbage collector will call its finalizer.
Because your garbage collector calls the finalizer, any managed resources you hold are also garbage. So, clearly, you
cannot use a finalizer to clean up managed resources: They
might already have been disposed of.
There are also performance penalties associated with executing finalizers. When the garbage collector runs, it immediately removes any garbage objects that do not have finalizers
from memory. Any objects that do have finalizers remain in
memory. These objects are added to a finalization queue, and
the garbage collector spawns a new thread to run the finalizers on those objects. After the finalizer thread has finished
its work, the garbage can be removed from memory. The net
result of this algorithm is that objects that need finalization
stay around for two garbage collection cycles instead of one.
Putting these facts together, a finalizer is a defensive programming mechanism that you write to ensure that even
if clients of your objects dont clean up after themselves,
you do. Furthermore, finalizers are used only to clean up
unmanaged objects.
You might think that you should only add the finalizer
if your class contains unmanaged resources. Unfortunately, that makes it much harder on developers deriving
new classes from yours. Theyll have these questions:
Should they add a finalizer, or did you do so when you
implemented the IDisposable Interface? Will your class
suppress finalization, if it does not have its own finalizer? Given this, you should always add a finalizer if you
Premiere 2004 Issue

Cover Story
implement IDispose yourself. The only exception to this
rule is if you create a sealed class that implements IDispose. You no longer need to worry about derived classes
in those cases, as youll see in a moment.

Manage Your Memory

public class PocketDrawMain : System.Windows.Forms.Form


{
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );

Call the Protected Dispose Method


Implementing a finalizer and IDisposable ensures that the
Stroke class, and any derived class, can clean up resources.
But, how does a derived class clean up and still let its base
class clean up as well? Re-implementing interfaces defined
in base classes is possible, as is overriding the finalizer.
But who is responsible for suppressing finalization? And,
you have almost certainly duplicated code between the
Finalize method and the Dispose method, when you are
freeing unmanaged resources. This is where the protected
Dispose method comes into play. The default definition for
the protected Dispose method is:
protected virtual void Dispose (bool isDisposing);

Youll call this method from both your finalizer and your
IDisposable.Dispose method. It does the work necessary to
support both Finalize and Dispose, and because its virtual,
ensures that clients do not need to add their own finalizer nor
do they need to implement IDisposable. All derived classes
need to do is override this method, and provide the proper
implementation. You should factor out all the code from
IDisposable.Dispose and your finalizer into this method. In
essence, clean up managed and unmanaged resources when
isDisposing is true; clean up only unmanaged resources when
isDisposing is false. In both cases, call the base class Dispose
(bool) method to let it clean up its own resources.
IDisposable.Dispose and the finalizer are the public entry
points in your class, and both are defined in the uppermost
class that implements IDisposable. Both call the protected
virtual Dispose method. The isDisposing flag on the protected Dispose method is true if all resources should be freed;
its false if only unmanaged resources should be freed.
Lets go back to the drawing program I introduced you to
earlier. A Drawing object owns Stroke objects (see Figure 3).
Ive made the Drawing class a sealed class, and it owns a
set of strokes. In order to dispose of those strokes, it needs
to implement the IDisposable interface. Implementing the
IDisposable interface is simpler here, because Drawing is
a sealed class. I no longer need to be concerned with how
derived classes will free their resources. It is enough to
free the drawing resources in IDisposable.Dispose. And the
Drawing class does not hold any unmanaged resources, so
there is no need to add a finalizer.
Finally, take a look at the protected Dispose method in
the PocketDrawMain class:

_drawing.Dispose ();
}
//other details elided.
}

The System.Windows.Forms.Form class implements


IDisposable, and provides a protected virtual Dispose
method for derived classes, just as I described above. All
the PocketDrawMain class needs to do to free its drawing
is to add that call to this protected method.
Those are the three different cases where you might
need to free resources: One is when you need to implement IDisposable in a class that might be used as a base
class. In this case, implement IDisposable, provide a finalizer, and provide a protected virtual protected Dispose
function that derived classes can hook into to release
resources. Another is when you need to implement
IDisposable in a sealed class. Here, simply implement
IDisposable and free your resources. And yet another is
when you allocate resources in a derived class, where
the base class implements IDisposable. In this instance,
simply override the protected Dispose method to free your
resources, and remember to call the base class version.
There are rules in the C# syntax that make implementing
the standard Disposable idiom very easy to follow. Whenever you find you need to clean up resources, try to add an
override to a protected Dispose (bool isDisposing) method.
If one doesnt exist, it wont compile. Then, change the
override keyword to the virtual keyword, add a finalizer,
and implement the IDisposable interface. Others can follow
the same idiom up and down the hierarchy of your class.
Only in the special case of a sealed class can you forego
the finalizer and implement the IDisposable interface. #
The sample code in this article is available for download
on the Delphi Informant Magazine Complete Works CD
located in INFORM\2004\APR\CS200404BW.

Bill Wagner began developing commercial software in 1986. Bill founded


SRT Solutions, a firm that specializes in advancing software development. He
began writing magazine articles in 1992. He wrote C# Core Language Little
Black Book (Paraglyph Publishing) in 2001, and is currently writing Effective
C# for Addison-Wesley. Bill is the Microsoft Regional Director for Michigan.
Contact Bill at wwagner@srtsolutions.com.

Premiere 2004 Issue

Ask the C# PRO

By Richard Grimes

Return a Value From


a Thread

Also, should you use .resx or .res files for application resources?

Q.
A.

CAN YOU TELL ME HOW I CAN START A THREAD


THAT CAN RETURN A VALUE TO ME?

When you create a .NET thread, you must pass a


delegate to the Thread constructor for the thread
procedure. The thread procedure does not return a value,
nor does it take a parameter. There are two ways that
you can solve this. The first way is to use a class that
is shared between the new thread and the thread that
will create it. Take a look at the first attempt for such a
class (see Figure 1). The problem with this code is that
when thread A calls CreateAndStartThread, a new thread,
thread B, is created to run the procedure Proc. Both
thread A and thread B can access Data.result, so how
does CreateAndStartThread know that thread B has completed its work and, hence, Data.result has been updated?

When you create a .NET thread,


you must pass a delegate to the
Thread constructor for the thread
procedure.
Thread A could simply wait for thread B to finish (by
calling Thread.Join), but then there would be no point in
using a multithreaded solution!
Another way to do this is to use some thread synchronization (an event, for example) as part of the Data class. The
Proc method can set the event when the result has been calculated, and the calling code can either wait on the event or
poll it for completion. A waiting thread is a thread that does
no work; and for high-performance apps you should try to
prevent this from happening.

class Data
{
public int result;
public void Proc()
{
result = 42;
}
static int CreateAndStartThread()
{
Data data = new Data();
Thread t = new Thread(new ThreadStart(data.Proc));
t.Start();
// what goes here???
return t.result;
}
}

Figure 1. This figure shows example code used to pass data to a thread
and return data from the thread. The code is not complete.

A similar way to get another thread to do some work


is to use asynchronous calls on delegates or use a
ThreadPool thread. In both cases a thread from the process thread pool is used and you can pass a state object
that can be used to pass parameters to the thread, or to
receive return values. Again, the state object is shared
between two threads so you have to be careful that the
calling thread only accesses the results once the called
thread has completed its work. In the case of async delegates, you get an IAsyncResult that you can check for
method completion. If you start the thread pool thread
through a call to QueueUserWorkItem on ThreadPool, you
must put the synchronization object as a member of the
state object and poll that member for completion. Download the code accompanying this article for an example of
performing a calculation on another thread.
Premiere 2004 Issue

Ask the C# PRO

Q.
A.

I WANT TO USE RESOURCES IN MY APPLICATION.


WHICH SHOULD I USE: A .RESX OR A .RES FILE?

The simple answer is that if you want to add


resources to a managed application, you should
add managed resources. You do this through a .resx file.
However, as with everything in life, the simple answer
only tells part of the story. As the name suggests, a
resource is noncode data that youll use in your application. It can be a GUI resource such as an icon or a cursor,
but, equally, it can be text (for example, a localized caption for the buttons on your form) or even binary data. In
general, these resources will be used by managed code;
therefore, you should add it as a managed resource. However, the current version of .NET houses applications in
a Win32 Portable Executable (PE) file. Windows Explorer
doesnt understand managed resources. When it looks for
the icon to show for your application, it looks for unmanaged Win32 resources. This means that a C# application
will always have unmanaged resources.

Managed resources can be stored


in your assembly (embedded) or in
a separate file (linked).
You have to tell the compiler to use a specific icon for
the assembly. To do this, use this switch:
/win32icon:myicon.ico

The file myicon.ico will be used as the Win32 icon for


the file. Visual Studio .NET gives access to this switch
on the projects property dialog (go to the Application
icon under Common Properties on the General page).
This icon will be stored in part of the file used for Win32
resources. There is no mechanism in the .NET Framework
library to access this icon, but you can use the Win32 API
through Platform Invoke. Given it is far easier for a form
to use managed resources to store a form icon, it generally means your GUI .NET application will have at least two
icons (one for the application and the other for the form).
Other Win32 resources can be added to the output file,
but it isnt straightforward. The first point to make is that
there is no option in Visual Studio .NET to do this. If you
want to add Win32 resources to a .NET assembly, youll
have to do it using the command line. The next issue is
that the resource is added as a compiled Win32 resource,
which means that youll have to create a resource script
(.rc) and then compile it with the Win32 resource compiler (rc.exe). This generates a compiled resource with
the extension .res, which you mentioned in the question.
Resource scripts are straightforward, but the simplest way

Return a Value From a Thread

to learn about them is to create a test Win32 C++ GUI


application and add some resources to the project. Then
use a tool such as Notepad to open the .rc file and examine its contents. Once you have created a .res file, it can
be added to the assembly using this C# switch:
/win32res:myresources.res

Managed resources can be stored in your assembly


(embedded) or in a separate file (linked). They can be
stored as compiled resources or uncompiled resources.
The advantage of a compiled resource is that more than
one item can be aggregated into the resource. This is
important, for example, for the resources that you use for
a GUI application because you can store all the resources
for the form in one assembly resource. To add an embedded resource (noncompiled or the entire compiled
resource) you use the /resource command line switch,
and to add a linked resource you use /linkresource. In
Visual Studio .NET, you add a noncompiled resource
through the Add Item option in Solution Explorer and
then use the Properties window to specify that its an
embedded resource (the Build Action property and select
the Embedded Resource item). Visual Studio .NET does
not support linked resources.
To compile a resource, you use the managed resource
compiler, resgen. This nifty tool can compile a text file or an
XML (.resx) file to a .resources compiled resource file, and
it can decompile a .resources file back into its source. When
you add a form to a project, the compiler automatically creates a .resx file for each culture that you use and for every
resource you add to, the form will be added to the .resx file.
Your code can access a noncompiled resource using
Assembly.GetManifestResourceStream, which returns a
stream object. Compiled resources are accessed through
the ResourceReader class. However, youll rarely see this
class used; instead, youll use ResourceManager. This class
is important because it will load a resource from a satellite
assembly based on the culture of the current thread. #
The sample code in this article is available for download
on the Delphi Informant Magazine Complete Works CD
located in INFORM\2004\APR\CS200404RG.

Richard Grimes is an author and speaker on .NET. He is the author of


Developing Applications with Visual Studio .NET (Addison-Wesley, 2002) and
Programming with Managed Extensions for Visual C++ .NET 2003 (Microsoft
Press, 2003). He can be contacted at pro.csharp@richardgrimes.com.

Premiere 2004 Issue

Frameworks

By Michle Leroux Bustamante

Protect Your Intellectual


Property With Code
Access Security

Reduce your proprietary components possible exposure to


misuse from unauthorized access and potentially malicious
activities.

HEN .NET MADE ITS OFFICIAL RELEASE IN


February 2002, developers had a lot of new features
to absorb, all of them providing new productive and powerful ways to write enterprise applications. Now, after almost
two years, Im discovering that many developers are just
now thinking about the importance of code access security
(CAS), and the impact of features such as reflection. As
with application and system architecture, security should be
built into component design. However, this fact eludes many
developers, usually due to looming deadlines. So, security
is often last on the list of priorities until something goes
wrong. Then, of course, it becomes priority #1.
Reflection is a feature that gives developers good reason to
think about securing their components. Assemblies are selfdescribing units of code that carry metadata describing their
versions, cultures, public key signatures, types, members,
dependencies, security declarations, and more. This information can be easily inspected using reflection programmatically,
or by using tools such as the MSIL Disassembler (Ildasm.exe).
Furthermore, reflection makes it possible to load assemblies,
create instances of types, bind to existing types, invoke methods by name, and retrieve or modify runtime values. That
said, you can imagine that any assembly is potentially at risk
of being used maliciously once its deployed. In this article
Ill discuss what can, and cannot, be done today to protect
your intellectual property, and Ill provide you with some best
practice techniques for deterring evildoers from realizing their
intentions on your components.

The Good, Bad, and Ugly of Reflection


The .NET Framework runtime relies on reflection for a number
of practical uses, such as loading and executing assembly code,
object serialization, and other fundamental activities we cant
live without. The presence of metadata also makes it possible

for any developer to inspect your distributed assemblies with


the intention of writing code to invoke methods or extend
existing functionality. For example, non-sealed classes can be
extended, assembly types can be loaded and their methods
invoked, and even private and protected data members can be
altered by highly trusted code at runtime. Scary stuff.
To demonstrate some of these issues, and some possible
techniques for controlling access to your components, Ive
provided sample code that includes these components (see
end of article for download details). I include a Web service
that saves and retrieves photo images and related information (ImageServices.asmx), a Windows client application
(ImagingClient.exe)
that leverages two
client-side DLL
assemblies, an imaging utility layer
(ImagingTools.dll),
and an imaging services layer to invoke
remote Web services
(ImagingServices.dll).
With this sample,
I also provide a
few other test cliFigure 1. The sample application is
ents Ill call on to
separated into several client tiers to
demonstrate a few
demonstrate issues with security demand
points throughout
stack walks.
the article: a console
client (ImageClientConsole.exe) and two malicious clients
(EvilClientConsole.exe and EvilClient.exe).
The ImagingServices.dll includes a ServiceUtil class that
has a private member containing a required license key for
invoking remote Web services (see Figure 1). The problem
Premiere 2004 Issue

Frameworks

Protect Your Intellectual Property With Code Access Security

is that if this key isnt encrypted, its completely exposed


even though its contained in a private member and
embedded in the assembly. Without security measures in
place, any developer can freely reference this assembly and,
using reflection, access this private member to extract the
license key. In fact, he or she need not even create an earlybound reference because reflection also makes it possible
to dynamically load types by name. Consider the simplified
listing of the ServiceUtil class (see Figure 2).
This code (see the EvilClientConsole project) dynamically creates an instance of the ServiceUtil object and
stores the private key inside gotYourKey:
System.Reflection.Assembly asm =
System.Reflection.Assembly.LoadFrom("ImagingServices.dll");
Type type = asm.GetType("ImagingServices.ServiceUtil");
object svcUtil = Activator.CreateInstance(type);
object gotYourKey

svcUtil.GetType().InvokeMember("m_privateLicenseKey",

prevent the modification of private and protected values.


Lets consider the Web service application for a moment,
which might use an assembly as a data access layer component (DALC) on the server side. The DALC might contain some private members holding connection strings and
partial SELECT statements that are completed at runtime
with values passed to the Web service. Who says that SQL
injections are only an issue with Web applications? Who
says that your components are safe when theyre hosted
on your own site? Statistics show that almost 70 percent
of cyber crime is performed by employees or ex-employees
with internal knowledge of the application architecture.
(For more, see www.realtimenorthamerica.com/download/
disgruntled_employee_alert.pdf)
That said, it is possible that a malicious component
could be created that edits a string member containing a
SELECT statement, modifying it from this:
private string m_sqlGetImage =

BindingFlags.GetField|BindingFlags.NonPublic|

"SELECT id, title, imgDate, imgThumb

BindingFlags.Instance|BindingFlags.Public, null,

FROM images WHERE id=%putid%";

svcUtil, null);

to this:
Worse, if the distributed code is not obfuscated, you
can open Intermediate Language Disassembler (ILDASM),
dump the IL into a text file, and clearly see this same private value clear as day:
//000014:
private string m_privateLicenseKey = "ad340d2301";

private string m_sqlGetImage =


"SELECT id, title, imgDate, imgThumb
FROM images WHERE id='; DROP TABLE images; --";

by using a reflection statement that modifies the private


field such as this:

Consider the Web Service App

object [] args =

So, lets assume that you encrypt this value or use alternate
licensing methods for your Web services, that still doesnt

FROM images WHERE id='; DROP TABLE images; --"};

{"SELECT id, title, imgDate, imgThumb

dalcComponent.GetType().InvokeMember("m_sqlGetImage",
namespace ImagingServices
{
public class ServiceUtil
{
private string m_privateLicenseKey = "ad340d2301";
public void SendImage(ImageInfoEx img)
{
SendImageInternal(img);
}
private void SendImageInternal(ImageInfoEx img)
{
// code to invoke ImageManager Web service
// using m_privateLicenseKey
return;
}
}
}

Figure 2. The ServiceUtil class is responsible for invoking the


ImageManager Web service described in Figure 1. Supporting
assemblies to smart client applications such as this sometimes contain
proprietary information for invoking remote services.

BindingFlags.SetField|BindingFlags.NonPublic|
BindingFlags.Instance|BindingFlags.Public,
null, dalcComponent, args);

And whoops, the images table was just dropped!


Now, assuming you employ better programming practices
than the above examples, and you have protected sensitive
data by encryption or some other safety measure, there is still
the possibility of your components being leveraged by third
parties to build extended applications, or of specific methods
being invoked by un-trusted parties. Lets consider the clientside ImagingTools.dll in my sample, which contains proprietary
imaging functionality (well, sort of) and also contains code to
invoke imaging Web services through ImagingServices.dll. A
new application could be developed that references my proprietary imaging technology and extends it to save images to an
alternate data source. Using ILDASM or reflection, my component could be inspected for useful features and then referenced
by a new application that leverages those features.
Premiere 2004 Issue

Frameworks

Protect Your Intellectual Property With Code Access Security

Frankly, its difficult to protect components from both


inspection and misuse today. Code obfuscation engines
make it possible to, well, obfuscate your assembly code,
making it unintelligible to an IL disassembler so that the
previous IL dump would be unintelligible. But this might
only act as a deterrent to unwanted consumers of your components because de-obfuscators now surfacing are able to
reverse-engineer obfuscated code back to IL.

restrictions can be applied. Your code, and other managed


code invoked by your code, can demand, deny, or assert
permissions declaratively by applying attributes to the
appropriate target (for example, assembly, class, or method)
or imperatively using permission types at runtime. In the
rest of this article, Ill use examples applicable to protecting your intellectual property to demonstrate using some of
these CAS techniques.

CAS in a Few Paragraphs

Implementing a Strong Name Stack Walk

So far Ive focused on ways that reflection can be used to


access your components. But, who has the right to execute
reflection techniques? Callers that are granted the appropriate ReflectionPermission can. This is where we get to talk
about CAS a little bit, and I have also provided a reference
at the end of this article for those of you who are new to
CAS and would like additional information.
Security models have traditionally been focused on
users and role-based security. If the user was authenticated, he could freely execute any applications he had
access to. CAS applies rules to executing code that might
restrict a components capability to execute functionality,
not only based on both users and roles but also based
on the codes rights. This way if code originates from an
unknown source, such as from an e-mail attachment or
an Internet download, its permissions might be limited
based on permission sets defined for the machine on
which it executes.
It all boils down to permissions. Permissions individually
govern access to a particular resource or operation at runtime.
A predefined set of code access permissions are installed with
the .NET Framework and each has an associated runtime type.
For example, FileIOPermission is used to define how code can
access file and folder resources. Similarly, ReflectionPermission
defines how code can use reflection to inspect assembly metadata, or to dynamically load types and invoke members.
Assemblies are granted permissions at runtime, based on
evidence. Evidence can include the origin site or URL of
the assembly, its application directory, or its strong name.
These elements of evidence are used by the runtime to
assign the assembly to one or more code groups as can be
defined within the .NET Configuration utility. A predefined
list of code groups exists, but you can create custom code
groups as well. Code groups are associated with a permission set (a collection of specific permissions) and have
membership conditions that determine if an assembly
belongs to the group. So, after the runtime associates an
assembly with a particular code group, the union of those
code group permissions is assigned the assembly. Enterprise, machine, and user code access policies also play a
role in this process but I wont get into that here.
The bottom line: Your assembly is assigned permissions
by the .NET Framework runtime that govern access to
resources and control other behavior, but further runtime

With the help of CAS you can reject unauthorized callers and
help circumvent some of the potential misuses of reflection
in the process. Assemblies can be uniquely identified by their
strong name, so one way you can control access to types,
methods, and properties is to demand that callers have the
same strong name by using a StrongNameIdentityPermission
demand. In my example, all assemblies are signed with my
own key, mlbkey.snk; and this code puts a declarative demand
for that public key on the SaveImage method of the ImagingServices component:

[StrongNameIdentityPermission(SecurityAction.Demand,
PublicKey="0024000004800000940000000602000000240000
525341310004000001000100A7122EBA43D0ED4B7D5AA38517B
DC4D0ABECEFB455BF060A59560BEC50E238586A460AD53DD57F
9AB0EE8062BEC12BE51AB2CB5326ADBDB631E0F44717D2630AC
663A7F43A9617221691D09CC9217064800D7C4BED7B73EDEE46
B7A2A1D3B763BF19958A4B6B4410464816243D7AE559BE2DD69
0A65B370496A3A03DD0031CA8")]
private void SendImageInternal(ImageInfoEx img)
{
// code to invoke ImageManager Web service
// using m_privateLicenseKey
return;
}

What I did is apply a StrongNameIdentityPermissionAttribute to the method, telling it to perform a demand with
SecurityAction.Demand and providing the pubic key token
for the demand. This public key was extracted from the same
private key I use to sign my assemblies. A security demand
declared this way both adds the demand to the assemblys
metadata (so that administrators would see it if they actually
took the time to inspect the assembly) and causes a stack
walk every time this method is invoked. The stack walk will
ensure that every caller in the call stack has this permission,
which successfully prevents un-trusted callers from calling the
method. All calling assemblies must be signed with my key,
mlbkey.snk, which must be safely guarded.
This stack walk terminates when the runtime encounters a
stack frame that fails the permission demand (which throws
an exception), when the runtime successfully reaches the
topmost stack frame without exception, or when the runtime
encounters a security assertion for the requested permission. Assemblies with assertion permission can terminate a
demand stack walk early (and successfully) by claiming to
Premiere 2004 Issue

Frameworks

Protect Your Intellectual Property With Code Access Security

the runtime that the permission exists on behalf of all callers


above it. Using StrongNameIdentityPermissionAttribute with
SecurityAction.Assert and the same public key, I can intentionally assert the mlbkey.snk strong name from my ImagingUtil components SaveImage method, thus making it possible for any client caller to invoke SaveImage, which in turn
invokes SendImage in my ImagingServices component:
[StrongNameIdentityPermission(
SecurityAction.Assert,
PublicKey="0024...031CA8")]
public void SaveImage()
{
// some other code...
return m_serviceUtil.SendImage(imgInfo);
}

Only an assembly that is actually signed with a particular private key can assert its related public key in this way.
Makes sense right? Imagine if anyone could open ILDASM
and copy the public key token and then write un-trusted
code that asserts it? Regardless, assertions do open your
components up to luring attacks where now any client able
to invoke SaveImage will be granted access to the SendImage method that demands this strong name. Assertions can
improve performance by circumventing the stack walk;
however, they should be used sparingly (if at all) because
of possible security implications.
Demanding this strong name permission on the SendImage
method did not help me prevent access to the private license
key member stored inside the ServiceUtil object. As I demonstrated earlier in my EvilClientConsole example, this could
freely be hacked using reflection. In fact, most (if not all) permission attributes cannot be applied to fields, which means
in theory there is no way to isolate protection of a specific
field, even through its property accessor. You can, however,
achieve this by applying a security demand to the entire type,
or assembly.
If I move my StrongNameIdentityPermissionAttribute
declaration to the type level, all attempts to create the
ServiceUtil type and access any member (including private, protected, and public) will be rejected if the permission demand stack walk fails. This applies to both earlybound and late-bound access with reflection:
[StrongNameIdentityPermission(
SecurityAction.Demand,
PublicKey="0024...031CA8")]
public class ServiceUtil
{
private string m_privateLicenseKey = "ad340d2301";
// remainder of class definition
}

So, in theory, if you place a strong name permission


demand on all accessible types, youll successfully

10

prevent any access by un-trusted callers, assuming you


do not introduce an assertion within one of your own
components in the chain of calls.

When Security Demands Fail


Security demands perform a stack walk to verify all callers
have the requested permission and this has a performance
impact that must be weighed against your need for this level
of security. But there are other problems with strong name
security demands as well. If I were to give the ServiceUtil
class a strong name that differs from the demanded strong
name (I used newkey.snk in my example), this removes
its capability to call its own internal methods where the
demand applies! Of course the simple solution to this problem would be to sign all assemblies with the same private
key, which probably works for the majority of cases, but
there is a related problem we cant fix. What about callbacks or events invoked by the .NET Framework? Hmmm
Let me tell you why Ive been directing you to the console clients in my sample thus far. Its true that if we run
the Windows client sample, ImagingClient, with all the
appropriate security demands and assembly signatures in
place, youll find that the client cannot invoke methods
protected by the demanded permission. The stack walk
fails because the button click event handler is actually
invoked by the runtime (see Figure 3), so the stack frame
above this event handler is not signed with my private key!
Of course, per my earlier discussion, you can remedy
this by asserting the strong name permission as follows:
[StrongNameIdentityPermission(
SecurityAction.Assert,
PublicKey="0024...031CA8")]
private void button1_Click(object sender,
System.EventArgs e)
{
ImagingTools.ImagingUtil imgUtil = new
ImagingTools.ImagingUtil();
// set up image data....
imgUtil.SaveImage();
}

But, now weve opened the potential for a luring attack,


because any caller of button1_Click no longer requires the
asserted strong name.
There is an alternative to security demands that removes
the need to perform asserts while still ensuring only trusted
callers can access each assembly. Performing a link demand
on the same strong name permission evaluates calling
assemblies at Just In Time (JIT) compile time to ensure the
immediate caller is trusted and throws a PolicyException
if it is not. If I change my sample project so that all three
components (ImagingServices.dll, ImagingTools.dll, and
ImagingClient.exe) use the following link demand at the
Premiere 2004 Issue

Frameworks

Protect Your Intellectual Property With Code Access Security

without your specific strong name, you


might need to provide an outermost layer
that asserts the strong name or refrains
from demanding it from immediate callers.
In addition, using link demands in lieu of
demands means you must exercise caution
with the methods you invoke that might
trigger callbacks into your code to insure
they will not originate from unsafe callers.
Whether youre deploying a simple client application, licensing an enterprise
solution, or hosting Web applications and
services for your customers, you are not
immune to cyber crime and the possibility of component misuse. Hopefully, this
article has opened your eyes to the power
of reflection, the benefits of CAS, and its
limitations. Protecting your intellectual
property is an important part of developFigure 3. The message pump that picks up a button click from the user puts the .NET
ing a security model for your applications,
Framework runtime at the top of the call stack that invokes the buttons Click event
handler.
and by employing some of the techniques
discussed in this article you can at least
type level, Ill successfully be able to compile the EvilClient
reduce the likelihood of intrusion by making malicious
assembly to reference ImagingTools or ImagingServices:
would-be evildoers move more slowly and work harder. #
[StrongNameIdentityPermission(SecurityAction.LinkDemand,

Resources

PublicKey="002400000480000094000000060200000024000

0525341310004000001000100A7122EBA43D0ED4B7D5AA38517
BDC4D0ABECEFB455BF060A59560BEC50E238586A460AD53DD57
F9AB0EE8062BEC12BE51AB2CB5326ADBDB631E0F44717D2630A
C663A7F43A9617221691D09CC9217064800D7C4BED7B73EDEE4
6B7A2A1D3B763BF19958A4B6B4410464816243D7AE559BE2DD6

Michles page on Reflection and Security: www.dotnetdashboard.net/sessions/


reflectionsecurity.aspx
Programming .NET Components, by Juval Lowy
(OReilly & Associates, 2003)

90A65B370496A3A03DD0031CA8")]

However, when the JIT compiler is invoked to bind


EvilClient to one of those assemblies, it will raise a
PolicyException. Using a link demand removes the issue
of the runtime participating in the stack walk because
link demands dont perform a stack walk. The drawback
to this is that an assembly deep in the chain of calls cannot effectively guarantee that all callers are safe. If you
have control over all consumers of your assemblies, you
can place a link demand at the type level of every protected component, including the outermost client applications. If youre exposing an API that might have callers

11

The sample code in this article is available for download


on the Delphi Informant Magazine Complete Works CD
located in INFORM\2004\APR\CS200404MB.

Michle Leroux Bustamante is an associate of IDesign Inc., a Microsoft


Regional Director, a member of the International .NET Speakers Association
(INETA), a frequent conference presenter, and published author. At
IDesign, Michle provides developer training and high-end architecture
consulting. Reach her at mlb@idesign.net, or visit www.idesign.net and
www.dotnetdashboard.net.

Premiere 2004 Issue

N E W

&

U S E D

By Mike Riley

PE Explorer
Dig into Compiled Executable Resources with Ease

very copy of Delphi ships with a nifty example


program named ResXplor that demonstrates
how to view a simple compiled DLL or execut-

able resource bundle. The example code serves as a


great teaching opportunity for Delphi newbies, but it
was never intended as a robust resource spelunker.
Seeing this tool deficiency as a market opportunity,
Heaventools has created a resource explorer that
leaves ResXplor in the dust.

The most distinguishing feature that separates PE Explorer from other resource editors is its incredible ability to
perfectly render and visually represent an executables
menu and dialog structure for viewing and editing (see
Figure 1). Some editors simply read in string values and
coordinates; PE Explorer literally presents these visual
elements the same way they would appear in the running
application. This obviously makes editing and extracting
these elements considerably easier. Another benefit of this
approach is the education users receive when interrogating well-designed programs. Studying the clean, efficient
interfaces of commercial-grade apps can yield improved
GUIs in your own programs.
One of the more interesting uses of
the program is its ability to personalize or mod an application. In other
words, just as computer cases are
being decked out like the Funny Cars
of the 60s, PE Explorer allows creative
individuals the ability to fully edit and
replace strings, bitmaps, and icons
used by an application. Although this
probably infringes on many application
licenses, it nevertheless allows one to
alter Borland Delphi to My Favorite
Windows App Builder and insert cartoon character icons and family photo
bitmaps into toolbars and dialog boxes.

Figure 1: Check out the Delphi 7 development team photo uncovered by PE Explorers view of the
delphi32.exe resources.

29

DELPHI INFORMANT MAGAZINE | April 2004

Besides being an effective resource editor, PE Explorer provides several tools


that elevate it to Power Coder status.
The most obvious of these is the disassembler, which is especially tailored
toward digging into Delphi-compiled
programs via its ability to detect and
process Borland VCLs in a program
(see Figure 2). Naturally, the disassem-

New

&

Used

PE Explorer
In addition to the internal functions,
environment enhancements arrive in
the form of plug-ins via PE Explorers
plug-in API. Example source code is
included for Delphi and C, and the
API itself is fairly easy to leverage.
Hopefully Heaventools will post new
plug-ins on their Web site authored
by both the company and its
enthusiastic user base.

Figure 2: The disassembler is PE Explorers most powerful tool, providing developers with a deeper understanding
of an executables inner workings.

Ironically, and somewhat


disappointingly, PE Explorer refuses
to analyze itself. An internal error
results when attempting to explore
pexplorer.exes file structure. Whether
it was Heaventools intent or not,
the underlying message behind this
seemingly selfish behavior is that
PE Explorer can pluck resource fruit
from any other tree of executables
except its own.

Conclusion
Power users will appreciate the plug-in API and ability
to dig into any executable, ActiveX, EXE, DLL, SYS,
DRV, MSSTYLE, CPL, OCX, BPL, DPL, or SCR file that
the program will allow. Some developers might argue
that even the modest price for PE Explorer is overpriced
compared to the free ResXplor example that ships
with Delphi, but the additional enhancements more
than compensate for the expense. However, the value
proposition proposed by PE Explorer is only validated if
developers leverage the more sophisticated tools such as
the disassembler or manifest wizard.

Mike Riley is a chief scientist with RR Donnelley, one of North Americas


largest printers. He participates in the companys emerging technology
strategies using a wide variety of distributed network technologies, including
Delphi. Readers may reach him at mike_riley_@hotmail.com.

Figure 3: The Dependency Scanner tool is useful for determining the executable
components required by a program.

bler can analyze non-Delphi executables as well, making PE


Explorer a useful tool for any syntax-centric coder. Another
useful function for Windows XP targets is the XP Manifest
Wizard. This allows common controls oblivious to XPs new
visual style to adopt XPs common control look-and-feel.
The Dependency Scanner is another feature that recursively scans all modules (ActiveX controls, DLLs, EXEs,
etc.) required by the portable executable file being analyzed (see Figure 3). This is a godsend when packaging
companion files for distribution that were obtained from
external, potentially unknown, or poorly documented
sources. Remarkably, all of PE Explorers advanced functions are available with a few clicks of the mouse button,
providing immediate user feedback painlessly.
30

DELPHI INFORMANT MAGAZINE | April 2004

Just the Facts


PE Explorer is a powerful utility that allows you to view,
edit, and extract resources from an executable file.
Enhancements such as the disassembler and the plug-in
API elevate the tool beyond the simple resource-viewing
example included with Delphi.
Heaventools Software
Pacific Business Centre
101-1001 West Broadway
Dept. 381
Vancouver, BC, V6H4E4
Canada
Phone: (604) 221-2650 ext. 249
E-Mail: sales@heaventools.com
Web Site: www.heaventools.com
Price: US$129

N E W

&

U S E D

By Bill Todd

ImageLib Corporate Suite 7.0


Picture Perfect Imaging Toolkit

mageLib Corporate Suite 7.0 is a complete


imaging toolkit. Whether you need to download
and edit images from a digital camera, or write

a high-speed document scanning system, ImageLib


Corporate Suite 7.0 has the components you need
to do the job quickly. ImageLib Corporate Suite 7.0
includes more than 30 components, ranging from
those to acquire, display, and edit images, to specialized toolbars, to thumbnail components that let
users view and select from multiple images.
The document imaging components support high-speed
scanning, using scanners with automatic document feeders, as well as single-page scanners. With the click of a few
toolbar buttons you can deskew and despeckle your images,
add highlighting and annotations, and perform a host of
other manipulations to enhance the image quality. ImageLib
Corporate Suite 7.0 includes both data aware and non-data
aware components, so you can store images on disk or in a
database. Best of all, there are no deployment royalties.

amounts of paper that must be filed and stored. As mass storage prices have dropped, businesses have increasingly turned
to electronic document storage and retrieval systems.
But implementing a custom document storage system that integrates with the rest of your corporate data isnt easy, because
interfacing to a high-speed scanner with an automatic document feeder is complex. Fortunately, the developers at Skyline
Tools have done all the hard work for you. The best way to
explore some of the powerful features of ImageLib Corporate
Suite 7.0 is to build a document imaging application. I started
by dropping the following components on a form:
Coolbar
PrintDialog
ILDocImage
ILDocImageToolbar (on the Coolbar)
MMOpenDialog
MMSaveDialog

Document Imaging
The one type of imaging that more and more businesses need
is document imaging. In spite of the advances in moving to
a paperless environment, most businesses still generate vast
Property

Set To

DocumentImage

The ILDocImage component

MMOpenDialog

The MMOpenDialog component

MMSaveDialog

The MMSaveDialog component

PrintDialog

The PrintDialog component

Figure 1: The sample application property settings.

31

DELPHI INFORMANT MAGAZINE | April 2004

Figure 2: The sample application at startup.

New

&

Used

ImageLib Corporate Suite 7.0


To gain some appreciation for the power of the ImageLib
Corporate Suite 7.0 components, take a look at the
table in Figure 5, which describes all the buttons on the
ILDocImageToolbar. Of course, the ILDocImage component
has methods and properties that let you perform the same
functions (and more) in code. You can also override or add
to the functionality of any button by creating a BeforeAction
or AfterAction event handler for the Toolbar.

Figure 3: A scanned document, slightly askew.

Figure 4: The scanned document from Figure 3 after anti-aliasing, and removing
the tilt and border.

The Coolbar and PrintDialog are standard VCL components.


The others are part of ImageLib Corporate Suite 7.0. Next,
I set the properties of the ILDocImageToolbar, as shown in
the table in Figure 1. Finally, I set the ScanSaveOpt property
of the ILDocImage component to sMultiPageAppend, so I
could scan multiple pages into a single TIFF file.
Figure 2 shows the finished application at run time; notice
only three buttons are enabled on the toolbar. Using them you
can choose a scanner, scan documents, or open a file. After
scanning a couple of documents into a file, you can open the
multi-page TIFF file and manipulate the scanned images.
Figure 3 shows the first page of the document. Notice that
the text is hard to read and that the page is tilted slightly
to the left. To make the text more readable, I opened the
combo box at the left end of the toolbar and selected 16-bit
anti-aliasing. Next, I clicked the Deskew button to remove
the tilt, and then clicked the Remove Border button to
remove the border around the scanned image. Finally, I
clicked the Update TIFF button to save the corrected page to
the file. The result, shown in Figure 4, is a perfectly aligned,
easy-to-read image of the document.
Because skewing is a common problem with scanned images, you may want to deskew them automatically. Fortunately, its very easy to do this in code. One approach is to create a BeforeAction event handler for the ILDocImageToolbar,
and deskew the page in a file when the user opens the file.
32

DELPHI INFORMANT MAGAZINE | April 2004

To really appreciate the power of this simple application


you need a high-speed scanner with an automatic
document feeder. The ILDocImage component uses very
fast algorithms and multi-threading to achieve scan rates
of up to 100 pages per minute. Thats 6,000 pages an hour.
With that kind of performance you can write a document
imaging application that can handle any job. Just hook up
your ADF scanner, set the ScanUseAdf property to True and
youre ready to go.
If you need to store your scanned images in a database,
ImageLib Corporate Suite 7.0 includes data-aware
versions of all of its components. There are more
than 30 components in all that support document
scanning, color image manipulation, and multimedia.
The ILDocImageThumbNails component lets you see
thumbnails of multiple scanned documents at the same
time. Figure 6 shows the sample application with a
thumbnail component added to the form. One line of code
was required to have the ILDocImageThumbNails open
the same file that is opened by the Open button on the
toolbar. A second line of code changes the image in the
ILDocImage component to the thumbnail you click on.
Another example of Skyline Tools careful attention to the
needs of the document imaging market is the ability to
annotate scanned documents. I mentioned earlier that you can
click the Toggle Annotation Mode button on the ILDocImage
toolbar, then right-click the ILDocImage component to
access the annotation features through the context menu.
To make annotation even easier, add an AnnotationsToolbar
to your application, as shown in Figure 7. With the
AnnotationsToolbar users can easily draw rectangles, ellipses,
polygons, or any other shape. They can also highlight text in
the scanned document and add labels and notes. A variety
of line types lets users connect notes or labels to the items to
which they refer. You also have complete control of foreground
and background colors, line widths, and fonts. If you set
the AutoSaveAnno and AutoLoadAnno properties of the
ILDocImage component, your annotations will automatically
be saved and loaded with the document. Figure 7 shows two
areas of text that are highlighted, one in red and one in green.
It also shows a yellow label in the upper-left corner with a
line pointing to one of the highlights and a yellow annotation
note in the upper-right corner.
In addition to creating multipage TIFF files, the ILDocImage
component provides other options for saving scanned data.
You can supply a base file name and let ILDocImage create a
separate file for each page. The file names consist of the base
file name you supplied, plus an incremental number. If youre
scanning multiple pages with a scanner that doesnt have
an automatic document feeder, youll appreciate the ability

New

&

Used

ImageLib Corporate Suite 7.0

Button

Function

Image Enhancement
Combo

Selects an enhancement technique to


improve image quality.

Open

Opens a file and manipulates the images


it contains.

Save

Saves the current image to a file.

Print

Prints a file.

Toggle Panning &


Zooming

Enables panning when down. With panning on you can pan around an image
that is larger than the component by dragging with the mouse.

Toggle Magnifier

With the magnifier on a rectangular


magnifier this follows the mouse cursor
letting you see a magnified view of any
part of the image.

Zoom In

Makes the image larger.

Zoom Out

Makes the image smaller.

Reset Zoom

Shows the image actual size.

Best fit

Makes the entire page visible.

Scan pages

Activates the selected scanner.

Select Scanner

Lets you choose the scanner you want to use.

Cut, Copy and Paste

Lets you cut or copy the image to or paste


the image from the Clipboard.

Toggle Annotation
Mode

When pressed you can right-click to display the annotation menu and annotate
the document.

Edit TIFF tags

Lets you change any of the tag values in


the TIFF file. For example, you can change
the Description tag.

Invert

Black areas become white, and vise versa.

Rotate 90 Degrees

Rotates the image 90 degrees clockwise.

Rotate 180 Degrees

Rotates the image 180 degrees.

Deskew

Straightens a tilted image.

Remove Border

Removes the border from the scanned


image.

Navigator buttons

Lets you move forward and back through


the pages of a multi-page file and jump to
the first or last page.

Purge Deleted TIFF


Pages

Removes deleted pages from the file.

Update TIFF

Saves the current page to the file.

Figure 5: The ILDocImageToolbar buttons.

to set the ILDocImage ShowScanUI property to False, so the


user interface for your scanners TWAIN driver doesnt appear
when you click the scan button on the toolbar.
ImageLib Corporate Suite 7.0 includes a complete document imaging application called SkyDocImage. You may
find the source code useful as an example of how to use
some of the components, although you should be aware
that, like all the other examples that come with ImageLib
Corporate Suite 7.0, there are no comments or help to
explain what the program does or how it does it.
33

DELPHI INFORMANT MAGAZINE | April 2004

Figure 6: The sample application with a thumbnail component added to the form.

Imaging Components
Working with color images, whether you scan them,
download them directly from a digital camera, or load
them from files, is just as easy as working with documents. Figure 8 shows an imaging application built by
dropping IlMultiImageToolbar, ImageLibThumbNails, and
PMultiImage components on a form and creating two simple event handlers. The toolbars AfterAction event handler loads into the thumbnail component all the files in
the same directory as the file the user opened. The ImageLibThumbNails components SelectThumb event handler
loads the image for the thumbnail the user selected into
the PMultiImage component.
Although the toolbar contains buttons to let you cut,
copy, paste, rotate, zoom, pan, invert, annotate, undo,
select scanner, scan, print, open, and save, the real power
is in the Effects Manager button. When you open the
Effects Manager, you can change image properties and
apply special effects, including AutoContrast, Bleed, Borders, Border Fade, Blur, Brightness, Color, Color Palette,
Darkness, Despeckle, Edge Detection, Engrave, Enhance,
Extrude, Gamma, Gray Area, Half Tone, Hue, Saturation,
Invert, Jiggle, Mirror, Mosaic, Motion Blur, Noisify, Oil
Paint, Page Curl, Paste Image, Pinch Hole, Polar, Rotate,
Sharpness, Spray, Softness, Tile Maker, Threshold, Transitions, Warp, Wave, and Whirlpool.
Documentation and Support
ImageLib Corporate Suite 7.0 is a technical tour de force
marred only by poor documentation. The only documentation supplied is the online help and the examples. There
are more than 40 examples, but they are undocumented.
The online help lists the examples and provides a brief
description for about two thirds of them. For the others,
only the name is given.
Examples, even examples with no commentary, were useful in the days before RAD tools. Examples built with
tools like Delphi that arent documented are, to me, an
exercise in frustration. Suppose you find an example
that does what you want using the ILDocImage component. Looking at the code alone does you no good. To
understand how the application was created you must
open a second instance of Delphi, drop an ILDocImage

New

&

Used

ImageLib Corporate Suite 7.0

Figure 7: An annotated document.

component on a form, then work your way through 107


published properties in the Object Inspector to see which
ones arent set to their default values in the sample program. After you do this with every component, you can
then look at the code to get an idea of how the sample
application works.
The online help has no real starting point. There is a Getting
Started topic, but it simply refers you to the demo applications and a tutorial. The tutorial has only eight steps. It
directs you to add four buttons to a form, but only tells you
what code to add to the OnClick event handlers of three of
the buttons. I was worried before I even started the tutorial
when I saw that the steps are numbered 1., 2., 3., 4., 5.5.,
6.6., 7.7, and 8.8. You can fall back on looking at the source
code for the visual components, for example the toolbars,
and this can be very useful. Unfortunately, even this is difficult because the help topics for the components dont list
the unit in which the component is declared. Every time you
want to look at the source for a component, you must start
by using grep or another multi-file search tool on the source
directory to find the file you need.
One of my favorite parts of the online help is that most
of the topics end with the statement, More questions
and answers (more than one thousand messages at the
time) can be found on news://skylinetools.com. A few
are listed here SkyLine News Group Topics. When you
click the SkyLine News Group Topics link you get a
pop-up window that says, SkyLine News Group Topics has been temporarily removed. Log on to news://
skylinetools.com. I leave it to you do decide how helpful
1,000 unorganized newsgroup messages will be. The one
bright spot in the documentation is that all the properties, methods, and events are listed for each component,
although some of the descriptions are very brief.
Technical support is available via newsgroups, phone, and
e-mail. Skyline Tools also offers consulting services if you
need them.
Conclusion
In spite of poor documentation, ImageLib Corporate Suite
7.0 is a great graphics toolkit, and the best set of tools for
34

DELPHI INFORMANT MAGAZINE | April 2004

Figure 8: A simple image editing program.

document imaging Ive seen. The support for high-speed,


high-volume scanning using scanners with automatic
document feeders is stunning. Plus, every feature you
can think of for manipulating, enhancing, and annotating scanned documents is at your fingertips. This is the
toolkit to get if you need to build applications to scan and
store documents.

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 btarticle@dbginc.com.

Just the Facts


ImageLib Corporate Suite 7.0 is a great component library
for imaging applications, and its support for document
imaging is superb. The power to use automatic document
feed scanners at scan rates up to 100 pages per minute
makes building document management systems for highvolume applications easy. The powerful annotation tools
and features to despeckle, deskew, and perform other
manipulations on the scanned images make ImageLib
Corporate Suite 7.0 a document imaging powerhouse.
Poor documentation is this products only drawback.
Skyline Tools
20537 Dumont St.
Suite 100
Woodland Hills, CA 91364
Phone: (818) 346-4200
Fax: (818) 888-5314
E-Mail: sales@imagelib.com
Web Site: www.skylinetools.com
Price: Contact Skyline Tools

F I L E

N E W

Exploring Delphi 8

By Alan C. Moore, Ph.D.

here has certainly been considerable interest in Microsofts new .NET platform. Not surprisingly, Delphi
developers have been waiting anxiously for their chance to
enter this exciting new world, with all of its well-publicized
benefits. The wait is over, of course, and Delphi 8 the
first Delphi version built specifically for Microsoft .NET
has finally arrived. This month Ill report on my first
explorations and initial reactions. Please bear in mind that
I will be describing the Architect version, so not every feature I discuss will be available in other versions.
You wont be too surprised to learn that the first thing to
catch my attention was the new look, one that reminded
me of Microsofts Visual Studio .NET. Upon opening Delphi 8 for the first time I saw much that was familiar: the
menu bar, tool bar, and an old friend the Object
Inspector. But the remainder of the desktop is filled with
additional windows, one of which is divided into several
tabs. These include a Project Manager, a central browserstyle window that starts its life as a Welcome page, and
a tool palette in the lower right-hand quadrant. Significantly, this desktop is much more configurable than those
of earlier Delphi versions.
I was pleased to find the essential information to get up
and running quickly, by simply clicking on Getting Started
in Delphis new HTML help system. Naturally there is
a description and overview of Delphi for .NET. A Tour
of the IDE (Integrated Development Environment) with
its various parts is also included. The good news is this:
With all the changes and the increased flexibility, building an application in this IDE isnt that different from
previous Delphi versions. The biggest change is in coding,
where we will have to get used to certain .NET practices.
Depending on the specific version you have, you can create a variety of application types, from the usual Windows desktop app, to ASP.NET Web applications. You can
also create .NET assemblies and custom components that
can be used by any .NET language, not just Delphi! Delphi developers can finally fully enter the .NET world.
The help resources include all the information that comes
with the .NET SDK, as well as information that is Delphi
35

DELPHI INFORMANT MAGAZINE | April 2004

specific, such as IDE command-line options and switches.


One of the largest topics in the help system is entitled
Procedures. This is the how-to section that explains
how to carry out a plethora of tasks. These tasks include
the most basic, such as creating a project, adding components to a form, and setting properties, as well as such
advanced tasks as working with threads.
Opening Whats New, I was impressed with the feature
set, which includes all the requisite tools for development in
the early twenty-first century. Delphi 8 includes many tools
(some of which are third-party) for managing the various
stages of the development/deployment process. For example,
Delphi 8 includes CaliberRM, described in the help system
as a Web-based requirements definition and management
system ... to help control the product development process.
However, on exploring the help system in more depth, I
found that information related to some of the error messages
wasnt nearly as detailed and informative as Id hoped. I
expect this will be corrected in an update patch.
In its new .NET incarnation, Borland continues the tradition established in previous Delphi versions by including modeling tools especially in its high-end versions.
Here it introduces the UML-based Enterprise Core Objects
(ECO). This tool uses a structure called ECO Spaces a
structure that contains various objects. Just as objects are
specific instances of classes, ECO Spaces are instances
of classes in a model at run time. These objects retain
both their domain properties (attributes and operations)
and the relationships defined in the model. Conveniently,
all you need to do to establish the basic framework is to
select File | New | Delphi for .NET Projects | ECO Windows Forms
Application. Delphi 8 will then generate all the necessary
code to get you started. (This is a very superficial description; the help system contains considerably more detail
and information on using this exciting new tool.)
Of course, user interface designing tools trace their lineage back to Delphi 1. The look is a bit different, but
the powerful functionality remains. As in all versions of
Delphi, this one continues to provide the convenient generation of much of an applications code. That automatic
generation includes all the common .NET application

File

New

Exploring Delphi 8

types. And all the familiar helpers, like code completion,


are included. There are also new tools to support team
development and track changes to code using the Microsoft Common Source Code Control API (SCC API) as the
central mechanism. There is a great deal to like about
Delphi 8, but I wish that more of my favorite third-party
tools were available. I suppose I need to be more patient.
My early experiences of coding in Delphi 8 have been
quite positive. I was able to easily build a Windows Forms
application from scratch. I also began the process of porting some old multimedia code to the new platform; youll
probably be hearing more from me on that topic in the
coming months. Again, this represents my initial reaction
to Delphi 8. As I explore further, I will be sharing additional information with you. Until next time...

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.

36

DELPHI INFORMANT MAGAZINE | April 2004

Anda mungkin juga menyukai