On The Cover: July 2000, Volume 6 Number 7
On The Cover: July 2000, Volume 6 Number 7
On The Cover: July 2000, Volume 6 Number 7
ON THE COVER
6 On the ’Net
Real-world Web Apps — Dr Mark Brittingham
You know the basics; Dr Brittingham tells us what it takes to build a
Web application to withstand the rigors of daily use, from WebBroker
components, to ISAPI DLLs, to tracking state, and more.
FEATURES
14 Columns & Rows
Exploiting SQL Server 7 DMO: Part II — Jason Perry REVIEWS
Mr Perry ends his two-part series on Microsoft SQL Server 7 Distributed 30 Wise for Windows Installer 2.0
Management Objects by sharing an invaluable Database Information Product Review by Bill Todd
and Reconciliation Tool, as well as a simple security object.
35 Delphi 5 Developer’s Guide
20 Greater Delphi Book Review by Alan C. Moore, Ph.D.
Palm Conduits: Part II — Ron Loewy
Completing his two-part series, Mr Loewy builds a conduit that moves
information from the Palm device to a Paradox database, and a conduit
that synchronizes the Paradox database and Palm device.
DEPARTMENTS
24 DBNavigator 2 Delphi Tools
Delphi and ASP — Cary Jensen, Ph.D.
Microsoft Active Server Pages are extremely popular. Dr Jensen shows 5 Newsline
you how to get in on the action by explaining the basics of ASP, and 36 Best Practices by Clay Shannon
demonstrating how to create and use ASP objects with Delphi. 38 File | New by Alan C. Moore, Ph.D.
1 July 2000 Delphi Informant Magazine
Delphi Joseph D. Booth Consulting Releases JBC UI/Scan for Delphi
Joseph D. Booth
T O O L S Consulting, Inc. released
JBC UI/Scan for Delphi,
New Products a lint checker that allows
and Solutions developers to provide a
more professional look
and feel to all their appli-
cation’s GUI design.
The developer simply
scans an application, and
UI/Scan will report tab
order problems, menus
without code attached,
hard-coded colors, fonts
that are too small or too
large, hard-to-read color
combinations, and other
user interface concerns. Developers can customize colors, oper to print a report or save it
UI/Scan will assist the devel- fonts, lists of obsolete controls, to a file.
oper in putting in the final etc. There are also filters so
touches to the visual design UI/Scan can be set to only focus Joseph D. Booth Consulting, Inc.
of an application. In addition, on fixing one problem at a time. Price: US$149
UI/Scan allows the developer to Another feature is the reporting Fax: (610) 409-8859
customize all aspects of a scan. option, which allows the devel- Web Site: http://jbooth-consulting.com
Quma Releases QVCS 3.4
Quma Software, Inc. announced file revisions. You can set up a used to compare files to earlier
the release of QVCS 3.4, a Win- global journal file to keep track revisions, or compare revisions
dows 95/98/NT4/2000 version- of all changes to files in all to other revisions. In an emer-
control system designed to bring your projects. You can insert key- gency, you can restore a project
order and control to the applica- words into source code or binary to an earlier-labeled release.
tion development process. files for automated documenta- In addition to using the Win-
QVCS automates the tracking tion and audit trail generation. dows program, QVCS allows
of files as they change during At the programmer level, QVCS programmers to use batch files to
the development of a new soft- keeps track of which changes are check files in and out, by provid-
ware application. By requiring made to which files, and when. ing 14 command-line utilities.
programmers to log files in and When a programmer checks out QVCS provides a range of
out, QVCS prevents multiple a file, QVCS will retrieve the reports, including reports of all
programmers from unknowingly latest version of the file, and locked revisions (by individual or
working on the same file simul- record a date/time stamp for that team), revisions created after a
taneously. programmer. QVCS ensures that release, revisions created between
QVCS makes it easy to control the programmer is authorized to any two dates, revisions with com-
which file revisions are included work on the module. It will warn ment lines containing any given
in which releases, and to recover the programmer if a writeable text string, or any combination of
previous versions of files. QVCS copy of the file already exists, and these criteria.
allows you to control executa- deny access to the file if another
bles, documentation, Web pages, programmer is already working Quma Software, Inc.
and all files associated with a on that file. Price: US$25; QVCS-Pro, US$40.
development project. When questions arise about E-Mail: [email protected]
QVCS stores and retrieves program versions, QVCS can be Web Site: http://www.qumasoft.com
O&A Productions Announces oaAgent 1.0
O&A Productions released The first component, component. ToaAgentScript man-
oaAgent 1.0, a pair of native ToaAgent, wraps the Microsoft ages the interaction of multiple
VCL components that make Agent server COM. Addition- agents and other program ele-
working with the Microsoft ally, ToaAgent features enhance- ments. Developers can also extend
Agent COM server easer. ments not found in the ActiveX the scripting language to add their
oaAgent brings Microsoft Agent control, including persistent own commands.
technology to the Delphi and voice commands and custom
C++Builder IDEs in the form context menus. O&A Productions
of a non-ActiveX, native VCL The second component, Price: US$35
wrapper of the Microsoft Agent ToaAgentScript, is an extensible Phone: (858) 618-1904
COM server. scripting language and interpreter Web Site: http://www.o2a.com/agent.htm
By Dr Mark Brittingham
I f you’ve been working in Delphi for a while, you know it’s the most productive Windows
development tool available. What you may not realize is that it’s also an excellent tool
for developing Web applications. In this article, we’ll cover how to build a session-aware
ISAPI DLL that can be used in a real-world application.
Page Requests Page Responses If the rise of Internet technologies has you worried that you’ll have
to leave the world’s best development environment behind, fear no
more. Delphi’s WebBroker technologies give you an easy way to build
fast, scalable Web applications.
Web Server Web applications are defined by a client-server model, where the
(IIS, Omni) client is a Web browser that interacts with the Web server by sending
page requests and form results and receiving HTML in response. In
ISAPI Requests & Responses a very important sense, your job as a Web developer is to “program”
(each in its own thread) the user’s browser to behave the way you want it to by sending it the
appropriate HTML and JavaScript code.
Matching For a Web server, you may choose to use Microsoft’s Personal Web
WebActionItem Server (PWS) or Internet Information Server (IIS) because these
Linked come free with Windows 98, NT, and 2000. However, I recommend
PageProducer that you download and install the OmniHTTPd server from Omni-
cron. It’s available at their Web site: http://www.omnicron.ab.ca/
httpd. Omni is far easier to use as a debugging host in Delphi than
the Microsoft Web servers, and is free for local and development use.
When it comes time for deployment, you can either substitute IIS, or
Other Actions license the commercial Omni distribution. Because both support the
same ISAPI extension standards, they’re interchangeable.
WebBroker ISAPI DLL In practical terms, your work will proceed by creating your HTML
Figure 1: Delphi’s ISAPI architecture. pages, building and compiling your ISAPI DLL, and refreshing your
Note that you won’t be able to compile your ISAPI DLL while the
server is running, because Delphi won’t be permitted to overwrite
the old DLL being run by the server. Thus, the server will have to
be shut down each time you’re finished testing and started up again
before requesting another page. To shut down Omni, just use its
icon in the system tray. Use the Internet Services Manager to stop
PWS or IIS.
If this will be your first ISAPI application, then you need to know
how to tell Delphi to create an ISAPI DLL. Do this by using the File |
New menu. On the New tab in the New Items dialog box, select Web
Server Application. In the dialog box, select the ISAPI/NSAPI Dynamic
Link Library radio button, and press OK. You’re in business!
When the Web server receives a request, it bundles the data Note that, in addition to the PathInfo property, this action item has
related to the request and sends it in a new thread to the ISAPI a producer named Page2. In my development, I give the PathInfo, the
DLL. The TWebApplication object in the DLL, in turn, uses an action, and the producer the same name to make it clear they all work
existing WebModule, or, if necessary, creates a new WebModule together. (Yes, you can give an action and a producer the same name.)
instance to service the request. This WebModule executes within
the thread context passed by the server. This means you can use There are two ways in which you can respond to a request arriving at
variables defined within the TWebModule class and be assured they your DLL: with a producer, or via the action item’s OnAction event.
aren’t shared with other threads. However, global variables will be If you respond to the OnAction event, you’ll have to generate your
shared across threads, so you should avoid them, or be certain HTML and pass it back by assigning the Response.Content field:
your access is thread-safe.
Response.Content := SomeHTMLGenFunction;
If you need data access, you’ll be happy to know that the BDE
and ADO datasets can be placed on the WebModule and used in If you’re using a PageProducer, you’ll generally assign a file to the
a thread-safe manner. The only constraint is that, for the BDE, a PageProducer’s HTMLFile property (see Figure 3). This will cause the
TSession object must be placed on the WebModule, and its file’s contents to automatically be sent as a response.
AutoSessionName property must be set to True.
If you wish, you can keep your entire HTML document in a
A URL that calls an ISAPI DLL should name an action to be PageProducer’s HTMLDoc property. The advantage of this is that
executed. For example, a URL like: you won’t have to distribute HTML pages with your DLL. The
disadvantage is that your site is much more difficult to update: Every
www.Mysite.com/ISAPI/MyIsapi.dll/Signin update will require a re-compile, and your work will slow down every
time you need to cut-and-paste your HTML between Delphi and
will call MyIsapi.dll and hand it the Signin action. When the your HTML editor. Personally, I never use the HTMLDoc property.
WebModule receives the request, it attempts to locate a
TWebAction whose PathInfo matches the action passed in the URL Of course, if your DLL did nothing more than pull HTML files
(see Figure 2). You should generally create one action item with a from disk using PageProducers, then there would be no sense in
Default property set to True to handle the case where no other action creating the DLL. The PageProducer family (TPageProducer,
item handles a request made to the DLL. TQueryTableProducer, TDataSetTableProducer, and
To close out the topic of basic ISAPI development, note that one of Figure 4: Using a cookie to manage a session ID.
the best things that Delphi does for you is take all of the form, URL,
and cookie arguments submitted by a visitor and package them neatly or fat URLs. In all three cases, you might consider storing the
in the ContentFields, QueryFields, and CookieFields string lists. For user’s entire state in the cookie/URL/form. However, unless you’re
example, if you want to know the value a user recorded in the “Name” implementing a very small site, it’s far more appropriate to include
field of a form, you need only look at the result contained in: a numerical ID in the cookie/URL/form that serves as the key to
finding that information on the server.
Request.ContentFields.Values['Name']
Forms. To maintain state using forms, you would have to place every
To become a good ISAPI developer, working with string lists must link on your site in an HTML form, and store the state ID in a
become second nature! hidden field. Then, as the user navigates the site, the ID would be
passed in the Request.ContentFields variable in each request. This isn’t
Dynamic Web Pages and Sessions a viable approach, because of the overhead and pain involved in
After the elation of building your first ISAPI DLL, you’ll probably implementing every link as a form.
come to the realization that you’re nowhere close to a real Web
application. This is because the Web is a completely stateless environ- Cookies. To maintain state in a cookie, you’ll generate a unique
ment. In standard Windows development, you can pop up a dialog session or user ID, and store it in a cookie that is placed on the
box, have the user fill in some answers, and return to the main form visitor’s computer when they first enter the site. On subsequent pages,
without any worry that a different user has requested each of these this cookie is pulled from the page request and used to access the
actions. This isn’t true on the Web! Each request your DLL receives is visitor’s state information. Figure 4 shows Delphi code to accomplish
logically independent of every other request. this. Note that I’ve placed this code in the WebModuleBeforeDispatch
method of the WebModule. This is because BeforeDispatch is called at
That means that, although your simple ISAPI DLL can now produce the beginning of every page request, and is thus an ideal place to set
a Web page that includes dynamically-generated information, it will up the session for the rest of the request. Note that all functions called
either generate the same information for every user, or will use only in the process of satisfying a request are within a single thread. This
the immediately preceding page to generate the response, e.g. process- means you can assign a variable in one function, and use it in another
ing a form page. if the variable is defined in your WebBroker class.
To get around this limitation, we need to implement some mecha- The advantage of a cookie-based solution is that you can set the
nism for state maintenance. A state mechanism permits you to cookie up in your DLL just once, and not have to worry about it
store information about a visitor (their “state”) so that each page again. Also, you can use cookies to store a permanent ID for a visitor
request has access to any information the visitor entered on any so that when he or she returns, you can immediately provide them
earlier page. A prime example of the utility of state management with personalized information.
is an e-commerce application that must remember the products
a visitor has selected, their name and address, and their payment The disadvantage of cookies is that some users turn them off. There is a
information, even though all of this information has been entered great deal of fear-mongering, and downright ignorance, when it comes
on different pages at different times. to cookies — as well as some legitimate concern — so it’s not surprising
that some people disable them. If you’re using cookies to maintain
Typically, applications also differentiate between a user’s session (state state, your site simply won’t work for people who turn them off.
information stored in the course of their current visit) and a user’s
permanent information. Of course, information entered in a session It’s also important to recognize the distinction between using cookies
usually becomes part of a user’s permanent information. However, to maintain state, and using them to uniquely identify a user. If you
sessions are usually set up to expire after a relatively brief period of use cookies to maintain state, they should expire soon after their last
inactivity (20-30 minutes). use. If you use cookies to identify a user, it’s very important that he
or she have some control over this process. Not all computers are used
The Art of Maintaining State by a single individual, so storing a cookie on the assumption that the
There are only three mechanisms for identifying and tracking a visi- same visitor is using the cookie on each visit could lead to confusion,
tor as they progress from page to page in your site: forms, cookies, or worse, to a significant breach of personal privacy.
If you choose to manage sessions by embedding session IDs in the then the “SID” index to the QueryFields StringList will retrieve the
intra-site URLs, some mechanism for simplifying the substitution of value “1234.” If no SID argument was passed, then the result of
actual session IDs should be implemented as well. this retrieval would be 0. Note that we could use cookies instead by
simply using Request.CookieFields.Values['SID'] in the state-
Enough Theory! ment. We would also have to make sure the cookie is stored in the
Now that you know some session management theory, let’s see how call to CreateSessionObject.
things work in a sample application. Recall that the first thing a
If the Session ID is 0, then this request comes from a new visitor. In
<FORM METHOD="post"
Action="/ISAPI/DemoWeb.dll/Page3?SID=<#SessID>"> this case, we generate a new session object with the CreateSessionObject
<TABLE WIDTH="550" CELLPADDING="0" CELLSPACING="0" procedure. Otherwise, we tell the SessMgr to get the session object ready
BORDER="0" ALIGN="center"> for use with the FindSessionObject function. In either case, the result
<TR> is the same: The SessMgr object in the current thread will now be
<TD ALIGN="right" WIDTH="50%">
What is your favorite color? <BR>
able to store or retrieve user values specific to the current visitor. The
</TD> actual storage or retrieval will occur as this particular request percolates
<TD ALIGN="left"> through the Web action items and/or MDPageProducer classes. Let’s see
<INPUT NAME="COLOR" MAXLENGTH="12" ><BR> how this happens.
</TD>
</TR>
<TR> Assume for a moment that our site needs to assess the current color
<TD ALIGN="middle" COLSPAN="2"> preference of our visitor. This information will be used later in the visit,
<BR> and thus must be stored in the current user’s session variables for later
<INPUT TYPE="submit" NAME="Submit" VALUE=" Submit "> use. The HTML for the color request form is shown in Figure 6.
<INPUT TYPE="reset" NAME="reset" VALUE=" Reset ">
</TD> When the color preferences form is filled out and submitted, the Page3
</TR> action will be requested. Note the inclusion of the SessionID in the page
</TABLE> request. It’s in our response to this new action that we process the informa-
</FORM>
tion sent from this form. In this case, we store the color preference via a
Figure 6: The color preference HTML form. function call in the action item that handles the page request:
are also functions for storing and accessing integer values in the interface
MDWeb library.
uses
Conclusion Windows, HTTPApp, Classes, SysUtils, MDBTree;
The problem I sometimes have in explaining session management is that
type
storing and accessing data in the Session Manager seems pretty boring TMDSessionMgr = class(TPageProducer)
and obvious. It’s not! Keep in mind that a busy site might have hundreds private
or even thousands of visitor sessions running concurrently. The data protected
storage and access for every one of these visitors must be kept straight. function HandleTag(const TagString: string;
TagParams: TStrings): string; override;
The art isn’t in storing and retrieving the data; it’s in doing so in a public
manner that ensures that every visitor sees only their own information. SessID : Cardinal;
SessNode : PTNode;
Having a lightweight mechanism for maintaining state is the gateway procedure DoTagEvent(Tag: TTag;
const TagString: string; TagParams: TStrings;
to all of the more advanced work you’ll do on the Web. Do you
var ReplaceText: string); override;
want to create an e-commerce site? Do you want to provide a highly function ContentFromFile(filename: string): string;
personalized experience for your visitors? Need to manage logins and procedure CreateSessionObject(inUID: Integer = 1);
secure site access? In all of these cases, session management is the function FindSessionObject: Boolean;
foundation from which you will build. ∆ procedure SetValue(const Name, Value: string);
function GetValue(const Name: string): string;
procedure SetIntValue(const Name: string;
All source described in this article (including the B-tree code) is available const Value: Integer);
on the Delphi Informant Magazine Complete Works CD located in function GetIntValue(const Name: string): Integer;
INFORM\00\JUL \DI200007MB. property Values[const Name: string]: string
read GetValue write SetValue;
procedure Register;
implementation
By Jason Perry
I n Part I of this two-part series, we looked at the basics of SQL Server DMO objects. We
also looked at a script-writing tool for SQL developers, named SSB (SQL Script Builder).
The first thing I did was write a method to DIRT uses the same ODBC connection you cre-
connect to the named source server and named ated in SSB. Start it up and you’re presented
destination server: with a screen that has a Conn Source and a Conn
Dest button (see Figure 1). Type in the names
of the server and the database. Click the cor-
responding Conn... button to create the server
and database objects.
Figure 3: Adding lines to the Memo component. Figure 5: Adding information about database sizes.
Building a COM/SQL-DMO Role-based Security Object All you have to do on the application side is create the Automation
You’ve seen a couple of neat tools that can be written using SQL- object and call the Login method:
DMO objects. What about using them in application develop-
ment? In this small demo, I’ll create a simple COM-based security // A login routine. Wimpy.
object. It interrogates the SQLServer object to see if a given function TSQLDMO_Security.Login(const sServer,
sDatabase, sLogin, sPWD: WideString): WordBool;
login is in a particular role, using the DatabaseRoles collection.
begin
Some additional methods are added to return the users’ roles, oServer.Connect(sServer, sLogin, sPWD);
available roles, and the ability to add or remove a person to/from Result := oServer.VerifyConnection(
a particular role. SQLDMOConn_ReconnectIfDead);
end;
procedure TSQLDMO_Security.Initialize;
begin
inherited Initialize;
if Assigned(oServer) then
oServer := nil;
else
begin
oServer := getServer;
oServer.Set_QueryTimeout(5);
oServer.Set_LoginSecure(True);
oServer.Set_ApplicationName(
'SQLDMO Security Object');
end;
end;
Figure 7: The SQLDMO_Security class in Delphi’s Type Library editor. Figure 9: Implementing GetRoles and GetUsers.
procedure TForm1.FillUsers; Looks familiar doesn’t it? Just spin through the collections and
var exploit the attributes you need, and voilà! You now have a list of
o : OleVariant; DatabaseRoles and Users.
lcv : Integer;
begin
To help convince you that the SQL-DMO objects are flexible, I
o := Security.GetUsers(txtDatabase.Text);
lstUsers.Items.Clear; implemented the GetUserRoles method using the IStrings interface,
for lcv := 1 to o.Count do instead of the “built-in” collection mechanism (see Figure 11).
lstUsers.Items.Add(o.Item(lcv).Name);
end; This method will return an IStrings of roles for a specific login and
database. What I did was spin through the Users collection to get the
procedure TForm1.FillUserRoles(sLogin: WideString);
var
User object for the specified Login. If I find a user, I return a NameList
o : IStrings; collection of the roles the user is a member of. As an interesting twist,
lcv : Integer; I implement the IStrings interface using GetOleStrings, and fill it up by
begin spinning through the NameList collection. Now the test application can
o := Security.GetUserRoles(sLogin, txtDatabase.Text);
treat the list as a TStrings collection.
if Assigned(o) then
begin
lstUserRoles.Items.Clear; Now for the last methods — IsUserInRole, AddUserToRole, and
for lcv := 0 to o.Count-1 do RemoveUserFromRole (see Figure 12). These methods are simple.
lstUserRoles.Items.Add(o.Item[lcv]); They each take the arguments Login, Role, and Database Name.
end
else
What’s important here are the IsMember method of the User object,
raise Exception.Create('User not found in database');
end; and the AddMember and DropMember methods of the DatabaseRoles
object. The IsUserInRole method gets the specified database and the
Figure 10: This fills a TListBox with the resulting collections. specified user in the database, and asks the question: “Is this user
in this role?” The Add/Drop methods get the specified database and
function TSQLDMO_Security.GetUserRoles(const Login,
the specified Role object, and invoke the AddMember/DropMember
sDBName: WideString): IStrings;
var methods with the login argument. Could this be any easier?
lcv : Integer;
oUser : _User; So, what good is an Automation object without a test application?
oStrings : TStrings; Well, I included one of those too (see Figure 13).
oRoles : NameList;
lFound : Boolean;
oDB : _Database;
Simply fill out the pertinent server information and click Login. Select
oSA : IStrings; a database role and a user. In the far right-hand side, TListBox will
begin show a list of roles that the person has. Click the Add Selected Role
lFound := False; to Selected User and Remove Selected Role from Selected User buttons,
for lcv := 1 to getDB(sDBName).Users.Count do begin
and watch the user role list. Open a copy of Enterprise Manager to
oDB := getDB(sDBName);
if Pos(Login, oDB.Users.Item(Login).Login) > 0 then
confirm that the role was added. The Is Selected User in Selected Role?
begin button tests whether ... never mind — it’s obvious.
oUser := oDB.Users.Item(Login);
// So, is this user login a member of the specif ied role?
oRoles := oUser.ListMembers;
function TSQLDMO_Security.IsUserInRole(
lFound := True;
const Login, Role, sDBName: WideString): WordBool;
Break;
begin
end
Result :=
else
getDB(sDBName).Users.Item(Login).IsMember(Role);
begin
end;
lFound := False;
Continue;
// Add a user to a role.
end;
procedure TSQLDMO_Security.AddUserToRole(
end;
const Login, Role, sDBName: WideString);
if lFound then
begin
begin
getDB(sDBName).DatabaseRoles.Item(Role).AddMember(Login);
oStrings := TStringList.Create;
end;
GetOleStrings(oStrings, OSA);
for lcv := 1 to oRoles.Count do
// Remove a user from a role.
oSA.Add(oRoles.Item(lcv));
procedure TSQLDMO_Security.RemoveUserFromRole(
Result := oSA;
const Login, Role, sDBName: WideString);
end
begin
else
getDB(sDBName).DatabaseRoles.Item(Role).
Result := nil; DropMember(Login);
end; end;
Figure 11: Implementing the GetUserRoles method using the Figure 12: The IsUserInRole, AddUserToRole, and
IStrings interface. RemoveUserFromRole methods.
Conclusion begin
if Assigned(CompareUI) then
SQL-DMO is vast. This series has touched on the major collections CompareUI.Add(' Table Counts Match');
and objects to help get you started on your enterprise tool. You can end;
rebuild indexes, check for page integrity, add indexes, change object // Look for that table name in destination database.
attributes, replicate your servers, and more. Writing smart tools to // Note: The table could be out of order. If it isn't
// found, the table should be noted as missing.
manage routine database maintenance, such as status reporting and
lFound := False;
user security issues, are now a simple chore with the SQL-DMO for lcv2 := 1 to DB_Dest.Tables.Count do begin
objects. Hopefully, I’ve helped open a new world of possibilities for oDestTable := DB_Dest.Tables.Item(lcv2, DB_Dest);
managing your enterprise SQL Servers. ∆ // In case the table doesn't exist.
if not (Assigned(oDestTable)) then
Resources begin
if Assigned(CompareUI) then
§ Compass Technology Management: http://www.compass.net CompareUI.Add(' ** Missing Table: ' +
§ Object-oriented JAVA Enterprise Architecture Experts: oSourceTable.Name);
OOP.COM, http://www.oop.com Break;
§ Microsoft SQL-DMO FAQ: http://msdn.microsoft.com/library/ end;
// Looking for the same table.
techart/msdn_dmoovrvw.htm
if UpperCase(oSourceTable.Name) =
§ Object-oriented Programming in Delphi and Java: http:// UpperCase(oDestTable.Name) then
www.oop.com begin
lFound := True;
// Check each column.
if Assigned(CompareUI) then
Jason ‘Wedge’ Perry is a system architect for OOP.COM in Chesapeake, VA. CompareUI.Add(' Column Discrepencies');
Prior to accepting this position, Wedge was a self-employed consultant in if Assigned(CompareUI) then
development positions ranging from grunt programmer to system architect. CompareUI.Add(' --------------------');
lColErr := False;
In his spare time, Wedge races a Kawasaki KX250 moto-cross motorcycle for
for lcv3 := 1 to
the Elizabeth City MX Club. oSourceTable.Columns.Count do begin
// Test the data type.
if oSourceTable.Columns.Item(lcv3).Datatype <>
oDestTable.Columns.Item(lcv3).Datatype then
oSourceTable.Columns.Item(lcv3).
PhysicalDatatype + ' ' +
oDestTable.Columns.Item(lcv3).
PhysicalDatatype);
lColErr := True;
end;
// Test the AllowNulls property.
if oSourceTable.Columns.Item(lcv3).AllowNulls<>
oDestTable.Columns.Item(lcv3).AllowNulls then
begin
if Assigned(CompareUI) then
CompareUI.Add(' AllowNulls: ' +
oSourceTable.Columns.Item(lcv3).Name +
IntToStr(ord(Boolean(oSourceTable.
Columns.Item(lcv3).AllowNulls))) + ' '+
IntToStr(ord(Boolean(oDestTable.
Columns.Item(lcv3).AllowNulls))));
lColErr := True;
end;
// Test the Length property.
if oSourceTable.Columns.Item(lcv3).Length <>
oDestTable.Columns.Item(lcv3).Length then
begin
if Assigned(CompareUI) then
CompareUI.Add(' Length: ' +
oSourceTable.Columns.Item(lcv3).Name +
IntToStr(oSourceTable.Columns.Item(
lcv3).Length) + ' ' + IntToStr(
oDestTable.Columns.Item(lcv3).Length));
lColErr := True;
end;
Break;
end;
// If no errors, then put a NONE in.
if not lColErr then
if Assigned(CompareUI) then
CompareUI.Add(' NONE');
end;
end;
if not lFound then
// Update the UI;
if Assigned(CompareUI) then
CompareUI.Add(' **Table: ' + oSourceTable.Name +
' Not found in destination database.');
end;
end;
By Ron Loewy
Palm Conduits
Part II: Writing a Sample ToDo Application
I n Part I of this series, we introduced the concept of programming for the Palm handheld
device and the use of conduits, which performs the data synchronization between the Palm
and the PC. We also introduced EHAND Connect, a COM-based product that allows you to
write conduits with every Windows development tool that can create automation objects.
In this half of the series, I’ll demonstrate the use of to the user. The record will remain in the database
EHAND Connect to create a conduit in Delphi by until it is synchronized with the Palm device; oth-
writing a simple ToDo application that will store erwise, we won’t know that the record (assuming
information from the Palm device ToDo appli- it exists on the Palm) needs to be removed. In our
cation to a Paradox database. We will also write application, however, all the records are always dis-
a conduit that synchronizes between the Paradox played, including the “deleted” ones that are only
database and the Palm device. marked as deleted for synchronization purposes.
Synchronization Strategy The class provides some utility methods, including SetPalmRecord,
When we need to synchronize databases on different devices, we need which sets the attributes and properties from an EHAND Connect
to determine a strategy for clash resolution. Consider the case where Palm Data Record interface pointer, and SetPCRecord, which sets these
the same record is modified on the PC: It’s also modified on the Palm attributes and properties from the current record of a TTable instance.
device before synchronization can be performed. What should our
strategy be when we come to synchronize the databases? Should the Synchronization in Action
PC record prevail, or should the Palm record prevail? The project ToDoSync.dpr was created using Delphi’s ActiveX
Library wizard. It includes the automation object class, named
The situation could be even more complicated. What happens to a DIConduit, that hosts our code.
record that was modified on the Palm device, but was deleted on the
PC? Should we remove the record from both devices, or should we The file, SyncUnit.pas, contains the synchronization code and imple-
update the record on the PC and “undelete” it? ments the strategy we discussed earlier. I created an Automation
object (using Delphi’s Automation Object wizard) and used the
The truth is that there are no absolute answers. What you decide Type Library editor to add the OpenConduit method. This is the
to do is the way your application resolves these conflicts. Taking method called by EHAND Connect when the conduit code starts the
the first scenario (the record modified on both platforms before synchronization process.
synchronization), we can solve the problem by adding a Modifica-
tionTime field that is updated whenever the record is modified. In In OpenConduit, we set the variable ConduitObj to point to the
this case, the more recently modified record will prevail. Another conduit interface passed by EHAND Connect. We call the func-
solution (the one I am taking in the sample application) is to opt tion HandleUser, which checks against, and updates, the Users
for handheld modification over PC modification; if both records table. In a real application, the user information would be used
that need to be reconciled have been modified, I prefer the Palm to synchronize information only against the user information, but
modification. for simplicity’s sake, we assume that all the records in the Entries
table will be synchronized. I won’t discuss HandleIUser in this
In the sample application, I decided to ignore the archive bit that article, but you can inspect the source to see how the information
can be assigned to a Palm record. When this bit is turned on, the is retrieved from the Palm device.
PC needs to store a copy of the record for archival purposes during
synchronization. However, the size of the code that is needed to The next call is to the DefineSchema method. This method opens the
illustrate basic synchronization that takes care of essential stuff, like ToDoDB database on the Palm device, and defines the schema (data
record addition, modification, and purging, is large enough as it is, structure) of the database. The schema on the Palm device consists
and it seemed reasonable to try and keep the size of the code small of four fields: DueDate, CompletedFlag, Desc, and Note. Notice the
since this is an article, not a book about Palm programming. The use of field type constants (eDate, eByte, and eString) in the call to the
strategy I decided to follow is summarized in the table in Figure 2. DefineField method of the conduit interface:
Final Steps
To activate our conduit code, we need to register it with the
HotSync.exe application. After building the project in Delphi and
registering it (select Run | Register ActiveX Server from Delphi, or run
regsvr32.exe from the command line), I used CondCfg.exe, a utility
that can be found in the Bin sub-directory of the EHAND Connect
installation (C:\program files\ehand\ehand connect on my machine).
Using this utility, you need to associate the conduit with an appli-
Figure 3: The data in the Palm ToDo List application cation installed on the Palm device. We could, of course, associate
after synchronization. the conduit with the ToDo application, but this would remove the
standard ToDo conduit that ships with the Palm Desktop software.
Because we don’t really want to replace this conduit, and just want
PC to the Hand Held device, Copy from the Hand Held device to disable it while we test our code, I chose to associate our conduit
to the PC, or synchronize both. In the sample application, I only with the Calc application (the Palm application that displays a
implemented the synchronize both option, because it’s the most calculator), which does not store data in a database and therefore
common and most complicated option (the others are sub-sets). does not need to synchronize information with a desktop. It is
thus perfect for our sample. Obviously, in the real world, you will
Complete synchronization is performed in the method called probably synchronize with a custom application you created and
SyncBoth, which we’ll examine in a moment. After synchronization installed on the Palm device, and you will associate the conduit
is finished, the database is closed and an entry of our success is with this application.
written to the synchronization log. The synchronization log can be
inspected on the Palm device after the HotSync program finishes the At this point (before adding the reference to our conduit code), if
synchronization process. you use the ToDo application on the Palm device, I would suggest
using HotSync to synchronize your data with the PC. The data we
SyncBoth is the method that performs the actual data synchroniza- might scramble playing with the conduit code can then be restored
tion. Let’s take a “grand” view of its operation: later from the PC.
One. The “suspect” records are read from the database into a memory Right-click on the HotSync application’s icon in the tray and choose
structure (held in a TStringList named DatabaseRecords) using the Custom from the popup menu. Select the To Do List conduit from the
DBToMemory method. Initial suspect operations are assigned to the list box and click the Change button. In the Change HotSync Action
memory records, e.g. opModifyPalm, opAddPalm, etc. dialog box, set the action to Do Nothing and click the OK button. This
will ensure that the records we add, modify, or delete to the Palm
Two. All records from the Palm device are read into a memory database will not find their way into the PC application and we’ll be
structure, held in a TStringList named PalmRecords. The record’s able to restore the real data later.
attributes are inspected, and an initial operation is assigned to
the memory record. At this time, clashes are inspected against the Activate the CondCfg.exe application mentioned previously and use
DatabaseRecords structure, and we determine the operation that the Add button. Set the Creator ID to “calc” (no quotes). This will
will be taken based on the strategy we discussed previously. If associate the conduit with the Calc application. In the Conduit Set-
needed, the DatabaseRecords record object’s operation property is ting dialog box, point the DLL Name entry to the file EHConnect.dll
modified. At the end of this stage, we have all the information we in the EHAND Connect installation directory, C:\program files\
need to physically perform the synchronization. ehand\ehand connect\EHConnect.dll by default. In the Class Name
entry, enter the name of the conduit automation object we created —
Three. The UpdatePalmRecords method is called. It traverses the ToDoSync.DIConduit in our example.
Conclusion
While the official Palm Conduit Development Kit supports only
Visual C++, with a little help from EHAND Connect, every
COM-enabled development tool can be used to create conduits
to exchange and synchronize data between a Palm device and a
Windows desktop.
The project files referenced in this article are available on the Delphi
Informant Magazine Complete Works CD located in INFORM\00\
JUL \DI200007RL.
Ron Loewy is a software developer for HyperAct, Inc. He is the lead developer
of eAuthor Help, HyperAct’s HTML Help-authoring tool. For more information
about HyperAct and eAuthor Help, contact HyperAct at (515) 987-2910, or visit
http://www.hyperact.com.
I n brief, Active Server Pages (ASP) are text files that contain standard HTML commands
that are processed by a Web browser, and scripting commands that are executed on
a Web server. These scripting commands can be written in VBScript, JScript (the Internet
Explorer equivalent of Netscape’s JavaScript), or any other language for which a valid
scripting engine is installed, e.g. Perl and Python. Using these scripting commands,
you can introduce basic programming operations into your HTML, such as performing
conditional execution and expression evaluation, as well as invoking the features of COM
servers installed on the server. In this article, we’ll take a look at using these scripting
commands to achieve the results you want.
When an HTTP request for an ASP document § ASP code is often easier to write than other
is received by an ASP-enabled Web server, such CGI programs, in that it doesn’t require com-
as Microsoft’s Internet Information Server (IIS), pilation or the installation of a secondary pro-
the server processes the scripting commands cessor (although when you create a COM
within that file. The output generated by the server designed to be accessed by an ASP, the
scripting language is combined with the stan- COM server requires compilation).
dard HTML in the ASP to produce the HTTP
response returned to the Web browser. In other As noted already, ASP can only be used with an
words, the server doesn’t return the ASP docu- ASP-enabled Web server. Initially, ASP support
ment to the browser. Instead, it returns static was introduced in Microsoft’s Internet Infor-
HTML, plus the output from the processing mation Server (IIS). However, any Windows-
of the scripting commands. In most cases, the based server can potentially support ASP. This
returned data is pure HTML, although it can is crucial in that ASP servers use COM technol-
potentially contain any valid MIME (Multi- ogy, which is supported almost exclusively by
Purpose Internet Mail Extensions) content type. the Windows operating system. (Although some
non-Windows implementations of COM exist,
There are numerous benefits to using ASP: these are quite rare.)
§ The user can’t readily access your code. Unlike
with browser-side JavaScript or VBScript, in Before continuing, it’s worth mentioning that JSP
which the scripting instructions are delivered (Java Server Pages) is a technology similar to ASP. The
intact to the browser, ASP commands aren’t primary difference is that JSP-enabled servers aren’t
sent to the Web browser. limited to Windows-based servers. Any Web server
§ ActiveX objects accessed using ASP — when that runs on a Java 2-supporting operating system
Microsoft Transaction Server (MTS) isn’t (JDK 1.2) can be a potential JSP-enabled server.
being used — are loaded into the process
space of the server, which reduces overhead Using ASP Commands
when compared with CGI (common gate- In addition to basic HTML, ASP contains four
way interface) server extensions. An ASP types of commands: primary scripting com-
object can also be processed in a separate mands, output directives, processing directives,
process space, reducing the likelihood that and include statements.
a crashing ASP object will bring down your
Web server. With the exception of include statements, com-
§ ActiveX objects used by IIS can be installed mands contained within an ASP document are
into MTS, providing activation on demand, enclosed within <% %> delimiters. As mentioned
transactions, resource sharing, crash protec- earlier, these commands can consist of VBScript,
tion, and additional security. JScript, or any other scripting language for which
<%= expression %> There are two special classes of variables that are visible to more than
one ASP page. These are Session and Application variables. Session
The value of expression can be a variable, constant, formula, property, variables are available to all pages accessed by a particular user within
method, or function. For example, the following line inserts a string a session. By comparison, Application variables are shared by all ASP
indicating the current time, based on the internal clock on the Web pages in an ASP application. These variables are stored using the
server (because the Web server can be at any location in the world, this Session and Application built-in objects, respectfully. These objects
isn’t necessarily the same time as in the time zone of the Web browser!): are described in greater detail later in this article.
The current time on this server is: <%=Now%> You access Session and Application variables by passing the name of
the variable to the Session or Application object. For example, the
Processing directives. Processing directives provide instructions to following code stores the session start time in a Session variable:
the ASP processing engine and typically appear at the beginning of an
ASP page. Processing directives use the following syntax: <% Session("SessionStart") = Now %>
<% @ keyword=value%> All pages on the site accessed by a specific user can then read this
value using the following statement:
The keyword must be a reserved word recognized by the ASP engine
and it must be separated from the @ sign by at least one space. The You began your session at <% = Session("SessionStart") %>
The include statement has the following syntax: <% = intobj.parseInt(somestring) %>
<!--#include VIRTUAL|FILE="dirname/inc.htm"--> Although the availability of Java objects within ASP pages provides
you with some flexibility, Java objects typically can’t access the
If the keyword VIRTUAL is used, the dirname part is a virtual built-in objects, nor can they be part of an MTS transaction. Con-
directory (defined through server configuration). If FILE is used, sequently, their use is usually reserved for special operations not
the directory can either be an absolute or a relative directory. For normally available through the scripting language of COM objects.
example, if you have created a virtual directory under IIS named
Chunks, you can use the following include statement to include the Using methods. You call an object’s methods using dot notation to
header.htm HTML in your ASP page: invoke the qualified method name. If the method requires one or
more parameters, then follow the method name using the syntax
<!--#include VIRTUAL="Chunks/header.htm"--> of your scripting language. For example, to invoke the built-in
Response object’s Write method, you use a statement similar to the
In addition to including static HTML, include files can also reference following example:
ASP pages or ASP segments. However, note that ASP segments refer-
enced by include directives are processed on the server before the <% Response.Write "Text to return to the browser" %>
referencing ASP page is processed. This processing is unconditional.
That is, all included ASP pages are processed — even those where the Using properties. You reference an object’s property by using dot
include statement is referenced within an <%IF%> statement — before notation to reference the qualified property. You write to the prop-
the first primary command of the referencing ASP page is processed. erty by placing the property reference on the left side of an assign-
ment statement. You read the property by including the qualified
Using Objects property name in an expression. Because properties in COM make
ASP objects are Automation servers, and therefore can be either use of accessor methods, reading and writing properties of ASP
in-process servers or out-of-process servers. There are two general objects results in the execution of the defined read and write meth-
classes of objects that you can use from an ASP: built-in objects ods, respectively. From these methods, you can invoke custom code,
and custom objects. or even reference both built-in and custom ASP objects.
The Server and Application objects referenced previously are examples Built-in objects. ASP pages have access to a number of objects created
of built-in objects. These objects are available to all ASP pages. These automatically by the ASP-enabled server. These objects can be refer-
built-in objects are automatically created for you by the server, and enced by your server-side VBScript, permitting you to get information
can be referenced within any primary commands. By comparison, you about the environment, as well as to control the behavior of your
must explicitly create custom objects before you can access them. You ASP. The following built-in objects are available to all ASP pages:
create custom objects by calling the CreateObject method of the Server Application, ObjectContext, Request, Response, Server, and Session.
built-in object, passing to it the PROGID of the registered object. For
example, if you have registered an Automation server with the PROGID The Application object permits you to store application-wide data.
of Project1.Text, you can create an instance of it, and assign this instance An ASP application is a set of related ASP pages under a common
to the variable DelphiASPObj using the following command: directory structure. The application starts the first time a page of the
application is loaded, and shuts down when the server is shut down.
<%
Set DelphiASPObj = Server.CreateObject("Project1.Test") The ObjectContext object is used to start, commit, abort, and com-
%> plete transactions.
You can also use the HTML <OBJECT> tag to create an instance of The Request and Response objects permit you to get information about
an object. When doing so, however, you must include the RUNAT the HTTP request and to control the HTML response. For example,
directive with the Server option to ensure that this object runs on the the Request object permits you to read query strings, cookies, binary
server, and not on the browser’s machine: data, client certificates, and server variables. The Response object, by
comparison, can be used to write cookies, control page caching (affect-
<OBJECT RUNAT=Server ID=DelphiASPObj ing caching servers), and write the response, among other operations.
PROGID="Project1.Test"></OBJECT>
Change the generated code to look like that shown in Figure 6. Now save the .asp file to the root directory of your Web server.
Make sure you keep the file extension as .asp. Next, using the
CO (File | Open) selection from your Web browser, type http://
localhost/delphiasp.asp. (This assumes you’re testing this appli-
cation using a local Web server. If you’re using a Web server on
another machine, replace localhost with either the domain name,
the machine name, or the IP address of your Web server.) After a
moment, the page should load. Figure 7 shows what this page looks
like in Netscape Communicator.
<HTML>
<BODY>
<TITLE> Testing Delphi ASP </TITLE>
<CENTER>
<H3> You should see the results of your Delphi Active
Server method below </H3>
</CENTER>
<HR>
Figure 3: Adding the SayHello method in the Type Library editor. <%
Set DelphiASPObj=Server.CreateObject("Project1.DelphiASP")
DelphiASPObj.{ Insert Method name here. }
procedure TDelphiASP.SayHello; %>
var <HR>
i: Integer; </BODY>
begin </HTML>
with Self.Response do begin
Figure 5: Original ASP page.
Write('This is the response '+
'from the Delphi ASP object. <P>Here is a big ' +
'<H2><CENTER><I>Hello !!</I></CENTER></H2><P>' + <HTML>
'This request has come from a '); <BODY>
Write(Request.ServerVariables.Get_Item( <TITLE> Testing Delphi ASP </TITLE>
'HTTP_USER_AGENT')); <CENTER>
Write(' agent at address '); <H3> You should see the results of your Delphi Active
Write(Request.ServerVariables.Get_Item('REMOTE_HOST')); Server method below </H3>
Write('.<BR> Furthermore, the last page you '+ </CENTER>
'visited was (none if blank): '); <HR>
Write(Request.ServerVariables.Get_Item( <% Set DelphiASPObj = Server.CreateObject(
'HTTP_REFERER')); "DelphiASP.DelphiASP") DelphiASPObj.SayHello %>
Write('<P>The request method was '); <HR>
Write(Request.ServerVariables.Get_Item( </BODY>
'REQUEST_METHOD')); </HTML>
Write('<P>There were ');
Write(IntToStr(Request.QueryString.Count)); Figure 6: Modified ASP page.
Write(' items in the query string.');
if Request.QueryString.Count <> 0 then
begin
Write('<P>The query string is :');
Write(Request.QueryString);
end;
if Request.Form.Count <> 0 then
begin
Write('<P>There were ');
Write(IntToStr(Request.Form.Count));
Write(' items sent by an HTML FORM.');
Write('<P>These are ');
for i := 1 to Request.Form.Count do begin
Write('<BR>');
Write(Request.Form.Key[i]);
Write(' : ');
Write(Request.Form.Item[i]);
end;
end;
if (Request.QueryString.Count = 0) and
(Request.Form.Count = 0) then
Write('<P>There was no data sent with this request.');
end;
end;
Figure 7: The updated ASP page as seen in Netscape
Figure 4: Demonstrating the use of several Response.Write methods. Communicator.
<HTML>
<TITLE>Demonstrating an HTML form calling an ASP</TITLE>
<BODY>
<H2>Enter some data into this HTML form</h2>
<FORM Method=GET Name=Form Action=delphiasp.asp>
<BR>First Name :<INPUT TYPE="text" NAME="firstname">
<BR>Last Name :<INPUT TYPE="text" NAME="lastname">
<BR>Age :<INPUT TYPE="text" NAME="age">
<INPUT TYPE="hidden" NAME="userstatus" VALUE= "new">
<P><INPUT TYPE="submit" VALUE="Enter">
</FORM> Figure 12: HTML opting for the GET action rather than the
</BODY> POST action.
</HTML>
Figure 9: An HTML form calling an ASP page. Calling Your ASP from an HTML Form
As mentioned earlier, if you had displayed your ASP-generated HTML
by clicking on a link, the HTTP_REFERER server variable would
contain the URL of the linking page. Likewise, if this link was gener-
ated by either a GET or POST action from an HTML form, the ASP
would display the data sent by the form.
This effect is demonstrated using the HTML page defined by the text
in Figure 9, which is found in the TESTASP.HTM file located along
with the sample code for this article.
If you save this page to the same directory in which you have placed
the ASP page, then load it into your browser and enter data into the
Figure 10: The HTML text from Figure 9 displayed in a browser.
text input fields, your browser will look something like that shown
in Figure 10.
If you click the Enter button on the HTML form to invoke your ASP
object, the resulting HTML downloaded to your browser may look
similar to that shown in Figure 11.
Alternatively, if your HTML form used the GET action (the default)
instead of POST, the resulting page may look something like that
shown in Figure 12.
Conclusion
In this article, we’ve seen a number of different ways you can benefit
from the use of ASP in Web development. And when you add
ASP’s ability to utilize Delphi’s COM capabilities, Web development
becomes all that much more robust. ∆
Figure 11: The resulting HTML after invoking your ASP object. The files referenced in this article are available on the Delphi
Informant Magazine Complete Works CD located in INFORM\00\
Because this page was displayed using the Open Page feature, there JUL \DI200007CJ.
was no HTTP referrer. However, if you loaded this page by clicking
an anchor tag link (<A>), or submitting an HTML form with either
a GET or POST action, the URL of that linking page would be Cary Jensen is president of Jensen Data Systems, Inc., a Houston-based database
displayed following the “last page you visited” text. development company. He is co-author of 17 books, including Oracle JDeveloper [Oracle
Press, 1998], JBuilder Essentials [Osborne/McGraw-Hill, 1998], and Delphi in Depth
If you now select View | Page Source, you will see the HTML gener- [Osborne/McGraw-Hill, 1996]. He is a Contributing Editor of Delphi Informant Maga-
ated by the ASP (see Figure 8). Notice that what you see here is plain zine, and an internationally respected trainer of Delphi and Java. For more information,
HTML. None of the ASP commands are visible, because the server visit http://www.jensendatasystems.com, or e-mail Cary at [email protected].
replaced them with the HTML text generated by the ASP object.
By Bill Todd
Windows Installer brings a number of new con- want to download the Windows Installer SDK
cepts and terms to the world of software installa- (about 6MB) from http://download.microsoft.
tion. If you are already familiar with Windows com/msdownload/platformsdk/i386/
Installer, you can skip this section. In introduc- InstallerSamples/IntelSDK.msi. Even if you do
ing a new installation technology for its new not plan to use the SDK to develop your own
family of operating systems, Microsoft did not installation software or interact with the Win-
want to force developers to create one installa- dows Installer through COM, the SDK Help
tion using traditional technology for Windows file is a valuable resource for learning about Win-
95, 98, and NT 4, and another using Windows dows Installer.
Installer for Windows 2000 and Millennium. To
solve this problem, Microsoft has created ver- Windows Installer offers a host of new features,
sions of Windows Installer that will run on Win- such as self-healing applications. When you launch
dows 95, 98, and NT 4. a self-healing application, it will automatically
detect missing or damaged files, and automatically
To install software using Windows Installer, you reinstall the missing files. An application can ask
must create a special relational database that con- Windows Installer if a specific application feature
tains all of the information Windows Installer is installed, and alter its appearance and behavior
needs to install your software. The database is con- based on the answer. In large organizations, admin-
tained in a single file with an .msi extension. Wise istrators can advertise applications that are available
for Windows Installer lets you build this database. to users. These applications will appear on the
user’s Start menu, just as if they were installed,
Learning to use Wise for Windows Installer, or and Windows Installer will automatically install
any other Windows Installer tool, is much easier the application the first time the user opens it.
if you have a basic understanding of Windows
Installer concepts and terminology. You will find Windows Installer also lets you install features of your
the “Roadmap to Windows Installer Documenta- application on demand. If a user chooses a feature
tion” at http://msdn.microsoft.com/library/psdk/ that isn’t installed, one of two things will happen,
msi/leglivt_0002.htm. From this page, you can depending on whether the application was installed
access all of the information Microsoft has pub- from removable media or a network location. If the
lished about Windows Installer. You may also original installation was from a network location that
Once you’ve defined your features, you must define the files
that must be installed for each feature. Choose Files in step 1
to display the file selection screen, shown in Figure 3. When
Figure 3: Adding files to your installation. the Files display appears, the Features drop-down list, just
Step 4 consists of a single choice, Dialogs. Here, you set which dialog
boxes will be displayed when a user runs your installation. If you
choose to include the License or Read Me dialog boxes, you can enter
the text of your license agreement or read me file in .rtf format, or
import the text from an existing .rtf file.
below the buttons for steps 5 and 6, is enabled. All of the files and folders There must be at least one release, so Wise for Windows Installer
you add to your installation apply to the selected feature; that is, they will automatically creates one, named Default. You can click the Details
be installed when that feature is installed. button to change the name or other properties of the Default release,
and click the Add button to add a new release. Figure 4 shows the
The lower-left pane in Figure 3 shows the directory structure on Release Settings dialog box used to add or edit a release.
the target computer. To add a new subdirectory, select its parent in
the directory tree and click the New Folder button. The upper-left The key setting in this dialog box is EXE Options. If you know the
pane shows the directory structure on your computer. To add files destination computer is running Windows 2000, or that it already has
to a folder, locate the files on your computer, select them, and Windows Installer installed, you can safely choose the Do not create
click the Add File button to add them to the selected folder in the an EXE file option. In this case, only the MSI database file required by
destination computer pane. Windows Installer will be created when you compile your installation.
If the target computer is running Windows 95, 98, or NT 4, and
If you need to add an entire directory or directory structure, select you cannot be sure that Windows Installer has already been installed,
the parent directory on the destination computer, select the directory you will want to choose one of the options that creates an installation
you want to add to the installation on your computer, and click the EXE. When you create an installation EXE, launching the EXE on
Add Wildcards button. You can specify both include and exclude filters the destination computer will first install Windows Installer, then use
to include all files that match the template you enter, or exclude files Windows Installer to install your application. Creating an EXE adds
that match a template you define. For example, you could set the about three megabytes to the size of your installation.
include filter to *.EXE;*.DLL;*.OCX to include all of the executable,
DLL, and ActiveX control files in the directory. You can also include There are two EXE options. The first is Single File Executable, and is
subdirectories, and tell Wise for Windows Installer to automatically suitable for a single file installation that will fit on a CD-ROM, or will be
update the installation each time it’s compiled to reflect the files downloaded or installed from a network. If you need to create installation
currently in the source directory. diskettes or CDs, choose Executable That Launches External MSI.
The last option in step 1 is Merge Modules. A merge module is a Moving to the Build page lets you set the properties, summary
pre-compiled installation designed to allow third parties to give you items, and features to be included in the selected release. Properties
a way to install their products with your own. Hopefully by the time are variables supported by Windows Installer, such as ProductName,
you read this there will be available a merge module for the BDE. ProductVersion, and DiskPrompt. If you want to know the purpose
To add a new merge module to the feature you have selected, click and legal values for all of the property variables, you will need
the Add New button to display the Add Merge Module Wizard. You the Windows Installer SDK Help file or other Windows Installer
can choose a merge module from the list of modules that comes with documentation. Summary information consists of information that
Wise for Windows Installer, or click the Browse button to locate a can be displayed by right-clicking the MSI file. Examples are Author,
module on your hard disk. If the feature you are working with needs Comments, Keywords, and Minimum Installer Version.
a merge module that you’ve already added to another feature, click
the Add Existing button and choose the module you need. The Build page also shows the feature tree with a checkbox next to
each feature. Simply check the features you want included in this
Moving to step 2 gives you options to install icons; add, delete, or release. The Media page, in step 5, allows you to define the media
change registry settings; create, delete, or change .ini file settings; settings for the selected release. You can opt for a single installation
install Windows services; or add ODBC drivers or data sources. file with all installation files compressed into the MSI database, or
These options are almost identical to the corresponding options in you can choose to have files compressed into CAB files outside the
Wise InstallMaster 8.0, another installation product from Wise Solu- MSI. This is the option you must choose if your installation will span
tions. The only difference is that you set each of these options for multiple disks. You can use any type of media for your installation by
each feature in your installation. setting the media size and the cluster size.
Step 3 lets you check the user’s system configuration for a minimum If your installation will span multiple disks, you must create and
version of Windows or Windows NT, a minimum screen resolution, enter the path to a subdirectory to hold the contents of each disk.
The sixth — and final — step in creating your installation starts by Wizards
prompting you for project information. The most important item Wise for Windows Installer includes six wizards to guide you
on this page is the Product Code. The Product Code is a GUID through various tasks. The wizards are described in Figure 6.
used by Windows Installer to determine if this product is already
installed on the destination computer. This code must be different Documentation
for different languages and versions. The Summary page provides Wise for Windows Installer comes with a 132-page Getting Started
another opportunity to enter the summary information described Guide that provides a great introduction to using the product. The
earlier in step 5. The Upgrades page allows you to identify features description of each feature begins with an overview and continues
of an existing installation to remove as part of this installation. with step-by-step instructions. If you’ve had no prior exposure to
The final three options are Windows 2000, Status MIF, and Code Windows Installer, you will find a few places in the manual where
Signing. The Windows 2000 page lets you configure the options you will be scratching your head trying to understand some of the
available to the user in the enhanced Add/Remove Programs dialog terms and concepts. To avoid this, download the Windows Installer
box in Windows 2000. On the Status MIF page, you can configure SDK and read the introductory sections in the Help file, or visit the
your installation to be run under Microsoft Systems Management Windows Installer Web site and read the introductory documenta-
Server. The Code Signing page allows you to create a code-signed tion there. An extensive online Help file gives you context-sensitive
single file installation for Internet distribution. help in both the Installation Expert and the Setup Editor.
Conclusion
Windows Installer is a giant step forward in making software
installations safe, and Wise for Windows Installer is the perfect
tool to let you take advantage of this new technology. The installa-
tion expert will make anyone who has used Wise 7 or 8 feel right
at home, and help new users build their installations with ease.
The ability to create installation executables that will automati-
cally install Windows Installer on any machine lets you switch
to Windows Installer now for all of your installations. If you’ve
been using Wise products, the import wizard makes the move to
Windows Installer very easy. Again, Wise Solutions has done a
superb job of providing both ease and power in a single product.
The Setup Editor provides a fast, intuitive interface that will take
you all the way to the lowest level of building an installation:
editing the Windows Installer tables directly. This is the future of
Windows software installation. ∆
Bill Todd is president of The Database Group, Inc., a database consulting and
development firm based near Phoenix. He is co-author of four database program-
ming books, author of over 60 articles, a Contributing Editor of Delphi Informant
Magazine, and a member of Team Borland, providing technical support on the
Borland Internet newsgroups. He is a frequent speaker at Borland Developer
Conferences in the US and Europe. Bill is also a nationally known trainer and has
taught Paradox and Delphi programming classes across the country and overseas.
He was an instructor on the 1995, 1996, and 1997 Borland/Softbite Delphi World
Tours. He can be reached at [email protected].
Be Resourceful
I f you routinely (no pun intended) use the keyword resourcestring in your programs, read no further. This
article would merely be “preaching to the choir.” But for those of you who are asking: “What in the Dickens is
resourcestring?” read on.
In the “olden” days of Delphi programming (prior to version 4), To take the plunge into the brave new world of using the resourcestring
there were basically two ways of using strings in your pro- keyword, simply add a unit to your project, name it ResStrngs (or
grams. You could either embed them into the source code using whatever), and then add any string literals (especially those the user
string literals: would see — contents of string lists, feedback, error messages, etc.) in
the interface section of the unit, like this:
MessageDlg(
'Leave your stinkin' mitts off that button, fool!', unit ResStrngs;
mtError, [mbOK], 0);
interface
Or, you could create a text file with an .RC extension, such as: resourcestring
// Famous military personalities.
STRINGTABLE DISCARDABLE SGeneralElectric = 'General Electric';
{ SGeneralMills = 'General Mills';
1 "Dialog Expert" SGeneralUsage = 'General Usage';
2 "Dialog Expert from demonstration Expert DLL." SGeneralHospital = 'General Hospital';
3 "Application Expert" SGeneralLedger = 'General Ledger';
4 "Application Expert from demonstration Expert DLL" SGeneralProtectionFault = 'General Protection Fault';
5 "&Create" SGeneralSQLError = 'General SQL Error';
6 "&Next" SGeneralLeeSpeaking = 'General Lee Speaking';
7 "An application name is required!" SCorporalPunishment = 'Corporal Punishment';
8 "The application name is not a valid identifier." SSgtFury = 'Sgt. Fury';
9 "The path entered does not exist." SSgtCarter = 'Sgt. Carter';
10 MAIN.PAS" SSgtSchultz = 'Sgt. Schultz';
11 "MAIN.DFM" SSargentShriver = 'Sargent Shriver';
12 "MAIN.TXT" SCaptKangaroo = 'Capt. Kangaroo';
... SCaptUnderpants = 'Capt. Underpants';
// Variable names. SColonelKlink = 'Colonel Klink';
20 "StatusLine" SPrivateBenjamin = 'Private Benjamin';
... SPrivateProperty = 'Private Property';
} SLeftenantDan = 'Leftenant Dan';
SMutineerChristian = 'Mutineer Christian';
SAtlantaHawks = 'Atlanta Hawks';
Then all you had to do was compile it into a resource file, add // Kindly reminders.
it to a Delphi project or unit, compile it using the command-line SDontSleepInTheSubwayDarlin =
tool Brcc32.exe, and then programmatically extract the strings in the 'Don't sleep in the subway darlin'';
// Additional silly strings left as a reader exercise.
appropriate places in your code using the LoadStr function. That
may have seemed a bit too much of a bother, so you may have stuck implementation
with the tried and true — and now tired — old way of introducing
string literals ad infinitum into your source code. end.
The resourcestring keyword has come to the rescue. It gives us the Add this unit to the implementation uses clause of any unit that
best of both worlds: the simplicity (almost) of string literals, and the refers to any of its strings. Then access them in this way:
convenience of storing all strings in a central location. Also, using
resourcestring provides better memory management, because the if ItIsPetulasVirtualHusband and HeIsLate then
strings in the resourcestring section are saved as resources associated MessageDlg(SDontSleepInTheSubwayDarlin,
mtInformation, [mbOK], 0);
with your application.
R obert A. DelRossi is president of TurboPower Software Co., one of the oldest and most successful third-party
producers of Delphi tools. In the most recent Delphi Informant Magazine Readers Choice Awards (published in the
April, 2000 issue), TurboPower scored big — even bigger than last year. In 1999, TurboPower won four awards: Async
Professional won the Best Connectivity Tool, Memory Sleuth won Best Utility, Orpheus 3 won Best VCL, and SysTools won
Best Add-in Library. This year, TurboPower came in first place in five categories and placed second in two others. Memory
Sleuth’s new incarnation, Sleuth QA Suite, won the award for Best Testing/Debugging Tool, with each of the other
three 1999 winners repeating. Abbrevia won the award for Best VCL Component, and two other products — FlashFiler
and LockBox — came in second in their respective categories. Especially remarkable was Delphi Informant Magazine’s
decision to give a new award this year, Company of the Year, won by — you guessed it — TurboPower. You can find more
information about TurboPower products at its Web site, http://www.turbopower.com.
DelRossi has not always been the president of this company. He became president I imagined that the biggest challenges would be
began many years ago as a TurboPower customer. After he joined financial, but it’s really finding the right people to maintain your
the company as its first marketing director, he became vice president, company’s growth, and then keeping them engaged.
and, finally, its third president.
DI: You must be gratified with the great success that TurboPower
DI: You’ve had a varied and impressive career at TurboPower. Could has enjoyed in recent years, particularly the unprecedented show-
you elaborate a bit more about your experiences at TurboPower? ing in this year’s Delphi Informant Magazine Readers Choice
What advice do you have for the developer who aspires to embark Awards. To what do you attribute this success? Is there one factor
upon a similar career path? — the quality of the software, the documentation, the support —
that stands above all the others as the key to their
DelRossi: For starters, TurboPower is a great place popularity with users?
to work. That’s always been the case, and our man-
agement team has dedicated itself to keeping it that DelRossi: You probably won’t be surprised to hear
way. Many companies lose the positive corporate me say that I believe our success comes from several
culture they start with, especially as they grow. At factors. I think our customers see us as providing
TurboPower we regularly review not just how well more than just good code. When they buy one of
the products are doing, but also how the staff is our products, they’re really making us a partner in
doing, and we make adjustments to meet the chal- their projects. If they succeed, we succeed — plain
lenges of a growing company. Good communication and simple. And if they fail, they’ll look to us and
among team members — the engineers and our ask why — particularly if they feel that our products
terrific operations group — is a key to our success. let them down. So, we try to produce a unified set
of services: great code, great documentation, great
For me personally, and this is especially true having Robert A. DelRossi support, and a willingness to learn, accept criticism,
come from a different kind of business, I’ve learned that and help our customers achieve their very best work.
managing highly intelligent people requires a certain amount of flex-
ibility. Software development is still not 100 percent science. There’s a DI: I’d like to explore one of your newer products, one that I am
large dose of art involved too. Managing risk, practicing sound business currently in the process of reviewing: Sleuth QA Suite. Many in the
fundamentals, and applying proven project management strategies is a Delphi community have been encouraging you for some time to fill
big part of successful software company management, I believe. the gap that used to exist and create a solid Delphi profiler. What
were some of the challenges in developing this tool that might have
DI: What do you feel was your biggest challenge as a developer; contributed to it taking longer than you expected?
as a manager?
DelRossi: It seemed like everyone was asking us to develop a profes-
DelRossi: As a developer, keeping up with the incredible pace of sional profiling tool for Delphi and C++Builder! For years, the big tool-
change in our industry is the biggest challenge. For most of us, the makers have focused solely on Microsoft developers. The key tools, like
older you get, the harder it seems to keep up with the amazing degree NuMega BoundsChecker, really ignored the fact that real-world devel-
of change. That’s why TurboPower is successful, I think. We make opment was being done with Borland compilers. Over time, Bounds-
it easier to get the latest technology advances into your programs Checker, and some similar products were ported to work with Borland
without having to learn everything about them first. compilers, but in our opinion, they never quite made the grade.
Without question, the biggest challenge any software executive faces Like a lot of our products, Sleuth QA Suite was born out of a very
today is finding and retaining qualified engineers. When I first real need we had internally. We needed a bug detection tool and
profiler that was an expert when it came to the VCL. The trouble ogy available for Web development. In fact, the COM object in
is, getting all that VCL knowledge into Sleuth QA Suite took a lot SysTools 3 is used extensively on our own Web site.
longer than we expected. Plus, we kept coming up with additional
capabilities that we felt were imperative to Sleuth QA Suite’s success. DI: In previous interviews with developers I have always included a
question on operating systems. Windows is very popular right now
In the end, I think we came up with a great product, and to tell the as a computing environment, but Linux is becoming such a strong
truth, though it was a lot later than we would have liked, rushing competitor that Inprise/Borland has decided to develop tools for
it out the door wouldn’t have been the answer. Building quality this environment. I noticed that you included an article on this
products sometimes takes a little longer than any of us expect. topic in the February, 2000 issue of your newsletter, Powerlines.
Please share some of your thoughts on the future growth of Linux
DI: I’d like to change the topic a bit and talk about some of the and how you see this impacting TurboPower.
other folks who work at TurboPower. For example, I’ve known
Julian Bucknall for several years and have been very impressed DelRossi: We’re incredibly excited about the potential for Linux,
with his articles and book chapters. I understand he is currently particularly with the advent of Borland’s Kylix project. For some
in the process of finishing a book on algorithms. Of course, he time now, I’ve regarded the Linux marketplace as the Windows
is just one of several of your engineers active as Delphi writers. market was back before Visual Basic. In those days, creating
Talk a bit about some of these active writers and some of the Windows programs was essentially a labor of love. You had to
reasons you not only allow this but encourage such “extra- learn all those API calls and bring out the old command-line C
curricular” activities. compiler. Then Visual Basic arrived and millions of developers
turned to it as an easy way to build Windows applications.
DelRossi: I’ve been at TurboPower four years now and I’m still awed Love it or hate it, Visual Basic changed everything for Windows.
by our programmers. I was a programmer too, once upon a time, but Even programmers who wouldn’t have considered programming
I was never anywhere near the caliber of our engineering staff. in Basic again started using Visual Basic, because it was so much
easier to build Windows programs with it.
One reason I think we’ve been so fortunate at attracting such
high-end developers is that we give our developers a chance to The Linux development world is also waiting for its Visual Basic and,
participate in every level of the product development cycle, from quite frankly, we think Kylix may be it. As it was back then, there
design to marketing. Not everyone likes every part of the process, are probably many C and C++ programmers that think their Pascal
of course, but there’s much to be learned and at the end of the day, days are behind them. But Kylix may represent such a fundamental
a greater appreciation for how each phase of the cycle develops. improvement in Linux-targeted programming that Pascal’s glory days
could be coming back. In a big way!
Another area, as you say, is to encourage our engineering staff to
write and give lectures. Again, not all of our engineers want to do DI: I’d like to conclude by talking a bit about Inprise. We’ve wit-
these things, but for those that enjoy it, this allows for a sharing nessed a lot of changes in recent years — certainly some ups and
and exchange of knowledge; it’s great for them, as well as for the downs. Among other remarkable developments, there is now a signifi-
whole company. cant merger with Corel Corporation on the horizon. What is your
perspective on some of the new developments at Inprise? How will
DI: By all reports, the latest version of Delphi seems to be one of this affect the future of Delphi and your company?
the most solid ones to appear in several years. Does this seem to be
an accurate assessment, and if so, do you feel it might help Delphi’s DelRossi: Inprise has always had great development talent and terrific
market share begin to expand? developer products. Of course, my crystal ball is no better than
anyone else’s. But from where I stand, Inprise has a better outlook
DelRossi: We’re very satisfied with this version of Delphi, although now than at any time in its recent history. Naturally, much of that
there’s lot more that could be done. Of course, all those gaps give will depend on the rate of Linux adoption.
us plenty to do!
DI: Thank you very much, and best of luck with all your future
DI: Some component-producing companies also produce versions for endeavors. ∆
other tools like Visual Basic. Have you ever considered this? What do
you see as the advantages and disadvantages? — Alan C. Moore, Ph.D
DelRossi: We’ve certainly considered expanding our product line to Alan Moore is a Professor of Music at Kentucky State University,
support Visual Basic, and COM developers generally. Many of our specializing in music composition and music theory. He has been
customers have for one reason or another — perhaps at the request of developing education-related applications with the Borland languages
a key client — moved away from Delphi to embrace COM, and they for more than 10 years. He has published a number of articles in
tell us they’d like to use our products too. various technical journals. Using Delphi, he specializes in writing
custom components and implementing multimedia capabilities in
Expanding our products to support all kinds of developers is an applications, particularly sound and music. You can reach Alan on
important part of my vision for TurboPower. As a matter of fact, the Internet at [email protected].
we’ve already begun including COM implementations in some of
our products, including Abbrevia and SysTools 3. In my view,
COM support is important for a number of reasons. Naturally, it
exposes TurboPower products to a wider audience of Visual Basic
and Visual C++ developers, but it also makes some of our technol-