October 2000, Volume 6, Number 10
Cover Art By: Arthur Dugoni
ON THE COVER 26 Columns & Rows
7 On the ’Net A Practical Guide to ADO Extensions: Part I
Internet Messaging Made Easy — Kristen Riley — Alex Fedorov and Natalia Elmanova
Need to interact with Microsoft Exchange/Outlook? Ms Riley introduces Mr Fedorov and Ms Elmanova demonstrate the use of two ADO exten-
the main CDO library with example applications that gather address lists, sions from Delphi: ADO Extensions for DDL and Security, and the Jet and
retrieve and send e-mail, and more. Replication Objects library.
FEATURES
12 Dynamic Delphi REVIEWS
Automating Word: Part II — Ron Gray 32 IBObjects 3.4
Mr Gray completes his two-part series. This month the focus turns to the Product Review by Robert Leahey
Word components available in Delphi 5, and how to link and embed
documents using OLE. 35 Delphi Graphics and Game Programming
Exposed! with DirectX
17 OP Tech Book Review by Alan C. Moore, Ph.D.
Database Persistent Objects: Part II — Keith Wood
Last month, he showed us classes that can automatically store their
published properties in a relational database. This month, Mr Wood
shares a form wizard for creating them easily. DEPARTMENTS
2 Symposium by Jerry Coffey
21 On Language 3 Delphi Tools
A Quick Way to Shortcuts — Bill Todd
6 Newsline
Mr Todd examines Windows shortcuts in detail, then shows us how to
build a custom component you can use to create and modify shortcuts 36 Best Practices by Clay Shannon
in any folder. 38 File | New by Alan C. Moore, Ph.D.
1 October 2000 Delphi Informant Magazine
Symposium
Last Man Standing
The mood at the 11th Annual Borland Conference (held this
July in San Diego) was upbeat. This hasn’t always been the case;
there were several years where many thought they were attend-
ing the last Borland conference. In particular, the Orlando
conference in 1994 had the feel of a final fond gathering
of friends before dissolution. Delphi came out the next year.
Which reminds me... this year, some of the Delphi R&D team
were wearing baseball caps with the snappy slogan: “Delphi:
Saving Borland One Quarter at a Time.” Gotta love that.
And the Borland community should be jubilant. There’s plenty
to be happy about: Delphi 6, Kylix, and — lo and behold —
JBuilder for the Macintosh. I didn’t think I could be excited by
a Macintosh product, but seeing JBuilder fire up on an iMac
was a genuine thrill. Perhaps I could create something for my
son at school... For the first time in a long time, Borland has
direction, new markets to conquer, and every reason to expect
success in those markets.
There was also a novel aspect to this year’s conference: the Kylix
previews. And, as exciting as it is, I don’t mean Kylix per se.
Rarely has a product been shown to a conference crowd this
early in its development cycle. Since the Kylix IDE wasn’t yet
functional, the speakers were using Emacs as the editor, then
creating the executables with a command-line compiler. Delphi
was never shown in public that early.
So what has changed? How can Borland reveal Kylix at such
an early stage? There’s literally no competition. When Delphi
was being developed, Borland had to keep it from prying eyes
at Microsoft, Symantec, and elsewhere. There’s now only one
competitor left, Microsoft, and they’re not about to create a
RAD tool for Linux. Now that we live in the Pax Microsofta,
there’s no need to keep the kimono closed. In retrospect, the
Bruce Willis vehicle Last Man Standing might have been a better
choice than The Matrix as the conference motif.
Perhaps the most exciting event occurred after the conference,
when I saw the August 2000 issue of Visual Basic Programmer’s
Journal. VBPJ is the premiere VB magazine, and this month
it came in a polybag containing a fully-functional, 60-day
evaluation version of Delphi 5 Enterprise. There’s also a full-
page ad inside pointing readers to www.vbforlinux.com. This
is the best news to come out of Borland in recent memory.
And I don’t just mean the polybag coup. What’s important is
that Borland thinks it can take on Microsoft directly in the
RAD marketplace. I think they can as well — always have
— but it’s immensely heartening to see Borland marketing
Delphi so aggressively.
Thanks for reading.
Jerry Coffey, Editor-in-Chief
[email protected]2 October 2000 Delphi Informant Magazine
Delphi Woll2Woll Announces 1stClass 2000
Woll2Woll Software be found in the InfoPower 2000 Woll2Woll Software
T O O L S announced the availability of VCL; improved image painting Price: Standard, US$199; Professional,
1stClass 2000, a new upgrade in 256-color environments when US$299.
New Products to its visual component suite for using the following controls: Phone: (800) 965-2965
and Solutions Delphi and C++Builder. TfcImageBtn, TfcImager, TfcIm- Web Site: http://www.woll2woll.com/
1stClass 2000 offers image- ageForm; and C++Builder 5 com- 1stclass
shaped forms and buttons, patibility (Professional version).
data-aware and non-data-aware
treeview controls, an Office 97
Outlook Bar Style container
control, the Imager control, and
a statusbar with many built-in
styles and the ability to size
panels proportionally. You can
use edit controls like the font,
color, tree, and image combos
that can be embedded directly in
an InfoPower Grid.
1stClass 2000 offers several
new features, including support
for custom framing and transpar-
ency effects; support for the same
button and glyph effects that can
SkyLine Tools Announces ImageLib Corporate Suite 5.0 for Delphi 5
SkyLine Tools Imaging version of the ImageLib Cor- newer scanners; a features pack-
announced the release of Image- porate Suite include upgraded age that allows annotations to
Lib Corporate Suite 5.0 for memory for larger images pro- be customized by the developer;
Delphi 5, the company’s imaging duced by newer digital cameras; and a magnifying glass feature,
solution for Delphi 5 developers. upgraded TWAIN scanning which allows the user to zoom
Features of the newest that meets the specifications for in to a specific area of the image
rather than zooming in to the
entire page, as well as lets the
user move the magnifying glass
around on the screen, continu-
ing to magnify only the area
under the “glass.”
In addition, this toolkit
contains all the features from
previous versions, such as anti-
aliasing, multi-page TIFF files,
and over 40 image-manipulation
effects and filters.
SkyLine Tools Imaging
Price: US$599
Phone: (800) 404-3832
Web Site: http://www.imagelib.com
DBI Technologies Announces Solutions::PIM Professional 2.0
DBI Technologies Inc. start and end dates, as well property. Or make the calendar
announced the release of as drag-and-drop appointments. interactive via the Web. All com-
Solutions::PIM Professional 2.0, You can identify appointments ponents have been made Internet-
designed to allow developers to using colors or pictures, or print ready, making them compliant
build Microsoft Outlook-style calendars in multiple views using with Web page scripting.
appointment-based information various paper sizes with a single
management applications. Ver- line of code. DBI Technologies Inc.
sion 2.0 allows the presentation This version offers developers Price: US$489 (includes 17 32-bit licensed
of appointments by day, week, definable security features at mul- ActiveX components; fully functional sample
month, year, or developer def- tiple levels. Manage the addition applications with source code; online tutori-
inition with the new Virtual of appointments with advanced als; and comprehensive online help).
Calendar. It also features a conflict checking, including a Phone: (800) 670-8045 or (204) 985-5770
scrolling calendar with definable new appointment interval-offset Web Site: http://www.dbi-tech.com
3 October 2000 Delphi Informant Magazine
Delphi Tabdee Announces TurboSync 1.0
Tabdee Ltd. announced the to be developed in Delphi. TurboSync components allow
T O O L S release of TurboSync 1.0, a set Conduits are DLLs that developers to define the Palm
of VCL components and Delphi handle the synchronizing of data database structure visually in the
New Products classes that enable Palm conduits between Palm devices and PCs. IDE, and use familiar Delphi
and Solutions idioms to access the data —
InstallShield Announces InstallShield for Windows so there’s no more struggling
Installer 1.5 with VC++ and inheriting from
InstallShield Software Corp. wizard; an enhanced dialog editor; poorly documented classes.
announced InstallShield for and additional language support.
Windows Installer 1.5, the latest Tabdee Ltd.
version of the company’s com- InstallShield Software Corp. Price: US$60 (includes full source code for
prehensive setup solution for Price: US$995 (sold as part of the the components, translations of the major con-
developers of Windows 2000 InstallShield Professional 2000 bundle). duit APIs, and free minor version upgrades).
logo-compliant applications. Phone: (800) 374-4353 Phone: +44 (0) 118 9882561
This new version offers Internet- Web Site: http://www.installshield.com Web Site: http://www.tabdee.ltd.uk
enabled update patching, which
allows users of Microsoft’s Win-
ProWorks Releases Flipper Graph Control 2.0
dows Installer service to create ProWorks LLC announced the types, and the ability to place
“software patches” of their Win- release of Flipper Graph Control symbols from fonts as marks on
dows Installer-based applications version 2.0, an upgrade to the a graph. Context menus with
for distribution over the Internet. company’s ActiveX charting con- editing dialog boxes provide an
The .MSI patching capabilities trol. Version 2.0 features include effortless user interface at run
allow setup authors to build soft- enhanced compatibility with the time.
ware patches — updates to an Web, increased flexibility for sci- Other new features include
existing installation or set of instal- entific and financial charts, an ADO Recordset reading, OLE
lations — that contain only dif- improved look and feel, and drag-and-drop support, tool tips,
ferences between the installations, greater customizing capability. and improved date formatting
not the complete file set. The Flipper Graph Control 2.0 and incrementing.
update is created as the difference can download saved data across Graph types include 2D
(file- or byte-level) between two the Internet, as well as easily and 3D line, bar, points, areas,
releases of the product. integrate into ASP or HTML stacked bars, hi-lo, pie, bubble,
InstallShield for Windows Web pages, enabling users to spider, and true 3D surfaces.
Installer allows developers to manage and display off-site data. Multiple graphs can be added to
write and debug custom actions Included with version 2.0 is the control for drill-down func-
using the InstallScript language, a signed .CAB file, permitting tions or merged to present data
without leaving the installation client machines to download a on more than two y axes. The
development environment. run-time version of the control user can find objects and graph
Developers can also reuse exist- for use on a Web page. items with the mouse, causing
ing or previous InstallScript to Flexibility has been added to events to be fired to the devel-
take advantage of prior setup scientific and financial graphs, oper. Creating graphs continues
investment. with the addition of new func- to be fast with auto-scaled axes,
InstallShield for Windows tions for polynomial curve fit- numerous examples, and a full
Installer 1.5 delivers a GUI- ting, moving average, and setting tutorial.
driven setup-authoring environ- axis data aspect ratio.
ment and an assortment of The look and feel of version 2.0 ProWorks LLC
wizards and help features to has been enhanced with gradient Price: US$349
shorten development time and fills for 2D bar charts, a variety Phone: (541) 752-9885
decrease the learning curve asso- of fill style patterns for all chart Web Site: http://www.proworks.com
ciated with using the Microsoft
Windows Installer service.
Additional features include the
ability to automatically include
updated COM information in a
project at build time; the ability
to programmatically add/-
remove features, components,
merge modules, registry entries,
and shortcuts; dynamic file link-
ing with support for subfolders;
the ability to call any exportable
DLL function and define the
required parameters with a simple
4 October 2000 Delphi Informant Magazine
Delphi Active+ Software Announces ServiceMill 3.1.35
Active+ Software announced 3.1.35, the system utility that processes, they will all run in
T O O L S the release of ServiceMill instantly turns any DOS, Win- the same Job. Stopping the ser-
dows or OS/2 exe- vice will cleanly stop all pro-
New Products cutable, as well as cesses in the Job, not only
and Solutions batch files, into a the primary process. Another
Windows NT/2000 advantage of running a service
service. program through a Job is that
ServiceMill now all processes running in it will
supports Windows be restricted to a predefined
2000 Kernel Job scheduling priority.
Objects. When a
program is started Active+ Software
as a service, the pro- Price: US$65 (single-machine license for
gram process will Windows NT Workstation/2000 Professional);
be attached to a US$125 (single-machine license for Win-
Job object. There- dows NT Server/2000 Server).
fore, if the program Phone: +33 468054774
starts other Web Site: http://www.activeplus.com
VMware Announces VMware Express for Linux
VMware, Inc. announced the which was designed for Linux applications they need.
release of its latest desktop prod- users who want to run Windows VMware Express for Linux
uct, VMware Express for Linux, 95/98 to access the Windows allows users to run Windows
95 or 98 in a VMware virtual
machine on top of a Linux
host system. A VMware virtual
machine works just like a full
PC, with full networking and
multimedia support. In addi-
tion, users will easily be able
to share files between the Linux
host system and the Windows
virtual machine, using the
Samba file-sharing service that
comes preconfigured.
VMware, Inc.
Price: Call for pricing.
Phone: (650) 475-5000
Web Site: http://www.vmware.com
combit Announces List & Label 7.0
combit GmbH announced the offers an increased level of user sion, the List & Label Designer dent of a specific database.
newest version of its report gen- friendliness and a variety of new and Internet/e-mail modules can The Unicode/Multibyte module
erator, List & Label 7.0, which features. As with the previous ver- be passed on to the end-user roy- can process most character sets,
alty free. including Asian.
Developers can integrate the The DTP form designer offers
report generator in existing appli- encompassing layout tools and
cations with a few lines of code. many filter and layout options.
Applications are equipped with These can be used via drag-
functions for creating reports, lists, and-drop. The formula assistant
forms, and labels. Code examples enables complex calculations and
in various programing languages string manipulations directly at
are available to simplify develop- run time. The integration of RTF
ment. The tool is available for all text, graphics, and barcodes is
DLL-capable programming lan- also possible, as well as individual
guages. Special versions, which printer control for the first and fol-
only work with Delphi or Visual lowing pages.
Basic, are also available.
List & Label consists of a print combit GmbH
engine and a form designer. Data Price: Contact combit for pricing information.
is transferred directly from the Phone: +49 7531 906010
application, so the tool is indepen- Web Site: http://www.combit.net/
5 October 2000 Delphi Informant Magazine
News Inprise/Borland Introduces Borland CLX
New York, NY — Inprise/- component libraries for ultra- visual development tools for
Borland announced Borland high performance and native Windows and Linux platforms;
L I N E CLX (component library for services; an object-oriented and support for commercial,
cross-platform), the next-gen- component framework for proprietary, and open-source
eration component library and building reusable components licensed development. Borland
October 2000 framework for developing in Delphi or C++; simplified CLX also allows developers to
native Linux and Windows migrating of Delphi and leverage existing Delphi and
applications and reusable com- C++Builder applications to Visual Basic skills.
ponents. CLX simplifies graph- Linux; integration with the For more information, visit
ical user interface, database, next generation of Borland http://www.inprise.com.
and Web application devel- Inprise/Borland Announces JBuilder Support for
opment with a cross-platform
component framework design Apple’s Mac OS X
based on the Delphi and San Diego, CA — Inprise/- JBuilder Java IDE, developers
C++Builder visual component Borland announced plans to will build Java applications run-
library (VCL). provide its JBuilder pure-Java ning on Mac OS X and realize
CLX will be available in development environment for the benefits of the Aqua look-
the forthcoming Linux version Apple’s next-generation operating and-feel, designed to advance the
of the Borland Delphi and system. JBuilder’s support for ease-of-use of personal computer
C++Builder tools, code-named Apple’s new Aqua graphical user user interfaces.
“Kylix.” The Kylix project is a interface lets Java developers JBuilder for Mac OS X
native rapid application develop- build applications on the Macin- will be available in conjunction
ment environment for the Linux tosh platform that take advantage with Apple’s planned release of
operating system, and will also of the Aqua design elements. Mac OS X early next year. To
be incorporated into the next Mac OS X will support the Java learn more, visit Inprise/Borland
Windows versions of Delphi and 2 Platform, Standard Edition at http://www.borland.com, the
C++Builder. (J2SE), including the Java Hot- community site at http://
Borland CLX offers true Spot Client Virtual Machine, for community.borland.com, or call
native Windows and Linux optimal performance. Using the the company at (800) 632-2864.
Inprise/Borland’s Kylix to Support Apache Application Development
Scotts Valley, CA — Inprise/- By supporting Apache Server Borland Delphi and C++Builder.
Borland announced support of applications, the Kylix project Web servers supported include
native database-driven Apache will allow developers to build Microsoft Internet Information
Server applications in its forth- native applications in an object- Server (IIS), Netscape servers
coming Linux developer tool oriented, component-based devel- through Common Gateway Inter-
set, Kylix. The Kylix project, opment environment, with Linux face (CGI), Internet Server Appli-
planned to be available later in OS and Windows OS cross-plat- cation Program Interface (ISAPI),
2000, will enable Web applica- form flexibility. In addition to and Netscape Server Application
tion development for the Apache supporting the Apache Server, Programming Interface (NSAPI).
Web Server on the Linux operat- the Kylix project will create a The Kylix project is intended
ing system, and for the Windows migration path from other HTTP to be the first high-performance
platform in an upcoming version Web servers to the Apache Server rapid application development
of Delphi. for applications developed with tool for the Linux platform.
6 October 2000 Delphi Informant Magazine
On the ’Net
CDO / Microsoft Outlook / Microsoft Exchange / Delphi 4, 5
By Kristen Riley
Internet Messaging Made Easy
Getting Started with Collaborative Data Objects
C ollaborative Data Objects (CDO) provides a way to include messaging functionality
in applications. It does this by providing a simplified, but limited, interface to the
underlying MAPI (Messaging Application Programming Interface) library. Since CDO is a
COM object that takes the form of a dynamic-link library (DLL), the properties and methods
of CDO can easily be accessed with Delphi after importing the DLL as a type library.
There are two parts to CDO: the main CDO regsvr32.exe cdo.dll command from your
library, and the CDO rendering library. The \Windows\System32 directory.
main CDO library provides the ability to send
and receive mail messages, interact with folders To use CDO in Delphi applications, first import
and address books, and generate meeting items the library. To do this, click Project | Import
and appointments. The CDO rendering library Type Library to open the Import Type Library
is used to render CDO objects and collections dialog box (see Figure 1). (Note: The library may
into HTML for use on the Web. This article intro- already be installed, so it’s a good idea to check
duces Delphi developers to the main CDO library the Open Type Library dialog box for its pres-
with example applications that retrieve address ence before trying to install it.) Click on the Add
lists, retrieve Inbox e-mail, and send e-mail. button to open the Open Type Library dialog
box. Find where you put your copy of cdo.dll,
Getting Started with CDO highlight it, and click the Open button. MAPI
Microsoft Outlook (97, 98, or 2000) must be (Version 1.21) should now be highlighted at
installed on your machine to use CDO. If the top of the Import Type Library dialog
Outlook 98 or Outlook 2000 box, with Class names containing TSession. Click
is installed, make sure they the Create Unit button. This will create the
were installed with the Cor- file MAPI_TLB.pas in the Delphi \Imports
porate or Workgroup Mode directory, e.g. \Program Files\Borland\Delphi5\
to ensure that the proper Imports. When requested, recompile the package
underlying MAPI files were and save it.
also installed. A message
store is also necessary, pref- Now let’s look at the file we’ve created. Open
erably Microsoft Exchange the MAPI_TLB.pas file in Delphi. Search for
Server. the first occurrence of the string _Session =
dispinterface;. As stated in the MAPI_TLB file,
The latest version of CDO is you should be looking at the “forward declarations
1.21. However, there are dif- of interfaces defined in the type library.” This is a
ferent versions for Windows list of the structures used in CDO, and defined fur-
95, 98, and NT. If cdo.dll ther down in the file. The structures we’ll use in the
isn’t already on your machine, examples are: _Session, AddressLists, AddressList,
copy the correct version of it AddressEntries, Messages, Message, Recipients,
into your \Windows\System32 Recipient, Attachments, and MessageFilter.
directory. The CDO files can
be downloaded from http:// Now, search for the second instance of the string
www.microsoft.com/exchange/ _Session = dispinterface;. This will go to
downloads/CDO.htm. Reg- where the Session object is defined. All the struc-
Figure 1: The Import Type Library dialog box. ister the file with the tures of CDO are accessed beginning with the
7 October 2000 Delphi Informant Magazine
On the ’Net
Session object. Following the session’s structure are the rest of the variables for the Logon function call (ProfileName, ProfilePassword,
CDO structures and their properties and methods. As you can see, and ProfileInfo are declared as strings).
the properties and methods of these structures are all accessed with
variant parameters. The ProfileName field is an Outlook profile. This is the name you
use when normally logging on to Outlook. However, if you log in
As mentioned, three CDO examples are illustrated in this article: 1) using the second method, this field is set to an empty string, and
Listing our recipient groups (address books) and their corresponding the ProfileInfo field is used. The ProfileInfo field will then consist of a
recipients; 2) Gaining access to the Inbox and sorting, filtering, and server and log in name separated by the ASCII linefeed character.
listing received e-mails; and 3) Sending e-mail.
Note that the ProfileInfo field is set to EmptyParam in the first login.
First, let’s describe the variables and code necessary to add a CDO EmptyParam works as a parameter placeholder in COM. It tells the
session to your application — and then log on using that session — COM object to use the default parameter value since the field isn’t used.
since this is common to all three examples. Because the ProfileInfo field is the last parameter of the Logon function,
it could be left off the argument list. However, if ProfileInfo was the
To use CDO in your application, add MAPI_TLB to your uses second parameter, EmptyParam would have to be used so that the next
clause. Then, for the purposes of this article, add a session variable to parameter, ProfilePassword, would be called in the correct place.
your private declarations:
The next parameter is the password. If you’re already logged in to
private Outlook, and you’re going to log in under the same profile in your
fSession: _Session; application, the password doesn’t seem to be necessary.
For these examples, also add the following variable to the private The ShowDialog parameter, when set to True, will open the Outlook
declaration: logon dialog box if the profile/server/password information was
searched and deemed incorrect. If this is set to False and incorrect
fConnected: Boolean; logon information is supplied, a temporary profile will be created.
However, the temporary profile won’t have an Inbox — a fact you can
Now you can create and refer to the session in your code. In this case, put to use when automating applications. This will be examined later
I create the Session object when the form is created: when looking at the Inbox example application.
if fSession = nil then The ParentWindow parameter is set to -1, so that any Logon popup
fSession := Session(CoSession.Create); dialog boxes aren’t linked to a parent window. The NoMail and
NewSession variables are set to True.
Mailstore Logon
Once your session is created, you can log on, which will connect Now, wrap the Logon call with an exception handler. Even though the
your application to the mailstore. The Logon method is defined as Logon is supposed to return a value, in my testing, it always returned
follows: NULL whether or not the login information was valid:
function Logon(ProfileName: OleVariant; try
ProfilePassword: OleVariant; ShowDialog: OleVariant; fSession.Logon(ProfileName, ProfilePassword, True, True,
NewSession: OleVariant; ParentWindow: OleVariant; -1, True, ProfileInfo);
NoMail: OleVariant; ProfileInfo: OleVariant): fConnected := True;
OleVariant; dispid 119; except
on E: Exception do
ShowMessage('Logon Failed: ' + E.Message);
There are two ways to log on to the mailstore. The first uses end;
your profile and password; the second uses your server, login, and
password. I was forced to use the second way because the first After calling the Logon function, provided there are no exceptions
method didn’t work with a modem connection (at least in our (fConnected will be True) and assuming the logon information
system). For these example applications, all three fields can be given was correct (see the Inbox example for more detail), then you
entered, but the second logon method is used only if there is can start working with the other properties of the Session object.
text in the server field. Figure 2 shows the code that fills in the For instance, f Session.CurrentUser contains the logged-in user’s
name, and f Session.Name contains profile account information.
if Length(ServerEdit.Text) <= 0 then The following examples will be using the session’s AddressLists,
begin
Inbox, and Outbox properties.
ProfileName := MailBoxEdit.Text;
ProfileInfo := EmptyParam;
end Recipient Listing Example
else Figure 3 shows the hierarchy of the
begin Session
structures we’ll use to list stored
ProfileName := ''; AddressLists
recipients.
ProfileInfo := AddressList
ServerEdit.Text + #10 + MailBoxEdit.Text; AddressEntries
end; First, access the session’s list of
Recipient
address lists. This is done using
ProfilePassword := PasswordEdit.Text;
the AddressLists property of the Figure 3: Object hierarchy
Figure 2: Assigning values to the variables for the Logon function. session. The AddressLists structure used in first example.
8 October 2000 Delphi Informant Magazine
On the ’Net
RecipientListBox.Clear; try
fSession.Logon(ProfileName, ProfilePassword, True, True,
I := AddressListBox.ItemIndex; -1, True, ProfileInfo);
Inc(I); fConnected := True;
try
MyAddressItem := fSession.AddressLists.Item[I]; InBox := fSession.InBox;
AddEntries := MyAddressItem.AddressEntries; fValidInbox := True;
except
for I := 1 to AddEntries.Count do begin on E: Exception do
MyRecipientItem := AddEntries.Item[I]; ShowMessage('Invalid Inbox: ' + E.Message);
RecipientListBox.Items.Add(MyRecipientItem.Name + ', ' + end;
MyRecipientItem.Address); except
end; on E: Exception do
ShowMessage('Logon Failed: ' + E.Message);
Figure 4: Adding recipient information to the RecipientListBox. end;
Figure 6: Raising an exception if the Outlook Inbox isn’t present.
can be examined by searching for
Session
AddressLists = dispinterface shown in Figure 6) where Inbox is a local OleVariant variable, and
Inbox
in the MAPI_TLB file. Each item f ValidInbox is a Boolean variable declared in the private section
Messages
of the AddressLists is an AddressList of the form.
MessageFilter
(which is defined after AddressLists
Message
in the MAPI_TLB file). This exam- By encasing the logon in a try..except block, and then accessing the
Attachments
ple iterates through the items of Inbox within another try..except block, you can determine whether
Recipients
AddressLists and uses a temporary there was a successful connection to the message store, as well as
Recipient
OleVariant variable, MyAddressItem, whether the login information was valid.
to hold the AddressList information.
Figure 5: Object hierarchy
used in second example.
Then, using the MyAddressItem vari- After successfully accessing the Inbox, the list of messages in the Inbox
able, it adds the name of each can be retrieved (see “Messages = dispinterface” in the MAPI_TLB
address list to the AddressListBox: file). They can also be filtered and/or sorted. The following code is
an example of filtering out previously read messages, and then sorting
AddressListBox.Clear; the remaining messages based on the time they were delivered. Inbox,
for I := 1 to fSession.AddressLists.Count do begin InMessages, and MessageFilter are all OleVariant variables:
MyAddressItem := fSession.AddressLists.Item[I];
AddressListBox.Items.Add(MyAddressItem.Name);
end; Inbox := fSession.Inbox;
InMessages := InBox.Messages;
MessageFilter := InMessages.Filter;
MessageFilter.UnRead := 1; // Only access unread messages.
Note that the AddressLists count goes from 1 to n. Because the InMessages.Sort(CdoDescending, CdoPR_DELIVER_TIME);
count for this property is relatively static, the placement of the
AddressList item is used later to obtain additional information on
the item. The AddressList item will have an AddressEntries property To filter the Inbox messages, first access its MessageFilter. Then set
(see AddressEntries in the MAPI_TLB file). The items of the the filter to one of the available values, as defined in the MessageFilter
AddressEntries are the recipients to be listed. The structure of a recipi- structure shown in the MAPI_TLB file under “MessageFilter = dispin-
ent can be found by searching for Recipient = dispinterface in terface.” In this case, any messages that have been read are filtered out,
the MAPI_TLB file. Using the OleVariant variables MyAddressItem, but other filters, such as sender, size, or subject, can be used.
AddEntries, and MyRecipientItem, the names and addresses of the
recipients contained in the selected address list are added to the Sorting the messages is done with the message’s Sort function, defined as:
RecipientListBox (see Figure 4).
function Sort(SortOrder: OleVariant;
The value returned by AddressListBox.ItemIndex was incremented PropID: OleVariant): OleVariant;
because the listbox has a 0 index and fSession.AddressLists starts at 1.
The AddEntries count also begins at 1. The SortOrder parameter is simply none, ascending, or descending, cor-
responding to CdoSortOrder (see MAPI_TLB): CdoNone, CdoAscending,
Inbox Access Example and CdoDescending. The PropID parameter defines what the messages
Figure 5 shows the hierarchy of the structures we’ll use to list received will be sorted by. In this example, it’s delivery time, but many more are
e-mail messages. In this example, the Inbox folder will be accessed listed under CdoPropTags in the MAPI_TLB file.
after logging on to the mailstore.
Now that the Inbox messages are sorted, the individual messages
In this case, however, there’s a gotcha. Say you’re running an can be retrieved. There are two ways to do this. The first is to
automated application in which you want the Logon dialog box simply get the count of messages in the Inbox and iterate from
popping up on an invalid logon. If you pass the Logon function 1 to that count (InMessages.Count). The second is to use the
invalid information, an exception won’t be raised and a flag doesn’t message’s GetFirst and GetNext functions. When using this second
signal this incorrect logon. Instead, a temporary profile will be method, every message retrieved must be checked to see if it’s
assigned. As mentioned previously, this temporary profile won’t valid by passing it to the VarIsNull and VarIsEmpty functions.
have an Inbox. This means that as soon as you try to access the If either of these functions comes back positive, then the last
Inbox, an exception will be raised. You can trap this error (as message in the list was already retrieved.
9 October 2000 Delphi Informant Magazine
On the ’Net
Recipients := SingleMessage.Recipients;
for I := 1 to Recipients.Count do begin
SingleRecipient := Recipients.Item[I];
if SingleRecipient.Type = CdoTo then
ccstr := 'To: '
else
ccstr := 'Cc: ';
RecipientsListBox.Items.Add(ccstr +
string(SingleRecipient.Name) + '; ' +
string(SingleRecipient.Address));
end;
Figure 9: The second demonstration application uses CDO to
Figure 7: Distinguishing mail recipients. send messages.
aren’t difficult to reach. As in the first
example, in which recipients were con- Session
tained and accessed from an AddressList, Outbox
the recipients of a message are contained Messages
in its Recipients property. But in this Message
case, the recipients can be distinguished Recipients
as directly mailed or CCd (see Figure 7).
Figure 10: Object
hierarchy used in
I must mention something now, before third example.
delving into the last example on sending
mail: the message type property. When
running this example, the message type will be “IPM.Note” for most
messages received. For Outlook to easily handle e-mails, make sure
that any e-mails sent by your application have their corresponding
type declared as “IPM.Note.”
Figure 8: The first demonstration application allows login and
I should also mention that two example applications accompany this
lists messages in the Inbox.
article, and are available for download (see end of article for details).
The following code shows the Inbox messages being retrieved and then The first demonstrates the techniques we’ve discussed so far, and is
added to a listbox with their IDs as text (SingleMessage is an OleVariant): show in Figure 8.
SingleMessage := InMessages.GetFirst; Send Mail Example
while not (VarIsNull(SingleMessage) or This last example will briefly demonstrate how to send e-mail via
VarIsEmpty(SingleMessage)) do begin
CDO. The example application is shown in Figure 9. Figure 10
MessageListBox.Items.Add(string(SingleMessage.ID));
SingleMessage := InMessages.GetNext; shows the structures involved.
end;
In this example, the logon process is similar to that of the Inbox
example, but checks for a valid Outbox. After this, once the
If desired, the information associated with each message could be needed information to send is ready, create a new message with the
retrieved in the loop. However, because the ID of each message is message’s Add function:
actually a GUID and unique to the mailstore, for this example, the
ID will be used to retrieve the message contents later, from the session function Add(Subject: OleVariant; Text: OleVariant;
itself, when the listbox is clicked: Type_: OleVariant; Importance: OleVariant): OleVariant;
GUIDstr := MessageListBox.Items[MessageListBox.ItemIndex];
ANewMessage := fSession.OutBox.Messages.Add(
SingleMessage := fSession.GetMessage(GUIDStr, EmptyParam);
SubjectEdit.Text, TextEdit.Text, 'IPM.Note', CdoHigh);
Once a message is assigned to an OleVariant variable, SingleMessage if (VarIsNull(ANewMessage) or
VarIsEmpty(ANewMessage)) then
in this case, its properties can be accessed. The Message structure can
Exit;
be viewed in the MAPI_TLB under “Message = dispinterface.” So, to try
show the message’s subject, you would use: ANewMessage.Recipients.Delete;
ANewMessage.Recipients.AddMultiple(ToEdit.Text, CDOTo);
// Bring up dialog if bad address.
SubjectLabel.Caption :=
ANewMessage.Recipients.Resolve(True);
'Subject: ' + string(SingleMessage.Subject);
ANewMessage.Update;
ANewMessage.Send(True, False, 0);
Subject, time sent, time received, body, size, as well as many other except
values associated with the message, can be easily retrieved in this on E: Exception do
ShowMessage('Send Failed: ' + E.Message);
manner. Other values, such as importance, are simply translated
end;
using the constants listed in the MAPI_TLB file. Still others,
such as recipients and attachments, require a little more work, but Figure 11: Remaining code to send an e-mail.
10 October 2000 Delphi Informant Magazine
On the ’Net
The Subject and Text parameters are the subject and text of the
e-mail message to be sent. Type_ is the string “IPM.Note” as previ-
ously described. Importance is one of the CdoImportance constants
(CdoLow, CdoNormal, CdoHigh) defined in the MAPI_TLB file. To
begin, call the Add function and place the result into an OleVariant
variable, ANewMessage in this case. Figure 11 shows the rest of the
code needed to send an e-mail.
Before sending the e-mail, clear any recipients that were previously
listed. Then add the sending e-mail recipient to the message’s recipi-
ents with the AddMultiple function. After adding the recipients (with
CDOTo, CDOCc, or CDOBcc), use the Resolve function to check that
the e-mail addresses given are in the proper format. If True is passed
to the Resolve function, a dialog box will pop up if any of the given
addresses are badly formed. Then update the message.
The Send function sends the e-mail. The Send function is defined as:
function Send(SaveCopy: OleVariant; ShowDialog: OleVariant;
ParentWindow: OleVariant): OleVariant;
Setting the SaveCopy parameter to True will put a copy of the
sent message into the sender’s “Sent Item” mailbox. The ShowDialog
parameter, when True, will cause an extra dialog box to pop up
on sending. Note that this dialog box will cause an exception if
cancelled. The ParentWindow can be set to 0, or a valid window
handle, for the ShowDialog’s dialog box to use.
Conclusion
Although these examples have been brief, I hope they have given you a
taste of how to use CDO to build mail-enabled applications. Although
there’s plenty of information about CDO that can’t be covered in this
article, I recently bought an excellent book on CDO called Professional
CDO Programming (listed at the end of this article). If you need to
develop CDO applications, I highly recommend this book. Although it’s
aimed at C and Visual Basic programmers, once you understand how to
use CDO with Delphi, it isn’t difficult to figure out how to translate the
COM functionality for use with Object Pascal. ∆
Bibliography
§ ADSI CDO Programming with ASP by Mikael Freidlitz and Todd
Mondor [Wrox Press, 1999].
§ Professional CDO Programming by Dan Mitchell, Siegfried
Weber, and Donald Xie, [Wrox Press, 1999].
§ White paper: Collaborative Data Objects: Using E-Mail in
Your Application by Dr Bruce E. Krell and Ken Miller
(http://msdn.microsoft.com/library/techart/collabdataobjs.htm).
The files referenced in this article are available on the Delphi Informant
Magazine Complete Works CD in INFORM\00\OCT\DI200010KR.
Kristen has been programming Windows applications for over 10 years. She works
at Vast Solutions, Inc., a pioneering wireless application integration and outsourc-
ing company. She can be contacted there at [email protected].
11 October 2000 Delphi Informant Magazine
Dynamic Delphi
Word Automation / Delphi 5 / Microsoft Word 97, 2000
By Ron Gray
Automating Word
Part II: Word Components in Delphi and Using OLE
M icrosoft Word offers many features beyond simple word processing. Using Automa-
tion and OLE (Object Linking and Embedding), any Delphi application can easily
integrate Word functionality. Last month, the first installment of this two-part series
examined Automation in Delphi and looked at the Word object model. This installment
presents the Word components available in Delphi 5, and looks at how to link and embed
documents using OLE.
Delphi 5 Word Components These components can be used independently, or
Before describing the Word components, consider while connected to each other. Some can be created
how compile-time Automation is handled. Word and applied to properties of other components. For
provides type information about its objects, meth- example, to change the paragraph formatting in
ods, and properties in a type library (msword8.olb multiple documents, you could create and format a
for Word 97, msword9.olb for 2000). Delphi pro- WordParagraphFormat object, then assign it to the
vides the ability to import the file and create wrap- Selection property of each document.
per classes to the interfaces.
Sample code presented in Part I of this series
The type library defines the Word Application called CoApplication_.Create to return an
object, which is the top-level object through which _Application data type. Using the component,
all other objects are available. However, the library the initialization code changes to this:
also defines CoClasses for several other global
objects that can be accessed directly by calling the var
Create method of the CoClass client proxy class. In oWord : TWordApplication;
begin
other words, they are stand-alone objects that don’t
oWord := TWordApplication.Create(Self);
need to go through the Application object. For oWord.Connect;
example, rather than using an Application object ...
to create a document, the Document object can be end;
used directly.
Of course, TWordApplication still calls
Delphi 5’s Word components elegantly wrap the CoApplication_.Create. So what has changed? A lot.
CoClasses, and greatly simplify and encapsulate the Not only have the events been wrapped in the com-
Automation process. The following Word compo- ponents, much of the management work has been
nents are available in Delphi 5, on the Servers page added as well. TWordApplication and the other Word
of the Component palette: components descend from TOleServer — an abstract
§ WordApplication represents the entire Microsoft class added to Delphi 5 that represents an imported
Word application, and is the top-level object of COM server. TOleServer provides a framework for
the Word object model. connecting to Automation servers, dispatching events,
§ WordDocument represents a document. and performing other COM-related work.
§ WordFont contains font attributes (such as name,
size, color, and so on) for a specified object. Another benefit of the Word components is that
§ WordParagraphFormat represents all the format- many of the common methods have been over-
ting (such as alignment, spacing, and style) for loaded to support variable parameters. This allows
a paragraph. you to omit unused parameters, so code that used
§ WordLetterContent represents elements of a letter. to look like this:
12 October 2000 Delphi Informant Magazine
Dynamic Delphi
oWord.ActiveDocument.PrintOut(EmptyParam, EmptyParam, with WordApplication1 do begin
EmptyParam, EmptyParam, EmptyParam, EmptyParam, WindowState := wdWindowStateNormal;
EmptyParam, EmptyParam, EmptyParam, EmptyParam, Caption := 'Integrating Word and Delphi';
EmptyParam, EmptyParam, EmptyParam, EmptyParam); Height := 300;
Width := 300;
Left := 0;
can omit unused parameters to be written like this: Top := 0;
Visible := True;
end;
oWord.ActiveDocument.PrintOut;
Getting Started When managing multiple documents, it’s easier to use the Application
There are many subtleties when integrating Word into a Delphi object, because the Documents property already contains a list of all
application, but the basics involve connecting to the Automation open documents. Assign individual documents to a WordDocument
server (Word), then manipulating documents. object as needed. An earlier example showed how to create a new
document with the Application object:
Establishing a Connection
Before using any of the Word components, a connection must be WordApplication1.Documents.Add(EmptyParam, EmptyParam);
established between the Delphi application and the Word Automa-
tion server. For a number of reasons, the component may fail to con- The return value of the Add method is an interface to the new
nect to the server. Therefore, you should always wrap the connection Document object. To manipulate the document, it must be assigned
in a try..except statement: to a variable, such as a WordDocument object. The following code
uses the Application object to create a new document, and assign it
try to a WordDocument object:
WordApplication1.Connect;
except on E: Exception do
WordDocument1.ConnectTo(
begin
WordApplication1.Documents.Add(EmptyParam, EmptyParam));
E.Message := 'Word unavailable.';
raise;
end; The ConnectTo method is used to connect the WordDocument object
end; to an interface of the Application object, i.e. the Word document.
WordApplication1.Visible := True;
Working with Documents
How the connection is established depends on three important You can reuse the same WordDocument variable to connect to any
properties. First, AutoConnect specifies whether the server is loaded document. The following example connects the WordDocument to
when the application first starts. Second, ConnectKind determines the active document:
how the connection to the server is established. It can be set to the
following possible values: WordDocument1.ConnectTo(WordApplication1.ActiveDocument);
§ ckRunningOrNew attaches to a running server or creates a new
instance of the server. This example connects to a specific document:
§ ckNewInstance always creates a new instance of the server.
§ ckRunningInstance only attaches to a running instance of the server. WordDocument1.ConnectTo(
§ ckRemote binds to a remote instance of the server located on the WordApplication1.Documents('MyDoc.Doc'));
machine for RemoteMachineName.
§ ckAttachToInterface doesn’t bind to the server. Instead, the applica- A WordDocument object can also be used, without the Application
tion supplies an interface using the ConnectTo method, which is object, to manage a single document:
introduced in descendant classes.
WordDocument1.ConnectKind := ckRunningOrNew;
Third, RemoteMachineName specifies the machine running the COM WordDocument1.Connect;
server, when ConnectKind is ckRemote.
To save a new document the first time, use the SaveAs method:
The application might require a specific connection type. However,
to reduce load time, you should connect to a running instance of var
Word when you can. In applications where documents are heavily FileName: OleVariant;
begin
manipulated, the AutoConnect property can be set to True to load
FileName := 'MyDoc.Doc';
Word when the application starts. WordDocument1.SaveAs(FileName);
end;
Once the Word component is successfully connected, you can call
properties and methods of that object.
To save an existing document, call the Save method.
Using the Application Object
The Application object is used mainly to set application-specific attri- Working with Text
butes (such as the size and appearance of the application window), Word objects exist for manipulating the various elements of a
or to get access to other objects (such as the Documents object). The document: characters, words, sentences, paragraphs, and sections.
following example uses the WordApplication component to set the size These objects provide access to Range or Selection objects, which
of the window: are used to modify text. The Range object represents a contiguous
13 October 2000 Delphi Informant Magazine
Dynamic Delphi
area in a document defined by a starting and ending character Events
position. A Selection object represents the selection in a document The Word Application object publishes three event interfaces, shown
window pane. in Figure 2. They are exposed in their appropriate components.
The following example creates a Range object at the beginning of the These events let the Delphi application monitor a specific document,
document, and inserts some text (see Figure 1): or set of documents. For example, by responding to the Close event,
an application can create a separate backup copy, or save the docu-
var ment to a BLOb (Binary Large Object) field.
Text, nStart, nEnd: OleVariant;
begin
nStart := 0;
Object Linking and Embedding
nEnd := 0; So far, this article has presented ways to automate Word from a
Text := 'Hey now! '; Delphi application. This approach manipulates Word as a separate
WordDocument1.Range(nStart, nEnd).InsertBefore(Text); application to control separate, external Word documents. However,
end; to truly integrate Word, the application must be able to link or embed
documents. This would be necessary in the following situations:
This next example marks the second paragraph in bold: § The functionality of the Delphi application requires certain Word
documents (such as mail merge templates). A safer approach
WordDocument1.Paragraphs.Item(2).Range.Bold := 1; would be to store the documents in BLOb fields, rather than have
them external to the application.
This example does the same thing, but uses the WordFont object: § The Delphi application must display the contents of Word docu-
ments directly on a Delphi form, without loading Word each
WordFont1.ConnectTo( time.
WordDocument1.Paragraphs.Item(2).Range.Font); § Users want to edit the Word document from within the Delphi
WordFont1.Bold := 1;
application, without launching Word.
Working with Other Objects These requirements can be satisfied with OLE 2. OLE is closely
Of course, there are many other objects available in Word for related to Automation, and is also built on the COM architecture.
working with text. The Find and Replacement objects, Table and It enables applications to create compound documents that contain
other associated objects, and the HeaderFooter object are just a few. components of other applications. In other words, a Delphi applica-
tion can store and manage documents created in Word.
Object linking allows you to paste information as a link into an appli-
cation, and have the data dynamically updated when it is changed
in the source application. Double-clicking on the data launches the
source application for editing. Object embedding allows you to create
compound documents where data from one application can be placed
in another application as an object. A Delphi application can display
the data of an OLE object, without knowing the application that
created it. Double-clicking on the object can initiate in-place activa-
tion. This means that, rather than launching the source application
for editing, the client application temporarily acts like the source
application by changing its menus. This is possible because the object
contains all the necessary information to edit itself.
The client application must use a container to host the OLE
object. Delphi’s TOleContainer encapsulates the complexity of
embedding or linking OLE objects. There are several ways to get
the object into the container. An object can be pasted (using Paste
or PasteSpecialDialog), inserted (using InsertObjectDialog), dragged
Figure 1: The sample application in action. Here, it changes and dropped, loaded from a file (CreateObjectFromFile), created
properties of the Application object, then inserts text in a document. directly (CreateObject), or streamed (CreateFromStream). Delphi’s
OLE Container samples demonstrate most of these features, so
there’s no need to describe
Class Event Description
them here. There are MDI
Application DocumentChange Occurs when a new document is created, an existing document is and SDI versions in the
opened, or when another document is made the active document. \Delphi5\Demos\ActiveX\
Application Quit Occurs when the user quits Word. Olecntrs directory.
Document Close Occurs when a document is closed.
Document New Occurs when a new document based on a template is created. These demonstrations show
Document Open Occurs when a document is opened. that Word documents can be
ActiveX GotFocus Occurs when focus is moved to an embedded ActiveX control. created, stored, and edited
ActiveX LostFocus Occurs when focus is moved from an embedded ActiveX control. directly in OLE containers
Figure 2: Event interfaces in Microsoft Word. without using the Word com-
14 October 2000 Delphi Informant Magazine
Dynamic Delphi
ponents. You can even make calls to the Word object through the must be managed by the developer. Likewise, the Word components
container. The example in Figure 3 uses the OLE container to in Delphi 5 must also be managed in the sense that a mechanism for
create a new document, switch to in-place activation, activate the loading documents must be implemented. If direct links to specific
document, then insert some text. documents must be maintained by the Delphi application, then a
common approach is to save the name and location of the external
Note that the call to CreateObject passes Word.Document as the class document in text fields, or save the actual document in a BLOb field.
name to create a new document. The second parameter (False) indicates Each has advantages and drawbacks.
the document won’t be displayed as an icon, but as it would be displayed
by the server application. The call to DoVerb is made with ovPrimary, Saving only the name and location of the document allows other
the default action, to activate the object. Once the object is activated, applications to share documents and make edits, and doesn’t cause
you can get access to it through the OleObject property. The last state- a load on the database. However, when an application relies on
ment in the previous code uses OleObject to return the Word Document external documents, the rule of entropy usually makes them disap-
object. It then uses the Application object to insert the text. pear. Saving documents directly to BLOb fields as OLE objects is
easy and reduces the risk of losing them, but bloats the database
Referencing the Document object in this way uses run-time Automa- and reduces performance.
tion, described earlier in this article. The primary disadvantages of
this approach are lack of syntax checking at compile time, and slower Consult your database documentation to see how OLE objects
execution. Before making multiple Automation calls to a container should be stored in tables. For example, BLOb data in InterBase
object, consider assigning it to a WordDocument object to get the is further defined by a subtype. OLE objects must be stored in
benefits of compile-time Automation. The following example assigns a BLOb field with subtype 0, the default used to store binary
the container object created previously to a WordDocument object, data. The following SQL statement adds a BLOb field to the
and inserts some more text: CustomerLetter table:
WordDocument1.ConnectTo( ALTER TABLE CustomerLetter
IUnknown(OleContainer1.OleObject) as _Document); ADD OleObject BLOB SUB_TYPE 0 SEGMENT SIZE 80
WordDocument1.Application.Selection.TypeText(
'Hey Now! Again!');
TBlobStream is used to access or modify the value of a BLOb
field. Word documents are loaded from BLOb fields into
The object is cast to the IUnknown interface of a _Document object, TBlobStream objects. From here, it can easily be loaded into an
which is what the ConnectTo method expects. OLE container or WordDocument object. The example in
Figure 4 retrieves a document from a BLOb field, puts it in a
Saving Documents to Tables container, then activates it.
The OleContainer control is used to link or embed OLE objects,
such as Word documents. However, this link is not persistent and Note that the stream must be created and freed each time. You
should never reuse a BLOb stream. To save the contents of the OLE
OleContainer1.CreateObject('Word.Document', False); container back to the BLOb field, use the SaveToStream method, as
OleContainer1.AllowInPlace := True; shown in Figure 5.
OleContainer1.DoVerb(ovPrimary);
OleContainer1.OleObject.Application.Selection.TypeText(
'Hey Now!'); var
oStream : TBlobStream;
Figure 3: A TOleContainer object is used with in-place activation begin
and run-time Automation. oStream := nil;
if OleContainer1.State <> osEmpty then
try
var Table1.Open;
oStream : TBlobStream; try
begin Table1.Edit;
oStream := nil; oStream := TBlobStream.Create(Table1.FieldByName(
try 'OleObject') as TBlobField, bmReadWrite);
Table1.Open; OleContainer1.SaveToStream(oStream);
try Table1.Post;
oStream := TBlobStream.Create(Table1.FieldByName( MessageDlg('Document saved to BLOb field.',
'OleObject') as TBlobField, bmRead); mtInformation, [mbOK], 0);
OleContainer1.LoadFromStream(oStream); except
Olecontainer1.DoVerb(ovPrimary); MessageDlg('Document not saved to BLOb field.',
except mtWarning, [mbOK], 0);
MessageDlg( end;
'Document not read successfully from BLOb field.', finally
mtWarning, [mbOK], 0); oStream.Free;
end; Table1.Close;
finally end
oStream.Free; else
Table1.Close; MessageDlg('There is no document to save.',
end; mtWarning, [mbOK], 0);
end; end;
Figure 4: Retrieving a document from a BLOb field, placing it in a Figure 5: Using the SaveToStream method to save the contents of
container, and activating it. the OLE container back to the BLOb field.
15 October 2000 Delphi Informant Magazine
Dynamic Delphi
http://www.microsoft.com/officedev/.
§ See the Microsoft Office 2000 Visual Basic Programmer’s Guide for
detailed information on, and examples of, automating Word 2000.
The files referenced in this article are available on the Delphi Informant
Magazine Complete Works CD in INFORM\00\OCT\DI200010RG.
Ron Gray is a software developer specializing in business database applications.
He has written numerous articles using different languages and is the author
of LookUp Manager, a collection of components for visually managing lookup
codes and abbreviations in applications. He can be reached via e-mail at
[email protected].
Figure 6: An application demonstrating these techniques is avail-
able for download.
This time, the stream was created with read/write capabilities so
changes can be saved. The code also verifies that the container isn’t
empty before saving. A sample application that demonstrates all of
the techniques discussed in this article (see Figure 6) is available for
download; see end of article for details.
Reverse Role Playing
This article has discussed using a Delphi application as an
Automation controller of the Word Automation server. This allows
the Delphi application to “control” Word documents. However,
because a Delphi application can also be an Automation server, and
Word can also be an Automation controller, it’s possible to reverse
the roles so that Word can control parts of a Delphi application.
Consider a customer-locate dialog box in a Delphi application
that can be called from anywhere in the application, and returns
the record of the selected customer. By converting the Delphi
application to an Automation server, and exposing the customer-
locate dialog box through a method, users of Microsoft Word can
invoke the locate dialog box to find customers. Once the customer
is selected, the name and address can be automatically entered in
the document.
Conclusion
Microsoft’s Automation and OLE 2 technologies make it possible
to integrate Word into Delphi applications. Delphi 5’s Word compo-
nents make it easy. With just a few lines of code, applications can
become powerful word processors. However, the uses of Word extend
far beyond basic word processing, because there are so many other
features available. The benefits can be tremendous. ∆
References
§ The Word 97 object model is documented in the Help file
vbawrd8.hlp. This file comes with Office 97, but is not
installed by default. To install it, run the Microsoft Office 97
Setup program and select Add/Remove components. Select Help
for Visual Basic under the help options. The Word 2000 object
model is documented as part of the Microsoft Developer
Network, Office 2000 Developer edition, which is installed as
part of Office 2000 Developer.
§ Microsoft Office development information can be found at
16 October 2000 Delphi Informant Magazine
OP Tech
RTTI / Wizards / Delphi 3-5 / Object Persistence / Databases
By Keith Wood
Database Persistent Objects
Part II: Generating TDBPersistent Classes with a Wizard
L ast month we looked at how to create a class that could automatically store its
published properties in a relational database. Now it’s easy to create classes that
have this ability; we simply derive them from TDBPersistent and declare the properties.
There are some bookkeeping steps involved, so a constructor and assignment method
would be useful. To make it even easier, therefore, we can create a wizard that generates
all of this for us.
Delphi Wizards 3) GetName returns the new name.
Delphi wizards (also known as experts) allow us to 4) GetIDString returns a unique identifier. The
extend the abilities of the Delphi IDE. They come identifier must be unique world-wide and is
in four basic types: usually of the form
1) Project wizards that generate entire programs. <Company>.<ExpertName>.
2) Form wizards that build single forms or units
(these types appear in the New Items dialog The rest of the methods are optional depending on
box). the style of the wizard. For a form wizard we need
3) Standard wizards that appear on the Help the following: GetAuthor provides the name of the
menu. author, GetComment returns a longer description,
4) Add-in wizards that have full access to the and GetPage indicates on which page in the New
Delphi environment. Items dialog box the wizard should appear. GetGlyph
must be overridden to return an image for display
The type dictates how we interact with the wizard in this dialog box. With the Image Editor tool, open
and what the expected result is. the resource file for the project (.res) and add a new
icon. Update its appearance and give it a meaningful
All wizards are created as DLLs. They must export name before saving the file. Back in the code, we
a single function, InitExpert, which is called by load this resource with the LoadIcon function.
Delphi (when it’s loaded) to register the new
wizard. Inside this function it calls RegisterProc, Setting the style to a form wizard means it will
passing an instance of the expert as a parameter. appear as an entry in the dialog box invoked by
Thereafter, Delphi can enquire through the expert the File | New menu item. The previously men-
interface to determine its type and any other neces- tioned methods are then called to determine the
sary details. appearance of the wizard. Extended details, such
as the description and author’s name, are shown
Since we’re creating a single unit to be incorporated if we select the View Details option on the popup
into a larger application, we’ll create a form wizard. menu. When we activate the wizard, Delphi calls
To begin, we must derive it from TIExpert (defined its Execute method to perform its functionality.
in the ExptIntf unit), which is the base for all
experts and wizards. We must then override several Our wizard displays a dialog box to obtain the infor-
of the class’ methods to customize it for our use. mation necessary to generate the new unit. When the
The following four methods are required for all Finish button is clicked, it writes the code out to a file,
wizards: and then adds that file to the current project.
1) GetStyle is revised to return the form style
(esForm for our new wizard). User Interface
2) GetState indicates that the wizard is available The dialog box presented to the user consists of
(esEnabled). two pages: one for the class name, parent class,
17 October 2000 Delphi Informant Magazine
OP Tech
and the name of the file to be created; and one for the list of Generation
properties belonging to the class (see Figures 1 and 2). Now we have all the details necessary for creating the new unit.
Although Delphi provides several classes to assist in generating new
The parent class defaults to TDBPersistent, but can be replaced with form files (using proxies) and their associated units, our needs are
another name. This is done because the new class must eventually derive much simpler. We’re only producing a single-unit file that consists
from this base class to provide the desired abilities of automatic storage solely of text. Hence we can place the code into a string list, and use
in a relational database. Any value may be entered as the name of the its SaveToFile method to construct the resulting unit (see Figure 3).
new class (an initial “T” is added as necessary), but remember that
this also becomes the name of the database table. The output filename We use a string constant to provide the basic text for the generated
defaults to the class name (without the initial “T”) plus a .pas extension, unit. This has placeholders for variable sections within it for use
but (again) can be overridden. Clicking the Browse button allows us to with the Format function. These are replaced by variable values (for
search for an appropriate output location and filename. The Save dialog simple substitutions), or by the result of various functions (for more
box also warns us if we plan to overwrite an existing file. complex code). Examples of the functions can be seen in Figure 4.
They provide the list of internal fields for the class, the published
On the second page, we have a string grid containing the list of properties, and a copy-assignment method, respectively.
properties belonging to the new class. Entries are added and removed
with the Add and Delete buttons. Each entry consists of the property The final step in producing the new unit is to incorporate it into
name, a flag to indicate whether it’s a primary key field, and its type. the existing project. We can achieve this through the CreateModule
The spin control following the type name allows the length of strings method of the ToolServices object (see Figure 5). Normally this takes
to be specified and is ignored for all other types. Types can be selected as parameters streams that contain the code and graphical (.dfm) parts
from the drop-down list, or can be entered manually if they’re not of a unit, along with a unit name and flags indicating how the other
one of the standard types. items are to be treated. In our case, we are generating the unit directly
to a file, so we don’t need to fiddle around with the streams.
By providing the name of the new file, as returned by the GenerateUnit
method, and an appropriate flag, cmExisting, we can leave the stream
parameters set to nil, and have the unit read from disk. The other flags
add the unit to the current project (cmAddToProject), and bring the editor
window for the newly opened file to the fore (cmShowSource).
Installation
The completed wizard is compiled into a DLL. It’s suggested that pack-
ages are used to reduce the overall size of the final product; instead of
a 400KB monster, it can become a svelte 30KB. Move the DLL to an
appropriate directory, and then tell Delphi about it. To do this we need to
update the registry item containing the list of wizards to load.
{ Create the unit file for this persistent object. }
function TfrmDBPersistentGen.GenerateUnit: string;
const
sPKPrefixes:
array [Boolean] of string[3] = ('', sPKPrefix);
Figure 1: The DBPersistent Wizard in action. First, the class var
name, parent class, and filename are entered ... slsUnit: TStringList;
sClassName, sParentName, sUnitName: string;
begin
Result := edtFilename.Text;
slsUnit := TStringList.Create;
try
sClassName := edtClass.Text;
if sClassName[1] <> 'T' then
sClassName := 'T' + sClassName;
sParentName := edtParent.Text;
if sParentName[1] <> 'T' then
sParentName := 'T' + sParentName;
sUnitName := ChangeFileExt(
ExtractFileName(edtFilename.Text), '');
slsUnit.Text := Format(sUnitSource,
[sUnitName, sClassName, GetUses, GetTypes,
sClassName, sParentName, GetPrivateFields,
GetPublicAttrs, GetPublishedProps, sClassName,
GetConstructor, GetAssign, sClassName]);
slsUnit.SaveToFile(edtFilename.Text);
finally
slsUnit.Free;
end;
end;
Figure 2: ... then the properties are described. Figure 3: Generating the unit.
18 October 2000 Delphi Informant Magazine
OP Tech
Under the HKEY_CURRENT_USER\Software\Borland\Delphi\ to invoke it, enter appropriate values, and click the Finish button. A
n.0\Experts key in the Windows registry (where n is the version new unit is created and added to the current project. An example
number for Delphi), add a new string value. Set its name to DBPGen, unit, corresponding to the entries displayed in Figures 1 and 2, is
and its value to the full path to the DLL we just created. Note that shown in Listing One.
a recompile of the wizard is necessary for each version of Delphi,
because the expert interface internals have changed over time. Conclusion
The database persistence provided by the classes developed last
The next time Delphi is started, we should find a new option in the month makes the process of linking objects with a relational database
New Items dialog box. Double-click the DBPersistent Generator icon very simple. This month, we’ve made it even easier by creating a
form wizard that extends Delphi, and generates the necessary class
{ Generate list of internal fields for the properties. }
definition for us. This, of course, greatly decreases the effort, and the
function GetPrivateFields: string;
var opportunity for introducing coding errors. ∆
iIndex: Integer;
begin The files referenced in this article are available on the Delphi Informant
Result := ''; Magazine Complete Works CD in INFORM\00\OCT\DI200010KW.
with stgProperties do
for iIndex := 1 to RowCount - 1 do
if Cells[0, iIndex] <> sDefProperty then
Result :=
Result + Format(sFields, [Cells[0, iIndex], Keith Wood is an analyst/programmer with CCSC, based in Atlanta. He started
sPKPrefixes[IsPrimaryKey(iIndex)] + using Borland’s products with Turbo Pascal on a CP/M machine. Often working with
Cells[1, iIndex]]); Delphi, he has enjoyed exploring it since it first appeared. You can reach him via
end;
e-mail at
[email protected].
{ Generate the published properties. }
function GetPublishedProps: string;
var
iIndex: Integer;
begin Begin Listing One — TContact
Result := '';
with stgProperties do unit Contact;
for iIndex := 1 to RowCount - 1 do { TContact - database persistent object
if Cells[0, iIndex] <> sDefProperty then Generated by DBPersistent Expert. }
Result := interface
Result + Format(sProperty, [Cells[0, iIndex],
sPKPrefixes[IsPrimaryKey(iIndex)] + uses
Cells[1, iIndex]]); Classes, Controls, DBPersist;
end;
type
{ Generate the copy method. } String15 = string[15];
function GetAssign: string; String50 = string[50];
var
iIndex: Integer; TContact = class(TDBPersistent)
begin private
Result := Format(sAssign1, [sClassName]); FContactId: TPKInteger;
with stgProperties do FName: String50;
for iIndex := 1 to RowCount - 1 do FOrganisation: String50;
Result := FPhone: String15;
Result + Format(sAssign2, [Cells[0, iIndex]]); FPhysicalAddr: TAddress;
if sParentName = sTDBPersistent then FMailingAddr: TAddress;
Result := Result + sAssign3; FLastContact: TDate;
Result := Result + sAssign4; public
end; constructor Create(dbmManager: TDBManager;
ContactId: TPKInteger);
Figure 4: Selected wizard functions. procedure Assign(Source: TPersistent); override;
published
property ContactId: TPKInteger
{ Run the expert and open the resulting file. } read FContactId write FContactId;
procedure DBPersistentExpert(ToolServices: TIToolServices); property Name: String50 read FName write FName;
var property Organisation: String50
sFilename: string; read FOrganisation write FOrganisation;
begin property Phone: String15 read FPhone write FPhone;
with TfrmDBPersistentGen.Create(Application) do property PhysicalAddr: TAddress
try read FPhysicalAddr write FPhysicalAddr;
if ShowModal = mrOK then begin property MailingAddr: TAddress
sFilename := GenerateUnit; read FMailingAddr write FMailingAddr;
ToolServices.CreateModule(sFilename, nil, nil, property LastContact: TDate
[cmAddToProject, cmShowSource, cmExisting]); read FLastContact write FLastContact;
end; end;
finally
Free; implementation
end;
end; { TContact ---------------------------------------------- }
Figure 5: Running the wizard.
19 October 2000 Delphi Informant Magazine
OP Tech
{ Initialization. }
constructor TContact.Create(dbmManager: TDBManager;
ContactId: TPKInteger);
begin
inherited Create(dbmManager);
Self.ContactId := ContactId;
end;
{ Copy TContact. }
procedure TContact.Assign(Source: TPersistent);
begin
if Source is TContact then
with TContact(Source) do begin
Self.ContactId := ContactId;
Self.Name := Name;
Self.Organisation := Organisation;
Self.Phone := Phone;
Self.PhysicalAddr := PhysicalAddr;
Self.MailingAddr := MailingAddr;
Self.LastContact := LastContact;
Exit;
end;
inherited Assign(Source);
end;
initialization
RegisterDBPersistentClass(TContact);
end.
End Listing One
20 October 2000 Delphi Informant Magazine
On Language
Windows Shortcuts / COM / Interfaces
By Bill Todd
A Quick Way to Shortcuts
A Component for Creating and Modifying Shortcuts
W indows shortcuts provide a way to have as many links to a file as you need —
in as many folders as you want. Shortcuts are also the tool for adding a file to
the Windows Start menu.
In Windows 3.x, creating shortcuts was easy. You The Interfaces
had to learn a couple of simple DDE calls, and that Shortcuts, or links as they are sometimes called, are
was it. In 32-bit Windows, working with shortcuts actually binary files stored on your hard disk with
is more complex, and requires the use of COM and the .lnk extension. The Windows shell includes
interfaces. This article will look at working with a COM object named ShellLink for working
shortcuts in detail, and show you how to build with shortcuts. The ShellLink object implements
a custom component you can use to create and two interfaces, IShellLink and IPersistFile, that
modify shortcuts in any folder. define the methods for working with shortcuts.
Figure 1 shows the declaration of IShellLink from
IShellLinkA = interface(IUnknown) { sl. } SHLOBJ.PAS, and Figure 2 shows the declaration
[SID_IShellLinkA]
of IPersistFile from ACTIVEX.PAS.
function GetPath(pszFile: PAnsiChar; cchMaxPath: Integer;
var pfd: TWin32FindData; fFlags: DWORD): HResult;
stdcall; Into the TWinShortcut Component
function GetIDList(var ppidl: PItemIDList): HResult; The shell of the TWinShortcut custom compo-
stdcall;
nent was created with the Component Wizard
function SetIDList(pidl: PItemIDList): HResult; stdcall;
function GetDescription(pszName: PAnsiChar;
in the Object Repository, using TComponent as
cchMaxName: Integer): HResult; stdcall; its ancestor. Listing One (beginning on page 23)
function SetDescription(pszName: PAnsiChar): HResult; shows the finished component. To make things
stdcall; easier to find, the properties and their private
function GetWorkingDirectory(pszDir: PAnsiChar;
member variables are in alphabetical order. In the
cchMaxPath: Integer): HResult; stdcall;
function SetWorkingDirectory(pszDir: PAnsiChar): HResult; implementation section, the methods are divided
stdcall; into three groups: constructor and destructor,
function GetArguments(pszArgs: PAnsiChar; property getter and setter, and custom methods.
cchMaxPath: Integer): HResult; stdcall;
Within each of these groups, the methods are
function SetArguments(pszArgs: PAnsiChar): HResult;
stdcall;
in alphabetical order. The constructor is overrid-
function GetHotkey(var pwHotkey: Word): HResult; stdcall; den to automatically create an instance of the
function SetHotkey(wHotkey: Word): HResult; stdcall; ShellLink object using the following code:
function GetShowCmd(out piShowCmd: Integer): HResult;
stdcall;
FShellLink :=
function SetShowCmd(iShowCmd: Integer): HResult; stdcall;
CreateComObject(CLSID_ShellLink) as
function GetIconLocation(pszIconPath: PAnsiChar;
IShellLink;
cchIconPath: Integer; out piIcon: Integer): HResult;
FPersistFile := FShellLink as IPersistFile;
stdcall;
function SetIconLocation(pszIconPath: PAnsiChar;
iIcon: Integer): HResult; stdcall;
function SetRelativePath(pszPathRel: PAnsiChar;
The first statement creates the ShellLink object
dwReserved: DWORD): HResult; stdcall; by calling CreateCOMObject and passing the
function Resolve(Wnd: HWND; fFlags: DWORD): HResult; ShellLink object’s class ID as the parameter. The
stdcall; return value is cast to type IShellLink to provide a
function SetPath(pszFile: PAnsiChar): HResult; stdcall;
reference to the IShellLink interface and its meth-
end;
ods. FShellLink is a protected member variable
Figure 1: The IShellLink interface. of type IShellLink. FPersistFile is also a protected
21 October 2000 Delphi Informant Magazine
On Language
IPersistFile = interface(IPersist) FullShortcutPath returns a Pascal ANSI string, the path variable
['{ 0000010B-0000-0000-C000-000000000046 }'] FullPath is first cast to type WideString, and then cast to type
function IsDirty: HResult; stdcall; PWideChar to match the type of the parameter. Note that Load
function Load(pszFileName: POleStr; dwMode: Longint): is called as a parameter to the OleCheck procedure. OleCheck
HResult; stdcall;
function Save(pszFileName: POleStr; fRemember: BOOL):
examines the value returned by SHGetSpecialFolderLocation, and
HResult; stdcall; if that value indicates an error, OleCheck raises an EOleSysError
function SaveCompleted(pszFileName: POleStr): HResult; exception. This technique is used for all of the interface method
stdcall; calls in this example, so normal Delphi exception handling can
function GetCurFile(out pszFileName: POleStr): HResult;
be used to trap errors that occur when using this component.
stdcall;
end; The last line of the LoadShortcut method calls the custom method
GetPropertiesFromShortcut, which calls each of the get methods of
Figure 2: The IPersistFile interface. the IShellLink interface and assigns the returned value to the cor-
responding property of TWinShortcut.
member variable and is of type IPersistFile. Casting FShellLink to
IPersistFile provides an interface reference to the IPersistFile meth- Before continuing, let’s look at the GetFullShortcutPath and
ods implemented by the ShellLink object. TWinShortcut’s destruc- GetPropertiesFromShortcut methods used by OpenShortcut. If
tor is overridden, and both FShellLink and FPersistFile are set to nil the ShortcutPath property is null, GetFullShortcutPath calls
to destroy the ShellLink object. Because COM objects are reference GetSpecialFolderPath. Otherwise, it assigns the value of the
counted, both variables must be set to nil before the ShellLink ShortcutPath property to Result. It then adds the ShortcutFileName
object will be destroyed. property to the end of the string. This is safe because
GetSpecialFolderPath always returns a path that ends with a backs-
You must be able to specify the name and location of the shortcut lash, and the write method for the ShortcutPath property ensures
file you want to work with, and that capability is provided by three that the property value always ends with a backslash. The write
properties: ShortcutFileName, ShortcutPath, and SpecialFolderLocation. method for the ShortcutFileName property ensures that the file-
One big problem in working with shortcuts is figuring out where to name always includes the .lnk extension.
create them. For example, if you want to create a shortcut on the
user’s desktop, you have to know the path to the desktop folder, and GetSpecialFolderPath calls SHGetSpecialFolderLocation and passes the
that is different for different versions of Windows. value of the SpecialFolderLocation property as the second parameter.
This call loads the ItemIdList variable passed as the third parameter.
The solution is a Windows API function named Next, GetSpecialFolderPath calls SHGetPathFromIdList, passing two
SHGetSpecialFolderLocation, which takes three parameters. The parameters. The first is the ItemIdList variable initialized by the call to
first is a window handle, which can be set to zero. The second SHGetSpecialFolderLocation, and the second is a char array, CharStr,
is a constant that identifies the folder you want. To find a partial into which the path will be placed. Finally, CharStr is assigned to the
list of constants, click Start | Programs | Borland Delphi 5 | Help Result variable and a backslash is appended to the path.
| MS SDK Help Files | Win32 Programmers Reference and search
for SHGetSpecialFolderLocation. If you have the MSDN Library The final step in the OpenShortcut method is the call to
CD, search for SHGetSpecialFolderLocation and you’ll find a list GetPropertiesFromShortcut. This method calls each of the get methods
of over 40 folder constants. The Win32 Programmers Reference in the IShellLink interface and assigns the returned value to the
Help file also contains detailed information about IShellLink and corresponding property of TWinShortcut. For example, the first call
IPersistFile and their methods. The third parameter is a variable is to the IShellLink GetPath method, which returns the path to the
of type PItemIdList. target file, i.e. the file to which the shortcut points. These calls are
straightforward with two exceptions. If you create a shortcut manu-
After calling SHGetSpecialFolderLocation, you will call ally in Windows, and the shortcut is to a program that requires
SHGetPathFromIdList to extract the actual path from the PItemIdList command-line arguments, you type them in the Target edit box
parameter. The SpecialFolderLocation property of TWinShortcut is of following the path to the EXE file. However, the command-line
type Word and corresponds to the second parameter, the folder number, arguments are stored separately in the shortcut file and are retrieved
of SHGetSpecialFolderLocation. This lets you specify the location of the with a separate call, GetArguments.
shortcut by setting the value of the SpecialFolderLocation property, or by
providing a path in the ShortcutPath property. The call to GetHotkey returns the hotkey information in a single
parameter of type Word. The virtual key code is stored in the low
TWinShortcut has a public OpenShortcut method that’s used to open byte, and the modifier flags that indicate which shift keys were
an existing shortcut. This method is only three statements long. The pressed are stored in the high-order byte. If you want to display the
first statement is a call to the protected method GetFullShortcutPath. hotkey as text, or give users the ability to enter a hotkey, the easy
GetFullShortcutPath returns the full path and filename of the short- way is to use the THotkey component from the Win32 page of the
cut. The second statement, shown here, actually opens the file by Component palette. The problem is that the THotkey component
calling the IPersistFile interface’s Load method: stores the virtual key code in its HotKey property, and the modifier
flags in its Modifiers property. To make things worse, the values used
OleCheck(FPersistFile.Load(PWideChar(WideString(FullPath)), to represent the C, A, S, and extended keys in the high
STGM_READWRITE)); byte of the value returned by GetHotkey aren’t the same as the values
used to represent the same keys in the Modifiers property of THotkey.
Load ’s two parameters are the name of the file and the mode.
Because this function is Unicode-compatible, the path must be (Note: The extended-key flag indicates whether the keystroke
a null-terminated string of wide chars. Because the call to Get- message originated from one of the additional keys on the enhanced
22 October 2000 Delphi Informant Magazine
On Language
keyboard. The extended keys consist of the A and C keys on the
right-hand side of the keyboard; the Z, X, h, e, u, Begin Listing One — TWinShortcut
d, and the arrow keys to the left of the numeric keypad; the unit WinShortcut;
n key; the k key; the p key; and the divide (/) and
interface
J keys in the numeric keypad. The extended-key flag is set if the
key is an extended key.) uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
To make life easier for anyone using TWinShortcut, it has two Forms, Dialogs, ComObj, ShlObj, ShellAPI, ActiveX, Menus,
properties, Hotkey and HotkeyModifiers, that are assignment- ComCtrls;
compatible with the properties of THotkey. The code following the type
call to GetHotkey converts the modifier flags from the form used TWinShortcut = class(TComponent)
by GetHotkey to the form used by the HotkeyModifiers property private
and by the THotkey component. The modifier constants used with { Private declarations. }
FArguments: string;
GetHotkey and SetHotkey (HOTKEYF_ALT, HOTKEYF_CONTROL,
FDescription: string;
HOTKEYF_SHIFT and HOTKEYF_EXT) are declared in the FHotkey: Word;
CommCtrl.pas unit. The constants used with the THotkey component’s FHotKeyModifiers: THKModifiers;
Modifiers property (hkAlt, hkCtrl, hkShift, and hkExt) are declared in the FIconFile: string;
ComCtrls.pas unit. FIconIndex: Integer;
FShortcutFileName: string;
FShortcutPath: string;
Creating or saving a modified shortcut is handled by the FRunWindow: Integer;
TWinShortcut’s public SaveShortcut method. SaveShortcut begins FSpecialFolder: Integer;
by calling PutPropertiesToShortcut. This method calls the FTarget: string;
FWorkingDirectory: string;
IShellLink put method for each property to assign the current
protected
value of the TWinShortcut properties to the corresponding short- { Protected declarations. }
cut properties. The only part of this process that is complex is FPersistFile: IPersistFile;
converting the HotkeyModifiers property to the form required by FShellLink: IShellLink;
the SetHotkey method. The series of if statements set the appropri- function GetFullShortcutPath: string;
procedure GetPropertiesFromShortcut;
ate bits in the byte variable HotKeyMods. SetHotkey is called with function GetSpecialFolderPath: string;
a single-word parameter that’s constructed by shifting HotKeyMods procedure PutPropertiesToShortcut;
left eight bits to place it in the high-order byte of the word procedure SetShortcutFileName(Value: string);
and adding the value of the HotKey property. Next, a call to procedure SetShortcutPath(Value: string);
procedure SetSpecialFolder(Value: Integer);
GetFullShortcutPath returns the path to the link file. Finally, the
public
IPersistFile Save method is called with the full path to the link file { Public declarations. }
as a parameter. Again, the path must be cast first to a WideString, constructor Create(AOwner: TComponent); override;
and then to a PWideChar. destructor Destroy; override;
procedure OpenShortcut;
Conclusion procedure SaveShortcut;
published
You can create and modify any Windows shortcut using the methods { Published declarations. }
of the IShellLink and IPersistFile interfaces implemented by the property Arguments: string
ShellLink object. Although this article doesn’t cover every method read FArguments write FArguments;
property Description: string
in detail, it should give you everything you need for most shortcut
read FDescription write FDescription;
operations. For more detailed information about the interfaces or any property HotKey: Word read FHotkey write FHotkey;
of their methods, consult the Win32 Programmers Reference online property HotKeyModifiers: THKModifiers
Help file that’s installed with Delphi 5. ∆ read FHotKeyModifiers write FHotKeyModifiers;
property IconFile: string
read FIconFile write FIconFile;
The files referenced in this article are available on the Delphi Informant property IconIndex: Integer
Magazine Complete Works CD in INFORM\00\OCT\DI200010BT. read FIconIndex write FIconIndex;
property RunWindow: Integer
read FRunWindow write FRunWindow;
property Target: string read FTarget write FTarget;
property ShortcutFileName: string
read FShortcutFileName write SetShortcutFileName;
property ShortcutPath: string
read FShortcutPath write SetShortcutPath;
property SpecialFolder: Integer
Bill Todd is president of The Database Group, Inc., a database consulting and
read FSpecialFolder write SetSpecialFolder;
development firm based near Phoenix. He is a Contributing Editor to Delphi property WorkingDirectory: string
Informant Magazine, co-author of four database programming books, author of read FWorkingDirectory write FWorkingDirectory;
over 60 articles, and a member of Team Borland, providing technical support on end;
the Borland Internet newsgroups. He is a frequent speaker at Borland Developer
procedure Register;
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. implementation
He was an instructor on the 1995, 1996, and 1997 Borland/Softbite Delphi World
Tours. He can be reached at
[email protected]. uses CommCtrl;
23 October 2000 Delphi Informant Magazine
On Language
WinFindData: TWin32FindData;
const RunWin: Integer;
Backslash = '\'; HotKeyWord: Word;
LinkExtension = '.LNK'; HotKeyMod: Byte;
begin
{ ********* Constructor and Destructor ********** } OleCheck(FShellLink.GetPath(CharStr, MAX_PATH,
WinFindData, SLGP_UNCPRIORITY));
constructor TWinShortcut.Create(AOwner: TComponent); Target := CharStr;
begin OleCheck(FShellLink.GetArguments(CharStr, MAX_PATH));
{ Create the ShellLink object and get an IShellLink and Arguments := CharStr;
an IPersistFile reference to it. } OleCheck(FShellLink.GetDescription(CharStr, MAX_PATH));
inherited; Description := CharStr;
FShellLink := OleCheck(
CreateComObject(CLSID_ShellLink) as IShellLink; FShellLink.GetWorkingDirectory(CharStr, MAX_PATH));
FPersistFile := FShellLink as IPersistFile; WorkingDirectory := CharStr;
end; OleCheck(FShellLink.GetIconLocation(CharStr, MAX_PATH,
FIconIndex));
destructor TWinShortcut.Destroy; IconFile := CharStr;
begin OleCheck(FShellLink.GetShowCmd(RunWin));
{ Free the ShellLink object. } RunWindow := RunWin;
FShellLink := nil; OleCheck(FShellLink.GetHotkey(HotKeyWord));
FPersistFile := nil; { Extract the HotKey and Modifier properties. }
inherited; HotKey := HotKeyWord;
end; HotKeyMod := Hi(HotKeyWord);
if (HotKeyMod and HOTKEYF_ALT) = HOTKEYF_ALT then
{ ********* Property Getter and Setter Methods ********** } Include(FHotKeyModifiers, hkAlt);
if (HotKeyMod and HOTKEYF_CONTROL) = HOTKEYF_CONTROL then
procedure TWinShortcut.SetShortcutFileName(Value: string); Include(FHotKeyModifiers, hkCtrl);
begin if (HotKeyMod and HOTKEYF_SHIFT) = HOTKEYF_SHIFT then
FShortcutFileName := Value; Include(FHotKeyModifiers, hkShift);
{ If the file name does not end with the .LNK extension, if (HotKeyMod and HOTKEYF_EXT) = HOTKEYF_EXT then
add the extension. } Include(FHotKeyModifiers, hkExt);
if CompareText(ExtractFileExt(FShortcutFileName), end;
LinkExtension) <> 0 then
FShortcutFileName := FShortcutFileName + LinkExtension; function TWinShortcut.GetSpecialFolderPath: string;
end; { Returns the full path to the special folder specified in
the SpecialFolder property. A backslash is appended to
procedure TWinShortcut.SetShortcutPath(Value: string); the path. }
begin var
FShortcutPath := Value; ItemIdList: PItemIdList;
{ Make sure the path ends with a backslash. } CharStr: array[0..MAX_PATH] of Char;
if Copy(FShortcutPath, begin
Length(FShortcutPath), 1) <> Backslash then OleCheck(ShGetSpecialFolderLocation(0, FSpecialFolder,
FShortcutPath := FShortcutPath + Backslash; ItemIdList));
end; if ShGetPathFromIdList(ItemIdList, CharStr) then begin
Result := CharStr;
procedure TWinShortcut.SetSpecialFolder(Value: Integer); Result := Result + Backslash;
begin end; // if
FSpecialFolder := Value; end;
{ Clear the ShortcutPath when a value is assigned to the
SpecialFolder property. The SpecialFolder property will procedure TWinShortcut.OpenShortcut;
not be used to get the path to the link file if the { Opens the shortcut and loads its properties into the
ShortcutPath property is not null. } component properties. }
FShortcutPath := ''; var
end; FullPath: string;
begin
{ ********* Custom Methods ********** } FullPath := GetFullShortcutPath;
OleCheck(FPersistFile.Load(PWideChar(WideString(
function TWinShortcut.GetFullShortcutPath: string; FullPath)), STGM_READWRITE));
{ Gets the path to the shortcut file. If the ShortcutPath GetPropertiesFromShortcut;
property is null, the path comes from the SpecialFolder end;
property. }
begin procedure TWinShortcut.PutPropertiesToShortcut;
if FShortcutPath = '' then { Calls the appropriate IShellLink method to assign the
Result := GetSpecialFolderPath value of each of the components properties to the
else corresponding property of the shortcut. }
Result := FShortcutPath; var
Result := Result + FShortcutFileName; HotKeyMods: Byte;
end; begin
HotKeyMods := 0;
procedure TWinShortcut.GetPropertiesFromShortcut; OleCheck(FShellLink.SetPath(PChar(FTarget)));
{ Calls the appropriate IShellLink method to get the value OleCheck(FShellLink.SetIconLocation(PChar(FIconFile),
of each property of the link and assign that value to the FIconIndex));
corresponding property of this component. } OleCheck(FShellLink.SetDescription(PChar(FDescription)));
var OleCheck(FShellLink.SetWorkingDirectory(PChar(
CharStr: array[0..MAX_PATH] of Char; FWorkingDirectory)));
24 October 2000 Delphi Informant Magazine
OP Tech
OleCheck(FShellLink.SetArguments(PChar(FArguments)));
OleCheck(FShellLink.SetShowCmd(FRunWindow));
if hkShift in FHotKeyModifiers then
HotKeyMods := HotKeyMods or HOTKEYF_SHIFT;
if hkAlt in FHotKeyModifiers then
HotKeyMods := HotKeyMods or HOTKEYF_ALT;
if hkCtrl in FHotKeyModifiers then
HotKeyMods := HotKeyMods or HOTKEYF_CONTROL;
if hkExt in FHotKeyModifiers then
HotKeyMods := HotKeyMods or HOTKEYF_EXT;
OleCheck(
FShellLink.SetHotkey((HotKeyMods shl 8) + HotKey));
end;
procedure TWinShortcut.SaveShortcut;
{ Copies the component properties to the shortcut
and saves it. }
var
FullPath: string;
begin
PutPropertiesToShortcut;
FullPath := GetFullShortcutPath;
OleCheck(FPersistFile.Save(PWideChar(
WideString(FullPath)), True));
end;
procedure Register;
begin
RegisterComponents('DI', [TWinShortcut]);
end;
end.
End Listing One
25 October 2000 Delphi Informant Magazine
Columns & Rows
ADOX / JRO / Delphi 5
By Alex Fedorov and Natalia Elmanova
A Practical Guide to
ADO Extensions
Part I: Using ADOX and JRO
D elphi is well known for its ability to access various databases. Until recently, however,
this access has required the Borland Database Engine (BDE). As you probably know,
Delphi 5 features an alternative technology called ADO Express. This set of components
provides the object-oriented, high-level interface to Microsoft ActiveX Data Objects
(ADO), which is a part of the Microsoft Universal Data Access architecture.
Although ADO and ADO Express have already define the metadata in most modern databases.
been covered in Delphi Informant Magazine, several Before ADOX, the only way to extract metadata
extensions to this data access technology deserve from data sources using ADO was to use the
separate attention. This month, in Part I, we’ll OpenSchema method of the ADO Connection
look at ADO Extensions for DDL and Security object. To create new database objects, you used
(ADOX), and the Jet and Replication Objects the Data Definition Language (DDL) component
library (JRO). Next month, we’ll explore ADO of SQL and the ADO Command object. In other
Multi-dimensional (ADO MD). [Visit http:// words, you were passing in SQL statements, which
msdn.microsoft.com/library/officedev/odeopg/ — obviously — necessitated a knowledge of SQL.
deovradocomponentlibraries.htm for a full descrip-
tion of the ADO component libraries.] ADOX provides a way to manipulate metadata
that doesn’t require an understanding of SQL.
Introduction to ADOX Note that ADOX doesn’t work with all databases in
ADOX can be used to perform various tasks not the world; its functionality is limited to Microsoft
available using ADO alone. For example, you Access, Microsoft SQL Server, and a few databases
can extract information about users and/or create from other vendors. For more information, visit
new user accounts. ADOX extends the ADO http://www.microsoft.com/data.
object model with 10 objects that can be used
separately or in conjunction with ADO. You can The ADOX object model is shown in Figure 1, and
use the ADO Connection object, for instance, to many of its objects are briefly described in Figure
connect to a data source and extract metadata. 2. The top-level object in the ADOX object model
is Catalog. It contains the Tables, Views, Procedures,
Metadata describes the database itself (e.g. tables, Users, and Groups collections. The Catalog object can
columns, indexes, keys, stored procedures, etc.), be used to open an existing database (through the
rather than the data it contains. SQL is used to ADO Connection object), or to create a new one. In
Properties Property
Catalog
Tables Table Table Columns Columns
Groups Group Users User Properties Property
Users User Groups Group
Indexes Index Columns Column
Procedures Procedure Command
Properties Property
Views View Command
Figure 1: The ADOX object model. Keys Key Columns Column
26 October 2000 Delphi Informant Magazine
Columns & Rows
keys, and then programmatically or manually fill this newly created
Object Description
database with the information. This can be a great help in situations
Catalog Represents the schema of the database, and where we have raw data that needs to be organized in some way. In
provides access to collections of all the tables, a general case, to create a new database, we use the Catalog object,
procedures, users, and groups in a database. and then use the Add method of the Tables, Columns, Keys, and
Column Represents a column in a table, or the columns Indexes collections to add the database objects to it.
involved in an index or key.
Connection Used to provide a connection to a data source. Now that we’ve discussed the ADOX objects, let’s use them to create
Group Represents a group account that has access to a a simple ADOX viewer.
secured database.
Index Represents an index on a table. Contains a Creating a Simple ADOX Viewer
collection of the Column objects upon which the Let’s look at how to use ADOX objects in Delphi. We’ll create an
index is based. application that can:
Key Represents a key on a table. Contains a collection § Show the database metadata in a tree view.
of the Column objects upon which the key is based. § Show properties of database objects.
Procedure Represents a stored procedure or query. § Show the source code of views and stored procedures.
Table Represents a table in the database and provides
access to columns, indexes, and keys. To perform this task, we’ll create a new project and place the following
User Represents a user of a secured database. components onto the main form: MainMenu, TreeView, Memo, and
View Represents a view (a virtual table). StatusBar. The completed demonstration application is shown in
Figure 2: Selected ADOX objects. Figure 3. (It’s also available for download; see end of article for details.)
Next, we must include a reference to the ADOX type library, i.e. the
Msadox.dll file. To do this, select Project | Import Type Library from
the main menu of the Delphi IDE, then select Microsoft ADO Ext.
2.1 for DDL and Security from the list of available type libraries. To
avoid conflicts with previously declared classes, rename the ADOX
classes (e.g. TTable to TTablexxx), uncheck the Generate Component
Wrapper checkbox (we only need the .pas file), and press the Create
Unit button. This will create an ADOX_TLB.pas file — the interface
unit to the ADOX type library. We’ll need to refer to it in the
uses clause of our main project unit. We’ll also need to include the
ADODB unit in the uses clause. Now we’re ready to write code that
uses ADOX objects. We’ll create the File | Open Catalog menu item,
and modify its OnClick event handler to appear as shown here:
procedure TForm1.OpenCatalog1Click(Sender: TObject);
begin
// Get DataSourceName through standard MS dialog box.
DS := PromptDataSource(Application.Handle, '');
// If user selected one...
if DS <> '' then
BrowseData(DS);
end;
Figure 3: The demonstration application for this article loads the
metadata of a selected database into a TreeView component. Here we use the
PromptDataSource
the current version of ADO, we can only create new Jet 4.0 databases, method, imple-
but in future versions, this ability will be extended to other databases. mented in the
ADODB unit,
Once we have the Catalog object, we can work with Tables, Procedures, to display the
and Views. For example, by iterating the Tables collection, we can find standard Data
what tables are in the database. Going deeper, we can check a Table Link Properties
object’s Columns, Indexes, and Keys collections. By checking the prop- dialog box (see
erties of a database, we can get information about its metadata, and, Figure 4). If you
for example, store it in a separate file or transfer it somewhere. Using have the Microsoft
the Users and Groups collections, we can obtain security information Access sample
to find group accounts and users of a secured database. Note that this databases installed,
requires a secured database. In the case of Access, we must include the you may want to
System.mdw database in the connection string to the data source. test with the
Northwind.mdb
The other, more exciting, thing about ADOX is that we can use database using the
it to create databases and objects from scratch. For example, we Microsoft Jet OLE
can create a Jet (Access) database, add tables, columns, indexes, and DB provider. Figure 4: The Data Link Properties dialog box.
27 October 2000 Delphi Informant Magazine
Columns & Rows
Once a data source is selected, the BrowseData procedure is called. This is done to avoid possible errors with data sources for which
The purpose of this procedure is to fill the TreeView component with ADOX does not support Views collections. The CheckViews function
metadata extracted from the data source. Figure 5 shows the code is shown in Figure 7.
that does the job.
Now we have the TreeView component filled with metadata informa-
There are three loops that iterate through the Tables, Views, and tion. To get more information about this particular object, we need to
Procedures collections of the Catalog object. Each object found is implement the OnChange event handler for the TreeView component
placed on the appropriate branch of the TreeView component. The (see Figure 8).
Tables collection contains one or more Table objects, each of which
should be processed to find Columns, Indexes, and Keys within it. This As you can see, this is straightforward code that calls one of six pro-
is done with the ProceedTables procedure, shown in Figure 6. cedures, depending on which node was clicked on the TreeView com-
ponent. For example, the ViewTables procedure displays the number
Again, we have three loops. In this procedure they iterate the of objects within the table selected, and ViewColumns, ViewIndexes,
Columns, Indexes, and Keys collections of the Table object. and ViewKeys are used to study the properties of the Column, Index,
and Key objects (see Figures 9, 10, and 11 respectively).
Returning to the BrowseData procedure, we find that before the loop
through the Views collection, we perform the following check: procedure TForm1.ProceedTables(T: Table; N: TTreeNode);
var
I : Integer;
if CheckViews(Catalog) then ...
SubNode : TTreeNode;
begin
// Add Columns.
procedure TForm1.BrowseData(DataSource: string); if T.Columns.Count > 0 then
var SubNode := TreeView1.Items.AddChild(N, 'Columns');
RootNode : TTreeNode; for I := 0 to T.Columns.Count-1 do
OneNode : TTreeNode; TreeView1.Items.AddChild(SubNode,
SubNode : TTreeNode; T.Columns.Item[I].Name);
I : Integer; // Add Indexes.
OldCursor : TCursor; if T.Indexes.Count > 0 then
begin SubNode := TreeView1.Items.AddChild(N, 'Indexes');
// Change the default cursor to an hourglass. for I := 0 to T.Indexes.Count-1 do
OldCursor := Screen.Cursor; TreeView1.Items.AddChild(SubNode,
Screen.Cursor := crHourglass; T.Indexes.Item[I].Name);
StatusBar1.Panels[0].Text := // Add Keys.
'Extracting metadata, please wait.'; if T.Keys.Count > 0 then
// Clear TreeView and Memo. SubNode := TreeView1.Items.AddChild(N, 'Keys');
ClearTree; for I := 0 to T.Keys.Count-1 do
Memo1.Lines.Clear; TreeView1.Items.AddChild(SubNode, T.Keys.Item[I].Name);
Application.ProcessMessages; end;
// Connect to the data source.
Catalog._Set_ActiveConnection(DataSource); Figure 6: Processing a table’s objects.
RootNode := TreeView1.Items.Add(nil, 'Catalog');
// Add Tables. function CheckViews(C: _Catalog): Boolean;
OneNode := TreeView1.Items.AddChild(RootNode, 'Tables'); var
for I := 0 to Catalog.Tables.Count-1 do begin I : Integer;
SubNode := TreeView1.Items.AddChild( begin
OneNode, Catalog.Tables[I].Name); try
// Process Columns, Indexes, and Keys. I := C.Views.Count;
ProceedTables(Catalog.Tables[I], SubNode); CheckViews := True;
end; except
// Add Views. CheckViews := False;
if CheckViews(Catalog) then begin end;
OneNode := TreeView1.Items.AddChild(RootNode, 'Views'); end;
for I := 0 to Catalog.Views.Count-1 do
SubNode := TreeView1.Items.AddChild( Figure 7: The CheckViews function.
OneNode, Catalog.Views[I].Name);
end; procedure TForm1.TreeView1Change(Sender: TObject;
// Add Procedures. Node: TTreeNode);
OneNode := TreeView1.Items.AddChild(RootNode, begin
'Procedures'); if Node.Parent.Parent <> nil then
for I := 0 to Catalog.Procedures.Count-1 do case Node.Parent.Text[1] of
SubNode := TreeView1.Items.AddChild( 'C' : ViewColumns(Node.Parent.Parent.Text,Node.Text);
OneNode, Catalog.Procedures[I].Name); 'I' : ViewIndexes(Node.Parent.Parent.Text,Node.Text);
RootNode.Expand(False); 'K' : ViewKeys(Node.Parent.Parent.Text,Node.Text);
'T' : ViewTables(Node.Text);
// Restore the default cursor and clear the status bar. 'V' : ViewProps(Node.Text);
Screen.Cursor := OldCursor; 'P' : ProcProps(Node.Text);
StatusBar1.Panels[0].Text := ''; end;
end; end;
Figure 5: Filling the TreeView component with metadata Figure 8: The OnChange event handler for the TreeView
extracted from the data source. component.
28 October 2000 Delphi Informant Magazine
Columns & Rows
The ViewProps and ProcProps procedures display the source code of Creating Databases and Objects
the view or stored procedure. The ProcProps procedure is shown in The first step in creating a new database is to create a new instance of
Figure 12. The ViewProps procedure is similar, so it’s not shown here. the Catalog object. This allows us to specify not only the type of the
database to be created (through the OLE DB Provider), but also the
Here we use the fact that the stored procedure stored in the Procedures location of the database file. Figure 13 shows how this can be done
collection actually points to the ADO Command object. Thus, we use for an Access database.
the Get_Command method to extract the IDispatch interface to the
Command object, and use its Get_CommandText method to obtain This creates a new database of the specified type, at the specified loca-
the source code of the stored procedure. tion. After that, we can append tables and columns to the database.
Here are the steps:
Now we know how to use the ADOX object to retrieve metadata 1) Create a new instance of the Table object.
from various data sources, as well as display the resulting information. 2) Create a new instance of the Column object.
Another ADOX possibility, which we’ll cover next, is the ability to create 3) Specify the properties of the new column.
databases from scratch, without complex SQL DDL statements. 4) Add the Column object to the Columns collection of the Table object.
Property Description
const
Attributes Contains characteristics of a column. BaseName = 'c:\data\demo.mdb';
DefinedSize Contains the maximum size of a column. DS = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' +
BaseName;
NumericScale Contains the scale of a numeric column.
var
ParentCatalog Indicates the catalog to which this Catalog : TADOXCatalog;
column belongs. ...
Precision Contains the maximum precision of data in // Create an instance of an ADOX Catalog object.
the column. Catalog := CoCatalog.Create;
// If the database exists, delete it.
RelatedColumn Contains the name of the related column for
if FileExists(BaseName) then
key columns. DeleteFile(BaseName);
SortOrder Indicates the sorting order for a column. // Create new .mdb file.
Type Contains the data type for the column values. Catalog.Create(DS);
// Specify the active connection.
Figure 9: Column object properties. Catalog._Set_ActiveConnection(DS);
...
Property Description
Clustered Indicates whether the index is clustered. Figure 13: Creating an Access database programmatically.
IndexNulls Shows how null indexes are processed.
PrimaryKey Indicates whether the index is the primary key. // STEP 1
// Create a new instance of the Table object.
Unique Indicates whether the keys in the index must
Table := CoTable.Create;
be unique. // Give it a name...
Figure 10: Index object properties. Table.Name := 'Customers';
// ...and specify the Catalog it belongs to.
Property Description Table.ParentCatalog := Catalog;
// STEP 2
DeleteRule Shows how primary key deletion is processed. // Create a new instance of the Column object.
RelatedTable Indicates the name of the foreign table for the Column := CoColumn.Create;
foreign key. with Column do begin
ParentCatalog := Catalog;
Type Contains the type of key.
// STEP 3
UpdateRule Shows how the primary key update is processed. // Set the properties.
Figure 11: Key object properties. Name := 'CustID';
Type_ := adInteger;
Properties['Autoincrement'].Value := True;
procedure TForm1.ProcProps(Name: string); Properties['Description'].Value := 'Customer ID';
var end;
S : string; // STEP 4
Disp : IDispatch; // Append the Column to the table's Columns collection.
Command : _Command; Table.Columns.Append(Column, 0, 0);
begin Column := nil;
S := 'PROCEDURE : ' + Catalog.Procedures.Item[Name].Name; // STEP 5
S := S + ^M^J + 'Created : ' + // Ceate more Columns and append them to the Table.
VarToStr(Catalog.Procedures.Item[Name].DateCreated); with Table.Columns do begin
S := S + ^M^J + 'Modified : ' + Append('FirstName', adVarWChar, 64);
VarToStr(Catalog.Procedures.Item[Name].DateModified); Append('LastName', adVarWChar, 64);
if CmdSupported(Catalog.Procedures.Item[Name]) then begin Append('Phone', adVarWChar, 64);
Disp := Catalog.Procedures.Item[Name].Get_Command; Append('Notes', adLongVarWChar, 128);
Command := Disp AS Command; end;
S := S + ^M^J^M^J + Command.Get_CommandText; // STEP 6
end; // Add the Table object to Tables collection of the
Memo1.Text := S; // Catalog object.
end; Catalog.Tables.Append(Table);
Catalog := nil;
Figure 12: The ProcProps procedure displays a stored proce-
dure’s source code. Figure 14: Adding tables to a database.
29 October 2000 Delphi Informant Magazine
Columns & Rows
5) Repeat steps 3 and 4 for each new column. Using Jet and Replication Objects
6) Add the Table object into Tables collection of the Catalog object. The second ADO extension we’ll cover this month is Jet and Replica-
tion Objects (JRO). Although the ADOX and ADO MD (which
The example in Figure 14 shows how these steps can be we’ll cover next month) are able to work with various data sources,
implemented. the JRO objects were implemented specifically to facilitate operations
with Jet databases. In other words, contrary to ADOX and ADO
After our table is created and its columns are defined, we can add MD, JRO objects can be used only with Access databases.
indexes and keys as necessary. The following code shows how to
create an index on the LastName column: Introduction to JRO
Like other ADO extensions, JRO exposes the object model that contains
Index := CoIndex.Create; objects, methods, and properties that can be used to create, modify, and
with Index do begin synchronize replicas. The main object in the JRO object model is Replica,
Name := 'LastNameIndex';
which can be used to create new replicas, check the properties of existing
IndexNulls := adIndexNullsDisallow;
Columns.Append('LastName', adVarWChar, 64); replicas, and synchronize changes with other replicas.
Columns['LastName'].SortOrder := adSortAscending;
end; The JRO object model includes the JetEngine object, which exposes
Table.Indexes.Append(Index, EmptyParam); some features of the Jet engine. In particular, the JetEngine object
can be used to compact the database, set password and encryption
The logic of this code is straightforward. First, we create an instance of on databases, and refresh data from the memory cache. These objects
the Index object. Then we set its Name property, specify how null indexes form the hierarchy shown in Figure 15.
are processed, associate it with the column, and — finally — add it to the
Indexes collection of the Table. The same logic is used for keys. The topic of Jet replication per se is outside the scope of this article, so
we’ll just briefly describe the methods used for replication.
So far, we have not
touched upon the The first step in replication is to create a design master; indicate
JetEngine Replica User and Group the database that will serve as a source for the replicas, and
objects. In the cur- make that database replicable. This involves the Replica object
rent implementation, and its MakeReplicable method. Then we need to change the replica-
Filters these objects are tied bility status of the database objects; the GetObjectReplicability and
to the Jet Engine SetObjectReplicability methods of the Replica object are used for this.
Filter and Microsoft Jet After that, depending on the task, we can create either the partial or
OLE DB Provider, full replica of the objects that are replicable in the design master.
Filter and there is no indi-
cation that support Next, we can define some update rules. The Filter object is used for
... for other databases this purpose. Finally, we can synchronize data in two replicas. There
Figure 15: The JRO will be implemented can be direct or indirect synchronization or synchronization over the
object model. Filter in future ADO ver- Internet. In the latter case, we need to use the Replication Manager
sions. that comes with Microsoft Office Developer.
const
To use the JRO library in your Delphi applications, use the Import
Provider = 'Provider=Microsoft.Jet.OLEDB.4.0;';
// Replace these paths with the location of the Type Library dialog box to select the Microsoft Jet and Replication
// Microsoft Access sample databases. Objects 2.1 Library (Version 2.1) and press the Install button. This will
SrcMDB = 'c:\data\northwind.mdb'; create the JRO_TLB unit, which must then be included in your code
DstMDB = 'd:\data\newnorth.mdb';
to access the objects exposed by the JRO library.
procedure TForm1.Button1Click(Sender: TObject);
var Using the JetEngine Object
JetEng : JetEngine; The JetEngine object can be used to compact the database and
Src : WideString;
refresh data from cache. Figure 16 shows how to compact the
Dest : WideString;
begin Northwind.mdb database, then create a new compacted copy
// Create an instance of the JetEngine object. named Newnorth.mdb.
JetEng := CoJetEngine.Create;
// Specify the source.
Without going into the inner depths of the Microsoft Jet Engine, let’s
Src := Provider + 'Data Source=' + SrcMDB;
// And destination. outline what really happens when we compact a database:
Dest := Provider + 'Data Source=' + DstMDB; § Tables pages are reorganized. After being compacted, they reside
in adjacent database pages. This gives us greater performance
// Check if the destination file exists and delete it.
since the table is no longer fragmented.
if FileExists(DstMDB) then
DeleteFile(DstMDB); § Unused space is reclaimed by the deletion of objects and records
// Compact the database. that are marked as deleted.
JetEng.CompactDatabase(Src, Dest); § AutoNumber fields are reset so the next value allocated will be in
// Free the JetEngine object instance.
the continuous sequence from the highest current value.
JetEng := nil;
end; § The table statistics used for query optimization are updated.
§ Since the database statistics were changed, all queries are flagged
Figure 16: Creating a new, compacted copy of the database. so they will be recompiled the next time the query is executed.
30 October 2000 Delphi Informant Magazine
Columns & Rows
Conclusion
This article introduces two ADO extensions: ADO Extensions for
DDL and Security (ADOX), and Jet and Replication Objects (JRO).
We’ve seen how to use ADOX objects to retrieve metadata from data
sources, and how these objects can be used to create databases from
scratch. We’ve also seen how the JRO can be used to compact Jet
databases, and have briefly outlined the replication process.
Next month, we’ll discuss ADO Multi-dimensional (ADO MD), which
is used for access to multi-dimensional data sources. See you then. ∆
The files referenced in this article are available on the Delphi Informant
Magazine Complete Works CD in INFORM\00\OCT\DI200010AF.
Alex Fedorov is an Executive Editor for ComputerPress magazine, published in
Moscow. He was one of the co-authors of Professional Active Server Pages 2.0
[Wrox Press, 1998] and ASP 2.0 Programmer’s Reference [Wrox Press, 1999].
Natalia Elmanova, Ph.D., is an Associate Professor of the Sechenov’s Moscow
Medical Academy, and a freelance Delphi/C++Builder programmer, trainer, and
consultant. She was a speaker at the 10th Annual Inprise/Borland Conference.
Natalia and Alex are authors of Advanced Delphi Developer’s Guide to ADO
[Wordware Publishing, 2000], and several programming books written in Russian.
You can visit their Web site at http://d5ado.homepage.com.
31 October 2000 Delphi Informant Magazine
New & Used
By Robert Leahey
IBObjects 3.4
Harness the Power of InterBase
I n Delphi Informant Magazine’s most recent Readers Choice Awards (see the April 2000
tissue), Jason Wharton’s InterBase Objects (IBObjects) collected the award for Best
Database Connectivity. So I decided to have a peek at IBObjects and find out why. I examined
IBObjects version 3.4b for this review, and found it to be an impressive set of components
designed to allow developers to harness the power of InterBase in their applications.
IBObjects has two data access paradigms: two dozen data-aware controls, some of which
TDataSet, a connectivity solution based on descen- provide familiar functionality, such as IB_Edit and
dants of Delphi’s TDataSet, and Native, a custom IB_DateTimePicker, and some of which are highly
connectivity solution for client/server applications. specialized, such as IB_Ledger and IB_IncSearch.
The latter provides for incremental searches in the
The TDataSet paradigm provides a set of data descendants of TIB_BDataSet, while the former
access controls designed to make converting is a robust control with a powerful property
applications to IBObjects as painless as possible. editor for creating data-aware ledgers. I can’t tell
TIBODatabase, TIBOTable, and TIBOQuery are you how much I wish I’d had that for my last
designed to simply replace their corresponding accounting application.
controls (TDatabase, TTable, and TQuery).
Because virtually all of the BDE functionality is Figure 1 displays the TIB_Ledger control and its
supported, complete replacement can be achieved Cells property editor. Notice the amount of control
with minimal fuss. and level of detail afforded by this dialog box. Not
only is each cell assigned its own field and format-
The Native paradigm provides a set of components ting, but the user can define multiple rows with
designed as a client/server solution that avoids the differing cell layouts.
problems of TDataSet’s desktop-centric nature.
IBObjects features eight custom toolbars, aimed,
In addition to these two data access paradigms, again, at automating some standard functionality.
IBObjects provides a suite of components There’s a connection bar, a transaction bar, and
intended to automate many of the tasks associated a search bar. Each of these provide functionality
with writing database applications. There are over in place by simply dropping them on a form and
hooking them up to the appropriate datasource.
No more writing the same old, mind-numbing
search code for that button’s event handler. Above
the ledger control, in Figure 1, you’ll see four of the
custom toolbars.
In addition, there are nine dialog components that
provide pre-built dialog boxes, such as lookups,
exporting, browsing, and SQL monitoring.
Suffice it to say, IBObjects provides an impressive
feature set: two-sided data connectivity, integrated
data controls, toolbars, and dialog boxes. It’s
beyond the scope of this review to cover all of the
functionality available, so let’s take a closer look at
Figure 1: TIB_Ledger and Setup dialog box. two things: ease of transition and the test bench.
32 October 2000 Delphi Informant Magazine
New & Used
Making the Change The Test Bench
Obviously, one test for IBObjects is how easily a developer can Another factor when considering a switch to IBObjects is, of course,
convert an existing BDE application to use the IBObjects con- speed. There’s not much point in making the switch if the tool doesn’t
nectivity components. I decided to try converting one of our end- perform, right? Fear not. In my testing, IBObjects demonstrated a
user sample applications that uses InterBase data. This project uses considerable edge in performance when compared to Delphi’s BDE
standard BDE controls (TTable, TDatabase, etc.) to access data for connectivity controls.
reporting, and to store user-created reports. The test was to replace
all existing BDE components with IBObjects components, and to see In my first test, I created a simple application, which queried and
what — if anything — would break. joined two InterBase tables, returning a result set of more than
123,000 records. Using Raize Software’s CodeSite, I sent elapsed-time
I didn’t relish the thought of replacing the controls and resetting messages at various points in the test. I built two nearly identical
properties by hand, so I opened the main form as text and simply versions of the application, one using TDatabase and TQuery, the
replaced all instances of TDatabase with TIBODatabase, all TTables other using TIBODatabase and TIBOQuery.
with TIBOTables, and all TQuerys with TIBOQuerys. When I viewed
the form, to my great surprise, only two messages about missing With a dozen test runs, the average times were as shown in Figure 2.
properties popped up. Notice the significant edge IBObjects holds over the BDE.
“That’s promising,” I thought, vowing not to get excited yet. For my second test, I again used a reporting scenario. I created an
application that displayed a report based on that same 123,000+ record
I did have to redirect my TIBODatabase at the proper data, but, result set, and set the report component to make two passes. This
buoyed by my early success, I decided to be daring and simply run would ensure a complete traversal of the data in one contiguous effort.
the application, thinking my afternoon would be dedicated to tweak-
ing settings and chasing bugs. Hey! The application was running! This time there was less disparity between the results, but IBObjects
still gets the nod. For this 2,625-page report, the average results are
I put it through its paces, creating reports, printing, saving, etc. No shown in Figure 3.
problems at all. I had converted our BDE + InterBase reporting
application to an IBObjects + InterBase reporting application in five The superior performance of IBObjects, combined with the ease with
minutes and one property change. which I was able to convert my application to use its data access
controls, makes a strong argument for its use. It’s definitely worth
Depending on the complexity of your application, you may need to your time to download the evaluation version of IBObjects.
do some preparation before trying to convert it using this technique.
Jason’s BDE-to-IBObjects Conversion Guide supplies an ever-shrinking Automation
list of properties and methods of the BDE controls that are currently While the advantages of IBObjects are many and varied, one of
unsupported by IBObjects. Browsing this list will help you decide how the most noteworthy is the amount of automation that it provides.
best to proceed when converting your application. Anyone who has written a client/server database application in
Delphi can recount how much code they had to write for menial
Connection Average Completion Time
tasks, such as lookup dialog boxes, record counts, etc. Using the
BDE 59.826 seconds IBObjects controls in concert can automate much of this.
IBObjects 40.863 seconds
Figure 2: Results from the first test. Take searching as an example. In Figure 4 you’ll see two forms. This
is a client/server application built with IBObjects. The application is cur-
Connection Average Completion Time rently in search mode (note that the IBObjects data controls have turned
blue to indicate that they are accepting search criteria). The second form,
BDE 1 minute 53.036 seconds
with the grid, is displaying the result set of the search (records where the
IBObjects 1 minute 44.328 seconds
Last Name begins with “B”). This search dialog box was created with a
Figure 3: Results of the second test. small amount of code and just a few property settings. The IBObjects
controls are working together to automate the search.
Granted, this is a simple example, but it illustrates the wealth of
functionality built into these components. IBObjects greatly simpli-
fies the act of writing client/server applications with InterBase by
automating much of the task.
Other Thoughts
Wharton offers an intriguing license for IBObjects, called the Trust-
ware License. Basically, the thought is that if you don’t think you’ll
make money using it, you don’t pay for the software. When, however,
you begin to show a profit, Jason trusts you to pay for your license.
The details of this generous license are on the IBObjects Web site at
http://www.ibobjects.com/ibo_trustware.html.
For those who know InterBase, IBObjects is a must-have tool set. If
Figure 4: Automated searches. you don’t know InterBase, IBObjects is a good reason to learn it.
33 October 2000 Delphi Informant Magazine
New & Used
IBObjects connectivity tools provide a thin wrapper for the Inter- Conclusion
Base API. This is a very good thing if you already know InterBase; InterBase Objects is an outstanding product, and I strongly recom-
everything you’re used to dealing with is available right there in mend it for those doing InterBase development. The improved
Delphi’s Object Inspector, or in IBObjects’ property editors. This performance attained by simply using the IBObjects connectivity
wealth of properties, however, can be daunting if you’re not that controls is reason enough to warrant the switch, but when the
familiar with InterBase. IBObjects doesn’t candy-coat anything for wide range of time and effort saving IBObjects components is also
you; you’ll have to know or learn InterBase well to really understand considered, the true value of IBObjects becomes quite evident. ∆
what these components can do for you.
Obviously no software is perfect, and we should run screaming
from reviews that try to tell us otherwise, but IBObjects’ prob- Robert is Director of Product Management of ReportBuilder for Digital Metaphors
lems, at least as I have seen, have been merely a matter of falling Corp. He graduated with a degree in Music Theory from the University of North
afoul of my personal preferences. Texas, but has been writing code since his Apple II+ and AppleBasic days. He has
been programming in Object Pascal since Delphi 1 and currently resides in Texas
Maybe I’m spoiled, or perhaps I’ve read a little too much Alan with his wife and daughters.
Cooper, but I have come to expect software packages to be well-
behaved and to pamper me upon installation. I want to have a
single executable file that I can run to install the software.
When it’s done, I want to launch Delphi and find my nicely
installed new package (complete with Delphi-integrated help)
waiting for me. This is rarely the case with third-party products,
and it was not my experience with IBObjects. Installation was
a chore, requiring me to move files, integrate the help, compile
packages. Not a big deal, but, as I said, I’m spoiled and want to
be taken care of.
Likewise, many IBObjects demos weren’t set up to handle non-
standard installation paths. I continually paid for my choice of
having installed my InterBase sample data to my D drive. How-
ever, in each demo, once I updated the DB path, things went
well. IBObjects exception messages are also a bit verbose and
unclear, and the documentation isn’t complete, although it does
cover much of the product.
Having asked Jason Wharton about these issues, it seems he
already plans to deal with them. He’s building an installation
routine that will drastically simplify the installation process. He’s
also working with a technical writer to produce more thorough
documentation, and a “Getting Started” guide. Jason is obviously
responsive, professional, and interested in improving his product.
This reflects well on the future of IBObjects.
Jason Wharton’s InterBase Objects offers a wide array of data-
aware components with a high degree of interoperability, as well as
two different sets of data connectivity controls. One set is for build-
ing high-performance client/server applications, and the other mir-
rors Delphi’s TDataSet architecture, allowing developers to simply
replace existing BDE controls with the IBObjects controls.
Jason Wharton
619 N. Macdonald St.
Mesa, AZ 85201
E-Mail: [email protected]
Web Site: http://www.ibobjects.com
Price: Full-source license, US$395; full-source upgrade license,
US$175; partial-source license, US$195; partial-source upgrade
license, US$75; partial-source to full-source upgrade, US$215.
34 October 2000 Delphi Informant Magazine
TextFile
Delphi Graphics and Game Programming Exposed! with DirectX
I was delighted when Delphi Informant components, color depth, and video modes.
Magazine asked if I would review John He presents techniques for drawing text,
Ayres’ new book, Delphi Graphics and Game manipulating palettes, and calling GDI func-
Programming Exposed! with DirectX. As the tions. As with most of the other chapters,
title suggests, the book provides a founda- there are many useful demonstration pro-
tion for graphics and game programming grams. Many chapters have 10 or more
with DirectX — a subject in which I am example projects.
very interested.
Chapter 4 provides a fifty-page introduction
Quite appropriately, Ayres begins with an to DirectX. It discusses the various compo-
introduction to game programming as a spe- nents included in this essential game-writing
cialization (including its ups and downs) and technology, with emphasis on DirectDraw.
a detailed exposition of its essential elements. The author explains how to use the various
Although these two chapters will be must- available functions to perform basic graphics
reading for new game programmers, those operations, such as working with display
with some experience in this domain may modes, surfaces, and bitmaps. The next two
want to quickly skim them and delve into chapters build on this foundation, exposing
the remaining chapters, which expose the techniques for working with palettes and
essential techniques. One of the high points sprites. These techniques are presented in
of Chapter 2 is the case study in which Ayres the context of game production — solving The last several chapters discuss these issues,
builds a simple Space Invaders-type game to game development problems, such as colli- and more. The last chapter, “Putting It All
demonstrate the game loop and other prin- sion detection in an action game. Together,” completes the sample application
ciples discussed in this chapter. begun in Chapter 2. This is a fitting way to
DirectX also provides support for user input conclude this well-crafted, informative trea-
The majority of the remaining chapters with the DirectInput component. Chapters tise. I strongly recommend this book for
use DirectX. To make this technology avail- 7 and 8 deal with this vital topic, with anyone who will be programming games in
able, Ayres uses Erik Unger’s Project JEDI the latter chapter explaining a relatively new Delphi, who wants to add special graphical
DirectX header conversion as a foundation, technique, Force Feedback. As in the pre- effects to their Delphi applications, or who
and shows how to use the various units vious chapters, Ayres begins by explaining wants to work with DirectX in Delphi.
and utilities that make up that library. theory and goes on to develop some actual
(Before you run any of the programs on examples in Delphi. He includes important — Alan C. Moore, Ph.D.
the CD-ROM, be sure to include the two notes, tips, and warnings — all highlighted
directories containing the JEDI files on the for easy recognition. He includes important Delphi Graphics and Game Programming
Delphi path.) technical terms in two locations: where the Exposed! with DirectX by John Ayres,
terms appear in the text, and in a glossary at Wordware Publishing, Inc.,
Before introducing the DirectX technologies, the end of the book. I strongly endorse this 2320 Los Rios Blvd., #200
Ayres provides a basic introduction to graph- excellent approach. Plano, TX 75074,
ics programming. He explains how to work http://www.wordware.com.
with graphics elements such as pixels, bit- There are other factors essential to the cre-
maps, and the Canvas object; he also dis- ation of a successful game: sound and music, ISBN: 1-55622-637-3
cusses important concepts such as color special effects, and optimal performance. Price: US$59.95 (544 pages, CD-ROM)
35 October 2000 Delphi Informant Magazine
Best Practices
Directions / Commentary
Paint Your Editor
H ow much time do you spend debugging? Unless you’re perfect, or you’re not a programmer (is there a connection
there?), you probably spend more time debugging than you care to admit.
If you’re anything like me, what you like most about programming It’s been said that a picture is worth 1,024 words (or so). Figure
is designing and coding. Analysis must be done (or it’s done by 1 illustrates what I mean. As you can see, it’s easy to identify the
somebody else), but it’s something to get out of the way so you can various elements of the code because they’re color-coded (no pun
get to the interesting and creative work. Simply put, debugging is intended!). Compare that to how your editor looks in its default
a necessary evil. What can be done to make this process less of a configuration — black on white! Boring! Blah! Unimaginative!
drudgery and more productive — so we can get through it and back Unsophisticated! Unhelpful! I submit also that this is easier on the
to coding? eyes. And we need all the help we can get to avoid carpal retinal
syndrome, right?
Something I find useful is modifying the appearance of the editor.
By doing this, you acquire visual clues that enable you to see exactly Of course, if you don’t care for my choice of colors, you can select
what it is you are looking at in the editor. Using colors to differenti- your own to suit your fancy and sensibilities. I must admit, I love
ate strings from numbers, keywords from identifiers, etc., can be a my color scheme and would rather fight than switch, but then
great help in cutting through the haze of information overload we’re again, a good portion of the blood flowing through my veins is
bombarded with as we try to get to the root of the problem. “Port-a-gee,” and we are not exactly known for subtlety in choice
of color (it’s not politically incorrect when you’re poking fun at
yourself, I strongly assert!).
So, do you want to take the plunge and spice up your debugging
experience? It’s easy. Here’s what you need to do:
1) In Delphi 5, select Tools | Editor Options to display the Editor
Properties dialog box; then select the Color tab as shown in
Figure 2. For Delphi 4, select Tools | Environment Options instead.
2) Select an item in the Element list box at the left, then click on
the color you want to set as the foreground color for the selected
element. “FG” will appear in that color indicating your choice.
3) Right-click on the color you want to set as the background
color for the selected element (“BG” will appear in that color).
Unless you’re crazy, you’ll want to stick with one color for the
background of each element. As probably did not escape your
notice, I have chosen black. You will doubtless want to choose
either a very light background, with dark foreground colors, or
vice versa (as I have).
4) Repeat steps 2-5 for each element you want to set.
If you want to copy my color scheme (especially you Portuguese
Figure 1: Use colors to modify the appearance of the editor. developers out there), check out the table in Figure 3.
36 October 2000 Delphi Informant Magazine
Best Practices
Figure 2: The Color page of the Editor Properties dialog box.
Element Foreground Background
Whitespace Blue Black
Comment Yellow (italics) Black
Reserved Word Magenta Black
Identifier Aqua Black
Symbol Yellow Black
String Lime Black
Number Red Black
Search Match White Black
Figure 3: My color scheme.
There are two certainties in life for programmers: debugging and
taxes. You can ease the pain a little by colorizing that tired old
Notepad-look-alike. Depending on your perspective and personal
history, it may bring back memories of Turbo Pascal, or it may
seem like a leap into the next era of RAD coding. At any rate,
the improved comprehension afforded by highlighting the various
elements of your (or somebody else’s) code should augment the
enjoyment of working with Delphi, and simultaneously reduce the
amount of time you spend locating and eradicating those pesky
bugs (“anomalies” to the intelligentsia; “issues” to the euphemism
inclined/politically correct). Happy debugging! ∆
— Clay Shannon
Clay Shannon is an independent Delphi consultant based in northern Idaho. He is
available for Delphi consulting work in the greater Spokane/Coeur d’Alene areas,
remote development (no job too small!), and short-term or part-time assignments
in other locales. Clay is a certified Delphi 5 developer, and is the author of
Developer’s Guide to Delphi Troubleshooting [Wordware, 1999]. You can reach him at
[email protected].
37 October 2000 Delphi Informant Magazine
File | New
Directions / Commentary
BorCon 2000 a.k.a. The Kylix Konvention
T his was my fifth Borland Conference (hardly anyone is saying “Inprise” these days), and like the first one I attended,
it took me to southern California — this time San Diego. The focus of this conference, as you might guess, was the
important move to support Linux with versions of Delphi and C++Builder. Code-named Project Kylix, this development
has engendered more excitement than anything Borland has initiated since its first release of Delphi.
All the other features that make Borland conferences so popular take shortcuts (such as releasing a product prematurely) simply
were present as well. The opening show — I mean keynote — was to increase profitability, and then shifted the burden back to
another spectacular multimedia event based on the popular movie, the audience by encouraging everyone to become “Delphi Evange-
The Matrix, with company CEO Dale Fuller, David Intersimone, lists,” becoming part of the new campaign to convert Visual Basic
and others making entrances to its powerful theme music. The care- programmers to Delphi.
fully crafted presentation was punctuated with some of the most
powerful scenes from the movie, scenes that underlined aspects of the Ray Lischner asked about what he viewed as a “scattershot
company’s emerging philosophy. approach” where the company would release a foundation version
of one product over here, and open-source another product over
That philosophy contains some elements that will please many who there. He wondered if there was a coherent strategy in place to
have been critical of it in the past. For one thing, the company has map Borland’s future development. I’m not certain that Fuller
returned to its original developer-centered focus. It is also committed answered the question completely, but he did point out that the
to achieving success through responsible action. This means that it company could not open-source or release foundation versions of
will “not release any product before its time,” very good news for all all its products, since it was, after all, a for-profit company. Still,
of us who remember early versions of Delphi 4. Ray’s questions will hopefully give Borland executives something
to ponder as they consider important marketing decisions in the
The most important new element of this philosophy is “platform coming year. For example, if people could download a very basic
independence.” This commitment goes beyond just support for version of Delphi with a few controls and no source, would they
Windows and the platforms supported by Linux. In fact, Fuller then be inspired to purchase it?
made it clear that the company was open to supporting new
platforms that market demands justified. To punctuate some of There are other marketing issues that weren’t discussed in open ses-
these new directions, a Macintosh computer was unveiled and sions. As an academic, I very much want to see Delphi become more
used briefly during the opening keynote. Delphi for the Mac? of a player in University Computer Science curricula. If Visual Basic
This may be stretching expectations a bit, but then who knows? can be used to teach beginning classes, why not Delphi? I promised
JBuilder for the Mac is defiantly coming, and was demonstrated in some of the Borland people I spoke with that I will do what I can
a technology keynote by Blake Stone, one of JBuilder’s architects. to help make this happen.
The key point to remember is this slogan: “The platform is the
Net.” Support for more traditional platforms — specific comput- As in past years, this year’s conference provided a surplus of sessions
ers and/or operating systems — will be driven by demand. Good on Delphi and other tools. Again, I attended one of the pre-confer-
business sense, in my view. ence tutorials, a four-hour session in which Mark Miller presented
“Design Patterns in Delphi.” Mark did an outstanding job of present-
As he did at last year’s conference, Fuller took questions from the ing the Singleton, Composite, Factory, Proxy, Observer, Iterator, and
audience. One person asked when Inprise would have a profitable Flyweight patterns. With each he discussed its applicability to a
quarter. The CEO pointed out that the company was in better programming challenge, its advantages and disadvantages, and dem-
shape now than at any time in recent memory, that he would not onstrated its use in an actual Delphi application.
38 October 2000 Delphi Informant Magazine
File | New
Mark’s sessions are wild — exciting to many, exasperating to some. increased support for wide strings and variants (including custom
He loves to take questions from the audience, explore interesting variants). Of course, all Windows-specific units (including low-level
tangents that come up, and interject wonderful humor and anec- multimedia support, sad to say) will be gone. Clearly, there will be
dotes. After editing his slides a few times in front of the audience further challenges for Project JEDI in the new Kylix world, and
and making other spontaneous changes, he shifted attention away several JEDI knights are already working in that arena.
from possible criticisms about his style by suggesting to the audi-
ence that when they attended a session by his friend Ray Konopka, This was probably the most enjoyable conference so far. Personally,
they should evaluate the latter with, “He’s too professional, too I had the gratification of having my new Delphi multimedia book
prepared.” Personally I have no problem with Mark’s approach, released simultaneously with the beginning of the conference. I met
and go out of my way to attend his sessions. many old and new friends, including a couple of folks involved with
Project JEDI. Unfortunately, we were not able to schedule a JEDI
Delphi 6. Of course, again this year the vast majority of partici- Birds-of-a-Feather session this year. If you think I’m excited about
pants at the conference were Delphi folk. For that reason there Kylix and the future of Delphi, you’re right. I can hardly wait for
was a good deal of interest in the newest version of Delphi. I will next year’s conference. ∆
be writing a column on Delphi 6 as soon as it ships, so I’m not
going to go into great detail now. This year we’ll see a number of — Alan C. Moore, Ph.D.
new and expanded units. The Math unit will be expanded, with
exciting new developments in the world of variants. There is a Alan Moore is a Professor of Music at Kentucky State University, specializing
new StrUtils unit that will feature a plethora of string conversion in music composition and music theory. He has been developing education-
routines. The R&D team has made enhancements to the related applications with the Borland languages for more than 10 years. He
TCollection and TList classes. has published a number of articles in various technical journals. Using Delphi,
he specializes in writing custom components and implementing multimedia
One of the most exciting new developments is the support for Web capabilities in applications, particularly sound and music. You can reach Alan on
development. There will be a test Web server built into Delphi 6, the Internet at
[email protected].
and support for working with XML documents. Also, we saw the
beginnings of a Delphi scripting language that can be used within
HTML documents. The idea, of course, is to enable us to leverage
our Delphi skills even more to develop Web applications.
Kylix. Many of the sessions I attended had to do with Kylix, the
code-name for Delphi for Linux. Turbo Power’s Gary Frerking pro-
vided an excellent introduction to Linux for newbies such as me.
Charlie Calvert and David Intersimone presented an exciting intro-
duction to Kylix. Let me make a prediction: The introduction of
Delphi for Linux will be more monumental than the introduction of
Delphi 1. Further, this implementation of Delphi will be the major
focus of next year’s conference, with a stampede of Linux developers
eager to write GUI applications.
What specifications for Kylix did we learn about at the confer-
ence? Of course the Linux version of Delphi will be a component-
based, visual development tool to rapidly produce native Linux
applications. Its native code generator will use the ELF object
format, allow two-way visual development (just as in Delphi for
Windows), and be able to produce the three common types of
applications: desktop, database, and Web.
Replacing Delphi for Windows’ Visual Component Library (VCL),
CLX (pronounced clicks) will provide a library of native Linux
components. CLX comes in four flavors, with only the first one,
VisualCLX, restricted to Linux. There will also be BaseCLX, pro-
viding basic components and system support; DataCLX, providing
client data access; and NetCLX, providing support for Internet and
related programming. To help ensure compatibility and ease of port-
ing, there will be a new file version for Delphi for Linux form files
(instead of .DFM). In his session entitled “New Language and RTL
Features in Kylix,” Danny Thorpe outlined additions, deletions, and
changes to this exciting new flavor of Delphi.
Although much of the standard Delphi tool-set will be there,
the stand-alone assembler (TASM) and resource compiler won’t be
included. There will be new conditional defines in both flavors
of Delphi to support cross-platform development. There will be
39 October 2000 Delphi Informant Magazine