On The Cover: August 1998, Volume 4, Number 8
On The Cover: August 1998, Volume 4, Number 8
On The Cover: August 1998, Volume 4, Number 8
35 At Your Fingertips
Better Coding through APIs Robert Vivrette
Less is more, as our own Mr Vivrette explains. This time around he
FEATURES discusses simple, useful API routines that few developers even realize
exist, yet can save considerable programming effort.
12 On the ’Net
IE4’s DOM Advantage Ron Loewy
IE4 introduced DHTML and the Document Object Model, greatly REVIEWS
expanding the control a Windows app can have over a Web browser
control. Now Mr Loewy puts it to use from Delphi. 37 ODBCExpress
Product Review by Steve Garland
17 Algorithms
Vivid Equations Rod Stephens
42 Nathan Wallace’s Delphi 3 Example Book
Book Review by Warren Rachele
Mr Stephens introduces 3-D graphics programming, including transla-
tion, scaling, projection, and rotation, and — as usual — provides
hands-on examples to get you started.
DEPARTMENTS
24 DBNavigator 2 Symposium by Zack Urlocker
Delphi Database Development Cary Jensen, Ph.D. 3 Delphi Tools
Dr Jensen closes a three-year circle and re-examines database pro- 5 Newsline
gramming from a Delphi perspective. A lot has changed since version 44 From the Trenches by Dan Miser
1, but one thing has not: Delphi is still the best tool for the job. 45 File | New by Alan C. Moore, Ph.D.
1 August 1998 Delphi Informant
Symposium
By Zack Urlocker
I t’s been a busy, but exciting time in Scotts Valley the past several months. The company has continued to
grow revenues and maintain profitability, we’re shipping new products every quarter, oh, and we
changed our name. I’m sure there was a collective “What?” heard when Delphi developers around the
world fired up their browsers to www.borland.com and saw an announcement about INPRISE Corporation.
The Borland brand name is a good one. edge in this area that’s hard to beat. At depending on who we were talking to.
It’s associated with high quality, high pro- the same time, we’ve made sure to add Delphi was variously known as VIP,
ductivity development tools such as some of the latest productivity features to Visual Foo, Del Mar, Del Rio, Del Fuego,
Delphi, C++Builder, and JBuilder. Delphi 4, including more debugging VBK, and Wasabi (Don’t ask!). Internally,
However, development is just one part of tools, a more modern user interface with the compiler was “Version 8” but we
what INPRISE Corp. is focused on. Our dockable toolbars and windows, a sophis- wanted a name that showed just how far
mission is to radically simplify the devel- ticated application browser, more com- the product had evolved from Borland
opment, deployment, and management of ponents, and much improved documen- Pascal. We struggled with such convoluted
distributed enterprise applications. That tation, among other things. names as Visual App Builder, but in the
makes development tools, including end, we decided to go with the name that
Delphi, critical to our strategy, but it also Ongoing change. One of the things I’ve our beta testers liked best: Delphi.
reinforces the need for additional products learned in my years at Borland is that
that support deployment and management you must keep innovating, or you’ll fall So what’s it all mean? For Delphi devel-
of enterprise applications. For example, we by the wayside. It’s as true in program- opers, the company name change should
have the Entera RPC-based middleware, ming as it is in business. If you look be pretty minor. We’ll continue to create
the VisiBroker CORBA Object Request back at programming techniques and new versions of our development tools
Broker, ITS Integrated Transaction Service, tools five years ago and compare them and try to have innovative features for
AppCenter application management, and with the state of the art today, it’s a hobbyists, individual developers, consul-
a forthcoming Enterprise Application pretty radical change. Not only have the tants, and corporate developers. The most
Server. We will make sure that these prod- tools and techniques changed, but so significant aspect of the name change is
ucts are accessible from our development have the applications being built. Five that it symbolizes how much the compa-
environments, including Delphi, years ago, when the Delphi project first ny has changed over the last 18 months
C++Builder, and JBuilder. began, we were attempting to bring with a broader focus of integrating the
Borland Pascal into a new era of growth enterprise. We believe the name
The Delphi team has been particularly by solving much more difficult prob- INPRISE better reflects our overall
innovative in its support for COM, lems. We decided (though I had to drag strategy and direction than the Borland
CORBA, and RPC-based middleware. the R&D team kicking and screaming) name did. That being said, the Borland
Delphi 4 Client/Server Suite has all of that we needed to simplify both brand name will continue to be used for
the facilities to make it the most produc- Windows and client/server program- our development tools under the
tive development environment for any ming. It was quite a gamble, but it INPRISE Corp. umbrella. This is similar
distributed infrastructure. Because helped Borland launch into some new to what other large companies have done
INPRISE doesn’t have a platform agen- growing markets with our most success- when bringing to market multiple differ-
da, we’ll continue to support all the plat- ful product in years. Along the way, well ent product lines. For example, Network
forms our customers are asking for. That beyond a million copies of Delphi were Associates has the McAfee brand of anti-
means you can expect ongoing support sold, and hundreds of thousands of pro- virus software as one of its brand names.
for the latest Microsoft technologies, grammers made the transition to learn-
such as Windows 98, MTS, IIS, as well ing client/server programming skills and Delphi remains the product that I’m
as the latest cross-platform standards. became more successful in their careers. most proud of in my career, and you
Delphi has certainly exceeded our can expect Delphi will continue to play
In fact, I think people will be surprised expectations, and the Delphi team con- a pivotal role in our overall corporate
at how much innovation there is in tinues to amaze me with their dedica- strategy. After eight years of working at
Delphi 4. Delphi will continue growing tion and insight into programming. Borland International, I’m pleased to
and attracting even more new customers report that there is life after Borland.
who want to develop distributed applica- At the time, there was a lot of debate There’s INPRISE.
tions, as well as desktop and client/server about what Delphi should be called.
applications. Certainly, no tools have Many of you know the name Delphi was Zack Urlocker is Vice President of
made distributed development easy originally chosen as a code name. In fact, Marketing at INPRISE Corp., developer
before, and we think Delphi’s got an we used a lot of different code names, of Borland and VisiBroker products.
By Bill Todd
Delphi 4 Multi-Tier
MIDAS Reaches New Levels
U sing MIDAS components, Delphi 4 provides a host of new features for both
multi-tier and single-tier applications. It all begins with TClientDataSet; you
can now define aggregates for a ClientDataSet component. Figure 1 shows the
Multitier page of the Delphi 4 New Items dialog box.
Controlling Data Packet Contents If you’re working with tables that have a referential integrity
You now have three ways to control the contents of data relationship, you can specify that the server automatically han-
packets retrieved from the application server — including dle cascading updates and/or deletes. The Provider compo-
the ability to include custom information. Using the Fields nent’s Options property also lets you declare the dataset to be
Editor to create persistent field objects for the dataset on the read-only. This prevents the ClientDataSet from applying
application server lets you control which fields are supplied updates to the Provider. If you are working with a server that
to the client. Lookup and calculated fields will be returned may modify records with triggers when they are updated, you
to the client as read-only fields. The only restriction is that can include the poIncServerUpdates option. This tells the
you must include the primary key field(s) so each record can Provider that it must automatically retransmit all updated
be uniquely identified if the client will edit the data. records to the client, so the client will immediately see the final
state of the records — including the effects of any triggers.
TProvider has a new Options property, shown in Figure 4, that
lets you control whether BLObs and detail records will be One of the more potentially powerful new multi-tier fea-
tures is the ability to include custom information in the
data and delta packets passed between the application serv-
er and the client. This capability is implemented through a
new event, OnGetDataSetProperties, on the Provider side.
OnGetDataSetProperties passes three parameters. The first is
Sender, a pointer to the object that triggered the event. The
second, DataSet, is a pointer to the dataset that supplies
the Provider’s data.
var
dispinterface: IMyAppServerDisp;
begin
dispinterface := TheConnection.AppServer;
dispinterface.CustomMethod(aValue);
end;
Bill Todd is President of The Database Group, Inc., a database consulting and
development firm based near Phoenix, AZ. A Contributing Editor of Delphi
Informant, he is also co-author of four database programming books and a
member of Team Borland, providing technical support on the Borland Internet
newsgroups. He is a frequent speaker at Borland Developer Conferences in the
US and Europe. Bill is also a nationally known trainer, and has taught Paradox
and Delphi programming classes across the country and overseas. He was an
instructor on the 1995, 1996 and 1997 Borland/Softbite Delphi World Tours.
He can be reached at Bill_Todd@compuserve.com or (602) 802-0178.
By Ron Loewy
Clipboard Support
The OnDocumentComplete event is used to inform your The IOleCommandTarget interface allows you to provide
application that the control finished navigating to a new copy functionality from your application. The following
URL, that it finished rendering the information, and that statement will copy the currently selected content in the
it’s safe for your application to retrieve the DOM entry browser to the Clipboard:
point via the Document property. It’s important you don’t
try to access the IHtmlDocument2 interface before the con- IECommand.Exec(nil, OLECMDID_COPY,
trol is in the ReadyState_Complete mode. Use code such OLECMDEXEOPT_DONTPROMPTUSER, VI, VO);
Figure 5: All the tags in the homepage.html file after “walking” In your IDocHostUIHandler, you need to implement the
the DOM. GetOptionKeyPath method and provide the registry path of
14 August 1998 Delphi Informant
On the ’Net
procedure TForm1.Button3Click(Sender: TObject); procedure TForm1.MemLoadClick(Sender: TObject);
var var
IECommand: IOleCommandTarget; v: Variant;
V, O: OleVariant; begin
const if (assigned(Document)) then
OLECMDEXEOPT_DONTPROMPTUSER = 2; begin
begin v := VarArrayCreate([0, 0], varVariant);
V := 1; v[0] := '<html><head><title>Hello World</title>' +
IECommand := WebBrowser.Document as IOleCommandTarget; '</head><body>Start me up</body></html>';
IECommand.Exec(nil, OLECMDID_ZOOM, Document.Write(PSafeArray(TVarData(v).VArray));
OLECMDEXEOPT_DONTPROMPTUSER, V, O); Document.Close;
end; end;
end;
Figure 6: This code sets the font size to 1.
Figure 7: Using IHtmlDocument’s write and close methods to
write content directly from memory.
your browser defaults. The default font values should be writ-
ten under the International\CodePage key (where CodePage is
the code page Windows uses) in the IEPropFontName and
If you look at mshtml_tlb.pas, which was created when you
IEFixedFontName entries.
imported the TypeLib of mshtml.dll, you’ll see a dispatch
interface named HtmlDocumentEvents. To hook this events
Speeding up the WebBrowser
interface, you need to use the IConnectionPointContainer and
In my application, I need to provide fast updates to the con-
IConnectionPoint interfaces to connect the DOM entry point
tent of the page displayed by the browser control. The
to an object that implements HtmlDocumentEvents.
WebBrowser control’s functionality allows it to host Java
applets and ActiveX controls, handle style sheets, and execute
The method of connecting events using connection points
scripts. Unfortunately, all this functionality comes at a cost —
is beyond the scope of this article, but you can simply copy
speed. To speed the preview portion of my application, I need
the procedures InterfaceConnect and InterfaceDisconnect
to extract every bit of performance from the control.
that INPRISE includes as part of OleCtrls.pas in the
Source\VCL directory.
Instead of writing every change to the displayed page content
to a disk file and using the Navigate method, I use the
I used Delphi’s Automation Object Wizard to create a
IHtmlDocument interface’s write and close methods to write
TAutoObject descendant that implements the
the content directly from memory. The code in Figure 7
HtmlDocumentEvents interface (see Figure 8). Because this
demonstrates how this can be done.
is a dispatch interface, I used Delphi’s TypeLib Editor to
copy the dispatch IDs to the automation object I was cre-
It seems that Microsoft did not implement all the functionality
ating. The result is displayed in Figure 9.
of the Navigate method in write and close. For example, your font
defaults implemented using IDocHostUIHandler will be ignored.
My automation object implements the OnClick handler using
Another problem with close is that the OnDocumentComplete
the following code:
event is not fired. Instead, I use a Timer component, set it to 100
milliseconds, and use the following code to set the font size after function THtmlDocumentEventsHandler.OnClick: WordBool;
the document has been loaded into the control: begin
showMessage('Hello World!');
Result := True;
procedure THtmlPageEditorForm.Timer1Timer(Sender: TObject);
end;
begin
inherited;
if (WebBrowser.ReadyState = ReadyState_Complete) then
begin
Timer1.Enabled := False;
ZoomFont;
end;
end;
Now comes the fun part. This statement connects the event
handler object to the document object model:
InterfaceConnect(Document, HtmlDocumentEvents,
DocHandler, DocConnectionID);
InterfaceDisconnect(Document, HtmlDocumentEvents,
DocConnectionID);
Wrapping It Up
You should install the INetSDK, and acknowledge that a
large portion of your time will be spent reading the docu-
mentation, looking at the samples, and wondering why no
one documented the function you are interested in.
As you can see, a lot can be done with IE4 from a Windows
application if you are willing to get down and dirty with
16 August 1998 Delphi Informant
Algorithms
Delphi / 3-D Graphics
By Rod Stephens
Vivid Equations
An Introduction to 3-D Graphics Programming
While you could spend years studying the To transform an object, a program trans-
topic, much of three-dimensional graphics is forms the points that define that object. It
based on fairly simple mathematics. This then connects the transformed points as
article describes the equations that are the they are connected in the original object
basis for three-dimensional graphics pro- using straight lines. For example, to trans-
gramming. It also shows how to use those form a rectangle, the program transforms
equations to draw simple three-dimensional the four corners of the rectangle, and then
objects and surfaces. connects them.
x = R * Cos(a)
y = R * Sin(a)
x’ = x * Cos(q) - y * Sin(q)
y’ = y * Cos(q) + x * Sin(q)
Translation by a distance of (tx, ty, tz) is represented by the matrix: Cos(q) 0 Sin(q) 0
0 1 0 0
-Sin(q) 0 Cos(q) 0
1 0 0 0
0 0 0 1
0 1 0 0
0 0 1 0
One of the nice things about these transformation matrices is
tx ty tz 1
that the product of two matrices represents the transforma-
tions applied one after each other. For example, if A and B
If you multiply this matrix by the point [a, b, c, s], you get:
are transformation matrices, and p is a vector, then:
1 0 0 0 (p * A) * B = p * (A * B)
[a, b, c, s] * 0 1 0 0 = [a + 1 * tx, b + 1 * ty, c + 1 * tz, s]
0 0 1 0 This means a program can multiply the matrices together
tx ty tz 1 first, and then apply the result to the point later.
This vector correctly represents the translation of the point. If the program is transforming a single point, this is no faster
Scaling by factors of sx, sy, and sz is represented by the matrix: than applying the matrices to the point one at a time. However,
if the program must transform hundreds or thousands of points,
sx 0 0 0 it can mean a large savings. Instead of applying both matrices to
0 sy 0 0 each point, the program need only apply the combined transfor-
0 0 sz 0 mation to each point. For more complex transformations involv-
0 0 0 1 ing many rotations and translations, the savings are even greater.
If you multiply this matrix by the vector [a, b, c, s], you get Drawing in Delphi
[a * sx, b * sy, c * sz, s], as desired. Projection along the Z Enough math! How can you use all this to draw three-
axis onto the X-Y plane is represented by: dimensional pictures in Delphi? One straightforward
19 August 1998 Delphi Informant
Algorithms
method is to create an array of points representing the end this approach to draw the Platonic solids: tetrahedron,
points of line segments. The program transforms the cube, octahedron, dodecahedron, and icosahedron. It
points, and then connects them on the screen. defines the record type Point3D to hold a point’s original
and transformed coordinates:
The example program Platonic, shown in Figure 2 (and
available for download; see end of article for details), uses TPoint3D = record
Coord: TVector3D; // The untransformed coordinates.
Trans: TVector3D; // The transformed coordinates.
end;
Figure 2: The example program Platonic displaying a dodecahedron. // Build a transformation matrix for display.
procedure TPlatonicForm.BuildTransformation(
var T: TMatrix3D);
// Make M an identity matrix. var
procedure TPlatonicForm.MakeIdentity(var M: TMatrix3D); r1, r2, ctheta, stheta, cphi, sphi: Single;
var T1, T2, T3, T4, T12, T34: TMatrix3D;
i, j: Integer; begin
begin // Rotate around the Z axis until the eye lies in
for i := 1 to 4 do // the Y-Z plane.
for j := 1 to 4 do r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
if (i = j) then stheta := EyeX / r1;
M[i, j] := 1.0 ctheta := EyeY / r1;
else MakeIdentity(T1);
M[i, j] := 0.0; T1[1, 1] := ctheta;
end; T1[1, 2] := stheta;
T1[2, 1] := -stheta;
// Perform matrix-matrix multiplication. Set R = A * B. T1[2, 2] := ctheta;
procedure TPlatonicForm.MatrixMatrixMult(var R: TMatrix3D;
A, B: TMatrix3D); // Rotate around the X axis until the eye lies within
var // the Z axis.
i, j, k: Integer; r2 := Sqrt(EyeX * EyeX + EyeY * EyeY + EyeZ * EyeZ);
value: Single; sphi := -r1 / r2;
begin cphi := -EyeZ / r2;
for i := 1 to 4 do MakeIdentity(T2);
for j := 1 to 4 do begin T2[2, 2] := cphi;
// Calculate R[i, j]. T2[2, 3] := sphi;
value := 0.0; T2[3, 2] := -sphi;
for k := 1 to 4 do T2[3, 3] := cphi;
value := value + A[i, k] * B[k, j];
R[i, j] := value; // We could project along the Z axis here. Instead we
end; // just ignore the Z coordinate when drawing.
end;
// Make the picture reasonably large on the form.
// Perform vector-matrix multiplication. Set r = p * A. // Here we scale y by -50 to reverse its sign since
procedure TPlatonicForm.VectorMatrixMult(var r: TVector3D; // the Canvas starts with (0, 0) in the upper left.
p: TVector3D; A: TMatrix3D); MakeIdentity(T3);
var T3[1, 1] := 50;
i, j: Integer; T3[2, 2] := -50;
value: Single; T3[3, 3] := 50;
begin
for i := 1 to 4 do begin // Center the picture on the form.
value := 0.0; MakeIdentity(T4);
for j := 1 to 4 do r1 := SolidOption.Width + SolidOption.Left;
value := value + p[j] * A[j, i]; T4[4, 1] := (ClientWidth - r1) / 2 + r1;
r[i] := value; T4[4, 2] := ClientHeight / 2;
end;
// Combine the transformations.
// Normalize the point. Note value still holds r[4]. MatrixMatrixMult(T12, T1, T2);
r[1] := r[1] / value; MatrixMatrixMult(T34, T3, T4);
r[2] := r[2] / value; MatrixMatrixMult(T, T12, T34);
r[3] := r[3] / value; end;
r[4] := 1.0;
end;
Figure 4: BuildTransformation creates a transformation matrix for
Figure 3: Matrix and point manipulation procedures. viewing from point (EyeX, EyeY, EyeZ ) looking toward the origin.
ates a transformation matrix for projecting the data. This // Draw the selected solid's segments.
transformation acts as if it were viewing the data from the Canvas.Pen.Color := clBlack;
i := SolidOption.ItemIndex + 1;
point (EyeX, EyeY, EyeZ ) and looking toward the origin. The seg1 := FirstSegment[i];
procedure applies rotations until the eye coordinates lie in the seg2 := FirstSegment[i + 1] - 1;
for i := seg1 to seg2 do begin
Z axis, and then it projects onto the X-Y plane. It also adds a // Apply the transformation to the points.
scaling and a translation to make the result fit nicely in the VectorMatrixMult(Segments[i, 1].Trans,
Segments[i, 1].Coord, T);
form’s pixel coordinate system.
VectorMatrixMult(Segments[i, 2].Trans,
Segments[i, 2].Coord, T);
When the program receives a Paint event, it calls // Draw the segment.
BuildTransformation. It applies the transformation to the seg- Canvas.MoveTo(Round(Segments[i, 1].Trans[1]),
ments that make up the selected solid. It then draws lines Round(Segments[i, 1].Trans[2]));
Canvas.LineTo(Round(Segments[i, 2].Trans[1]),
between the transformed points. It repeats these steps for the Round(Segments[i, 2].Trans[2]));
three segments that represent the coordinate axis if the pro- end;
gram’s Show Axes box is checked. Figure 5 shows the pro- // Draw the axes if desired.
gram’s Paint event handler. if (ShowAxesCheck.Checked) then
begin
Canvas.Pen.Color := clGreen;
On the Surface for i := FirstSegment[0] to FirstSegment[1] - 1 do
begin
The example program Platonic stores both end points for // Apply the transformation to the points.
each of the segments it draws. This wastes a little space VectorMatrixMult(Segments[i, 1].Trans,
Segments[i, 1].Coord, T);
since each of the segments in a Platonic solid connect to at VectorMatrixMult(Segments[i, 2].Trans,
least two others. For example, each corner of a cube is Segments[i, 2].Coord, T);
shared by three edges. For small data sets like this one, the // Draw the segments.
waste is not terribly important. Canvas.MoveTo(Round(Segments[i, 1].Trans[1]),
Round(Segments[i, 1].Trans[2]));
Canvas.LineTo(Round(Segments[i, 2].Trans[1]),
The Surface example program, shown in Figure 6, stores Round(Segments[i, 2].Trans[2]));
end;
the coordinates of each point for an array of X and Y val- end;
ues. This allows it to transform each point only once. end;
Figure 7 shows how the program transforms the data and Figure 5: When it receives a Paint event, the example program
draws the surface. Platonic builds a transformation matrix, transforms its data points,
and then draws the segments that make up the selected solid.
Rod is the author of such programming classics as Visual Basic Graphics Programming
[John Wiley & Sons, 1997] and Ready-to-Run Delphi 3.0 Algorithms [John Wiley &
Sons, 1998]. He also writes algorithm columns in Visual Basic Developer and
Microsoft Office & Visual Basic for Applications Developer. You can find pictures creat-
ed using 3-D graphics techniques at his Web site (http://www.vb-helper.com). You can
reach Rod via e-mail at RodStephens@vb-helper.com.
I have been writing this column in Delphi Informant since the premiere issue.
In that time, I have had the opportunity to cover a wide range of topics, from
creating local database names using the Database component (the very first
article), to cached updates; from OLE Automation to RTTI; from Code Insight to
QuickReports. In fact, in recent months I have wondered if the column title,
“DBNavigator,” continues to be appropriate.
I’ve decided to keep the “DBNavigator” of the role played by many of the most com-
name. Furthermore, with this issue, I am mon components used for data access.
beginning a series of columns focused on
database-related issues. This month’s column The BDE
begins with an overall introduction to data- Delphi provides you with access to your data
base development with Delphi. In coming using the BDE. From time to time, you’ll also
months, I will revisit and re-examine some see the BDE referred to as IDAPI (Integrated
topics I’ve covered in the past, as well as Database Application Programming Interface).
introduce new database-related topics I have
not yet had the opportunity to discuss. The BDE is a common data access layer for
all of INPRISE’s products, including Delphi,
While I still retain the right, and the desire, C++Builder, and JBuilder (through
to cover issues that extend beyond the DataGateway). Several Corel products,
realm of database development, I think the including Paradox 8 for Windows and
timing is right for an extended look at data- Quattro Pro, also use it. In addition, by
base applications. Over the past year, I have default, database applications written with
encountered numerous developers who are these products use the BDE. In other words,
new to Delphi, coming from languages Delphi uses the BDE, as well as those data-
such as Visual Basic, Clipper, and base applications you write with Delphi.
PowerBuilder. Consequently, although there
are many Delphi developers who are com- The BDE consists of a collection of DLLs.
pletely comfortable with the issues sur- Consequently, one installation of the BDE can
rounding database development, there is a be used by two or more BDE-aware applica-
growing number of programmers who are tions simultaneously. As a result, each individ-
new to these issues. ual Delphi application is smaller in size than it
would be if all data-accessing code needed to
Overview of Delphi Database Development be linked into the executable. Furthermore,
Delphi is not only a great language, it’s an because two or more applications can share
exceptional database development environ- the same copy of the BDE loaded into memo-
ment. This month, we begin with a general ry, overall RAM usage is reduced when two or
overview of the heart of database develop- more BDE-aware applications are running
ment in Delphi, the Borland Database Engine simultaneously (when compared to linking the
(BDE). We then continue with a discussion code into each .EXE).
24 August 1998 Delphi Informant
DBNavigator
The purpose of the BDE is to insulate you from the mun- Finally, the BDE also supports access to any file type for
dane tasks of data access. These include table and record which there exists an ODBC driver. ODBC is a Microsoft
locking, SQL construction, record updates, and basic I/O, standard for accessing databases, and is based on the Open
just to mention a few. The BDE permits you to concentrate SQL CLI (call level interface). To use one of these drivers,
on what data you want to access, instead of how to access it. it must be installed and configured (using the ODBC 32-
Because of the BDE, your Delphi applications can just as eas- bit Administrator, available on the Control Panel).
ily use data in dBASE or Paradox tables, files on a remote
database server, and files supported by ODBC (Open Following are some of the benefits provided by the BDE:
DataBase Connectivity) drivers. It provides seamless access to any data source, whether
it’s Paradox, dBASE, InterBase, Oracle, Sybase,
The BDE API consists of approximately 200 procedures and Informix, Microsoft SQL Server, or data accessed
functions, all of which are available through the BDE unit. through ODBC drivers.
Fortunately, you almost never need to call any of these rou- It provides the basic data engine for all your applica-
tines directly. Instead, you use the BDE through Delphi’s tions. Your clients only need one copy of the BDE on
data access components, which are found on the Data Access their systems, and this can be accessed by all the appli-
page of the Component palette. These components encapsu- cations you write.
late calls to the BDE API, providing you with a much sim- It creates an OS (operating system)-independent layer for
pler interface. However, if you have special data needs not all your applications. The BDE manages all file I/O, net-
provided by these components, you can use the BDE API work access, and memory management for data access.
directly. It provides enhanced performance with BLOB (Binary
Large Object) data through caching services.
The relationship between Delphi (and other applications), It includes internal support for language drivers, provid-
the BDE, and underlying files is depicted in Figure 1. As you ing you with an easier path to creating applications for
can see, the BDE is a software layout that lies between BDE- the international market.
aware applications (Delphi and the database applications you It performs data translation between various data sources.
create with it) and the sources of your data. It also shows that It includes a SQL generator. Data requests, other than
the BDE can access local tables directly, such as Paradox and pass-through SQL, are translated into a common local
dBASE. Connecting to Microsoft Access or FoxPro tables SQL, which is a subset of ANSI92 SQL. Using the
requires that you have the Microsoft DAO (Data Access Borland SQL Links for Windows, these can be translated
Objects) DLLs installed. into the appropriate dialect of SQL, based on the SQL
driver to which the BDE is sending its data requests.
Connection to other data sources requires additional dri- It offers access to data stored in formats supported by
vers. For the best performance in connecting to a remote your installed ODBC drivers.
database server, such as Oracle, Microsoft SQL Server,
InterBase, and so forth, use Borland SQL Links drivers. One of the most important benefits provided by the BDE
These native language drivers are provided in the Delphi is that your Delphi applications don’t need to be written
Client/Server and Delphi Enterprise editions. In addition, to a particular database standard. Specifically, use the
SQL Links drivers must use an additional network protocol same, simple interface provided by data access components
to connect to the remote server. to access your data. Even if later you need to change the
underlying data type (e.g. from Paradox
to InterBase, or Microsoft SQL Server to
Oracle Server), your applications don’t
necessarily need to be recompiled. The
description of where to find the data,
and how to access the data, can be con-
figured outside your applications using
the BDE Administrator.
Figure 2: The BDE Administrator. The BDE Administrator will generate a default
name for the driver. Select the driver in the
Databases page and enter the name you want to
use for the driver. Then, enter the appropriate
parameters for the new alias in the Definition page.
Configuring Drivers
Figure 3: The Configuration page of the BDE Administrator. Drivers are configured from the Configuration
page of the BDE Administrator. To change a particular dri-
aliases, and the Configuration page (see Figure 3) displays dri- ver’s configuration, select that driver, then modify its attribut-
vers and settings. The right pane contains the Definition es in the Definition page.
page, which is used to display the various parameters of the
selected database name, driver, or setting. Native drivers, which include both local and SQL Links, are
installed when you install Delphi. You cannot add new native
Working with Global Aliases drivers from the BDE Administrator. These can only be
Aliases, or database names, are labels that reference a direc- installed from the installation disk supplied by INPRISE, or
tory (or server) and a driver that can be used to access data one created by InstallShield Express.
in that directory (or server). Aliases defined on the
Databases page of the BDE Administrator are available to The driver settings you enter on the Configuration page of
all Delphi applications on that machine. Consequently, the BDE Administrator provide default settings for global
they are referred to as global aliases. By comparison, aliases aliases. However, when configuring a particular alias, you
defined within an application using a Database component can override any of these parameters.
are referred to as local aliases, because they are available
only to that application. Database Applications That Don’t Use the BDE
Under normal circumstances, the database applications that
Global aliases defined here are especially useful for data that you create using Delphi use the BDE. However, there are
must be accessed by more than one application. For example, alternatives that don’t require the BDE. These include:
a company may maintain a single table for storing general Applications that make use of explicit file I/O. For
information about its employees. If more than one applica- example, you can define your own file structures, then
tion needs to access this information, an alias can be created take responsibility for reading and writing this data
that points to the directory in which that table is stored. If all manually. The drawback to this type of application is
applications access this table using the defined alias (which is that you cannot use the data access and data control
26 August 1998 Delphi Informant
DBNavigator
components that ship with Delphi. Similarly, if you Session. The Session component represents a connection
want the data to be accessed in a multi-user environ- to the BDE. Conceptually, it represents the user for the
ment, you are responsible for programming the neces- purpose of file and record locking. Every database applica-
sary file- and record-locking mechanisms. tion has its own session, and Delphi creates this session
Applications that make use of an alternative database automatically. (The automatically-created session compo-
engine. Several third-party developers have created nent can be referenced by the instance variable named
add-on products for Delphi that provide for data Session.) It is because of the session that two applications
access. These either support file types not supported running on the same machine are seen by the BDE as two
directly by the BDE (such as Clipper or Btrieve) or different users (different sessions equate to different users).
have a smaller footprint than the BDE. Some of these Even if one user runs two copies of a given application,
third-party products can be used with Delphi’s data each copy will have a different session, and therefore will
access and data control components, while others appear to the BDE as two users.
provide their own component set for data access
and manipulation. There is a Session component on the Data Access page of
Applications that use the ClientDataSet component the Component palette. One of the few times you ever
that ships with the Client/Server and Enterprise edi- need to add Session components to a single project is
tions of Delphi. This component, which can be used in when that project is multi-threaded and data needs to be
place of other DataSet components, permits the reading accessed by more than one thread. Because each thread
and writing of single-user flat files. The ClientDataSet accesses data from a separate session, the BDE treats the
component relies on a 150KB DLL named threads as separate users. This provides a consistent mech-
DBCLIENT.DLL, but doesn’t make use of the BDE. anism for the resolution of record and table locking
Client applications that use INPRISE’s MIDAS between threads.
(Multi-Tier Distributed Application Services) technolo-
gy. With MIDAS, your Delphi client application Database. The Database component provides a pointer to a
receives data over a TCP/IP connection, or through the directory (on a stand-alone machine or a LAN), or to a
use of sockets. The data is provided by an application remote database server. Every database application will have
server, which you also write using Delphi. While the at least one Database component, and more if data in mul-
application server does make use of the BDE, the tiple directories and/or servers is being accessed.
client application does not. Client applications created
using MIDAS are often referred to as thin clients, If you use a DataSet, but have not specifically associated it
because they require less configuration and fewer files with a Database component, Delphi will create one
(specifically, no BDE). Database component for each directory and/or server
being accessed. It will do this in response to an attempt to
Delphi Database Components open the DataSet.
BDE-based database applications written in Delphi rely on
both data access and data control components that ship Unlike a Session component, you may need to use a Database
with Delphi. The relationship between these components is component if you want to control access to a database. For
depicted in Figure 4. The following sections describe each example, if you want to define custom parameters for access
of these sets of components. to a database server, store a username and password, or
explicitly control transactions, you will probably use at least
one Database component in your application.
Session
(default)
In a single-threaded application, all Database components
are associated with a single Session component. However,
Database many different DataSets can use one Database.
(default)
Database components, like Session components, are global-
DataSets ly available within your application. In other words, if your
(Table, Query, Database component appears on an auto-created form or
StoredProc) the main form, it’s available to all forms and data modules
in the application, without the need for a corresponding
DataSource
uses clause statement.
Data Controls (below)
DBGrid DBNavigator DBText DBEdit DBMemo DBImage DataSets. DataSets, which include the Table, Query, and
StoredProc components, are associated with individual
tables or SQL files. (In the case of Query and StoredProc
components, it’s possible for these entities to be associated
Figure 4: The relationship between the various data access with more than one, or even no, underlying data file. This
components and data aware controls. is the exception, however, rather than the rule.)
27 August 1998 Delphi Informant
DBNavigator
Each DataSet has its own cursor. Furthermore, you use the Conclusion
methods and properties of the DataSets to get information Delphi has established itself as one of the leading develop-
about — and to control — a table. For example, using a ment tools for building database applications for the
DataSet you can read data from a table, modify existing Windows platform. Essential to this role is the Borland
records, insert new records, or delete records. Database Engine, which serves as an independent layout
for all data access. In addition, the data access and data
From a developer’s standpoint, DataSets greatly simplify control components provide a seamless and easy-to-use
access to data. Specifically, they encapsulate calls to the interface to the BDE, permitting you to quickly build
BDE from within an easy-to-use interface. For example, if complex interfaces.
you wanted to create a pointer to a record using BDE API
calls, you would have to call at least five different BDE API In next month’s “DBNavigator,” we’ll take a look at configuring
functions. By comparison, calling a Table component’s data access components, including tables and queries. ∆
Open method performs the identical operation.
This month, we’ll go further, building on that When we open a line for communications,
foundation in several ways. We’ll show you we could have a number of intentions: from
how to determine the existing capabilities of conducting a simple voice conversation to
the particular TAPI implementation and downloading a file. The most important
monitor changes to the COMM port. We’ll function in determining what communica-
also show you how to access one of the dialog tions operations our application will be able
boxes included in TAPI. To accomplish this, to perform is LineOpen. This function is
we’ll have to take some additional steps that declared in TAPI.pas as:
were unnecessary in the first article.
function LineOpen(hLineApp: HLINEAPP;
You’ll notice in this structure that many new options were Then, we make certain these default values are reflected in
added in version two of TAPI. Figure 2 describes the fields our two combo boxes with these statements:
used by LineOpen. If you’re making a data call, you’re
// Default to LINEMEDIAMODE_DATAMODEM.
required to set the default values; in a voice call, this is cboxMediaMode.ItemIndex := 3;
optional. In the FormCreate method of TAPIU_2.pas, we ini- // Default to LINEBEARERMODE_VOICE.
tialize FMediaMode and FBearerMode as follows: cboxBearerMode.ItemIndex := 0;
Once we have terminated our call, we close the thread with: As we’ve seen, the WaitCommEvent function monitors events
on a particular communications resource. You can set or
FCommStatusThread.Terminate;
query the current event mask of the communications resource
by using the SetCommMask and GetCommMask functions. If a
Within the thread, all the work takes place in the Execute
requested overlapped operation cannot be completed immedi-
method. Here we call two low-level communications API
ately, the function returns False and GetLastError returns
functions, SetCommMask and WaitCommEvent. Let’s discuss
ERROR_IO_PENDING. This indicates that the communi-
each. The first, SetCommMask, is defined in Windows.pas as:
cations operation is continuing to execute in the background.
function SetCommMask(hFile: THandle; When this happens, the system will set the hEvent member of
dwEvtMask: DWORD): BOOL; stdcall; the OVERLAPPED structure to the not-signaled state before
the WaitCommEvent function returns. You can call
Its first parameter, hFile, is the handle of the communications GetOverlappedResult to determine the success or failure of the
port. The var parameter, lpEvtMask, is a mask containing one operation. The variable pointed to by the lpEvtMask parame-
or more events to monitor. The various modem events con- ter indicates the event that occurred. In the sample applica-
stants and their meanings are shown in Figure 4. tion, we used the non-overlapped approach.
Once we’ve told Windows the kind of communications events Putting It All Together
to monitor, we have to wait for one to occur. (Do you see In the CommStatus unit, we use these API functions in the
why we needed to do this in a separate thread?) The Execute method. As we stated earlier, GetCommModemStatus’
WaitCommEvent function waits for an event to occur on a second parameter, lpModemStat, points to the current status of
particular communications device. The WaitCommEvent func- the modem. The entire Execute method is shown in Figure 5.
tion is defined in Windows.pas as:
Major Ken Kyler is the Air National Guard Systems Analyst for the Defense
Integrated Military Human Resources System (DIMHRS). He has been program-
ming with Delphi for two years. He is also a free-lance technical writer with arti-
cles published in several Delphi magazines. You can reach him at
KylerK@PR.OSD.MIL.
By Robert Vivrette
I t happens all the time: You’re adding a feature to your latest programming
work of art, when what you thought would be fairly simple quickly becomes
a code morass. “There has to be a simpler way!” you exclaim. There often is. In
fact, my Delphi philosophy is: “If it takes more than two or three lines of code to
achieve a desired effect, you’re probably doing it wrong.” I live by this state-
ment, and I have seen it hold true hundreds — if not thousands — of times. I’ll
complete some routine, then sit back and look at it. “Hmm ... looks a little over-
weight!” Then I start trimming it down: finding simpler ways to accomplish the
same thing, looking for API calls to eliminate much of my code. After a few pass-
es, pruning here and there, it happens! Two or three lines of code remain.
Windows has been around for quite a while, so value of zero causes the thread to relinquish any
it’s unusual that a programmer will write some- time left on its current time slice to any other
thing completely unique, something never running thread with the same priority.
coded before. The programming APIs available
to Windows developers are substantial, but we Be aware of some of the behavior you may
can’t always remember them all, and can wind see using Sleep. Because you are stopping
up re-coding what already exists in an API. CPU cycles to your application’s primary
thread, your application’s message processing
In this spirit, this month’s “At Your loop will take a nap as well. This means that
Fingertips” is devoted to existing API rou- mouse messages won’t be processed, keyboard
tines that sometimes fall through the cracks: input will be on hold, and Timers will stop
simple and useful routines that, in my experi- working, to name just a few. In most cases,
ence, few developers even realize exist, yet however, this is what you want anyway.
can save considerable programming effort.
There’s more. The SleepEx function extends
Take a Nap! this capability to allow the sleeping thread to
Back in the days of Turbo Pascal and DOS be alerted before the completion of its sleep
programming, programmers had a wonderful delay. This would enable you to put the thread
little routine named Delay that would pause to sleep pending the completion of some other
execution of the program for a specified peri- supported event, such as the I/O functions
od. Then Windows development came along, ReadFileEx and WriteFileEx. There’s more to
and Delay was gone. <sniff>. Or was it? It the SleepEx function than I can go into here,
turns out the Windows API has a Sleep rou- but Delphi Help discusses it fairly well.
tine that performs the same function.
Show a Little Drive
In a nutshell, the Sleep procedure pauses execu- Sometimes, developers need to obtain a list of
tion of the current thread for an indicated num- all available drive letters on the machine. I’ve
ber of milliseconds. It takes a single DWORD seen a lot of approaches to this, including build-
parameter that specifies the number of millisec- ing a loop from ‘A’ to ‘Z’ and looking at each
onds to wait before continuing. A value of 1000 drive individually. The problem with most of
equals one second, so to delay for three seconds, these approaches is that validating the existence
you would use Sleep(3000). Specifying a value of a drive in this manner often involves access-
of INFINITE (i.e. $FFFFFFFF) causes the ing the drive itself. This can generate unwanted
process to sleep forever (now that’s useful). A system errors for an empty floppy drive.
35 August 1998 Delphi Informant
At Your Fingertips
You could use a DriveComboBox component and examine procedure TForm1.Button1Click(Sender: TObject);
var
its Items property to see what drive letters it’s populated
Buffer: array[0..500] of Char;
with. The disadvantage here is that DriveComboBox holds TmpPC : PChar;
a lot more code than you need. It’s also a visible control, Typ : Integer;
begin
so if you aren’t using it for any other purpose, you will
GetLogicalDriveStrings(SizeOf(Buffer),Buffer);
need to stash it out of the way and set its Visible property TmpPC := Buffer;
to False. Sloppy! while TmpPC[0] <> #0 do begin
Typ := GetDriveType(TmpPC);
with ListBox1.Items do
A much better solution is to use the Win32 API functions case Typ of
GetLogicalDrives and GetLogicalDriveStrings. GetLogicalDrives DRIVE_REMOVABLE : Add(TmpPC + ' (Removable)');
DRIVE_FIXED : Add(TmpPC + ' (Fixed)');
returns a 32-bit value that is bitmasked to indicate the valid
DRIVE_REMOTE : Add(TmpPC + ' (Remote)');
drives in the system. In the return value, bit position zero DRIVE_CDROM : Add(TmpPC + ' (CD-ROM)');
represents drive A, the next bit represents drive B, and so on. DRIVE_RAMDISK : Add(TmpPC + ' (RAM-Disk)');
else
If a bit is set to 1, the drive is present in the system. This
Add(TmpPC+' (Unknown)');
procedure adds the letters of all available drives to ListBox1: end;
TmpPC := StrEnd(TmpPC)+1;
procedure TForm1.Button1Click(Sender: TObject); end;
var end;
Drvs: DWord;
Figure 1: Use the GetDriveType function to determine the type
A : Integer;
of drive that GetLogicalDrives and GetLogicalDriveStrings returns.
begin
Drvs := GetLogicalDrives;
for A := 0 to 31 do The technique couldn’t be easier. Simply define a cursor con-
if (Drvs and (1 shl A)) > 0 then stant (anything greater than zero and not conflicting with
ListBox1.Items.Add(Chr(A+65));
end; another cursor constant) in the const section of your unit:
const
Note the use of the shl keyword to mask out only the bit crMyCursor = 5;
position I’m interested in. By taking a ‘1’ and shifting it left a
number of positions, I can mask out just the bit position I Then add this code to the FormCreate method:
am interested in using the and keyword.
procedure TForm1.FormCreate(Sender: TObject);
begin
The GetLogicalDriveStrings function is similar, but instead
Screen.Cursors[crMyCursor] :=
returns a string representing all the valid drives on the sys- LoadCursorFromFile('C:\WINNT\Cursors\sendmail.ani');
tem. After each drive reference, there is a null character; at Panel1.Cursor := crMyCursor;
end;
the end of the entire string, there is a pair of null characters.
This procedure can be used to extract the values returned in
a doubly null-terminated string: A standard Windows 95 or Windows NT installation will
have a number of animated cursor files located in the
procedure TForm1.Button1Click(Sender: TObject); Windows\Cursors or WinNT\Cursors directory, respectively.
var
Buffer: array[0..500] of Char;
TmpPC : PChar;
Conclusion
begin The various Windows APIs are chock-full of useful routines
GetLogicalDriveStrings(SizeOf(Buffer), Buffer); available to you as a Delphi programmer: It’s just a matter of
TmpPC := Buffer;
knowing where to look. The best way to find those interesting
while TmpPC[0] <> #0 do begin
ListBox1.Items.Add(TmpPC); new functions is to look up something you’re familiar with in
TmpPC := StrEnd(TmpPC) + 1; the Win32 API Help file, and then click on the “Group” link at
end;
the top of the page. This will show you a list of routines that fall
end;
into the same general category. Scan through them and you’ll be
amazed at what you find.
Alternatively, you could use the GetDriveType function to
determine the type of drive that GetLogicalDrives and
And remember: If it takes more than two or three lines of
GetLogicalDriveStrings returns (see Figure 1).
code to do anything in Delphi, there’s probably an API rou-
tine that does it all for you! ∆
Cursors Are Nice People Too
Most good Delphi programmers know they can create custom
cursors and load them into the Screen.Cursors array. But did
you know that you can also load animated cursors? You can Robert Vivrette is a contract programmer for Pacific Gas & Electric, and Technical
achieve this by using the LoadCursorFromFile API function. Editor for Delphi Informant. He has worked as a game designer and computer
This function returns a handle to the loaded cursor, which consultant, and has experience in a number of programming languages. He can
can be a standard cursor file (*.CUR), an Animated Cursor be reached via e-mail at RobertV@mail.com.
(*.ANI), or one of the system cursors.
36 August 1998 Delphi Informant
New & Used
By Steve Garland
ODBCExpress
Catch the High-speed Train to Database Access
M ost of the non-Delphi world of C++, Visual Basic ( VB), and PowerBuilder
developers access client/server databases using ODBC. Delphi’s BDE
allows the same access, but why use the BDE when you can go to a pure
Pascal/ODBC solution using the ODBCExpress components from Datasoft?
What do you gain? Speed, control, and no DLLs to distribute with your applica-
tions. What do you lose? Over 1MB of BDE DLLs.
After I started using ODBC and the BDE with I’m going to sketch out the basics of ODBC
SQL Anywhere, I was running into several for you and, at the same time, detail the way
“gotchas” with INPRISE (nee Borland) and that Datasoft implemented the ODBC API
Sybase pointing fingers at each other. One of in ODBCExpress.
the problems was getting SQL Anywhere to
handle BLOBs greater than 32KB using What Is ODBC?
Delphi/ODBC/BDE. I knew that SQL ODBC is Microsoft’s set-oriented API for
Anywhere could handle this with pure ODBC, accessing databases. The set-oriented part is
so what was I missing? I needed to take the important as ODBC expects data sources to
BDE out of the equation. be able to handle SQL. This means the data
must reside in tables and have the concept of
Writing directly to the ODBC API seemed like rows and columns. (As a side note, Microsoft
a lot of work, until I came upon a South has realized that there is other data that doesn’t
African company that had used Delphi and fit the row/column metaphor, and has created
ODBC to develop a document management OLEDB for that. ODBC becomes a subset of
application using Microsoft SQL Server as their OLEDB in their current scheme.)
back end. That company was Datasoft, and the
product was ODBCExpress. I soon had SQL ODBC is a specification, not an implementa-
Anywhere eating large text files like candy, and I tion, and many who remember early ODBC
no longer heard that rising rumble of the BDE drivers as slow and buggy should wake up and
DLLs loading. I started to like ODBC. smell the new drivers — especially the ones for
Microsoft SQL Server and SQL Anywhere.
ODBC was a new creature to me, so I quickly The Microsoft SQL Server drivers are so fast
went out and found a book by the ODBC that Microsoft uses them for performance
37 August 1998 Delphi Informant
New & Used
such as grids. ODBC
handles the buffering
and allocating of
memory to make this
possible, so when the
BDE allocates its own
buffering scheme, it
becomes redundant.
Why use both?
The Statement
Handle (Hstmt) is the
worker bee of ODBC.
All SQL statements
and requests for meta-
data require an
Hstmt, and each
Hstmt is associated
Figure 1: The building blocks for ODBC programming as imple- with one connection.
mented by ODBCExpress. Note that the GlobalHenv component Hstmt components
is now automatically instantiated at the unit level. handle the state infor-
mation of any query
benchmarks. It’s also important to note that you don’t need the activity. Hstmt is the
Delphi Client/Server version to access Microsoft SQL Server low-level equivalent of
using their ODBC driver. The promise of ODBC is that if you TQuery. DBCExpress Figure 2: Object Inspector view of
develop to the ODBC API, you can use the same code and has a THstmt class the THdbc component.
tools against any and all ODBC drivers. that implements the
Hstmt.
ODBC has a character all its own; it’s not the BDE. If you
are expecting ODBC to act as the BDE does with Paradox Pulling an Hdbc and
and dBASE tables, you’ll be disappointed. This isn’t to say Hstmt component
that one is better than the other; they’re just different. onto a form allows
you to query a data
ODBC uses the concept of Handles extensively. At the high- source or execute SQL
est level, there is an Environment Global Handle (Henv) that statements (see Figure
is much like the Delphi Session component. The Henv 3). At this level, you
Handle keeps track of the number of connections and their are just a thin wrapper
state, handles error messages that occur at the environment away from the native
level, and helps to manage transactions. In earlier versions of ODBC API calls, and
ODBCExpress, there was an Henv component, but it’s no ODBCExpress adds
longer needed, as it is created internally at unit initialization little overhead to your
to be used by the other components (see Figure 1). Again, application. Issuing
this is similar to the BDE’s Session component. update and insert
statements using
The Connection Handle (Hdbc or Handle to a database con- Hstmt components is
nection) manages the network connection, or tracks directory lean and fast.
and file information for file server databases. All function calls
are routed from the back-end database through the Connection Many developers pre-
Handle. This is equivalent to the Database component of the fer to stay at this level
BDE. ODBCExpress implements it in its THdbc class. Because to keep overhead
they exist independently, you can use Hdbc components to down, and that’s cer-
access the same table isolated in a separate thread (see Figure 2). tainly a choice you Figure 3: Object Inspector view of the
have with THstmt component.
It’s at the THdbc level that you set the isolation level and ODBCExpress.
other properties, such as cursor implementation. ODBC However, many of us like to use visual data aware controls;
has had a feature called a Cursor Library since version 1.2. after all, that’s one of Delphi’s great strengths.
ODBC does this by handling the buffering of data and ODBCExpress has two sets of visual components available,
providing a “moving window” of buffers for the database. but to explain them, we must first recap some Delphi VCL
This enables users to “browse” their data in visual controls, database history.
38 August 1998 Delphi Informant
New & Used
Delphi Database History There’s a happy ending to this story. Borland/INPRISE
Delphi database access starts with the db unit (db.pas). In heard many of us screaming for them to redesign the db
Delphi 1 and 2, there were specific BDE calls in the db unit, and they delivered with Delphi 3. Delphi 3 has a
unit. Therefore, third-party vendors who wanted to provide new and vastly improved version of the db unit that uses
database access using engines and access to the Delphi data- no BDE calls and allows developers to create their own
aware controls other than the BDE were forced to “hack” TDataSet descendants that use the database controls that
their own versions of the db unit and distribute them as come with Delphi. Hallelujah! All the BDE calls were
binary (dcu) files. This led to all kinds of problems with moved to the dbtables unit, and vendors (like
other third-party controls. This method basically intercept- ODBCExpress) could finally plug in their database
ed the low-level BDE calls, and replaced them with proxy engines by simply implementing the virtual abstract
calls to their own engines. In addition to the BDE calls in methods from the db unit. No more hacking necessary.
the db unit, many parts were declared as private, and thus,
impossible to extend using OOP. It All Comes Together: TOEDataSet
The Delphi 3 open dataset architecture paved the way for the
When ODBCExpress 1.0 was released, there was no way ODBCExpress team to use their core ODBC components to
to provide access to the native Delphi database controls provide a component
without shipping a proprietary version of the db.dcu bina- that would make a
ry file. So, ODBCExpress came out with their own data- Delphi database
base controls, which you’ll find installed on the developer feel right at
OEControls tab of the Component palette. These controls home. It’s very similar
were written from scratch by the ODBCExpress team, and to the Delphi Query
let’s just say that they have their own way of doing things. component with a
The grid, for instance, descends from TStringGrid, and, SQL property and a
while it provides fast access, it doesn’t provide design-time Params property with
viewing of data, and generally doesn’t behave like a native which to use stored
Delphi control. procedures. But this
is ODBC, so there is
So, the ODBCExpress team set out in the second version to also an Hstmt proper-
hack the db unit and provide a way for us to use the native ty in case you need
Delphi database controls. (They actually finished the prod- more granular control
uct, but never released it.) The reasons for this are very telling of the TOEDataSet
in comparing ODBC and the BDE and how they differ in (see Figure 4).
their philosophy. Reason one was that the BDE architecture
was undocumented and extremely complex. The more inter- Pull a THdbc compo-
esting reason, however, is that the BDE and ODBC have dif- nent, a TOEDataSet,
ferent “access paradigms.” a Delphi DataSource
and Delphi DBGrid,
The important point is that ODBC is SQL and set- and connect them
oriented, and the BDE is procedural and file-server orient- just like you would a
ed. BDE desktop database users are accustomed to using Query component.
data-aware controls, and editing within a grid with no real What could be easier? Figure 4: Object Inspector view of the
concern for isolation-level or concurrency settings. The Populate the SQL TOEDataSet.
BDE handles it for them — including locking. This is cer- property, set Active to
tainly a wondrous thing, but for client/server develop- True, and the DBGrid is populated. Use the Fields Editor to
ment, one needs to make decisions as to concurrency and change display labels or other TFields of the DataSet.
isolation issues, and live result sets cannot be expected at
all times without some sort of trade-off. So, ODBC All the data-aware controls can be used, including DBEdit and
requires decisions to be made, as there is no “one-size fits DBMemo. The difference is that TOEDataSet provides a thin-
all” philosophy. Many that come to try ODBCExpress ner layer between you and ODBC than does the BDE. The
complain that it doesn’t work exactly like the BDE. Well, ODBCExpress documentation states that by providing ODBC
they’re right. They are different creatures. Driver cursor support, the TOEDataSet “eliminates the need
for a resource-heavy front-end cache as used by existing data-
A lot of marketing hype promises that you can easily convert base engines.” By implementing all the low-level ODBC con-
file-server databases to client/server databases, e.g. Paradox to trols (Thdbc, THstmt), then providing a TDataSet descendant
Microsoft SQL Server. I just don’t believe it. If you expect in the TOEDataSet, ODBCExpress gives you the best of both
ODBC to act like the BDE, you’ll be disappointed; but if worlds: ease-of-use and granularity. At the ODBC level, you
you take the time to understand ODBC and use the tools as can still control the way you do updates, inserts, and deletions.
they were designed, you will find a fast, direct, and lean way You still get complete control over transactions, and can set all
to develop client/server applications. the properties of the THstmt that are part of the grid.
39 August 1998 Delphi Informant
New & Used
Conclusion
I guess it’s not hard to see that I like ODBCExpress. I find
it stays true to the ODBC API character, and allows me to
drop down to the API level when I want to make my own
ODBC calls, or use the TOEDataSet with the native
Delphi database controls. I’ve used these components from
version 1 to version 4, and have been impressed with how
they’ve evolved, the speed with which that evolution has
occurred, and the openness and availability of the
ODBCExpress development team. ∆
Steve Garland is a long-time Delphi developer who lives in Boise, ID with his
wife, 18-month-old son, and four wild pug dogs. He is the principal of Hyper
Logic Resource Group, a Boise-based custom software company. He is also
involved in developing thin-client technology for the Asta Technology Group
(http://www.astatech.com). He can be reached at steveg@hyper-logic.com.
A s you read this issue of Delphi Informant, hopefully you are in Denver, the site of BorCon98. This
annual conference provides a chance to receive technical training, network with fellow developers,
and meet some of the people from INPRISE. Historically, this is also the time that INPRISE paints a pic-
ture of what it intends to do for the coming year. The entire conference is surrounded with an air of
optimism and enthusiasm for users of Borland products. To add to the ambiance, I thought I’d share
my views on why I think distributed computing will be a good thing for developers and INPRISE.
Regardless of how you feel about the technology. Essentially, an escalating the developers needed. The shift to
company’s name change, the focus of war between language and compiler distributed computing is no exception.
Borland/INPRISE has been clear over vendors was fought to regain the
the last 18 months: Distributed comput- ground Borland had clearly taken. Distributed computing is viewed by
ing is in the spotlight. Products such as Today, it’s physically impossible to some as a return to mainframe men-
MIDAS, AppCenter, and OLEnterprise, develop software without seeing some tality, but I see this as a golden
the focus on SAP and AS/400 integra- passing reference to OOP. Borland opportunity to share information
tion, and the acquisition of Visigenics didn’t invent OOP, but they certainly across previously impassable bound-
are clear examples of the commitment to brought it to the forefront. aries. Data can be made available
this emerging arena. around the world easily using existing
With the advent and necessity of dis- infrastructures — and at a very rea-
These developments were not pro- tributed computing, INPRISE has sonable cost. The benefits of this
posed and developed in a vacuum. identified the next technology to model are many: centralized business
The reason paradigm shifts occur in show as much promise as OOP did. logic, thin clients, fault tolerance, and
the software development industry is Borland products can continue to load balancing to name just a few.
due to programmers who demand grow and flourish for both small-
more from their compiler vendor, company development shops and The market for developing distributed
tool, and/or language. Think of it as enterprise developers alike, but the computing solutions is ready to explode.
survival of the fittest for development reality is that the computer industry The dawn of distributed computing is
products. As a result of this has shifted more toward enterprise here. You owe it to yourself, and your cus-
Darwinian evolution of development development and, as a result, distrib- tomers, to find out how distributed com-
tools, we no longer have to use things uted multi-tier computing. puting can make your applications better.
like command-line editors and tools. Isn’t that what all of us really want? ∆
In fact, we have the ability to become Rick LeFaivre, Senior Vice President
even more productive because of of Research and Development at — Dan Miser
things like integrated development INPRISE, has a write-up of the waves
environments (IDE), GUI tools, and of technology available at
visual editors. How many people http://www.inprise.com/about/ Dan Miser is a Design Architect for
would really rather be using EDLIN executive/rickmultitier.html. The com- Stratagem, a consulting company in
instead of a visual editor? mon thread through all these waves is Milwaukee. He has been a Borland
innovation. In each case, both end- Certified Client/Server Developer since
Borland captured the OOP market users and developers clamored for 1996, and is a frequent contributor to
nine years ago and spurred its growth more, and the development tools Delphi Informant. You can contact
by remaining on the leading edge of industry responded by providing what him at http://www.execpc.com/~dmiser.
HTML references. Among the books I However, Dynamic Web Publishing 1996], Charles Calvert shows how to gener-
examined were two comprehensive refer- Unleashed, Second Edition includes useful ate HTML code. In Mastering Delphi 3
ences: Lois Patterson’s Using HTML 4 appendices on HTML 4 and Cascading [SYBEX, 1997], Marco Cantù demonstrates
[QUE, 1997], and HTML 4 Unleashed Style Sheets, one of the important HTML how to produce HTML tables with Delphi.
[Rick Darnell and John Pozadzides, SAMS, extensions. He also discusses CGI and ActiveX in build-
1997]. The former includes an excellent list- ing a “Web Application” with Delphi.
While these two books cover similar ground,
ing of HTML tags in one of its appendices; Finally, in Delphi 2 Multimedia Adventure
their emphasis is slightly different. Dynamic
the latter introduces the tags, systematically Set [The Coriolis Group, 1996], Chris D.
by function, throughout the text. Using Web Publishing Unleashed, Second Edition
Coppola, et al. devote several chapters to
HTML 4 includes a short appendix describ- provides an overview of the issues involved
building a multimedia Web browser. All
ing the tags new to HTML 4. A third work with creating a dynamic Web site; Dynamic
HTML Unleashed puts more emphasis on these examples have one important thing in
by Larry Aronson and Joseph Lowery, common: They use Delphi to create applica-
HTML 3.2 Manual of Style [Ziff Davis the techniques needed to accomplish that
goal. Besides HTML, publishing on the tions that read or write HTML files.
Press, 1997], is somewhat dated and consid-
erably shorter than the previous two. Web often involves additional technologies Hopefully, I’ve convinced you of the
Nevertheless, it provides a wonderful intro- and languages, including Java, ActiveX, need to develop an understanding of
duction to basic HTML. JavaScript, CGI, VRML, and more. While HTML, which, after all, isn’t that diffi-
some of these are specific to one of the two cult. By learning “another language” you
One of the newer developments in HTML major browsers, others are universal. increase your ability to work effectively
relates to building dynamic Web pages that Appropriately, another issue raised in in a field that is quickly becoming more
change in response to a surfer’s actions. The many of these volumes concerns the pro- multi-dimensional. ∆
new extensions to HTML that enable this prietary tags specific to a particular brows-
kind of flexibility are generally referred to as — Alan C. Moore, Ph.D.
er. But how does any of this relate to
Dynamic HTML. While each book men- Delphi programmers? Alan Moore is a Professor of Music at Kentucky
tioned so far deals with dynamic Web pages State University, specializing in music composi-
to some extent, there are two additional vol- The Delphi connection. There are several tion and music theory. He has been developing
umes that concentrate on these new devel- obvious ways we can use Delphi to work education-related applications with the
opments: Dynamic Web Publishing with HTML files: to generate HTML code, Borland languages for more than 10 years. He
Unleashed, Second Edition [Shelley Powers, et to parse and interpret HTML code, and to has published a number of articles in various
al., SAMS, 1997], and Dynamic HTML display HTML code in our own browser. technical journals. Using Delphi, he specializes
Unleashed [Rick Darnell, et al., SAMS, All these topics have come up in Delphi in writing custom components and implement-
1998]. As you might expect, neither of these books and some in articles. In the chapter ing multimedia capabilities in applications,
provides the same comprehensive introduc- “Introduction to CGI Programming with particularly sound and music. You can reach
tion to HTML the earlier books do. Delphi” in his Delphi 2 Unleashed [SAMS, Alan via e-mail at acmdoc@aol.com.