Delphi Informant Magazine (1995-2001)
Delphi Informant Magazine (1995-2001)
OLE Automation
Controlling One Application with Another
ON THE COVER
7 OLE Automation — Cary Jensen, Ph.D.
35 Delphi at Work — Douglas Horn
Using Delphi and Microsoft Word for Windows, Dr Jensen explains how to
You may not have noticed it; its very ubiquity makes it invisible.
use OLE Automation to have one application control another. He provides
Nevertheless, the humble Sender parameter can make many Object
all the information needed to turn any Delphi 2 application into an
Pascal programming tasks easier. Mr Horn explains how to put the
automation server.
neglected parameter to work.
FEATURES 39 Inside OP — Dan Miser
12 Informant Spotlight — Ray Lischner Using Delphi’s data-aware components as a model, Mr Miser has created
Mr Lischner explains the nuances of building DLLs in Delphi 1 TINISource, an INI-aware component that takes the drudgery out of inter-
and 2. The discussion includes a DLL refresher course, exporting a acting with Windows .INI files.
class from a DLL, importing a class into an application, and using
objects that cross module boundaries. REVIEWS
42 With Class — Product Review by James Callan
19 Visual Programming — Keith Wood
Mr Wood helps you become a pro at creating experts; step-by-
step, he teaches you how to use the TExpert class to build a 46 Delphi 2 Unleashed — Book Review by Cary Jensen, Ph.D.
customizable expert for creating screen savers — then there’s the 46 The New Delphi 2 Programming EXplorer
screen saver itself. It’s two articles in one. Book Review by Tim Feldman
Price: US$12.95
Sax Ships Basic
Contact: 303 Products, Inc., Engine Pro
PO Box 966,
Palo Cedro, CA 96073 Sax Software, of Eugene,
Phone: (916) 549-5617 OR is now shipping the lat-
Fax: (916) 549-5577
Web Site: http://www.303-products.com est version of Sax Basic
Engine Pro 3.0. An ActiveX
control, Sax Basic Engine
Pro integrates with Delphi,
Visual C++, and Visual Basic
applications, and supports
events, classes, and multiple
modules.
Sax Basic Engine Pro
offers syntax highlighting, Price: US$495; requires no royalties Phone: (800) 645-3729 or
and includes an integrated or run-time fees, and ships with a 30- (541) 344-2235
editor and debugger that day money-back guarantee. Fax: (541) 344-2459
can call a stack, set a vari- Contact: Sax Software, 950 Patterson E-Mail: Internet: [email protected]
able watch, or single-step St., Eugene, OR 97401 Web Site: http://www.saxsoft.com
through code. It also allows
users to manipulate events Open Window Releases OWShare (cont.)
by treating everything as an America Online in the Price: US$59.95
object. Whenever an object Development Forum Contact: Open Window, PO Box 49746,
receives an action, an event (OWSHR_10.ZIP). Colorado Springs, CO 80949-9746
is fired. In addition to the online Phone: (800) 531-0403 or
Sax Basic Engine Pro also Help, support is available via (719) 531-0403
works with multiple mod- CompuServe, America Fax: (719) 531-0403
ules in macro code. Users Online, Internet, or US E-Mail: Internet: 75236.3243-
can create complex macros Mail. OWShare requires @compuserve.com
and customize common dia- Windows 3.1 or Windows CIS Forum: GO WINUTIL
log boxes. 95, and Delphi. Web Site: http://www.openwindow.com
4 October 1996 Delphi Informant
News EDS Chooses Delphi to Develop Case Tracking System
Scotts Valley, CA —
Electronic Data Systems
Integration Services contract,
selected EDS to help convert
system is scheduled to begin
full deployment in 1997.
L I N E Corp. (EDS) has selected a mainframe case tracking For more information visit
Delphi to develop a system to a client/server EDS’ Web site at
October 1996 client/server-based Case architecture, and develop a http://www.eds.com.
Activity Tracking System new prototype system for Borland Ships Trial,
(CATS) for the National legal case tracking.
Labor Relations Board EDS used Delphi and the Learning, and
(NLRB). The NLRB will use Borland RAD Pack to create Low-Price Editions
CATS to manage over 40,000 preliminary CATS screens of Delphi
new legal cases annually, as currently being demonstrated Scotts Valley, CA — Borland
well as to report statistics. to NLRB staffers around the has released three new ver-
The NLRB, under the country for design review sions of Delphi, including a
Defense Enterprise and user input. The CATS trial edition that users can
download free from Borland
Borland Presents Golden Gate Internet & Online; a “Learn to Program
Intranet Strategy with Delphi” package for stu-
Anaheim, CA — In a With its Golden Gate dents and beginning pro-
keynote presentation to strategy, Borland hopes to grammers; and an easy-to-use,
ROI Systems Chooses more than 1,500 developers assist users building low-price version of Delphi 2
Delphi at the 7th annual Borland Internet-based applications for developers to evaluate.
ROI Systems, Inc., an Developers Conference, by integrating existing
Enterprise Resource Planning The “Learn to Program with
(ERP) software supplier, Borland presented its overall investments in client/server Delphi” package includes the
has selected Delphi to create Internet strategy and architectures with the
the interface for its next 16-bit Delphi 1 software,
generation of integrated demonstrated new technolo- emerging Internet technolo- Teach Yourself Delphi in 21
business systems. gies, including products gies. The strategy includes
ROI Systems is changing its Days, Computer-Based
GUI presentation, instead of from the planned acquisition combining object-oriented Training (an online curricu-
updating their ASCII screens of Open Environment Corp. development tools, Delphi,
with screen scraper products lum), and new sample appli-
that give a GUI appearance, Referred to as the Golden Latte, IntraBuilder, Borland cations with source code.
but still require multiple steps Gate initiative, Borland C++, as well as Open
to move from one function The new special version of
to another to complete a plans to merge both phases Environment’s Entera tech- Delphi 2 has been re-designed
transaction. ROI’s new client of its Internet plans: release nology and related Internet
interface will in some cases with features making it more
combine 15 separate func- Internet-enabled versions of products. accessible to new developers.
tions on one screen. its existing products; and For more information, con-
For more information, call: In addition, Borland has
(800) 544-7849, e-mail: then add Intranet solutions tact Borland at (408) 431- included new functionality to
[email protected], or visit for the workgroup market 1064 or visit Borland Online
ROI’s Web site at show how Delphi can be an
http://www.roisysinc.com. segment. at http://www.borland.com. add-on tool for Visual Basic
and C++ developers. This ver-
VCL Contest Winners Announced sion of Delphi 2 includes
Scotts Valley, CA — SAMS subscription to Delphi Teach Yourself Delphi 2 in 21
Publishing and Borland Informant. Days, an online Visual Basic-
Press have announced the VCL entries were judged to-Delphi command refer-
winners of Delphi by representatives from ence, and an online Delphi
Informant’s VCL Contest. Borland International, reference for C++ developers.
Due to the extended dead- SAMS Publishing, and The Delphi trial version is
line and delay in posting Informant Communications available from Borland
results, SAMS and Borland Group, Inc. for originality Online at http://www.bor-
are awarding an extra book and functionality. Entries land.com/delphi20/ or on
to each of the winners. were received from around CD (shipping and handling
Database Developer’s Guide the world in each of the five charges additional).
with Delphi 2 will be award- categories. The “Learn to Program with
ed in addition to Delphi The winner in the Delphi” package and the low-
Developer’s Guide, Second Internet/Communications price version of Delphi 2 are
Edition as part of the VCL category was Jower Garcia available now. “Learn to
contest prize package. Top Toppin of Venezuela. Program with Delphi” is
winners will also receive US$49.95; the modified of
“VCL Contest Winners Announced”
US$100 and a six-month continued on page 6 Delphi 2 is US$99.95.
OLE Automation
Controlling One Application with Another
var
MyServer: Variant;
MyServer := UnAssigned;
Creating an Automation Server In Class Name, enter a name for your derived class. Set OLE
Delphi 2 makes it easy to create automation servers from Class Name to the complete name that you want to register
your existing applications and DLLs. This allows you to as the automation server in the Registry. Delphi will auto-
expose some or all of your existing application’s features to matically complete this for you, based on your chosen class
automation clients. name, but you can change it if you wish.
The four steps to convert an existing application into an In Description, provide a description of the automation
automation server are: object. This description will also be stored in the Registry.
1) Add a unit to your project that derives a new class from Finally, set the Instancing. Normally, applications use a
the TAutoObject class. Single Instance value, and DLLs use Multiple
2) Within this derived class, declare any methods you want Instance.
to expose to automation clients within the automated
section. Once you accept the Automation Object Expert, it will
3) Within this derived class, declare any properties you want create and add a new unit to your project. Figure 6 is an
to expose to automation clients within the automated example of how this unit will appear. (For more informa-
section. These properties must use access methods to read tion on registering new applications, see the sidebar
and write the properties. “Registration Note” on page 11.)
4) Implement the class and access methods.
In this generated code, there are two critical elements to
This process is demonstrated with the project note. First, the expert has inserted a registration procedure
AUTOEX.DPR, an automated version of the local table that adds the server to the Registry. This procedure is
packing utility described in the article “BDE Basics” [see called from the unit’s initialization section. After you ini-
the March 1996 Delphi Informant]. tially run this project, do not modify this code.
If you want, you can include the dispid directive, followed The GetTabName, SetTabName, and PackTab methods appear
by an integer. This will register the method using the spec- as they would in any object to which you are adding meth-
ified OLE Automation dispatch ID. If you do not include ods. However, the PackTab method deserves special attention.
dispid, the compiler will automatically assign one.
PackTab is called by the automation client to produce an
Declaring Server Properties action by the server. From within PackTab you can set
You declare properties that you want to expose to your properties of objects within the server application, call
automation clients by declaring them within the automated object methods, and execute functions and procedures.
The only way I can successfully register an existing application This is demonstrated by the code in the project
is by performing the following steps. I begin by creating a new, CALLAUTO.DPR. The following shows how to open the
blank application. After saving this application to a directory, I OLEAutoDemo automation server, set the TabName prop-
add the automation unit, and again save the application. I then erty of the server, and then call the PackTab method, all
run the application to register it. from a button’s OnClick event handler:
Form1 from this application, and add each unit from my exist- uses OleAuto;
ing application that I want to convert into an automation serv-
{$R *.DFM}
er. When I am done, I have a new application with all the units
and forms from my existing application, in addition to the OLE procedure TForm1.Button1Click(Sender: TObject);
automation unit. Furthermore, this application is a registered var
PackServer: Variant;
automation server. begin
PackServer := CreateOLEObject('OLEAutoDemo');
PackServer.TabName := Edit1.Text;
I want to point out that this may not actually be a problem PackServer.PackTab;
with Delphi. Instead, it may be because of the large number of PackServer := UnAssigned;
end;
example applications I create while writing training materials. I
may have damaged my Registry, or somehow affected Delphi in
a way that prevents it from performing correctly. I haven’t tried Conclusion
re-installing Delphi 2.01, but doing so may clear up this prob- Delphi makes it easy to convert any of your Delphi 2 appli-
lem. If you can add an automation unit to an existing applica- cations into automation servers. By doing so, you enable
tion, you do not need to worry about the above steps. other programs, even those not written in Delphi, to leverage
the features of your existing applications. ∆
— Cary Jensen
The demonstration projects referenced in this article are available
on the Delphi Informant Works CD located in
INFORM\96\OCT\DI9610CJ.
In this example, the PackTab method calls the method
Form1.PackTable, passing to it the name of the table
stored in the TabName property. Cary Jensen is President of Jensen Data Systems, Inc., a Houston-based database
development company. He is author of more than a dozen books, including
Delphi In Depth [Osborne/McGraw-Hill, 1996]. He is also Contributing Editor of
Delphi Informant. You can reach Jensen Data Systems at (713) 359-3311, or via
CompuServe at 76307,1533.
By Ray Lischner
Classy DLLs
Exporting a Class and Its Methods from a Delphi DLL
This article describes how to export a class At run time, any application or DLL can
from a DLL, import a class in an applica- load another DLL, calling its functions
tion, and use objects that cross module and procedures. What makes DLLs so
(application and DLL) boundaries. attractive is that Windows keeps a single
copy of the DLL’s code in memory, no
There are two facets of mixing classes, matter how many applications load the
objects, and DLLs: same DLL.
the static, compile-time nature of export-
ing and importing classes, and For example, a spelling checker implemented
the dynamic, run-time nature of using in a DLL can be used by many applications,
objects and Run-Time Type Information such as a word processor, spreadsheet, or e-
(RTTI). mail tool. If all three applications use the
same spelling checker, a single copy of the
The general principles for dealing with these spelling checker’s code is loaded in memory,
aspects of DLLs are the same for Delphi 1 not three. And with a little more work, the
and 2, but the syntax can be different. This spelling checker can share its data as well,
article addresses the problems and solutions using a single copy of the dictionary, saving
for both versions. even more memory.
Exporting a Class
As we’ve seen, Delphi makes it easy to use normal functions
and procedures stored in a DLL. Classes and objects, however,
work slightly differently. This section describes how to export
a class from a DLL, and import a class into an application.
type
TSpellChecker = class(TISpellChecker)
...
public
function OpenDictionary(Path: PChar): Boolean;
override;
procedure ClearDictionary; override;
function CheckSpelling(Word: PChar;
Suggest: TSuggestProc): Boolean; override;
end;
menting the spelling checker, where the application and DLL Figure 9: InitSpellChecker creates and returns a spelling check-
share the abstract interface class. er object.
How Exported Methods Work You can think of the VMT as a means of exporting methods
An application can call the exported method of the DLL which replaces the table of indexes and names. In the case of the
because every class has a table of virtual method pointers. spelling checker, the only routine listed in the exports statement
This Virtual Method Table (VMT) contains a pointer to the is InitSpellingChecker. All other exported routines are methods.
code for every virtual method declared by a class and its base
classes. As such, the VMT for the interface class, Memory Management
TISpellChecker, contains a pointer for each virtual method Not all the methods of an interface class need to be exported.
declared by the class. When the DLL creates an instance of Sometimes it’s useful to declare a static method to take
the concrete class, TSpellChecker, it also creates a VMT that advantage of the fact that such a method is implemented in
points to all exported methods of the class. the application, and isn’t called across a module boundary.
The VMT for TSpellChecker originates in the DLL, so its The most notable example is the Free method, which frees
method pointers all point to the methods in the DLL. When the memory for an object. This method is declared static by
the application calls any virtual methods, it knows that it must the TObject class. Every class inherits from TObject, there-
get the method pointer from the VMT. The application is fore every class inherits the Free method, which calls the
unaware of whether the VMT comes from the application or object’s destructor, Destroy. Destroy is virtual, so the applica-
DLL. Figure 10 illustrates how an application calls a virtual tion gets the method pointer from the VMT, meaning the
method. DLL’s destructor is called from the application.
type
TISpellChecker = class
public
procedure Free;
procedure Release; virtual; export;
{$ifdef WIN32} stdcall; {$endif}
...
end;
{ Call the destructor from the DLL. } Figure 12: Run-Time Type Information.
procedure TISpellChecker.Release;
begin
Destroy;
end;
type
TISpellChecker = class
public
function IsEnglishSpellChecker: Boolean;
virtual; export;
{$ifdef WIN32} stdcall; {$endif} abstract;
function IsFrenchSpellChecker: Boolean;
virtual; export;
{$ifdef WIN32} stdcall; {$endif} abstract;
end;
Version Control
Version control presents a similar problem. Imagine what
happens if there is a new version of the application with a
different interface for the TISpellChecker class. Suppose a
problem arises when installing the new DLL. In this case, the
VMT for the TISpellChecker class in the DLL doesn’t match Figure 16: Conflicting VMTs because of different versions of
the VMT in the application. TISpellChecker.
As another example, let’s say that adding support for multiple it is also necessary to change the expected version number.
languages required the addition of several new methods. The When the application obtains a TISpellChecker object
application and DLL must have compatible VMTs. from the DLL, it checks the version numbers, and if they
Otherwise, the application can easily call the wrong method don’t agree, the application reports the appropriate error
(see Figure 16). message (see Figure 17).
To reiterate, Delphi has no way of knowing if the two ver- TInterface Class
sions of TISpellChecker have identical interfaces by looking at To make your job a little easier, Delphi defines the
the class names. It is your responsibility to ensure the applica- TInterface class in the VirtIntf unit. This class declares the
tion and DLL are using the same version of the Release and GetVersion methods, as described in this article.
TISpellChecker class.
Although the TInterface class is undocumented, every ver-
To solve this problem, define a GetVersion method to sion of Delphi 1 and 2 has the source code for the VirtIntf
return the current version of TISpellChecker. A constant, unit. However, each version’s source file is in a different
ExpectedVersion, records the version the application directory. Try looking in \DOC, \SOURCE\VCL, or
expects. When you change the interface of TISpellChecker, \SOURCE\TOOLSAPI. Your interface class must imple-
By Keith Wood
Saved by an Expert
An Expert to Generate a Screen Saver Application
elphi 1 provides many ways for developers to reuse code with mini-
D mal effort. These include the components we drop on our forms and
the other objects that Delphi defines. In addition, Delphi 1 provides us with
the Gallery, a container of templates and experts for forms and projects.
Templates allow us to copy static code that .EXE. It’s expected to interact with the
we have previously developed, while experts Windows Desktop through the Control
allow us to interact with the code generation Panel, and display its configuration or
to customize it for our needs. In this article, saver screen when requested. It should also
we’ll develop a Delphi expert to generate a define a title appearing in the list of avail-
project framework. To make this worthwhile, able screen savers.
we need an application that’s interesting,
hopefully useful, and not too trivial. A screen From the Command Line
saver seems to fit the bill nicely — it’s a fairly The program determines which mode it
simple application with code that is common should start in — configuration or display —
(indeed required) across all screen savers, and based on command line parameters passed to
features several extras that we can have our it. For example, if a /S switch is present, the
expert automatically include or exclude. screen saver opens its display window to
cover the entire screen and begins drawing
Screen Saver Basics on it. The screen saver must then monitor
Screen savers serve two functions on a com- the system for any user activity, via the
puter system: mouse or keyboard, which then causes it to
1) They are used to protect the monitor close itself.
from phosphor burn that can be caused
by leaving an image on the screen too Conversely, if a switch isn’t present, the pro-
long. This is not really a problem any- gram opens its configuration form. This
more with newer monitors. allows the user to control the display screen’s
2) They can be used to hide and possibly pro- appearance. These parameters can be stored
tect information on the screen from other in an .INI file for use when the display
people when the system is unattended. screen appears. Typically the form features a
Test button that the user can press to view
all right — maybe there’s a third function: the effects of the configuration selections.
3) Entertainment! For example, some screen
savers play vignettes of a longer story, or Some screen savers allow users to specify a
show selections from collections of graph- password to prevent unauthorized users from
ics or cartoons. halting it and returning to the original pro-
grams. To implement this, two additional
A screen saver is really just an ordinary forms are required. One to allow the pass-
program that has been renamed with an word to be changed, and another to ask for
.SCR extension instead of the normal the password when the saver is running. An
customize it. Figure 1: This project source code allows the screen saver to
swap main forms based on a command-line parameter.
Forms: First Things First
The application should create the configuration and display and can only be present in the project source itself — not one
forms automatically. In configuration mode the user will of the units. Only one of these entries can exist, per project.
probably want to test the saver with the new parameters,
requiring the display screen to be available. While in display Here’s a screen saver name example:
mode, we must also create the configuration form to provide
{ Description that appears in drop-down list }
access to the user parameters. {$D SCRNSAVE Dropping Off}
Regardless of the mode we’re in, the configuration form pro- The configuration form loads the screen saver parameters from
vides a single point for loading the parameters. Normally, their location — in our case from an .INI file — and displays
each form that is auto-created is constructed in the code of them onscreen. These can be updated and saved back to the file
the project source, with the main form as the first to appear. for later use. The parameters themselves depend on the screen
This form is made visible while the others are hidden. saver being implemented and cannot be automatically generated.
However, we want different forms to be the main form in
each of the screen saver’s modes. The form contains three standard buttons:
1) OK saves the parameters and closes the form,
To accommodate the different ways of invoking the screen 2) Cancel closes the form without saving, and
saver we must modify the code in the project source. We want: 3) Test makes the display form visible.
the display form to be the main form when the program
is invoked with a /S parameter, and If the screen saver is password protected, the form also con-
the configuration form to be the main form at all other tains a check box controlling whether to ask for the pass-
times. word, and a button allowing the user to enter a password.
Therefore, depending on the mode, we must alter the order The display form is a borderless window that maximizes on
of creation of these two forms. creation and stays on top of all other windows. It contains a
single Timer component that allows for the periodic updat-
The code in Figure 1 shows this form selection based on the ing of the display for the screen saver. This Timer is enabled
mode. It also includes a check to ensure that only one copy when the form is shown, and disabled when it’s hidden.
of the screen saver program is running. The hPrevInst variable
contains the handle of the previous instance of this program. To allow the form to monitor all user activity, we create a
If it’s zero, then there is no previous instance and we can con- customized message handler and direct all messages for the
tinue. If it’s non-zero, the program takes no further action form to it using:
and terminates.
Application.OnMessage := DeactivateScreenSaver;
The screen saver’s title is also encoded in the project source.
The {$D} compiler directive is used to insert the name into Within this message handler, we check if the message relates to
the executable. It must appear with an identifier of SCRNSAVE a mouse movement, mouse click, or key press. If one of these is
StartMonitoring; { Monitor all messages } The timing of this activity is critical. We must capture the
end;
screen before the screen saver is made visible or we’ll only get
an image of ourselves. The place to capture the screen, there-
Figure 2: The message handler for the display screen.
fore, is in the OnCreate event of the form. Unfortunately, at
detected, the user has returned and the screen saver is termi- this stage the form itself has not yet been created and thus
nated. Figure 2 shows the complete message handler code. cannot be referenced. This means that we cannot transfer the
screen image directly to the Canvas of the form.
The message type is available through the handler’s
Message property. Small mouse movements — less than For this reason, we create a bitmap to hold the image and
five pixels — are ignored, as it’s assumed that these are not copy it there instead. This is then transferred to the form
intentional. The bHandled parameter is set to indicate the first time that its OnPaint event is invoked using the
whether this handler responded to the message sent. Note canvas’ Draw method. This also means that the screen
that the call to StopMonitoring at the beginning deactivates image is available throughout the screen saver’s execution.
this message handler for the duration of its execution. Of course, both the device context and the bitmap must be
freed (when possible) to return their resources to Windows.
And the Password Is ...
If the screen saver is password protected, we must ask for Now that we have all the pieces necessary to build a generic
the password when we detect the user’s return. This is screen saver, we can incorporate them into an expert to auto-
done by the fmPassword form that is created and displayed mate the building process.
in response to any user activity. If the password entered
matches that held in the configuration form, then the Delphi Experts
screen saver terminates as before. Otherwise the message Delphi experts are a mysterious breed. Online Help contains
handler is reactivated and the screen saver continues. entries that describe how to use them, but it does not con-
Copying this project and modifying the various pieces to The string resource containing the code snippets must be
implement our own expert results in the ScrSavEx project file located and made available for use within the program. This
that accompanies this article. is done by the LoadResource and FindResource Windows API
functions, which copy the named resource into memory and
The application expert (the one we’re copying) is actually produce a handle to it. The resource is then locked to prevent
implemented in the APP.PAS file. The interface procedure, it from being overwritten in memory, and a pointer to it is
the one invoked by the Execute method of the expert class, returned so that we can access it directly.
{ Generate the source for the project file, { Generate the .DFM file for the screen display form }
with the specified name } procedure GenerateDisplayFormFile(
function GenerateProjectSource( fmExpert: TfmScrSavExpert);
fmExpert: TfmScrSavExpert): TFileName; var
var sFormName: TFileName;
stmSourceFile: TFileStream; stmFormFile: TFileStream;
begin stmTextFile: TMemoryStream;
{ Create the full path and name for the project file } begin
Result := fmExpert.edPath.Text; { Create the full path and name for the screen
if (Result > EmptyStr) and display form resource file }
not Result[Length(Result)] in [':', '\']) then sFormName := GetFileName(fmExpert.edPath.Text,
Result := Result + '\'; iDisplayFile, sResourceExt);
Result := Result + fmExpert.edName.Text + sProjectExt;
{ Check whether file can be created }
{ Check whether file can be created } CheckFileOverwrite(sFormName);
CheckFileOverwrite(Result);
{ Create text version of file and write object
{ Create the file and write the code to it } descriptions to it }
stmSourceFile := TFileStream.Create(Result, fmCreate); stmTextFile := TMemoryStream.Create;
try try
WriteSnippetFormat(stmSourceFile, csProjectBegin, WriteSnippet(stmTextFile, csDisplayForm);
[fmExpert.edName.Text]); if fmExpert.cbxCapture.Checked then
if fmExpert.cbxPassword.Checked then WriteSnippet(stmTextFile, csDisplayFormCapt);
WriteSnippetFormat(stmSourceFile, csProjectPwd, WriteSnippetFormat(stmTextFile, csDisplayFormEnd,
[LoadStr(iPasswordFile), [fmExpert.spnInterval.Value]);
LoadStr(iChgPswdFile)]); { Return to the beginning of the file for conversion }
WriteSnippetFormat(stmSourceFile, csProjectEnd, stmTextFile.Position := 0;
[LoadStr(iDisplayFile), { Create resource version of file and convert
LoadStr(iConfigFile), from text version }
fmExpert.edTitle.Text]); stmFormFile := TFileStream.Create(sFormName, fmCreate);
finally try
stmSourceFile.Free; ObjectTextToResource(stmTextFile, stmFormFile);
end; finally
end; stmFormFile.Free;
end;
finally
Figure 4: The function to generate the project source for the stmTextFile.Free;
screen saver and return the file name to the caller. end;
end;
The code snippets consist of text blocks that are each ter-
Figure 5: The procedure to generate the .DFM file for the dis-
minated by a vertical bar ( | ). This delimiter is searched play form.
for within the resource, with each section being pointed to
by the next position in the array. The delimiter is changed To generate the .DFM files, we capitalize on the fact that
to a null character so the array elements appear to be they can be represented as a “straight” ASCII text file. To see
PChars. Note that the for loop controlling this assignment this we can select File | Open File from the menu, change the
uses the Low and High functions to avoid hard-coding the file type to .DFM, and load any form file. Delphi then pre-
first and last values in the TCodeSnippet type. sents us with a list of all the objects comprising the form with
those properties that do not have default values being set.
The code for use in generating the basic screen saver can Objects that are contained within another on the form
be taken from an actual screen saver project. Code from appear within the definition of that object. This file can be
each of the units is combined into a single text file with altered as text and is then converted back into its normal
vertical bars terminating the different sections, including binary format by Delphi.
the optional parts. During the generation process, place-
holders act as substitutions for variable values. This is We can do the same with the ObjectTextToResource procedure.
done using the StrLFmt function wherein string positions It takes two parameters, being streams that connect to the
are marked by %s and integer values by %d. text source for the file and another for the binary output.
Figure 5 is the Object Pascal code that generates the .DFM
Loaded with Code file for the display form.
With the code snippets loaded, the code can now be gen-
erated. First we produce the project source by construct- The code snippets to construct the .DFM files are included
ing its name from the details (provided by the user) and in the string resource as described earlier. Each is opened in
then writing the necessary code from the code resource its text format, and cut and pasted into the combined text
into that file, substituting values where appropriate (see field of all snippets.
Figure 4). Then each of the program units follows, gener-
ating the Object Pascal source and the corresponding For all files being generated, we check that they do not
.DFM file. already exist, and ask permission to overwrite them if they
In the code for the display form, we add the functionality The demonstration files referenced in this article are available on
described above: to drop sections of the screen image down the Delphi Informant Works CD located in
and off the screen. To start, add two variables to the private INFORM\96\OCT\DI9610KW.
section of the form. These record the current section being
dropped and its position on the screen. Update the
FormShow code to initialize these variables (see Figure 9). To
alter the speed of the section’s movement we set the Timer’s Keith Wood is an analyst/programmer with CSC Australia, based in Canberra.
Interval property from the configuration’s speed parameter (a He started using Borland’s products with Turbo Pascal on a CP/M machine.
larger value produces a smaller interval). All this is done in You can reach him via e-mail at [email protected] or by phone
the FormShow event so that it is re-initialized each time the (Australia) 6 291 8070.
screen is displayed. This may happen several times during
configuration, with different parameters each time.
By Robert Vivrette
ike any programming language, Object Pascal has a few features that
L are obscure and not very well documented. One such feature is Delphi’s
use of the Controls and Components arrays. These arrays are internal list
structures used to keep track of components and their relation to one
another. This article clarifies their function and use in Delphi programs.
First, let’s take a brief look at these arrays. Show ...
The Components array is a TList structure As they say, a picture is worth a thousand
that is a part of TComponent and all of its words. Here then, is a simple application that
descendants. Similarly, the Controls array is a illustrates the relationship between the Controls
TList structure, and a part of TWinControl and Components arrays (see Figure 1).
and its descendants. Therefore all descen-
dants of TComponent will have a Components By clicking on an empty area of the main
property, and all descendants of form, this sample application shows which
TWinControl will have a Controls property. items are in the form’s Controls array and
Because most visual controls in the Delphi which ones are in its Components array. As
VCL are descendants of TWinControl, you’ll you can see, the Components array holds all
find most have both properties. items that are physically on the form, while
the Controls array holds only those items
that are immediate children of the form.
lowing code:
Every time the Panel is clicked, each of the controls within it
procedure TForm1.FormClick(Sender: TObject); are moved one pixel to the right.
var
I : Integer;
begin Conclusion
for I := 0 to ComponentCount -1 do The Controls and Components arrays can be very power-
if Components[I] is TPanel then
(Components[I] as TPanel).Color := clLime;
ful, if you know how to tap their power. Unfortunately,
end; the Delphi online Help files are a bit sparse in their dis-
cussion of these structures. Hopefully this article has
With this code, when the mouse is clicked on the form, cleared up some of the confusion, and will help you
all Panels are changed to Lime Green. Not a particularly understand how to best access and manage these arrays in
useful example, but it illustrates how you can easily filter your Delphi programs. ∆
through controls on a form and selectively make changes
to them. The demonstration project referenced in this article is available
on the Delphi Informant Works CD located in
The Controls array can also achieve some interesting effects. INFORM\96\OCT\DI9610RV.
You may want to move all controls within a Panel a relative
amount, but don’t want to disturb other controls outside the
Panel. The following code accomplishes this: Robert Vivrette is Technical Editor for Delphi Informant. He has worked as a game
designer and computer consultant, and has experience in a number of program-
ming languages. He can be reached on CompuServe at 76416,1373.
By Bill Todd
Versioning:
The InterBase Advantage
An Examination of Database Concurrency Models
Let’s consider the following scenario. An executive requests When an update transaction commits, the DBMS checks if
the total value of the company’s inventory by warehouse. there are transactions with lower transaction numbers that
While the query is scanning the inventory table, a user are still active. If so, a new version of the record is created
moves a pallet of platinum bars from warehouse A to that contains the updated values. Each version also contains
warehouse B and commits the transaction. It’s possible for the transaction number of the transaction that created it.
the query to count the platinum in both warehouses, thus
producing an erroneous inventory valuation report. When a read transaction begins, it retrieves the next transac-
The question becomes, “Which updates should a read transac- tion number and a copy of the transaction inventory pages
tion see, and when should it see them?” This is what the transac- that show the status of all uncommitted transactions. As a
tion isolation level controls. There are three basic isolation levels: read transaction requests each row in a table, the DBMS
Dirty Read — This isolation level allows any record in the checks if the transaction number for the latest version of the
database to be read whether or not it has been committed. row is greater than the transaction number of the transaction
Read Committed — This level allows read transactions to that’s requesting it. The software also checks if the transac-
see only those changes that were committed. tion was committed when the read transaction started.
Repeatable Read — A repeatable read allows the read
transaction to immediately see a snapshot of the database Let’s say the transaction number of the row’s latest version is
when the transaction began. Neither committed nor greater than the requesting transaction’s number; or, the
uncommitted updates that occur after the read transac- transaction which created the latest version was active when
tion starts will be seen. the read transaction started. With either scenario, the DBMS
looks back through the chain of prior versions. The software
Note that the TransIsolation property of Delphi’s continues until it encounters a version with a transaction
TDatabase component allows you to set all three of these number that is less than the transaction number of the trans-
isolation levels. However, this doesn’t mean that your serv- action that is trying to read the row, and whose transaction
er supports the isolation level you have selected. In addi- status was committed when the read transaction started.
tion, if you’re using an ODBC driver, the driver must also
support the isolation level you set. Search on When the DBMS finds the most recent version that meets
“Transactions | Transaction Isolation Levels” in the Delphi these criteria, it returns that version. The result is repeatable
online Help to view a table showing what each of these read transaction isolation without preventing updates during
isolation levels maps to on your server. the life of the read transaction.
In the example above, you need a repeatable read isolation Consider the following example of a row for which four ver-
to ensure the accuracy of your inventory valuation report. sions exist:
The problem is the price you must pay to get repeatable
read in a database with a locking architecture. With the Tran=100 (status=committed)
Tran=80 (status=active when read started)
locking model, the only way to ensure that data does not Tran=60 (status=rolled back)
change during a long read transaction is to prevent any Tran=40 (status=committed when read started)
updates from occurring until the read transaction ends. In
many situations, the effect on users of stopping all updates Assume that a read transaction with transaction number 90
for the duration of a long read transaction is unacceptable. attempts to read this row. The read transaction will not see
the version of the row created by transaction 100 because the
Versioning update that created this version took place after transaction
Versioning is another model for concurrency control. It over- 90 began. Also, transaction 90 cannot read the version creat-
comes the problems that locking model databases have when ed by transaction 80, even though it has a lower transaction
the environment consists of a mixture of update and long read number. This is because transaction 80 isn’t yet committed.
transactions. This model is called the versioning model. To date, Although the version for transaction 60 still exists on disk,
InterBase is the only DBMS to use the versioning model. transaction 60 has rolled back — and rolled back versions are
Note that in this example, transaction 80 is not allowed to Using locking, Fred reads the balance that places a read
commit. When transaction 80 attempts to commit, the lock, and then commits a withdrawal that places a write
DBMS will discover that transaction 100 has committed, and lock during the update. Ethel reads the balance, which
transaction 80 will be rolled back. attempts a read lock, but must wait because of Fred’s write
lock. Fred cancels the transaction before committing. This
Advantages of Versioning rolls back and releases the write lock. Ethel can now read
For a more complete understanding of how the locking and and get the correct balance.
versioning models compare, you must examine two things:
the types of concurrency conflicts that can occur in a multi- Under versioning, Fred withdraws the money. This updates
user database, and how each model behaves in each case. the balance and creates a new uncommitted version. At her
machine, Ethel reads the balance, but it does not reflect
The following examples assume that the locking model uses a Fred’s uncommitted withdrawal. Fred rolls back, so the ver-
shared read lock and an exclusive write lock to implement sion showing the withdrawal is marked rolled back. This
optimistic locking. Multiple users can place read locks, but illustrates a performance advantage of versioning because
no user can place a write lock if another user has either a read Ethel does not have to wait to read the balance.
or write lock. If one user has a write lock, another user can
neither read nor write the row. This is typical of databases The following is a different example, but it’s the same as
that use locking architecture. our earlier scenario of moving platinum from one ware-
house to another:
Consider the case where a husband and wife go to different Fred requests the total of all accounts.
ATMs at the same time to withdraw money from their joint Ethel transfers money from savings to checking while
checking account. Without concurrency control, the follow- Fred’s transaction is running.
ing sequence of events occurs: Fred receives the wrong total. The analysis of the data is
Fred reads the account’s balance as $1,000. inconsistent because the data’s state was not preserved
Ethel reads the account’s balance as $1,000. throughout the life of the read transaction.
Fred posts a $700 withdrawal.
Ethel posts a $500 withdrawal. Under locking, Fred requests a total of all accounts, thereby
placing a read lock. Ethel transfers money but cannot place a
At this point, the account balance is -$200 and the bank is write lock to commit the transfer because of Fred’s read lock.
not happy. This happened because without a concurrency Ethel must wait until the read transaction finishes. Finally,
control mechanism, Fred’s update is lost as far as Ethel is Fred gets the right total and releases the read lock, and
concerned. She never sees the change in the account balance. Ethel’s transaction can proceed.
However, under the locking model:
Fred reads the account’s balance, causing a read lock. Under versioning, Fred requests the total. At her ATM,
Ethel reads the account’s balance, also causing a read lock. Ethel transfers money from savings to checking, resulting
Fred posts his withdrawal, attempting a write lock that in new versions which Fred’s transaction does not see. Fred
fails because of Ethel’s read lock. gets the correct total and Ethel’s update is not delayed.
Ethel posts her withdrawal, attempting a write lock that
fails because of Fred’s read lock. Another variation of the repeatable read problem occurs if
you must reread the data in the course of the transaction.
A deadlock now exists. Hopefully, the DBMS will detect the For example:
deadlock and rollback one of the transactions. A query is started for all rows meeting certain criteria.
Another user inserts a new row that meets the criteria.
Under the versioning model, Fred reads the account’s balance Repeat the query and you will get one additional row.
and Ethel reads the account’s balance. Then, Fred posts his The appearance of this “phantom row” is not consistent
withdrawal, which causes a new version with a new balance within the transaction.
to be written. When Ethel posts her withdrawal, it’s rolled
back when the newer version is detected. With a database that uses the locking model, the only way
to prevent this inconsistency is to read lock the whole
A different problem occurs if a user does not commit a table for the duration of the transaction. Thus the
transaction. Let’s say Fred withdraws money from the sequence of events is:
account and this updates the balance. Ethel reads the balance Place a read lock on the table.
and Fred cancels the transaction before committing. Now Query for all records meeting certain criteria.
Ethel has seen the wrong balance. In this case, a dependency Another user attempts to insert a record, but is blocked
exists between the two transactions. Ethel’s transaction pro- by the table-level read lock.
Bill Todd is President of The Database Group, Inc., a Phoenix area consulting and develop-
ment company. He is co-author of Delphi 2: A Developer’s Guide [M&T Books, 1996],
Creating Paradox for Windows Applications [New Riders Publishing, 1994], and Paradox
for Windows Power Programming [QUE, 1995]; a member of Team Borland; and a
speaker at every Borland Developers Conference. He can be reached at (602) 802-0178,
or on CompuServe at 71333,2146.
By Douglas Horn
Return to Sender
The Lowly Sender Parameter
Can Make Applications Shine
TPanel1.Color := TObject(Sender).Color;
is fine if the code is always called by a TButton. However,
As TPanel’s ancestor type, TObject is perfectly valid in other this makes it troublesome to call the same event handler
respects; but it does not contain a Color property. Since from a corresponding menu item because the menu item
TObject contains no properties, developers cannot use it to causes an error each time it calls the event handler.
determine the properties of a wide range of objects. Thus, a
program can’t find the color of a TPanel, TLabel, or TForm The Sample Application’s Framework
all with the same TObject(Sender).Color code. The sample application provides the framework for a modu-
lar, easily-extendible application. It also demonstrates how to
In fact, these three component types all contain the Color make the simple Sender parameter do a lot of high-powered
property. However, because they do not share this property in work, and shows how to get around some of the limitations
common ancestor classes, programmers cannot easily create mentioned earlier.
one block of code to get the Color property from these vari-
ous types of objects. The simplest solution a developer could Most applications contain menu items and buttons that
use would be this: perform the same action. Delphi makes it simple to do
this by routing multiple events to a shared event handler.
if Sender is TPanel then If multiple buttons and menu items all use the same event
Brush.Color := TPanel(Sender).Color; handler, it’s simple to determine which control called the
if Sender is TLabel then
Brush.Color := TLabel(Sender).Color; procedure. However, it becomes more difficult to deter-
if Sender is TForm then mine, say, which menu item corresponds to the calling
Brush.Color := TForm(Sender).Color; event if the control was actually a button. The user may
obviously find that the Word Wrap button and the Word
Fortunately for developers, the Delphi designers added the Tag Wrap menu command are synonymous in functionality.
property. Tag is a little catch-all integer parameter that devel- However, to have Delphi understand this and update the
opers can use for whatever purpose they choose. What makes menu item’s Checked property — regardless of which con-
Tag especially useful is that it resides well up the inheritance trol was the Sender — takes some special programming.
tree in the TComponent class. Just two steps down the ladder
from TObject, TComponent is the ancestor of all controls. This The sample application contains four SpeedButtons, as well as
means that any of these objects will have the Tag property. main and pop-up menu items that correspond to each (see
Unlike the Color property cited earlier, the Tag property of any Figure 2). The buttons and menu items perform similar func-
object can be accessed with a simple line of code: tions, in this case, using a database to track the time spent on
TForm.Tag := TComponent(Sender).Tag;
(up or down). These procedures allow convenient spots for Figure 4: Referencing PushButton and ReleaseButton with the
messages to be handled. In the following example, the SpeedButtonClick procedure.
Caption of Label1 is set to report the event that occurred:
procedure TForm1.PushButton(Sender: TObject); event to the SpeedButton with the same Tag. This is han-
begin dled in the SpeedButtonClick handler:
Label1.Caption :=
TComponent(Sender).Name + ' was clicked.'; for A := 0 to ComponentCount-1 do
end; if Components[A] is TSpeedButton then
with Components[A] as TSpeedButton do
procedure TForm1.ReleaseButton(Sender: TObject); if Tag = TComponent(Sender).Tag then
begin
Label1.Caption :=
TComponent(Sender).Name + ' was released.'; This code snippet scans the Components array and examines
end; each component it finds. When a TSpeedButton is found, the
code compares this object’s Tag value with the Tag value of
If the Sender of the event is not one of the buttons, we the object that originally fired the message. If the values are
must determine which button to click. This is where the the same, then the code found the button that ultimately
Tag property and the Components array property come in. receives the event. Since the code has already determined
As you noticed in Figure 3, SpeedButton1 had a Tag value that it’s a TSpeedButton, this object’s up/down state can now
of 1. MainItem1 and PopupItem1 also have Tag values of 1. be toggled. Then, either the PushButton or ReleaseButton
Therefore, if any of the menu items are selected, it’s a sim- procedure is called with the selected SpeedButton passed as
ple matter of viewing their Tag values and transferring the the parameter as follows:
By Dan Miser
Delphi made access to the .INI file easy by Determining the Approach
wrapping almost all existing functionality of The first step in deciding how to create the
.INI files from the Windows API into an .INI-aware controls is to consider how
object named TINIFile. However, this still Delphi’s data-aware controls work. During
leaves a great deal of manual coding. The pro- Delphi’s development, Borland adopted a
grammer must read each item from the .INI standard in which each data-aware control
file and set the corresponding control with that is created as a descendant of its non-data-
value. When the user is done editing these aware ancestor, and any database access
controls, the programmer needs to save the needed would be added in every data-
value of each control back into the .INI file. aware control. For example, the standard
data-aware edit control, TDBEdit,
The entire process is very similar to the way descends from TCustomMaskEdit. It also
Delphi’s data-aware controls interface with has its own data-handling methods that
databases. In that model, the DataSource and interact with the database and various
DataField properties of the data-aware con- properties of the control. Therefore, the
trol determine the value of the data-aware .INI-aware controls should also descend
control. The programmer never needs to from their non-data-aware ancestors, and
worry about reading data from, or writing add methods to interact with the .INI file
data to, the database table again. By creating and control.
a set of .INI-aware controls to mimic the
behavior of the data-aware controls, the pro- An example best reveals the integration of
grammer will be freed from the mundane Delphi’s data-aware controls. Figure 1 shows
task of reading and writing individual .INI a DataSource, Table, and four DBEdit com-
file entries throughout the application. ponents. Note that the TableName property
is assigned in only one place, the Table com-
ponent. The DataSource component
becomes linked to that Table via the DataSet
property. This interaction allows the data-
aware controls to reference a database table
by assigning the appropriate DataSource
component in their DataSource property.
Open Architecture
Figure 7: The Transition Specification dialog box that displays
A CASE tool’s value increases proportionally to its usefulness for
when you double-click a transition line.
your project. Different projects and teams require varying
degrees of design documentation; one size never fits all.
Realizing this, MicroGOLD created a CASE framework to sup-
port popular methodologies, and opened its architecture.
MicroGOLD achieved this in two ways:
MicroGOLD created an OLE server. Using With Class’
OLE services, you can embed With Class CASE diagrams in
compound documents. With Class supports OLE2 in place
activation of CASE diagrams embedded in other documents.
With Class creates a parameter for each item that users
enter, and uses them in a simple text-replacement merge
tool to generate reports and code. Parametric programming
is the general term for this technique. As shown in Figure 9,
With Class calls its rendition Scripting.
he annual Borland Developers Conference (BDC) tends to be a harbinger of the company’s stand-
T ing each year. In 1994, a rather undisciplined keynote address by former CEO Phillipe Kahn typi-
fied the scatterbrained nature of the Borland of the early 1990s. 1995 was highlighted by a return to
respectability for the company, along with unbridled enthusiasm for the newly-released Delphi. This
year’s BDC, held in Anaheim in July, showcased Borland’s emerging technology in areas where the
software industry is rapidly moving — Internet/intranet and multi-tier architectures. To sum it up, I’ll
discuss four main impressions I have of BDC ’96.
Borland is well positioned for the desktop and two-tier client/server Delphi is maturing in the market-
emerging Internet marketplace. Its models, Delphi 97 aims to bring place. The task of establishing itself
new Web-related products — Latté multi-tier application servers, COM in a market ruled by Visual Basic
and IntraBuilder — drew a great (the Component Object Model), and PowerBuilder has been daunt-
deal of interest and enthusiasm at and Internet deployment into the ing, but Delphi is making definite
the conference. If this duo ships mainstream. You’ll learn more about inroads. Not only was this the
promptly, Borland can take a tech- Delphi 97 next month, but suffice largest BDC ever, of the 2,500 par-
nological lead in the Internet tools to say that as application architec- ticipants, 75 percent of them
market. Additionally, Borland is tures become more complex, Delphi attended Delphi sessions. Another
working closely with Sun and will look to encapsulate such tech- sign of a product’s acceptance is
Netscape on several Web technology nology into its environment. employment opportunities. While
fronts. These relationships have pro- newspapers continue to be dominat-
duced tangible results for Borland, The technology gap between soft- ed by PowerBuilder advertisements,
such as Sun’s adoption of Borland’s ware vendors and customers appears it was encouraging to see many
BAJA specification for the Java to be growing. By the nature of the more Delphi job postings than at
Beans Initiative, as well as business, software vendors will BDC ’95, including a representative
Netscape’s licensing of the Just-In- always be ahead of their customers of a Fortune 100 firm who intended
Time compiler for Navigator 3.0. on the technology curve. This year, to hire several hundred Delphi
Ironically, while firmly entrenched the chasm between Borland, developers during the conference. ∆
in the Sun/Netscape camp, Borland Netscape, and Microsoft and their
may have more to gain if Microsoft customers seems larger than ever. — Richard Wagner
succeeds in the Web wars, due to You have probably heard that an
their Windows orientation with Internet year is equal to three calen-
Delphi, IntraBuilder, and other dar months because of the rapid pace Visit the “File | New” home page at
products. of technology innovation in that http://www.acadians.com/filenew/-
market. While vendors race for the filenew.htm. In addition to down-
Delphi looks well poised to tackle leading edge, many customers are loading past articles, you can get the
the next generation of applications. still dealing with issues that are far latest tips and information from the
After working with Delphi 2, my more mundane. For example, in one world of software development.
immediate impression was “Where session I led on migrating to Delphi
could Borland possibly take this 2, the majority of developers in the Richard Wagner is Contributing Editor
product?” Besides minor enhance- audience were still building 16-bit to Delphi Informant and Chief
ments, how much more do you real- applications. Borland’s current Technology Officer of Acadia Software
ly need in a desktop development online survey of whether to develop in the Boston, MA area. He welcomes
environment? My horizons were a new 16-bit version of Delphi is a your comments at rwagner@-
broadened when I heard where they sign of the company’s realization that acadians.com, or on the File | New
are taking the next version, called the market is not moving as fast as home page at http://www.acadians.-
Delphi 97. Extending beyond the they’d like. com/filenew/filenew.htm.