Creating Lively Regions: February 2002, Volume 8 Number 2
Creating Lively Regions: February 2002, Volume 8 Number 2
Creating Lively Regions: February 2002, Volume 8 Number 2
ON THE COVER
REVIEWS
21
DeployMaster
23
InfoPower 3000
27
Addict 3.0
30
LockBox 2
32
Sound+Vision
FEATURES
8
On the Net
13
16
DEPARTMENTS
2
33
Delphi Tools
File | New by Alan C. Moore, Ph.D.
Delphi
T O O L S
New Products
and Solutions
Book Picks
XSLT Programmers Reference,
2nd Edition
Michael Kay
Wrox Press
ISBN: 1-861005-06-7
Cover Price: US$34.99
(938 pages)
www.wrox.com
ISBN: 0-201-73791-4
Cover Price: US$49.99
(503 pages)
www.aw.com/cseng
Delphi
T O O L S
New Products
and Solutions
Book Picks
XSLT
Johan Hjelm and Peter Stark
John Wiley & Sons
ISBN: 0-471-40603-1
Cover Price: US$49.99
(311 pages, CD-ROM)
www.wiley.com/compbooks
Applied XML
Alex Ceponkus and Faraz Hoodbhoy
John Wiley & Sons
ISBN: 0-471-34402-8
Cover Price: US$49.99
(474 pages, CD-ROM)
www.wiley.com/compbooks
Sound+Vision
Hot Spots / Component Development / Delphi 4-6
By Victor Hornback
Hot Spots
Part I: Building an Editor; Creating Events
hen you think of hot spots, whats the first thing that comes to mind? Spring Break
in Cancun, Mardi Gras in New Orleans, or maybe BorCon 2002 in Anaheim?
In Web pages, hot spots are regions mapped to an image that detect MouseMove and
MouseClick events, and that usually provide a link to another Web page. In a Delphi
application, you can use hot spots to give your application a more graphical means of
navigation or input. For example, you could use pictures as a type of selection menu, a
way to link the user to Web pages or online help, or a way to launch another form or
application. Once you understand what hot spots are and how they work, youll find it
easy to implement them in Delphi.
This article, and another next month, will take you
through implementing a custom hot-spot component. In Part I, youll learn how to create a hot-spot
editor. In Part II, Ill show you how to create and
capture the events necessary to make a hot spot. Youll
also learn some advanced component-writing tech-
Sound+Vision
procedure TForm1.Image1Click(Sender: TObject);
begin
ShellExecute(Handle, // Handle to parent window.
// Pointer to string that specifies operation.
'Open',
// Pointer to file or folder name.
'http://www.borland.com/delphi',
// Pointer to string that specifies
// executable-file parameters.
'',
// Pointer to string that specifies
// default directory.
'',
// Whether file is shown when opened.
SW_SHOWNORMAL);
end;
Okay, that was a neat little trick. But what if you want just Athenas
head to be the hot spot? (A more practical example might be a map
of the world, where each country on the map is a different hot spot.)
How do you do that? Obviously, these shapes are highly irregular, and
all your Delphi components are rectangular. Its not a very good fit,
but there is a solution.
Fortunately, the Windows API provides something named regions.
At its base level, a region is simply a dynamically allocated data structure that represents a shape definition. Regions can be mapped to
anywhere on the screen and can even be rendered. More importantly,
regions can define any shape imaginable and can detect the mouse
cursor being over them. So, all you have to do is map out the points
that define your shape (for instance, the outline of Athenas head) and
create one of these Windows regions.
Then, as you move the mouse around and click the mouse buttons,
youll ask the region if the cursor is over it, right? Yes, thats pretty
much it, but mapping Athenas head could be a bit of trick. Have you
ever tried counting pixels? Instead, create an editor that allows you to
5 February 2002 Delphi Informant Magazine
draw the hot spot by using the mouse. Then, you can use that editor
in a custom component that allows you to create a hot spot, and code
what happens when the mouse moves over it.
Sound+Vision
FCurrPt: TPoint;
//
FDrawing: Boolean;
//
FHotSpot: THotSpotRegion;//
FPicture: TPicture;
//
FPoints: THotSpotPoints; //
FPointCount: Integer;
//
FPrevPt: TPoint;
//
FRect: TRect;
//
From this definition, you can deduce that you will need to fill an
array of points with your editor, and keep track of the number of
points youve added. Then, using your array of points, you will be
creating an HRGN, or handle to a region. Add the following type
definitions to your HotSpotEditForm unit:
type
THotSpotRegion = HRGN;
THotSpotPoints = Array[0..99] of TPoint;
Sound+Vision
Its pretty simple stuff so far. Now, make use of those buttons you
added earlier. Define an OnClick event handler for each of the buttons, and code them as shown in Figure 7.
Now for the fun stuff: Draw a hot spot using the mouse. Naturally, you
will need to use the OnMouseMove and OnMouseUp events. You probably only want to draw on the Image component of your form, because
thats where your picture is displayed. Define MouseMove and MouseUp
event handlers for the Image component as shown in Figure 8.
Thats it! Youre done. Now compile it and take it for a test drive.
Make Athenas head into a hot spot (see Figure 9). (Its what every
Delphi programmer has been dying to do.)
Of course, you lose your hot spot as soon as you close the form.
You also havent provided any functionality for clicking on the hot
spot. What you need is a persistent component that can stream
your hot spot from design time to run time, and allow you to
define MouseOver and MouseClick events. The second article in
this two-part series will show you how to do just that. Youll be
entering the brave world of advanced component-writing techniques. The good news is that youve already done the hard part
in creating the hot-spot editor.
Conclusion
Now, lets get some housekeeping out of the way. Define OnCreate
and OnDestroy event handlers for your form and add the code shown
in Figure 5.
Now, youre ready to get down to the business of drawing a hot
spot. First, you can take care of the grunt work by creating three
procedures to help manage your array of points. Add the procedures shown in Figure 6 to the private declarations section.
Victor Hornback lives in Colorado Springs, CO, where he does Delphi consulting
work. Readers may contact him at victorhornback@yahoo.com.
On the Net
WebSnap / JScript / HTML / Delphi 6 Enterprise
By Corbin Dunn
his is the second of a two-part article series that demonstrates how to create an
online survey using the new WebSnap capabilities of Delphi 6 Enterprise edition. Part I article introduced WebSnap by comparing it to the Web Broker technology
available in previous versions of Delphi. It also described how to build the InterBase
database used by the sample application and the construction of the survey application itself.
<html>
<head>
<title>
<%= Page.Title %>
</title>
</head>
<body bgcolor="#FFFFFF">
<h1><%= Application.Title %></h1>
<h2><%= Page.Title %></h2>
<table cellspacing="0" cellpadding="0">
<td>
<% // This code was copied from the default
// script generated by WebSnap.
e = new Enumerator(Pages)
s = ''
c = 0
for (; !e.atEnd(); e.moveNext())
{
// Don't show the results page.
if (e.item().Name == 'ResultsPage')
continue;
if (e.item().Published)
{
if (c>0) s += ' | '
if (Page.Name != e.item().Name)
s += '<a href="' + e.item().HREF +
'">' + e.item().Title + '</a>'
else
s += e.item().Title
c++
}
}
if (c>1) Response.Write(s)
%>
</td>
</table>
On the Net
variables (such as Page and Response) come from, and what
methods and properties they have. Jim Tierney, a WebSnap architect at Borland, has created a comprehensive listing of everything
that you can do with server-side scripting. Its available at
http://community.borland.com/article/0,1410,27467,00.html.
Response and Page are Global Objects, accessible in any script. The
most important thing to remember is that Response.Write allows you
to output results to the generated HTML page from inside of script.
Next, create another text file named footer.html with the following content:
<P><i>WebVotes - Built with WebSnap and Delphi 6</i>
</body>
</html>
The purpose of using a footer and header is so you only have to change
one file to have your entire Web site show the results.
After you have created the header and footer, open the MainPg.pas unit
and select the MainPg.html tab from the bottom of the code editor. Delete
the default, generated script and add the contents from Figure 2.
First, notice the reference variables created, such as:
var survey = Modules.WebData.SurveyAdapter
These give us access to the adapters on the Web data module named
WebData. A key property on all adapters is Errors. You may recall the
following code, added to the OnExecute event handler of AddVoteAction:
except
on E: Exception do
AddVoteAdapter.Errors.AddError(E);
Its key to realize that what the item function returns depends on
what the Enumerator is enumerating through. In this example,
we are enumerating through survey.Errors, so e.item will return
AdapterErrorsType, which has a Message property.
Next, another enumerator is created with which to walk through all the
master records in the table SURVEY:
var surveyEnum = new Enumerator(survey.Records);
for (; !surveyEnum.atEnd(); surveyEnum.moveNext())
{
%>
<!--#include file="header.html"-->
<P>
Welcome! Feel free to vote on any of these surveys.
<%
// Create some shortcuts to commonly used adapters.
// This greatly improves performance.
var survey = Modules.WebData.SurveyAdapter;
var surveyData = Modules.WebData.SurveyDataAdapter;
var addVote = Modules.WebData.AddVoteAdapter;
// Display any errors that may have happened.
var e = new Enumerator(survey.Errors)
for (; !e.atEnd(); e.moveNext())
Response.Write('<li>' + e.item().Message)
// Enumerate through all the surveys.
var surveyEnum = new Enumerator(survey.Records);
for (; !surveyEnum.atEnd(); surveyEnum.moveNext())
{
%>
<form method="post">
<a href="<%=Pages.ResultsPage.HREF%>?SURVEY_ID=
<%=survey.SURVEY_ID.Value %>">
<b><%=survey.DESCRIPTION.DisplayText%></b></a><br>
<input type="hidden"
name="__act"
value="<%=addVote.AddVoteAction.AsFieldValue%>">
<input type="hidden"
name="<%=addVote.SurveyId.InputName%>"
value="<%=survey.SURVEY_ID.Value%>">
<%
// Add all the details in another sub-table.
var dataRecords = new Enumerator(surveyData.Records);
for (; !dataRecords.atEnd(); dataRecords.moveNext())
{
%>
<input
type="radio"
name="<%=addVote.SurveyDataId.InputName%>"
value="<%=surveyData.SURVEY_DATA_ID.Value%>">
<%=surveyData.DESCRIPTION.DisplayText%>
<%
}
%>
<input type="Submit" value="Vote">
</form>
<%
} // for
%>
<!--#include file="footer.html"-->
Notice the closing of the script tag allows us to easily embed HTML
inside the enumeration. The first thing to do is add an HTML form tag
to allow posting of a new vote. In order for us to tell WebSnap what we
are updating, there is the special hidden __act (action) input:
<input type="hidden"
name="__act"
value="<%=addVote.AddVoteAction.AsFieldValue%>">
On the Net
ing data fields, such as SURVEY_ID. Inside script, all the fields
are of AdapterFieldType. Value is a procedure that gives the underlying value of a given AdapterFieldType.
Other useful things you can do with AdapterFieldTypes include
limiting who can view the field, such as:
<% if (survey.SURVEY_ID.CanView) { %>
<!-- Actually write out the SURVEY_ID,
since the user can view it -->
<% } // end if %>
See the online Help files, or the aforementioned server-side scripting link, for more information about what properties are available
for this type.
The first thing to do is to move back to the first record with the
Enumerator. The DataSetAdapter components Mode is then set to
be Insert, preparing it for a new record. Next, the HiddenFields and
HiddenRecordFields are written. All Web applications are stateless;
however, by writing out HiddenFields, WebSnap is able to maintain
state, such as the current adapter mode.
As with all forms, there needs to be an __act input to tell the application what action to perform. As mentioned earlier, all DataSetAdapter
components have some default actions, including Edit, New, Cancel,
and Apply. In this case, we want to apply the changes (a new insert)
to the database and use the AsFieldValue property to set the value of
On the Net
the __act. All actions are of AdapterActionType, and AsFieldValue is the
key for WebSnap applications to determine which adapter action
to call:
<input type="hidden" name="__act"
value="<%=survey.Apply.AsFieldValue%>'">
Finally, in order to set the field values in the database, use the
survey.DESCRIPTION AdapterFieldType:
Create a New Survey:
<input type="text"
size="60"
name="<%=survey.DESCRIPTION.InputName%>">
<input type="Submit" value="Add">
</form>
procedure TResultsPage.WebPageModuleBeforeDispatchPage(
Sender: TObject; const PageName: string;
var Handled: Boolean);
begin
// Go to the correct record.
if Request.QueryFields.Values['SURVEY_ID'] <> '' then
with WebData do begin
tblSurvey.Open;
tblSurveyData.Open;
qryTotalVotes.Open;
if not tblSurvey.Locate('SURVEY_ID',
Request.QueryFields.Values['SURVEY_ID'], []) then
raise Exception.CreateFmt(
'Failed to locate survey id %s',
[Request.QueryFields.Values['SURVEY_ID']]);
end
else
raise Exception.Create(
'No Survey ID passed to the results page');
end;
Select the ResultsPg.html tab from the bottom of the code editor,
and set the HTML to what is shown in Figure 7. Notice the use of
a custom JScript function, WriteResult, to create an HTML table
with a width based on a percentage of the total votes.
The beauty of this is that the HTML designer can easily modify
the HTML script to change the appearance of the application
instantly, with no recompilation needed.
The last thing to do is uncomment the Response.SendRedirect
code in WebDataMod.pas (if you commented it out earlier). Add
On the Net
ResultsPg to the uses list of WebDataMod to get the application
to compile. Run the application to properly register the Web
App Debugger executable, and select Tools | Web App Debugger to
start the test server. You should now be able to browse to http:
//localhost:1024/WebVote.WebVote and view the results of your
labor.
Of course, by default, there will be no surveys in the database, so
select the ModifySurveyPage link and add a survey and some options.
Then, you can go back to the main page and do some voting.
The project and database referenced in this article are available on the
Delphi Informant Complete Works CD located in INFORM\2002\FEB\
DI200202CD.
<td><b>Description</b></td>
<td><b>Votes</b></td>
</tr>
<%
}
for (; !dataRecords.atEnd(); dataRecords.moveNext())
{
%>
<tr>
<td><%=surveyData.SURVEY_DATA_ID.DisplayText%></td>
<td><%=surveyData.DESCRIPTION.DisplayText%></td>
<td><%=surveyData.VOTES.DisplayText%></td>
</tr>
<%
} // for
%>
<!-- Add the ability to add another survey data item -->
<tr>
<td colspan="3">
<form method="post">
<%
dataRecords.moveFirst();
surveyData.Mode="Insert";
if (surveyData.HiddenFields != null)
surveyData.HiddenFields.WriteFields(Response)
if (surveyData.HiddenRecordFields != null)
surveyData.HiddenRecordFields.WriteFields(
Response)
%>
<input type="hidden" name="__act"
value="<%=surveyData.Apply.AsFieldValue%>'">
<input type="hidden"
name="<%=surveyData.SURVEY_ID.InputName%>"
value="<%=survey.SURVEY_ID.DisplayText%>">
<input type="hidden"
name="<%=surveyData.VOTES.InputName%>"
value="0">
New survey option:
<input type="text" size="60"
name="<%=surveyData.DESCRIPTION.InputName%>">
<input type="Submit" value="Add">
</td>
</form>
</tr>
</table>
</blockquote>
Corbin Dunn has worked for Borland Software Corporation for three years. He
started in Developer Support and is now a quality-assurance engineer for the
rapid-application-development product group, where hes currently working on
WebSnap. Readers may reach him at cdunn@borland.com.
<%
} // for
%>
<!-- Add the ability to add another survey -->
<form method="post">
<%
// Go back to the first record to be able to insert.
surveyEnum.moveFirst();
survey.Mode="Insert";
// Writing the hidden fields tells the adapter what
// mode it should be in.
if (survey.HiddenFields != null)
survey.HiddenFields.WriteFields(Response)
if (survey.HiddenRecordFields != null)
survey.HiddenRecordFields.WriteFields(Response)
%>
<input type="hidden" name="__act"
value="<%=survey.Apply.AsFieldValue%>'">
Create a New Survey:
<input type="text"
size="60"
name="<%=survey.DESCRIPTION.InputName%>">
<input type="Submit" value="Add">
</form>
<!--#include file="footer.html"-->
rror detection and handling play an extremely important role when interfacing
with the Windows API. In Part I, we saw how to detect error conditions after calling a Windows API function, and how to retrieve a message text describing the error.
This month, we conclude the series with a look at retrieving messages from a specific
message DLL, setting the last-error code, error modes, and Delphis approach to error
handling.
const
// Definition of a Lan Manager error code.
NERR_BASE = 2100;
NERR_NetNotStarted = (NERR_BASE + 2);
procedure TForm1.Button3Click(Sender: TObject);
const
Flags = FORMAT_MESSAGE_FROM_HMODULE or
FORMAT_MESSAGE_ALLOCATE_BUFFER;
// Message for Lan Manager exist in netmsg.dll.
NetMsg = 'netmsg.dll';
var
DLL: HMODULE;
Msg: PChar;
begin
// Simulate a Lan Manager error, see next section
// for SetLastError.
SetLastError(NERR_NetNotStarted);
// Retrieve the message text associated with the error.
DLL := LoadLibraryEx(PChar(NetMsg), 0,
LOAD_LIBRARY_AS_DATAFILE);
if DLL <> 0 then
if FormatMessage(Flags, Pointer(DLL), GetLastError,
0, @Msg, 0, nil) > 0 then
begin
ShowMessage(Msg);
LocalFree(Cardinal(Msg));
FreeLibrary(DLL);
end
else
ShowMessage('FormatMessage failed');
end;
class explicitly, its uncommon to do so. Instead, you will use the
helper routine RaiseLastOSError. This function, which takes no
parameters, raises an EOSError exception for you, and takes care
of initializing the Message and ErrorCode properties of the raised
EOSError exception object. Using this function, transforming
error codes into exceptions becomes a snap:
if not SomeWindowsApiFunction then
RaiseLastOSError;
If you refer back to Figure 1, you see there also is a class of functions that do not set the last-error code, but instead return the
error code directly as the function result. The trick in this case is
to call SetLastError first, then call RaiseLastOSError:
var
R: DWORD;
begin
R := SomeApiFunction;
if R <> 0 then begin
SetLastError(R);
RaiseLastOSError;
end;
end;
Action
Error Modes
A series about error handling wouldnt be complete without at least
mentioning error modes. Each process has an error mode associated
with it that determines whether the system handles certain types of
errors by informing the user, or if the process itself is allowed to
handle these. Examples of such errors are unhandled exceptions, findfile errors, and data misalignment.
The most common of these is the find-file error. This error
occurs, for example, when you attempt to read from a floppy
disk, but theres no floppy in the drive. By default, the system will
display a dialog box informing the user of this fact and fail the
operation. Sometimes, however, this isnt the behavior youd like
to see in your applications. Instead of the default error dialog box,
you might prefer some other course of action, such as displaying
your own dialog box. To override the display of the error dialog
box by Windows, use the SetErrorMode function to set the error
mode for the process. Figure 3 shows how this is done.
Figure 4 lists the possible values of the uMode parameter and their
meanings. The function returns the previous error mode that is useful,
so you can restore it at a later point as demonstrated in Figure 3. Such a
feature is especially important for general-purpose library routines. One
word of caution, though: The error mode is a process-wide setting. This
means that when a thread changes the error mode, it changes for all other
threads in the same process. For this reason, youll want to implement
some kind of access policy when changing the error mode.
Conclusion
Error detection and handling play an extremely important role when
interfacing with the Windows API. In this two-part series, Ive shown
you how to detect error conditions after calling a Windows API
function, how to retrieve a message text describing the error, and the
provisions the Delphi RTL has to make error handling as painless as
possible. Ive also shown how you can control how some Windows
API functions handle error conditions. How you handle error conditions after youve detected them is highly application- and domainspecific. It could mean retrying a function call, falling back on the use
of other Windows API functions, or simply aborting your application
after informing the user. In any case, you now know how to detect
any error and get some useful information about it.
Accompanying this article are two projects: ErrorDemo and ErrorLookup. The first one contains all the code shown in this series;
the second contains an IDE wizard that allows you to look up the
message text associated with any Windows API error code. See the
included readme.txt for installation details.
The projects and files referenced in this article are available on the
Delphi Informant Complete Works CD located in INFORM\2002\
FEB\DI200202MV.
Marcel van Brakel is an independent contractor specializing in Windows systemlevel development using Delphi. Hes a contributing member of Project JEDI (http:
//www.delphi-jedi.org) and is the founder of the JEDI Code Library. You can write
to him at brakelm@chello.nl, or visit members.chello.nl/m.vanbrakel2.
By Bill Todd
InterBase 6.5
XML Support and a Whole Lot More
nterBase is on the move again with an impressive update that adds significant new
features. InterBase has always been able to support large databases, but in the
past the way it did this was a holdover from the days when hard disk partitions were
limited to a maximum size of either 2 or 4GB. Creating a large database required
spreading it across multiple files and specifying a maximum size for each file except
the last. While this wasnt a serious limitation, it required more work and planning
to create a large database. You had to anticipate the maximum size of the database
and create enough files to accommodate that size. It also made moving the database
to a different location on the server difficult, because the full path to each of the
secondary files was contained within the database. The only way to move a multi-file
database was to perform a backup and restore.
InterBase 6.5 makes managing large databases
much easier by implementing 64-bit file I/O
on the Windows, Linux, and Solaris platforms.
Now your InterBase databases can expand to
the full size of the hard drives on which they
reside. You still have the flexibility to create a
multi-file database if you need to spread the
database across multiple drives, but fewer files
will be required since each file can expand to
fill the drive that hosts it. Note that a 64-bit
file I/O is only supported by Linux version 2.4
kernels. If you install InterBase 6.5 on an earlier
version of Linux, it will behave exactly the same
as InterBase 6.
This is the first step in enabling InterBase to
easily handle larger databases and transaction
volumes. The second step will be the addition of
symmetrical multiprocessing (SMP) support in
InterBase 7.0.
Canceling Queries
One problem you can encounter with any database is a query that takes much longer than you
16 February 2002 Delphi Informant Magazine
The best way to understand what the ROWS clause can do for
you is to look at some examples. All of the examples in this article
use the sample Employee database that comes with InterBase.
The following code shows the use of ROWS in conjunction with
the ORDER BY clause to select the five records with the largest
discount:
SELECT * FROM Sales
ORDER BY Discount DESC
ROWS 1 TO 5
If you want all of the rows that have the same values in the fields
in the ORDER BY clause as the first five rows, use the WITH
TIES option:
This query returns eight rows instead of five because some of the
values that appear in the Discount field in the first five rows also
appear in other rows. Note that you must use an ORDER BY
clause when you use WITH TIES.
Instead of a fixed number of rows, you can also request a percentage of the rows in the result set:
SELECT * FROM Sales
ORDER BY Total_Value DESC
ROWS 10 PERCENT
Property
Description
HeaderTag
DatabaseTag
TableTag
RowTag
Flags
The ROWS clause can also be used with UPDATE to give the 10
highest-paid employees a raise:
UPDATE Employee
SET Salary = Salary * 1.01
ORDER BY Salary DESC
ROWS 10
You can also use ROWS with DELETE to delete the 10 highest-paid
employees (if you want to delete everyone whose salary is equal to one
of the 10 highest-paid employees, just add the WITH TIES option):
DELETE FROM Employee
ORDER BY Salary DESC
ROWS 10
Stream
XML Support
InterBase Express now includes a new class, TIBOutputXML, that
lets you easily retrieve data in XML. Figure 2 shows the properties of TIBOutputXML. Figure 3 shows the code from the Show
XML buttons OnClick event handler in the sample application
Processor Affinity
Running prior versions of InterBase SuperServer under Windows
on multiprocessor machines can actually degrade performance.
Windows will switch the InterBase process from one CPU to
another, and this is an expensive operation.
The InterBase 6.5 ibconfig file for Windows installations will contain
the following line to attach the InterBase process to the first CPU:
#CPU_AFFINITY
If you wish to attach InterBase to a different CPU, change this directive to use the bit value that corresponds to the CPU, e.g. 2 for the
second CPU, 4 for the third CPU, and so on.
Other Changes
InterBase 6.5 also includes InterClient 2.5, and the latest version
of the InterBase JDBC driver has been updated to be consistent
with JRE 1.3. This means you must have Java 2 installed to use
InterClient 2.5.
19 February 2002 Delphi Informant Magazine
could cause performance problems on slow or heavily-loaded networks. This behavior has been changed so that only the actual data in
the VARCHAR field is transmitted.
IBConsole has also been enhanced to include a separate indexes node
for each database. Selecting this node displays information about all
of the indexes in the database grouped by table, as shown in Figure
7. Items shown include the index name, sort order, uniqueness, columns included in the index, column order, and whether the index is
active. For indexes that are part of a foreign key constraint, the name
of the primary index referenced by the foreign key is also noted.
Informant is a registered trademark of Informant Communications Group, Inc., Elk Grove, California.
Delphi is a trademark of Borland Software Corporation.
DeployMaster
Low-cost Installation Tool for Simpler Apps
ouve just finished your latest application masterpiece, and its time to show it to the
rest of the world. Unfortunately, gone are the days of Windows application deployment when all that was necessary was an application directory, the programs executable,
and perhaps a few support DLLs. Now you have to consider everything, from shortcuts and
file associations to registry key entries and language localization. DeployMaster, however,
could make application installations simple again.
DeployMaster provides developers a tabbed, formsbased interface with which to enter installation
attributes. It walks developers through a simple,
10-step process that covers:
file-type associations, which users may select
during installation, thereby allowing them to deselect any associations they prefer not to modify;
speaking-language localization;
user registration validation via a separate
DLL;
creating program shortcuts on the Windows
Start menu and desktop; and
generating a single, self-executable installation file.
Figure 1: DeployMasters Project tab. Enter the program name, main executable, license, and readme file paths here.
21 February 2002 Delphi Informant Magazine
DeployMaster is an inexpensive, albeit limited, installation-building program. While DeployMaster gets the job done for relatively
straightforward deployments, developers seeking a Windows
Installer-capable tool will need to look elsewhere.
JGsoft
Lerrekensstraat 5
2220 Heist-op-den-Berg
Belgium
E-Mail: sales@jgsoft.com
Web Site: http://www.deploymaster.com
Price: single-user license, US$99; five-user license, US$399.
Figure 2: This sequence provides a detailed log of events and
errors in the installation-building process.
Figure 3: The registry requires manual entry of program registry values. Unfortunately, DeployMaster cannot import or
export registry files.
One of the most troubling aspects for DeployMaster and other older
setup-application builders is that theyre based on old installation
technology. While Windows 9x and NT clients will exist for some
time to come, Microsoft has proclaimed that new program installations should use Windows Installer (WI) technology to comply with
Windows. The first well-known commercial application that shipped
using WI technology was Microsoft Office 2000. Since that time,
almost every new professional-level application has offered the option
of installing using WI. It addresses a number of installation deficiencies that exist in programs like DeployMaster. WI also helps prevent
infamous DLL Hell installation conflicts that plague non-WI-based
setup utilities, including DeployMaster.
Still, for basic installations that require simple functionality of singlefile, stand-alone setup executables and shortcut icons placed on the
Start menu and desktop, DeployMaster fits the bill. Its certainly less
expensive than even its closest competitor.
Figure 4: The final, built product. The layout of this dialog screen
is identical for all DeployMaster-constructed installations.
Another deficiency is that developers must enter registry keys manually, instead of allowing the developer to import a registry file. While
not a big issue with small application deployments, this can be a
major headache with applications that include ActiveX controls and
DLLs that require numerous registry key entries.
22 February 2002 Delphi Informant Magazine
Mike Riley is a chief scientist with RR Donnelley & Sons, one of North Americas
largest printers. He participates in the companys I-net and eMerging Technology strategies using a wide variety of distributed network technologies, including
Delphi 5. Readers may reach him at mike_riley_@hotmail.com.
InfoPower 3000
The Grid Alone Is Worth the Upgrade
have been using InfoPower since the first version, and, with each release, I am amazed
at the power of its new features. InfoPower just keeps getting better.
the columns are resized automatically to fit within the client area of
the grid. Users have always been able to resize columns at run time,
but now they can resize rows, as well, by dragging on the horizontal
line in the record-indicator column. The ability to control the color
of the lines in the grid is another nice touch. The enhancements to
the grid alone are worth the cost of upgrading to InfoPower 3000,
but many other components received new features too.
One of InfoPowers most unique controls is the data inspector. Shown
in Figure 4, the data inspector lets you display the data from one or
more tables in a tree view, with nodes that can expand and collapse to
let the user control what information is visible at any time. Now, you
can display multiple records simultaneously in adjacent columns. Like
the grid, the new data inspector supports a wider variety of embedded
controls, including non-InfoPower controls. The inspector also supports the same background-texture tiling described for the grid. Also
like the grid, you can now choose to have alternate rows displayed in
different colors. The tree view has been enhanced with lines that make
the hierarchical relationship between items stand out visually.
InfoPowers edit-mask feature always has been far more powerful
than the native Delphi edit masks, and this release increases that
power with automatic auto-filling of characters. If a field value
starts with the same character in every case, the edit mask will
automatically insert it when the next character is typed.
InfoPower 3000 introduces new check box and radio group controls,
shown in Figures 5 and 6 respectively. Both support custom glyphs for
the controls and the flexible framing and transparency of other InfoPower components. Additionally, both integrate with the grid, Record
View dialog, and data inspector. The Record View dialog has been
enhanced to support framing and transparency. New currency and
numeric-editing components eliminate the problem of users entering
formatting symbols with the data. Now, a user can enter an amount as
$12,344.27, and the component will remove the $ and comma automatically before converting the value from a string to a number. These
arent all the enhancements, but they are the most significant.
Conclusion
If I could only have one add-in component suite for Delphi, it would be
InfoPower. InfoPower will reduce the amount of code you have to write
and provide your users with a much more attractive, powerful, and functional user interface than any you could create using the standard Delphi
components alone. InfoPower continues to be the must-have add-in for
Delphi developers.
Addict 3.0
Modern Spelling-check Support for Delphi Apps
ts hard to imagine a word processor that doesnt include support for checking spelling.
Today, such support is part of many other kinds of applications, including Internet mail
applications. In fact, any application that includes a Memo, RichEdit, or similar component is
a good candidate for supporting a spelling checker.
One of the best-known Delphi (and C++Builder)
component libraries providing this kind of support
is Addict from Addictive Software. Currently in its
third major version, this excellent library provides the
full range of modern spelling-check and thesaurus
support. Addict provides these features the Delphi
way with native classes and components that
provide flexibility and power, and with well-conceived properties and methods.
Two versions are available, and there are two types
of libraries: Standard and Professional. Add-ons are
available in a special PlusPack. The Standard library
includes thesaurus and spelling-check components. The Professional version includes live-spelling RichEdit components, full source code, and
C++Builder support. The PlusPack includes a spelling-dictionary compiler, which requires Windows
NT or Windows 2000; a thesaurus file compiler; and
a utility to import and export custom dictionaries.
The product includes a 30-day, money-back guarantee, and you can download a trial version.
Addict offers considerable support for a variety of
languages. (See Addicts dictionaries page, http://
www.addictivesoftware.com/dicts.htm.) It also has
support in the user interface for Swedish, Brazilian
Portuguese, Afrikaans, German, Spanish, Russian,
Dutch, Danish, and French, among others. Theres
a good deal of flexibility in Addicts custom dictionary capabilities as well. Version 3 has a customdictionary editor that allows users to modify a list
of words they want the checker to ignore or autocorrect. You can set custom dictionaries to mark
a word as always misspelled, for instance if you frequently misspell a word in one context, but its correctly spelled as another word, such as typing their
when you mean there. Best of all, you can load any
combination of main and custom dictionaries simultaneously, a feature Ive not seen in other tools with
which Ive worked. Visit the Addictive Software Web
site to find out about the latest product updates.
For example, version 3.01 adds word counting and
Delphi 6 support, and version 3.2 adds suggestions
learning as well as additional third-party support.
The update is free for 3.0 users.
TAddictRichEdit
TAddictDBRichEdit
TThesaurus3
TConfigurationDialogCtrl
TThesaurusDialogCtrl
Description
Main support for checking spelling.
It adds a user interface to base class
TAddictSpell3Base. The latter provides
the ability to parse an edit control,
store multiple user configurations,
look up words in many dictionary
types, generate alternate word
suggestions, and run the user
interface needed to perform a
spelling check.
An extension of Delphis TRichEdit
wrapper class, TAddictRichEdit
provides context-sensitive spelling
features, such as automatic
underlining of misspelled words
and automatic replacement of
misspelled words with correct words
while the user types.
An extension of Delphis data-aware
TRichEdit wrapper class, it provides
context-sensitive spelling features,
such as those listed for
TAddictRichEdit.
Adds a default user interface
(dialog box) to the base class,
TThesaurus3Base. The latter provides
the ability to locate the current word
in an edit control, look up words in
a thesaurus file, and run the user
interface needed to look the word up.
Facilitates communication with
TAddictSpell3Base class, and
interacts with it directly.
TConfigurationDialogCtrl provides
all the capabilities of a custom
spelling-check and configuration
dialog box.
Facilitates communication with
TThesaurus3Base class to provide
the functionality of a custom
thesaurus dialog box.
Vendor
Integration
DBISAM
Elevate Software
DreamMemo
Dream Company
InfoPowers
rich-edit control
LMD-Tools
Woll2Woll Software
Orpheus
TurboPower
PlusMemo
Electro-Concept
Mauricie
Sergey Tkachenko
Addicts DB-aware,
live-spelling RichEdit
can be used with
this database product.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Spelling-check and
thesaurus support.
Live-spelling,
spelling-check and
thesaurus support.
TRichView
WPTools
LMD Innovative
Julian Ziersch
Software
One particularly attractive feature of this library is its extensibility, made possible through partnerships with third-party component vendors (see Figure 5).
The creators of Addict have constructed a parsing architecture
to facilitate adding this spelling-check and thesaurus support to
nearly any edit control. On the Addictive Software Web site, you
can read about and download demo applications and components
that add spelling-check and thesaurus support to one or more of
their components. As you read above, some vendors also support
live checks of spelling. In addition to those listed in Figure 5
(and described in more detail on the Web site), Ive learned of
a recent addition: support for TMS Softwares TAdvStringGrid
component, including live spelling-check support.
This product, including documentation, is completely electronic
(that is, no physical products are involved). The support consists
mainly of the help file and the sample programs. While these are
excellent, I feel this product would benefit greatly from the addition of a manual and perhaps a tutorial to show newer developers
how to use some of the more subtle features. I dont regard the
lack of these items as a serious defect, however. I found the library
easy to use despite their absence. The company provides support
through e-mail (through which I always received a quick response)
and newsgroups. For more information, please see the fact file
that accompanies this article.
To conclude, I feel this is an excellent library with only minor
defects. Having used thesaurus features in commercial word processors, I was somewhat disappointed by the default thesaurus file.
The thesaurus response for the word shown in Figure 6 seems fairly
acceptable. But, for other words, the thesaurus file provided just one
context, such as a words use as an adjective, when the word clearly
had at least one more use, such as a noun or verb form.
Perhaps improving the default thesaurus file could become a joint
project for Addict developers. But, despite these minor issues, I
feel this is the best tool Ive seen for providing spelling-check
support for Delphi applications.
LockBox 2
From Cyberspace to Cipherspace
Designed for Delphi 3 or later, the tools in LockBox 2 include in-house algorithms that formed
the core of LockBox 1 (included here mainly for
Class
Description
TLbBaseComponent
TLbCipher
TLbSymmetricCipher
TLbBlowfish
TLbDES
TLb3DES
TLbRijndael*
TLbAsymmetricCipher*
TLbRSA*
Description
TLbHash
TLbMD5
TLbSHA1*
Conclusion
LockBox 2 is a powerful and flexible encryption and decryption
library that provides a complete solution for adding data security
to your applications. LockBox supports all the most popular algorithms and provides both low-level routines and high-level classes
and components to access those algorithms. Best of all, you dont
have to be a master cryptologist to use LockBox. You can download a trial version from TurboPowers Web site. As TurboPower
does for its other products, it provides a generous 60-day, moneyback guarantee the most generous in the industry, as far as
Im aware.
TextFile
of view, this is the only Delphispecific book. I can also recommend it without reservation.
The book provides concise
information, and covers parsing and producing XML from
Delphi applications. The inclusion of sample applications
will help developers realize the
benefits of XML support. The
only problem I can see with
the book is that Delphi 6specific classes/technologies
arent addressed, but the
information presented is still
applicable. If you read the
book you will be able to take
advantage of new XML-related
technologies in Delphi 6 with
no problems.
Ron Loewy
File | New
Directions / Commentary
Plug-in Culture
lug-ins dynamic link libraries that extend functionality are nothing new. Theyve been used for years in
diverse applications from Netscape Navigator to Macromedia Director. Plug-ins allow third parties and their
customers many advantages: application vendors can provide updates and fixes without re-distributing an entire
application; users can add custom features. How do third parties facilitate communicating with their applications?
Typically, a vendor that supports plug-ins will provide an API so developers can extend or add functionality.
In Delphi, we could regard IDE extensions, such as Experts, as plug-ins.
Borland supports these extensions
through Delphis Open Tools API.
One of the most powerful and
feature-rich IDE add-ons is Eagle
Softwares CodeRush. Interestingly, CodeRush includes its
own plug-ins, as well as the
Rush API that allows users
to extend both CodeRush
and Delphi. Now in version 6,
CodeRush includes a plethora of
powerful plug-ins. Theres even one
plug-in, AutoFill, that permits extensions to itself (as Ill discuss at the end
of this column).
While discussing plug-ins with me, Mark
Miller (chief architect of CodeRush) made the
following observations: Plug-ins allow us to get
new solutions to our customers immediately. Other
companies develop and save features for their next
major product release, resulting in a delay between
the moment the feature is developed and the time
when customers can actually employ that technology, e.g.
by upgrading to the next product version. With a plug-in
architecture theres no delay; customers are always empowered with the latest technology.
I also wanted to find out how CodeRush users and plug-in
developers felt about the availability of plug-ins. So I posted
a series of questions on the Eagle Software Newsgroup, eagle
.public.support.thirdparty. The first question, What has the
availability of plug-ins meant to you, elicited responses that
demonstrated the extreme loyalty of CodeRush users. One
33 February 2002 Delphi Informant Magazine
File | New
particularly since Ive been expanding a large TAPI interface unit
containing a huge class to which Im constantly adding new fields.
I also asked about developers experiences writing plug-ins. One
reported a bit of a learning curve, but a couple of others characterized the process as easy. In fact, one said it was no more
difficult than writing a Delphi application. The first developer
recommended I use his own UnitNavigator plug-in to navigate
the lengthy RushIntf.pas, the CodeRush unit that provides
(mostly abstract) methods that support CodeRush/Delphi extensions. Support for writing plug-ins has increased in the Professional version of CodeRush with this highly commented and
bookmarked RushIntf.pas file, a great online tutorial, and a very
active newsgroup especially dedicated to plug-in developers.
My own experience in writing plug-ins has also been satisfying.
When the first version of CodeRush came out I wrote a Project
Identifier plug-in that made variable and other project-specific
names easily available to be pasted into the editor (I even wrote an
article about the process of writing it, which is still on the Eagle
Software site: http://www.eagle-software.com). These identifiers
were saved in a file that could be automatically loaded when the
unit was being edited. However, there were a few keystrokes and
navigational issues involved, and I was not entirely satisfied with
the functionality. With the introduction of the AutoFill plug-in
in CodeRush 5, Mark Miller suggested I write an extension to
this plug-in so it could incorporate those lists of identifiers in the