Delphi Informant Magazine (1995-2001)
Delphi Informant Magazine (1995-2001)
Delphi Informant Magazine (1995-2001)
ON THE COVER
40 In Development — Craig Jones
9 OLE Internet Access — Joseph C. Fung Mr Jones finishes his three-part introduction to Quality
Netscape’s Navigator dominates the Internet browser arena, Assurance. The focus this month is on the tools and processes
but how can we leverage its power and popularity with you need to put in place to develop and deliver reliable soft-
Delphi 2? Mr Fung answers this question by presenting an ware. The emphasis is on practicality, since these processes
example-laden guide to using Navigator as an automation are worthless if they’re never put to use.
server.
45 At Your Fingertips — David Rippy
16 An HTML Generator — Keith Wood Our favorite tipster returns with tips and tricks for Delphi 1
Planning on building a Web site? Is your Object Pascal and 2. This month, Mr Rippy reveals how to: tile bitmap
better than your HTML? Or is writing line after line of HTML images on forms and quickly make the same change to
simply too boring? If so, you’ll be happy to read Mr Wood’s several objects programmatically. He also has some com-
HTML primer, and download his full-featured HTML-gener- ments about Object Pascal comments.
ating component.
FEATURES REVIEW
26 Informant Spotlight — Ray Lischner 47 Teach Yourself Delphi in 21 Days
Virtual or dynamic? Dynamic or virtual? Which is Book review by James Callan
faster? Which is “better?” Mr Lischner analyzes Object Pascal’s
two types of virtual methods, benchmarks their performance, 47 Teach Yourself Database Programming
and demystifies the arcana of DMTs and VMTs. with Delphi in 21 Days
Book review by James Callan
30 DBNavigator — Cary Jensen, Ph.D.
In this month’s “DBNavigator,” Dr Jensen provides us with
an interactive tour of Delphi 2’s enhanced Fields Editor and DEPARTMENTS
expands on his discussion of the new data module. It is 2 Editorial
now simpler than ever to create and place DBEdit and 3 Delphi Tools
DBLookupCombo components. 6 Newsline
49 File | New
34 Dynamic Delphi — Andrew Wozniewicz
In the third installment of his series on building DLLs in Delphi,
Mr Wozniewicz shows us how to interface a DLL, export and
import functions by ordinal numbers, explicitly load libraries,
and more. He also provides us with step-by-step instructions to
build the example application.
By Joseph C. Fung
our Web browser makes it simple for you to surf the World Wide Web
Y and gain access to a wealth of information. The current crop of Web
browsers — or “Internet clients” as they are becoming known — can
retrieve hypertext Web documents, download files, handle e-mail, and let
you read newsgroups.
To use your Web browser to visit a particu- control or be controlled by another pro-
lar Web address or Internet site, simply gram. This ability lets you tap the func-
enter the site’s Web address — the URL tionality of many applications to create a
(Uniform Resource Locator) — and the single, integrated solution. Before OLE
browser connects and retrieves information automation, the few ways to do this
for you. A forms-capable Web browser, such included using macro or key stroke
as Netscape Navigator or Microsoft Internet recorders, or using the proprietary macro
Explorer, even lets you submit information language of another application.
to a Web server by filling in fields on a
form. A CGI (Common Gateway Interface) OLE automation changes this by letting an
program on the server can process this application known as an automation server
information, perform a desired action, then expose one or more objects that surface
return a response to you. The Web browser functionality you can access. The program
takes care of details such as network access, that works with these objects is called an
file retrieval, and security, so you can con- automation controller. A Delphi program
centrate on exploring the Internet. can have automation server and automa-
tion controller capabilities. Netscape
Delphi lets you build Internet and intranet Navigator is an automation server so you
programs that range from client/server can use a Delphi program to control it.
applications to CGI programs, but you may
need to obtain third-party Internet compo- Using OLE Automation to Control
nents or tools. [For more information on another Application
forthcoming third-party tools, see the side- To access an automation object, you use the
bar “Delphi and the Internet” on page 14.] Object Pascal CreateOLEObject function to
instantiate the object. CreateOLEObject
Fortunately, you can also use Delphi 2’s accepts the ProgID of an automation object
OLE automation capabilities to leverage and returns a reference to it as a Variant (a
Netscape Navigator’s built-in feature set special data type that can store different
and experiment with creating your own types of data and is intended primarily for
simple Internet-enabled program. OLE programming). ProgID is the pro-
grammatic string identifier, or name, of the
What Is OLE Automation? automation object. Each automation server
OLE automation defines a standard way in registers the ProgID of all its automation
which an application can programmatically objects into the system registry when you
Once you’ve instantiated an automation object in your The first argument, pBuffer, is an OLE BSTR type and
program, you use standard Object Pascal syntax to work refers to the buffer that receives the data. The second
with it. The automation object can be treated as a normal argument, iAmount, specifies the size of the buffer. To
object with the following exceptions. First, all the proper- keep things simple, you can use a string as the type for
ties and method arguments must be passed as Variants. the buffer instead of a BSTR; Delphi automatically
This means that any value that you use must be a Variant converts the string into a BSTR for you. When
or can be implicitly or explicitly converted into a Variant. Netscape Navigator stores any returned data into your
Second, calls to the automation object’s methods are late- buffer, it does not update the internal byte count of the
bound, so checking the method’s validity, type, or number string or BSTR, for that matter. Because of this, you
of arguments, is not done until run time. Therefore, if you must correct the byte count manually after calling Read.
call a method that is not part of the automation object, an
exception is raised in your program. This brief description only describes the methods used in
the examples in this article. If you are interested in learning
The code fragment in Figure 1 illustrates how to use more about the objects surfaced by Netscape Navigator, you
CreateOLEObject to instantiate an OLE object for Excel 7 can find the documentation for the OLE automation inter-
and use it to add a new workbook. face at http://home.mcom.com/newsref/std/oleapi.html.
Using Netscape Navigator as an Automation Server To run these examples, you’ll need to establish an Internet
One of the automation objects that Netscape Navigator 2.0 connection and have a copy of Netscape Navigator 2.0.
exposes has a ProgID of Netscape.Network.1. This automa- The Internet connection should be active before you try
tion object lets you use Netscape Navigator’s Internet any of the samples. If you’re using a dial-up connection,
access and file retrieval capabilities in your application. you may want to use Windows 95 Dial-Up Networking or
a third-party dialer to connect to your Internet Service
The automation object defines many methods and proper- Provider (ISP). If you don’t have a copy of Netscape
ties. Here are some of the methods: Navigator, you can obtain one by purchasing the Netscape
Open: Connects to and begins retrieving from a speci- Internet Starter Kit at a store, or by downloading an eval-
fied URL. If the method fails (e.g. Navigator is busy), uation copy from the Netscape home page at their Web
the method returns False. Open takes five arguments: address, http://www.netscape.com.
pURL, iMethod, pPostData, lPostDataSize, and
pPostHeaders. The first argument, pURL, is a string Quote.Com Background
that specifies the URL of the Web site. The second The first example uses the Quote.Com Web site
argument, iMethod, indicates the type of operation to (www.quote.com) to download stock quotes. Quote.Com
perform. When reading from a Web site, you pass a 0 is a company that provides financial market data, such as
to indicate that you want to read. The remaining argu- stock quotes, financial commentary, and business news to
ments pertain to posting data to the Web site. Since Internet users. Normally, you must have a paid subscrip-
you are only reading, you can also pass 0 as values for tion to the service to use it. New users who don’t want to
these arguments. pay can still try the service on a limited basis, but they
Close: Disconnects any current connection and resets must register online to obtain a user account and password.
the automation object. You should call Close when you Users who do not register may still try the service, but are
are finished with the automation object. limited to using the stock ticker “MSFT” (Microsoft) in
GetStatus: Returns an integer indicating the status of the the demonstration area (see Figure 2). This example
load. It returns a non-zero value in the case of an error. assumes that you are not registered, so it uses MSFT as the
Read: Retrieves data from the Web site and stores it into a default ticker. If you register with Quote.Com, you can use
buffer that you specify. Read returns -1 if there is no more other stock tickers with the sample application.
type
TStockForm = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Help1: TMenuItem;
About1: TMenuItem;
Exit1: TMenuItem;
Bevel1: TBevel;
StockButton: TButton;
Label1: TLabel;
StockSymbol: TEdit;
GroupBox1: TGroupBox;
QuoteLabel: TLabel;
Label2: TLabel;
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
procedure Exit1Click(Sender: TObject);
procedure About1Click(Sender: TObject);
procedure StockButtonClick(Sender: TObject);
Figure 2: A Web page for quick quotes. procedure FormCreate(Sender: TObject);
private
This screen allows you to enter your user name and pass- LoginName: string; // Registered user login name.
Password: string; // Registered user password.
word, a stock ticker, and the type of information you public
want. When you press the Retrieve button, a URL con- { Public declarations }
taining these parameters is sent to the Web server. A CGI end;
program at the Web server parses this URL and returns var
the requested information as a hypertext document. StockForm : TStockForm;
NetscapeObject : Variant;
type
TWeatherForm = class(TForm)
StatusPanel: TPanel;
Image1: TImage;
Gauge1: TGauge;
Label1: TLabel;
MainMenu1: TMainMenu;
File1: TMenuItem;
Help1: TMenuItem;
About1: TMenuItem;
Exit1: TMenuItem;
Bevel1: TBevel;
Panel1: TPanel;
CityListBox: TListBox;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure CityListBoxDblClick(Sender: TObject);
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
procedure Exit1Click(Sender: TObject);
Figure 9: The form for the weather service sample. procedure About1Click(Sender: TObject);
private
{ Private declarations }
The Weather Forecast Example public
{ Public declarations }
The Weather Forecast example program has a main form, end;
WeatherForm, with a TListbox component of major cities
and a TImage component. When you double-click on a var
WeatherForm : TWeatherForm;
city, the application connects to the Intellicast site and NetscapeObject : Variant;
downloads its weather image. This image is then displayed
in the TImage. Figure 9 shows a weather forecast for the Figure 10: The type declaration for the sample form.
New York metropolitan area.
The main unit, WEBFORM.PAS, defines the form // Initialize city list.
class TWeatherForm and the Variant NetscapeObject, to procedure TWeatherForm.FormCreate(Sender: TObject);
begin
reference the automation server (see Figure 10). with CityListBox.Items do begin
AddObject('Atlanta', TMapURL.Create('atl'));
The form contains the list box, CityListBox, which stores the AddObject('Atlantic City', TMapURL.Create('acy'));
AddObject('Boston', TMapURL.Create('bos'));
name of each city and an associated TMapURL object for AddObject('Chicago', TMapURL.Create('ord'));
that city. Each TMapURL object stores the three-letter abbre- AddObject('Dallas', TMapURL.Create('dfw'));
viation for a city and has a method, DisplayWeather, that is AddObject('Detroit', TMapURL.Create('dtw'));
AddObject('Hoboken', TMapURL.Create('ewr'));
executed when you double-click on a city in the list box. The AddObject('Houston', TMapURL.Create('iah'));
form’s OnCreate handler, FormCreate, populates the list box AddObject('Las Vegas', TMapURL.Create('las'));
with the list of cities and TMapURL objects (see Figure 11). AddObject('Los Angeles', TMapURL.Create('lax'));
AddObject('Miami', TMapURL.Create('mia'));
AddObject('Minneapolis/St. Paul',
The MAPURL.PAS unit defines the TMapURL class. TMapURL.Create('msp'));
TMapURL has a Create constructor that accepts a three- AddObject('New Orleans', TMapURL.Create('msy'));
AddObject('New York', TMapURL.Create('lga'));
letter city abbreviation as an argument. This abbreviation AddObject('Philadelphia', TMapURL.Create('phl'));
is stored in a private string, FCityAbbrev, which is used by AddObject('Phoenix', TMapURL.Create('phx'));
DisplayWeather to build the correct URL for the city’s AddObject('Pittsburgh', TMapURL.Create('pit'));
AddObject('San Francisco', TMapURL.Create('sfo'));
weather forecast. DisplayWeather does all the work of con- AddObject('Seattle', TMapURL.Create('sea'));
necting to the Web site, downloading the weather image, AddObject('St. Louis', TMapURL.Create('stl'));
then displaying it on the form: AddObject('Tulsa', TMapURL.Create('tul'));
AddObject('Washington, DC',TMapURL.Create('dca'));
end;
type TMapURL = class(TObject)
end;
private
FCityAbbrev : string;
public
Figure 11: The form’s OnCreate handler.
constructor Create(CityAbbrev: string);
procedure DisplayWeather;
end; used in a TImage, so it is translated in memory into a
bitmap (.BMP) before displaying it.
DisplayWeather defines a TMemoryStream (GIFStream), a
string (Buffer), and an integer (BytesRead ). These variables When DisplayWeather is executed, it creates the
are used to download the GIF image and convert it into a Netscape Navigator automation object by passing a
bitmap. The weather image is a GIF image that cannot be ProgID of Netscape.Network.1 to CreateOLEObject.
Next, the server’s Open method is called with the URL The demonstration projects referenced in this article are
of the city’s weather image. The URL for the image can available on the Delphi Informant Works CD located in
be easily constructed because its format is known. INFORM\96\MAY\DI9605JF.
Zeroes are passed as the remaining arguments to Open.
By Keith Wood
An HTML Generator
Use a Delphi Component to Build Your Web Site
uilding your own World Wide Web (WWW) site can be a daunting task.
B You need to understand HTTP, MIME, HTML, and possibly even CGI
[see the sidebar “Internet Definitions” on page 23]. However, by using the
encapsulation available in Delphi’s components, you can hide much of this
complexity from the novice user.
This article describes a component that browser. The advantage of this is that
allows us to generate HTML with a browsers can be implemented on many dif-
Delphi program, without an in-depth ferent platforms with various capabilities.
understanding of HTML. The compo-
nent, THTMLWriter, allows values from Let’s discuss the HTML elements that are
other sources — typically a database — to normally implemented on a home page.
be included in the documents produced,
creating truly up-to-date pages for display. Tags. An HTML document consists of a
straight text file with markup instruc-
HyperText Markup Language tions encoded within tags. These tags are
HTML is a language that describes how a delimited by angle brackets ( < > )
page will be presented. It’s interpreted by allowing them to be easily identified.
various browsers that display the page to Each tag has a name indicating its pur-
the user. HTML is not WYSIWYG since pose, and may have one or more attrib-
the final display is determined by the utes to control its function. Many tags
contain text or other tags, and some
<html> remain empty (i.e. they stand alone).
<head>
<title>Keith Wood's Home Page</title>
</head> To indicate the end of a particular tag, its
<body> name is enclosed in angle brackets as
<h1>Keith Wood's Home Page</h1>
<hr>
shown above, but with a slash preceding
<p>Welcome to your own home page.</p> the name. An example of a container tag is
<p>Text can be easily <strong>highlighted</strong> or the paragraph marker <p>, which matches
<i>italicised</i>. Special characters can be inserted :
©,Æ, é. Lists can be added :</p>
its closing version, </p>.
<ul>
<li>First list item. An example of an empty tag is the image
<li>Second list item.
<li>Third list item.
directive:
</ul>
<p>Include an image if you want :<img src="athena.jpg" <img ...>
border=0 alt="Demonstration image" align=middle></p>
<p>Links can also be added. This one
<a href="sourceb.htm">shows the code that produced
Figure 1 is a sample HTML document and
this</a>.</p> Figure 2 displays its appearance in a browser.
</body>
</html>
Head and Body. An HTML document is
Figure 1: A sample HTML document. The use of tags makes
divided into two parts: the head and
the code easily understandable. The result of this code is body. Among others, the head contains
shown in Figure 2. tags that provide information about the
document as a whole, including the document’s title <a href="sourceb.htm">shows the code that
and the base URL for this and related documents. The produced this</a>
These can be positioned relative to surrounding text Spacing, etc. Additional spacing within an HTML docu-
and resized if required. A text alternative to images is ment is ignored, unless it occurs within a preformatted
THTMLWriter consists of only four properties and many The sTags variable is a TStringList containing all the
methods to produce the various tags available in HTML. opening tags that haven’t been closed. This allows for
The properties are: warnings to be generated later when tags are closed out
Filename: The name of the file to which the generated of sequence. The try..except block ensures that the tag
HTML is directed. Filename defaults to is written to the output even when the warning in the
HTMLWRTR.HTM (in Windows, HTML files have the function is raised. It’s then re-raised to allow the gener-
.HTM extension). When changed, the previous file is ating program to see and handle this exception.
closed and the new one is opened, or created, ready for
output. The ParagraphEnd procedure first checks that the paragraph
Errors: We’ll discuss the Errors property in “Errors and tag is the next one that must be closed — an error is raised if
Warning.” it isn’t. ParagraphEnd must then remove the tag from the list
IncludeMIMEType: Set to True if the MIME header is of those open and write the result to the output. Note that
automatically included in the output. This would nor- the ending tag is written followed by an end-of-line, using
mally be done if the program is functioning in a CGI Writeln. Recall that spacing within an HTML document is
environment. generally ignored and that this is done merely to improve
Version: A read-only property that shows THTMLWriter’s the readability of the generated document. Thus, these
current version. methods allow us to write the following Object Pascal code:
To make it easier to create small paragraphs, two extra a negative value (with zero indicating that the default
methods are defined: the FormatParagraph function and the value be used).
Paragraph procedure. They combine the paragraph start and
end methods above with the specified text in between. This To avoid having to remember that we must negate the
allows us to generate an entire paragraph in one statement: value (and to reduce the confusion in the next person that
has to maintain our code) a function is provided with the
Paragraph('This text makes up the entire paragraph.', THTMLWriter component that converts the value for us.
ahDefault);
The Percent function takes a positive integer value and
returns its negative counterpart. The negative value is then
Some tags have many attributes, most of which are not nor-
decoded in HTML generation and produces the required
mally required. To make these tags easier to use, they can be
percentage value.
invoked in two formats: with all the available parameters
specified, or with just those parameters that are most com-
Thus, we can write the following code to have the image
monly used (including none).
fill the entire browser:
An example of this is the img tag that displays an image in ImageParams('athena.jpg','Athena',EmptyStr,aiDefault,
the document. In its full format, img takes 10 parameters, Percent(100),Percent(100),0,0,0,False);
of which three are common. Thus, one method,
FormatImageParams, is defined that takes all the parameters These relative/absolute values are also used in horizontal
and processes those that are not default values. The shorter rules, marquees, and tables.
version, FormatImage, simply calls the full one, passing
across those parameters that have been supplied and send- Colors in HTML are represented differently than those in
ing default values for the remainder (see Figure 4). Delphi. To overcome this, the processing converts from
Delphi TColor values to a format suitable for HTML. This
Doing it this way means that the actual processing is only format is #RRGGBB where RR is the hexadecimal notation
done in the one place, making maintenance easier, with for the red component, GG for the green, and BB for the
changes to the full version being immediately reflected in blue. Thus clYellow is converted to the string representa-
the shorter version. Other tags that have similar versions tion '#FFFF00'. All this is hidden behind the scenes and
include the body, the horizontal rule, lists, and tables. need not concern us.
With a polygon, a list of x and y coordinate pairs define non-active values. These characters are the angle brackets
the shape, with the last pair combining with the first. ( < > ), ampersand ( & ), and quotation mark ( " ). Two
methods are provided to perform this: FormatEscapeText,
To handle this in THTMLWriter we need to pass across a which returns the string in escaped form, and EscapeText,
variable number of parameters. Fortunately, this can be which writes the escaped text to the file.
done in Object Pascal by declaring the parameter as an
array of values, integers in this case. Normally an array’s Similarly, lists of strings can be written directly into the
size must be declared, but by omitting the array size in the document or can be escaped first with the TextList and
declaration, we can pass arrays of different sizes to the one EscapeTextList methods. Note that these methods can be
routine (provided that the elements are of the correct type): used to include any HTML constructs that have not been
covered in the rest of THTMLWriter. Simply build the
iCoords: array of Integer; required syntax in a string (or list of strings) and then
write it directly into the generated document.
This is known as an open array. We can then process the
array elements as necessary. To determine the number of The InsertFile method allows large sections of HTML or text
elements in the array we use the High function. It returns to be copied into the generated document. No escaping of
the number of items less one, since the elements are num- characters is performed by InsertFile as it is assumed that the
bered from zero. Figure 5 shows the implementation of document has already been processed in this way if necessary.
this construct. Note that this can be extended to cater to a This allows common sections of documents to be maintained
variable number of variable type parameters. in one place and used by many documents. Note that the file
is inserted as static text. (We’ll discuss allowing for dynamic
Straight Text replacement of values in the section, “HTML Templates.”)
Straight text can be added to the generated document with
the Text method, which simply writes the supplied value The Initialise method resets all internal variables that are
directly to the file. If the text contains any of the reserved used in checking relationships between tags. This should
characters, then these must be “escaped”, i.e. converted to be called before any HTML is generated to ensure that
errors are not introduced from any earlier processing. <p>The footer for the page comes from another file.</p>
The Finalise method checks that all the open tags have <p>View the <a href="sourcem.htm">source code and
templates</a>.</p>
been correctly closed, before writing the terminating <hr>
HTML tags (unless a file insertion or merge was per- <p><font size=-1>Generated by the THTMLWriter
formed) and then closing the output file. Finalise should component.</font></p>
</body>
be the last thing called in generating the document.
This technique can be used for common sections of
Figure 6 presents a complete list of the methods available
HTML, allowing them to be maintained in a single place
in THTMLWriter. Procedures are listed on the left and
while having their effect apply to the entire site. Examples
their corresponding functions (where applicable) are dis-
of their use are the body tag, allowing a common color
played on the right. Each set of procedures and functions
scheme to be applied in document footers and in toolbars.
are grouped by purpose.
Microsoft Internet
http://www.microsoft.com/intdev/browser/iexplore.htm
Explorer Extensions
CGI http://hoohoo.ncsa.uiuc.edu/cgi/
WinCGI http://www.city.net/win-httpd/httpddoc/wincgi.htm
current date and time and a random number between 1 Combine THTMLWriter with a CGI component and you
and 100. A second document is included in the first, have a Web site just waiting to happen. All from our
showing how common sections can be maintained in familiar Delphi environment.∆
one spot only and used in many documents as required.
The THTMLWriter component and demonstration project
(Note that the demonstration program runs best outside referenced in this article are available on the Delphi
the Delphi IDE. Otherwise, exceptions that are trapped Informant Works CD located in INFORM\96\MAY\-
internally appear and disrupt the flow of the program.) DI9605KW.
Conclusion
The THTMLWriter component allows us to generate
HTML on-the-fly. It provides access to most of the fea- Keith Wood is an analyst/programmer with CSC Australia, based in Canberra. He
tures of the various HTML versions, along with many started using Borland’s products with Turbo Pascal on a CP/M machine. Although
not working with Delphi currently he has enjoyed exploring it since it first
Netscape and some Microsoft Internet Explorer exten- appeared. You an reach him via e-mail at [email protected] or by phone
sions. It detects many errors and warns us of possible (Australia) 6 291 8070.
problems while generating our pages.
By Ray Lischner
Virtual or Dynamic?
An Examination of Object Pascal’s Virtual Methods
Figure 4 illustrates the VMTs for the example classes. A Nonetheless, it is a simple matter to hide these differ-
VMT contains two parts. The first part contains the ences in some simple functions, providing portable
instance size and several pointers: to other tables, to the access to the VMT.
class name, to the VMT for the base class, and to the vir-
tual methods defined by TObject. The second part of the Every object has a pointer to its class’ VMT. This pointer
VMT is a table of code pointers for all the virtual meth- is stored as the first field of the object. The ClassType
ods defined by the class and its base classes. method returns a pointer to the second part of the VMT.
To access the first part, you need to subtract the VMT size
In Delphi 1, the “pointers” in the first part of the VMT from the ClassType pointer.
are near pointers: offsets into the code segment that
contains the methods for the class. In Delphi 2, the To access the second part of the VMT, simply treat the
pointers are 32-bit pointers. Thus, the VMT is a differ- pointer value that ClassType returns as a pointer to an
ent size in Delphi 1 and Delphi 2, and using the point- array of code pointers. The compiler assigns a number to
ers is different in Delphi 1 and Delphi 2. That’s one a virtual method; this number is an index into the VMT
reason why Borland does not document these details. pointers. A call to a virtual method, therefore, is very
They are free to change the VMT implementation simple and quick: just index into the VMT, and call that
without dealing with backward compatibility issues. method. Figure 5 shows the equivalent Pascal code.
Figure 5: Pascal equivalent to calling a virtual method. has a special instruction (SCAS) that scans a sequence of
numbers, looking for a specific number.
The Dynamic Method Table
Dynamic methods are also assigned numbers, but the Figure 7 shows the equivalent Pascal code for looking up
numbers are not indexes into tables. Instead, a Dynamic and calling a dynamic method. The code depends on sev-
Method Table (DMT) is a sparse table. Each entry consists eral routines that get information out of the VMT and
of a method number and a method’s code pointer. A call DMT. These routines are included in the full code listings,
to a dynamic method is compiled into a search through in the VmtInfo unit.
the DMT for a matching method number. If the method
number cannot be found, the search continues with the Why Dynamic Methods?
parent class’ DMT. Dynamic methods have an advantage over virtual meth-
ods because a DMT does not contain any entries for
The search for a dynamic method is performed by a spe- ancestor classes. This means DMTs can be much smaller
cial subroutine which is written in assembly language for than VMTs. Adding a virtual method to a base class has a
maximum speed. Looking up a dynamic method multiplicative effect: the number of virtual methods times
requires understanding the format of the DMT. Borland the number of base classes.
does not document the format of the DMT, so let’s take
some extra time to understand what the DMT looks Conversely, dynamic methods avoid the multiplicative
like. Figure 6 depicts the DMTs for the example classes. effect of virtual methods. Changing one method from vir-
tual to dynamic might eliminate hundreds of VMT
entries, while adding one DMT entry. The downside of a
DMT is that searching for a dynamic method entry is
slower than looking up a virtual method.
Elysian Fields
Leveraging the Delphi 2 Fields Editor
elphi 2, the new 32-bit version of Delphi, has been available for a
D couple of months now. And although you might have expected this
version to be little more than a 32-bit port of the existing product, it is
much more. Delphi 2 contains so many new features that it represents a
major leap forward for this already revolutionary product.
Fortunately for database developers, many to the table). Likewise, the Table is defined
of the enhancements are database related. by setting its DatabaseName and TableName
This month’s “DBNavigator” takes a closer properties. In the data module shown in
look at one of these enhancements, the Figure 1, the DatabaseName has been set to
new Fields Editor. This powerful tool will DBDEMOS (an alias installed by Delphi), and
be demonstrated by creating several simple the TableName to CUSTOMER.DB.
data entry forms.
As mentioned earlier, you now can add the
In Delphi 2 this will almost always start data module unit’s name to the uses clause
with creating a data module. for any unit that needs to access the
DataSource or Table. This can be done
The Data Module manually by typing in the uses clause, or
As you learned in last month’s column, the by selecting File | Use Unit from the form
data module is a form-like object on which that needs this access. There is also a third
you place data access components. way in which Delphi automatically adds
Specifically, you place your DataSource this unit to the form’s uses clause — a tech-
and DataSet (Table, Query, and nique that employs the new Fields Editor.
StoredProc) components on a data mod-
ule. These components can then be made The Delphi 2 Fields Editor
available to any form in your application The Fields Editor is a tool you use to
by adding the data modules’ units to the instantiate (create) TField components for
form unit’s uses clause. the fields of a DataSet. In addition, the
Fields Editor permits you to create calcu-
An example of a data module is shown in lated fields (fields whose read-only con-
Figure 1. It contains a DataSource and Table tents are based on a calculation), as well as
component. Just as you would on a form, lookup fields (fields that display associated
you link the DataSource to the Table by set- data from another DataSet).
ting the DataSource’s DataSet property to
Table1 (or whatever name you have assigned The Fields Editor in Delphi 2 is similar in
several respects to that found in Delphi 1.
However, it sports a slimmed-down interface,
Figure 1: A
data module
as well as a number of powerful new features.
including a
DataSource and To access the Fields Editor, right-click a
a Table. DataSet component and select Fields Editor
from the SpeedMenu displayed, or simply
Figure 8: If
you select
more than one
field in the Figure 10: Use the New Field dialog box to create Calculated
Fields Editor, and Lookup fields. The dialog box in this image is being used to
the selected define a lookup field.
fields can be
dropped onto
a form in a
it is not necessary to instantiate all, or even any, of the
single drag- fields in a DataSet to create a lookup field.) We can now
and-drop create the lookup field. Right-click the list box in the
operation. Fields Editor and select New. Delphi responds by display-
ing the New Field dialog box (see Figure 10).
The lookup field, however, is new with Delphi 2. A
lookup field is a field in one DataSet that displays data The New Field Dialog Box
from another DataSet, based on an association between At Name, enter the name of the field you are creating. For
the two. For example, the ORDERS.DB table in the this example we will use the name CustName. As you type
DBDEMOS directory has a field named CustNo that the field name, the name of the TField component that
contains the customer number the order is associated will be created is entered for you in the Component box.
with. The table does not hold the customer name — You can change the name of the TField component, but
this information is stored in the CUSTOMER.DB this is rarely necessary.
table (specifically, in the Company field of the CUS-
TOMER.DB table). By creating a lookup field for the In the Type field enter or select the type of field. In this
ORDERS table, you can define a new field that dis- example, customer name is a string field, so we enter
plays the customer’s name based on the customer num- String here. You then use the Size field to define the
ber. The lookup field performs the task of “looking up” size of the field. This is only necessary with String,
the customer’s name in the CUSTOMER.DB table and Bytes, and VarBytes fields. In this case we will enter 30,
displaying it. which is the size of the Company field in the CUS-
TOMER.DB table.
This technique is demon-
strated by creating a cus- You use the radio buttons in the Field type group located in
tomer name lookup field for the center of this dialog box to define the type of field you
the ORDERS.DB table. The are creating. Select Lookup. Doing so enables the fields in
first step is to create a data the Lookup definition group of the New Fields dialog box.
module that contains two
DataSets, one for the table Figure 9: A data module You use the fields in the Lookup definition group of this
we will create the lookup for the example form. dialog box to identify the association between the cur-
field in, and one for the rent table and the lookup table. Begin by setting Key
lookup table (see Figure 9). It contains one DataSource Fields to the field in the current table that has a counter
and two DataSets. The DataSet property of the part in the lookup table. The value of this field is used
DataSource is set to Table1. The DatabaseName proper- to perform the lookup. In this example, select the
ties of both Table1 and Table2 are set to DBDEMOS. The ORDERS.DB field named CustNo, the identifier for
TableName property of Table1 is set to ORDERS.DB, and the customer.
the TableName property of Table2 is set to
CUSTOMER.DB. The Active property of both of these While the Key Fields drop-down menu only includes single
Tables has been set to True. fields, it is possible to define a lookup based on more than
one field. To do so, you must manually enter the fields
The next step involves the Fields Editor. Here all fields are that associate the two DataSets, separating the field names
instantiated, as described earlier in this article. (Note that with semicolons.
By Andrew Wozniewicz
ver the last two months, we’ve introduced Delphi as an environment for
O developing dynamic link libraries. Now it’s time for us to create a corre-
sponding import unit for the custom DLLFirst library we’ve created. [To create
the DLLFIRST.DLL, see Andrew Wozniewicz’s articles “DLLs: Part I” and “DLLs:
Part II” in the March and April issues of Delphi Informant.]
Creating the Import Unit unit. The only missing link is the ability to
Let’s develop a test application that will tell the using application which subroutine
use the subroutines implemented in the listed in the unit’s interface corresponds to
DLLFirst unit. Follow these steps: the appropriate entry points of a DLL.
Create a new, blank Delphi project and
call it DLLTest (DLLTEST.DPR). Save To delegate the details of the implementa-
the main form unit of the new project tion of a subroutine to an external module,
as FRMFIRST.PAS. and to allow the program to compile with-
Select File | New Unit. A new, minimal out actually “seeing” the subroutine’s imple-
unit file is created by Delphi. This will mentation directly, the external directive is
be the import unit that enables your used. This replaces the implementation of a
application to use the subroutines in subroutine, indicating that it’s given outside
DLLFIRST.DLL. the current project. The external directive is
Save the new unit file as FIRST.PAS. placed after the implementation heading of
a subroutine. For example:
Interfacing the DLL
procedure Clear; external 'CUSTOM';
The interface section of the library import
unit appears identical to the interface sec- function StripStr; external 'DLLFIRST';
tion of any regular unit. It simply lists the
subroutines available, which in this case, The external directive completely
are the subroutines exported by the DLL. replaces the implementation of a subrou-
tine. Instead of the familiar begin..end
The difference between an import unit keywords and the implementation code
and a regular unit lies in its implementa- inside the block they delimit, the exter-
tion. Whereas the implementation section nal directive indicates that the imple-
of a regular unit must provide the actual mentation code is compiled and deployed
implementation code, the implementation separately, outside the current project.
section of a library import unit delegates
the implementation’s details to the exter- Several possibilities can follow the external
nal library. It merely binds the subroutine keyword. The simplest of these calls for the
headings declared in the interface to the library’s file name, — DLLFIRST — in this
implementation provided elsewhere. case, to be included after the directive. This
enables the compiler to bind the subroutine
Remember that we already have implement- declared inside the import unit to its imple-
ed the string-handling subroutines inside mentation that resides in a DLL by the
the DLLFirst library. There is no point declared name. When giving the name of
repeating that code in the library import the external library in this way, a file name
implementation
function StripStr; external 'DLLFIRST';
const
indicates that the implementation for the StripStr function LibName = 'DLLFIRST';
resides in the external library DLLFIRST.DLL. Windows function FillStr; external LibName;
automatically loads DLLFIRST.DLL and searches for a function UpCaseFirstStr; external LibName;
subroutine StripStr that matches the requested function by function LTrimStr; external LibName;
function RTrimStr; external LibName;
name to resolve the call. function StripStr; external LibName;
Interfacing DLLFIRST Remember that recompiling the import unit does not
Based on the comments provided in our previous articles, cause the DLL itself to be recompiled. The two entities are
you’re ready to provide the interface unit for the DLLFirst totally separate. In fact, it’s possible to create discrepancies
library. The good news is that the declarative part of the between the two entities. For example, you can declare the
unit (the subroutine headings that you need to list there) same subroutine with a different number of parameters on
are nearly identical to those we entered within the interface either side. This leads to potentially serious bugs that can
of the XString unit. Figure 1 implements a library import be difficult to find. So be forewarned: Always double-
unit for the DLLFirst library we implemented previously. check that the subroutine declarations inside the import
unit correspond exactly to those declared in the DLL.
The interface section provides declarations of the subrou-
tines imported from the external library. Note that the func- The import unit in Figure 1 is a typical example of a simple
tion headings are (and in fact, must be) identical to those in import unit, using the implicit binding. Implicit here
XSTRING.PAS, with the exception of the export directive, means that we don’t have to do anything special for the calls
which is missing in the import unit. The import unit does to any of the listed subroutines to be resolved automatically
not implement these functions, but instead provides a way at run time — beyond declaring them with the external
of importing their implementations from an external DLL. directives as shown. Windows handles the details of when
Therefore, an export directive is unnecessary here. and how to load the external library and how to obtain the
actual run-time addresses of the subroutines you are calling.
The implementation section — instead of providing the
implementations of the functions listed in the unit’s inter- We’ve already taken the first step toward creating the unit in
face — provides their external bindings that enable Figure 1. Namely, we’ve created the FIRST.PAS import unit,
Windows to find the appropriate implementation at run whose function is to import implicitly the subroutines from
time. In addition, we can see the implementation headings the DLLFirst module. Complete the library import unit,
for each of the imported subroutines, followed by the FIRST.PAS, by entering the code as shown in Figure 1.
external directives indicating the library from where the Make sure that you save the unit file before proceeding.
functions will be imported.
Creating the Example Program
Note that you have a great deal of flexibility in how We’re now ready to try the external subroutines we’ve cre-
you can specify the library’s name. In Figure 1, the ated and imported via the FIRST.PAS import unit. To
name of the library is provided as a string constant so visualize the results returned by the various external func-
that the constant name is repeated with the external tions, let’s create a simple form-based Delphi application.
directives, and the constant itself is defined. This Follow these steps:
approach makes it easy to change the name of the 1) Select the FrmFirst unit and bring the Form Designer
external library because the constant declaration is the window to the foreground.
Delphi Informant May 1996 35
Dynamic Delphi
2) Inside the Form Designer, place two copies of the Edit unit FrmFirst;
component. These are found on the Standard page of
interface
the Component Palette. Place Edit1 and Edit2 (as they
are named by default) one below the other. Clear their uses
Text property so that they appear blank on the form. SysUtils, WinTypes, WinProcs, Messages, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls;
3) Change the ReadOnly property of the second edit box,
Edit2, to True. Change its Font.Color property to clRed. type
4) From the Standard page, select a Label component and TForm1 = class(TForm)
Edit1: TEdit;
place it on the form between the two Edit compo-
Edit2: TEdit;
nents. By default, the Label’s name is Label1. Change Label1: TLabel;
the Label’s AutoSize property to False, and extend the ButtonUpCaseFirst: TButton;
ButtonLTrim: TButton;
enclosing box so that it accommodates the names of
ButtonRTrim: TButton;
the external subroutines. Clear the Label’s Caption ButtonStrip: TButton;
property so that it initially appears blank. procedure ButtonUpCaseFirstClick(Sender: TObject);
procedure ButtonLTrimClick(Sender: TObject);
5) Place several Button components on the form, one for
procedure ButtonRTrimClick(Sender: TObject);
each of these four functions: UpCaseFirstStr, LTrimStr, procedure ButtonStripClick(Sender: TObject);
RTrimStr, and StripStr. Rename the buttons (named by private
{ Private declarations }
default Button1, Button2, etc.) to ButtonUpCase,
public
ButtonLTrim, ButtonRTrim, and ButtonStrip, respective- { Public declarations }
ly. Change the Caption property of each button to end;
reflect the functions they represent: UpCaseFirst,
var
LTrim, and so on. Figure 2 shows the completed main Form1: TForm1;
form of the example program.
implementation
uses
First;
{$R *.DFM}
Figure 5: A modified library import unit for the example DLL- So far, we’ve learned the techniques involving import
FIRST.DLL using the “import by ordinal” feature. units where the subroutines to be imported are identi-
fied with the external directive. Import units enable
This library import unit is functionally equivalent to the subroutines in DLLs to be imported implicitly; the
the import unit shown in Figure 3. The difference here process of loading the library and resolving the address
is that this modified import unit renames the imported of the subroutine at run time is automatic and trans-
functions so that they are known to the using applica- parent to the programmer using the DLL. It is the
tion under different names instead of their declared responsibility of Windows.
names inside the DLL that implements them.
The implicitly loaded DLLs usually are loaded by
This trick is possible thanks to the “import by ordinal” Windows at the time the application starts. If any of
feature of Windows. This enables us to bind a subroutine the DLLs imported implicitly are missing or simply
prototype inside an import unit to an arbitrary external cannot be found, the application fails to load. The
subroutine. The external declarations assume that the sub- application never gets a chance to correct the problem
routines were exported by the specified ordinal numbers by prompting the user to specify the location of the
from the DLL. required files and changing to the indicated directory,
for example. Windows prevents the application from
Note that the changes to the import unit do not affect the loading before it can respond to user events.
signatures of the imported functions: their parameter lists
and return types remain identical to those in Figure 3. Another serious drawback to using an implicit import is
that the name of the DLL has to be hard coded, or fully
This is understandable once you realize that we are known at compile time. As you may recall from the earlier
importing the same subroutines as before, implemented sections, the name of the implicitly imported DLL must
inside the DLLFirst library. The signatures of these sub- be a string constant.
routines did not change just because we chose to import
them in a different way. Fortunately, there is a way to gain an even greater con-
trol over the process of loading DLLs. Instead of
Exporting by Different Name importing the required subroutines implicitly via an
The flexibility of choosing a different name for an import- external statement, you can take the responsibility for
ed subroutine does not depend on it being exported by an loading the DLL into memory and retrieving the
ordinal number. It’s still possible to import a subroutine addresses of the subroutines inside that DLL explicitly.
by a name different than the export name specified within This way, you can defer loading the library until you
the DLL. can determine at run time what the name of the library
is and where it is located.
Correspondingly, it is possible to export a subroutine from
a DLL under a different name than the name declared in Next month, we’ll conclude our series on creating DLLs
the source code. Both these feats are accomplished with with Delphi. ∆
the name directive.
By Craig L. Jones
s every programmer knows, much can go wrong with even the sim-
A plest of programs. Worse yet, the number of possible problems grows
geometrically with the size of an application. The good news is that just a
little quality assurance (QA) savvy can go a long way towards heading off
disaster. As with any discipline, the fastest route to success lies in acquiring
the proper tools of the trade and learning how to use them correctly.
This is the third installment of a three-part these are tools no serious software devel-
series on assuring the quality of Delphi oper should be without.
programming projects. The series is pri-
marily directed towards Delphi program- (Please note that this article only describes
mers and assumes no prior knowledge on these tools in terms of general concepts,
the subject of QA. It is hoped that pro- and does not endeavor to name any partic-
grammers who previously gave quality ular products or vendors. An effort was
assurance little thought will discover some made to seek out commercial products
enthusiasm for applying the techniques with Delphi-specific features worthy of
presented here. mention. However, this author was not
made aware of any such tools in release as
The emphasis of this series has been on of the time of this writing. Perhaps the
exploring practical techniques for quality upcoming Software Testing Analysis and
assurance. In the previous installments, Review trade show, “STAR ’96,” will fea-
some general QA theory was presented ture Delphi-specific products. In any case,
along with how it applies to the different the May 15th event in Orlando, FL, will
stages of program development. Eight certainly provide ample opportunity to
types of testing were outlined: require- explore the offerings of all of the major
ments verification, design validation, unit players in the software quality assurance
testing, integration testing, user accep- industry. Call (800) 423-8378 to inquire
tance testing, alpha testing, beta testing, about the US$30 exhibits-only admission,
and gamma testing. And a tool kit was or to check on the possibility of obtaining
established for performing unit testing, an exhibitors guide without attending.)
probably the most important of the eight
types of testing. User Action Automation Tools
User action automation tools, the most
Testing Tools common type of commercially available
In this installment we’ll go on a whirl- testing tools, work by watching the events
wind tour of several other types of testing that occur as a tester runs through the
tools that are available, and some tech- execution of a program being tested. The
niques for using them effectively. Some key stroke events, mouse events, and
of these tools are commercially available, Windows messages are recorded in a
while others need to be developed specifi- script that can be played back at high
cally for the project at hand. In all cases, speed to repeat the test at will. Hundreds
Conclusion
According to Dr Dennis Waitley, “If you fail to plan, then
by default, you plan to fail.”
By David Rippy
I f one advances confidently in the direction of his dreams, and endeavors to live the life
which he has imagined, he will meet with a success unexpected in common hours.
— Henry David Thoreau
How can I tile a bitmap image on my form? code in the form’s OnPaint event handler,
Here’s a cool trick that you can implement the bitmaps are repainted appropriately if
on any Delphi form to give it a visual the form is moved or resized.
edge. With just a few lines of code, you
can tile a bitmap of your choice onto the Bitmaps of any size can be used, but you
form’s canvas as shown in Figure 1. typically want to use something small
(40x40 pixels or so). Also, it’s important to
select an image that tiles well. Appropriate
selections will appear to be a single, large
image when they are placed adjacent to one
another. The nerd bitmap in Figure 4 is an
example of an image that does not tile well
— plus your clients may not appreciate the
humor. — D.R.
contains a list of all the components Quick Tip: Delphi 2 Trim Functions
owned by an object. In our case, we Delphi 2 includes three new functions (defined in the
want to know all the components SysUtils unit) that allow you to trim white space with a
owned by the form. The minimum of effort: Trim, LeftTrim, and RightTrim. Each
ComponentCount property tells us of these functions accepts a single string as a parameter,
how many components are owned and returns the trimmed string.
by the form. Once we have the
number of components and their Trim removes both leading and trailing “white space” and
names, a for loop is used to iterate control characters from a string. LeftTrim and RightTrim
through the CheckBoxes, setting Figure 5: How remove white space and control characters from the begin-
each Checked property to True. can we access all
ning and end of a string, respectively.
six CheckBoxes
without hard-cod-
You could use code such as this to ing their names? Here’s an example of the Trim function:
perform just about any type of oper-
ation on a set of objects. For instance, you could change Edit3.Text := Trim(Edit1.Text) + ', ' + Trim(Edit2.Text)
W ewetalked last month about the decentralization of the computer world and its implications for Web development. This month
continue that theme by looking at a technology that deals with decentralization within the database realm.
Corporate data in the 1990s is moving bandwidth and high cost make this idea marketplace. However, as robust as the
down and out. The popularity of unattractive to many. Web technology offers current offerings of database vendors are,
client/server architecture has certainly been a possible alternative to WANs for a central- they are difficult to administer (especially
a driving force in this decentralization of ized solution, but as we discussed in last Oracle) and remain fairly strict and nar-
data. Not only are PCs replacing main- month’s “File | New,” Web applications are row in terms of how you can implement
frame terminals, people are doing more not a client/server panacea. A third alterna- them. First, not all products support all
with company data through powerful tive, commonly referred to as the “two- forms of replication. Oracle7 is the only
query and analysis tools on the front-end. phase commit,” has largely been considered major database to support the “update
Additionally, data is moving increasingly a failure. A common element of these three anywhere” model; in contrast, Microsoft
outward rather than being stored and approaches is that they require a “live” link SQL Server and Sybase support a primary
maintained at a single site. Geographically somewhere in the process in order for users site model only. Second, while Oracle and
dispersed companies that have already to actually work with data. However, syn- Sybase have solutions for notebook-to-
invested in client/server technology are chronous communication is not only server replication, Microsoft SQL Server
seeking solutions that can deal with dis- impractical in certain contexts, it also raises currently remains limited to a server-to-
persed data sets. Perhaps as important, the cost, bandwidth, and reliability concerns. server model (although Microsoft should
popularity of notebook computing has cre- Replication is thus proving to be more cost have a desktop solution before the end of
ated a demand by a mobile work force to effective, faster, and more reliable. the year). Third, when you buy into any
work with data remotely and periodically of these solutions, you are limiting your-
synchronize with their office. These trends Synchronizing Data. A fundamental issue self to a vendor-specific solution; none of
point out that there are more people doing you face with replication is how dispersed these products currently support replica-
more with data — data that is spreading data can be synchronized when data is tion across multiple vendor databases. The
out to more places. exchanged from one site to another. There third party replication products on the
are two approaches. First, a primary site (or market currently do not help much either;
For developers, the task of designing “publish & subscribe”) scheme has a single they fail to pack the power or speed you
client/server applications that can keep up site as the owner of the data; this site pub- need for many real world situations.
with these trends is proving difficult. For lishes the data to subscriber databases that Therefore, as much as replication technol-
many, the solution is fast becoming replica- have only read-only access to the original ogy has matured, there are holes remain-
tion. Asynchronous replication (sometimes data. Second, peer-to-peer (or “update any- ing in the marketplace. Given these limi-
referred to as “store and forward”) is a tech- where”) replication allows data to be updat- tations, developers continue to find the
nology that allows you to maintain a single ed at more than one site at the same time. need to develop custom solutions. We’ll
set of data among two or more separate While the latter approach is much more look at some of the issues involved with
locations. To achieve synchronization, repli- flexible, it also leaves open the possibility of designing your own replication scheme in
cation tracks updates made to a specific data “colliding records” (records that are simulta- a future “File | New.”
set and then ensures these changes are neously updated in two or more sites).
shared with other replicated databases. These collisions can be risky. Conflicts have Balancing Act. The early days of PCs brought
Replication is emerging as the most impor- to be resolved in an efficient manner, but users to computers in an effort to increase
tant database technology in the mid-1990s. not even a sophisticated conflict resolution productivity. Today, we are doing just the
While server-to-server replication is nothing scheme can deal with the non-database opposite: moving the computers out to the
new, what is becoming evident is the fact actions taken after the data is committed users. The challenge we have as application
that replication will eventually emerge as a locally, but before it is rejected during syn- developers is to support this flexibility while
technology for the “masses,” not just the chronization. Warnings aside, I am discover- maintaining the data integrity of a centralized
Fortune 500. Smaller firms and departments ing that many companies are demanding an environment. Replication can be a solution to
can employ replication to meet a variety of “update anywhere” solution, simply because this dilemma, but be sure to use caution as
needs unthinkable just a few years ago when it is the “real world” way in which their this technology continues to evolve.
floppy disks and ZIP files were the principle business works.
means of refreshing data. — Richard Wagner
Shrink-Wrapped vs. Custom Solution.
Dealing with Distributed Data. Replication Replication support is becoming as ubiq-
is not the first nor only approach to dealing uitous to SQL servers as spell checkers are Richard Wagner is the Chief Technical
with distributed databases. Many companies to word processors. Not just a value added Officer of Acadia Software in Boston,
have a centralized database operating over a feature, it is now becoming essential to MA. He welcomes your comments at
Wide Area Network (WAN), but available being competitive in the SQL database [email protected].