Professional Documents
Culture Documents
ON THE COVER
5 On the ’Net 30 Columns & Rows
Using SOAP — Keith Wood Using the XML Features of SQL Server 2000:
Keith Wood introduces SOAP, its uses and importance, then shares a Part I — Alex Fedorov
Delphi SOAPDispatcher component you can simply drop onto a data Alex Fedorov describes the XML features of SQL Server 2000 — the
module, connect to a SOAP parser, and compile. SELECT statement’s FOR XML clause in particular. It’s the start of a
series explaining how to leverage these capabilities using Delphi.
FEATURES REVIEWS
11 On Language 34 ModelMaker Code Explorer
Console Applications: Part I — Mike Edenfield Product Review by Robert Leahey
Console applications get no respect, but that’s just because they’re
misunderstood. Mike Edenfield kicks off a multi-part series to dispel 37 Protection PLUS
the myths and extol the virtues of these useful non-GUI apps. Product Review by Warren Rachele
40 Cryptography and e-Commerce
16 Inside OP Book Review by Mike Riley
Procedural Types — Rick Spence
Rick Spence shines a light on procedural types, “the most underused 41 The Official InstallShield for Windows Installer
type in Object Pascal.” Delphi itself uses them for event handlers. You Developer’s Guide
can use them to parameterize the logic of a subroutine. Book Review by Bill Todd
42 Red Hat Linux 7 Server
21 OP Tech Book Review by Bill Todd
A Little Help — Bill Todd
Delphi provides you with all the tools to easily add Windows Help 42 Designing Web Usability
to your applications, then fails to adequately document how to use Book Review by Mike Riley
them. Bill Todd steps into the breach with a help-full demonstration.
DEPARTMENTS
25 Kylix Tech 2 Delphi Tools
Shared Objects — Rick Ross 43 Best Practices by Clay Shannon
Delphi makes it easy to create Linux shared libraries (AKA shared 44 File | New by Alan C. Moore, Ph.D.
objects), but they can still be tricky. Rick Ross explains SO versioning,
compiler directives, calling conventions, exception handling, and more.
By Keith Wood
Using SOAP
Cleaner Remote Access with the
Simple Object Access Protocol
T he Simple Object Access Protocol (SOAP) is, according to the spec, a “lightweight
protocol for exchange of information in a decentralized, distributed environment.”
Based on XML, it defines a messaging framework for use in a distributed system.
The SOAP specification is currently a W3C Note messages can be combined to provide a request/
dated 8 May 2000. As such, it is intended for response pattern.
discussion only, although several organizations
— including Borland and Microsoft — are pro- The basic structure of a SOAP message is shown in
moting its use. It details the contents of the mes- Figure 1. An Envelope main element contains the
sages that are sent between distributed objects, message. Its namespace must be as shown, because
describes an encoding scheme for representing this value defines the version of SOAP in use, and
data within those messages, and sets out a con- a SOAP server must reject messages for versions
vention for using these to facilitate remote proce- it doesn’t recognize. Another optional attribute of
dure calls and responses. the Envelope may define the encoding scheme used
for data within the request. It should appear like
SOAP can be used with a variety of underlying the following, which denotes the standard SOAP
transport mechanisms; however, only HTTP is dis- encoding based on the XML Schema specification:
cussed in the specification. Using HTTP lets us
establish a SOAP server as a Web application and SOAP-ENV:encodingStyle=
communicate with it through the standard port. "http://schemas.xmlsoap.org/soap/encoding/"
This overcomes problems in opening other ports
for a more specialized connection. An optional Header element may appear as the
first child of the Envelope (and nowhere else). It
Introducing SOAP surrounds any number of elements that contain
SOAP messages are simple XML documents with a items of “global” interest to the main request.
defined structure and content that depends entirely For example, it may define a transaction to use
on the application. Although basically a one-way in database accesses. Each element in the header
transmission from one object to another, SOAP may have a SOAP-ENV:mustUnderstand attri-
bute that’s set to 0 (False, the default), or 1
(True). When True, the server must acknowledge
<?xml version="1.0"?>
and process this header element, because it’s
<SOAP-ENV:Envelope
xmlns:SOAP-ENV= assumed to affect the request in some fundamen-
"http://schemas.xmlsoap.org/soap/envelope/"> tal way. If it cannot be actioned, an error must
<SOAP-ENV:Body> be generated.
<findMovies>
<rating>PG-13</rating>
</findMovies>
The Body element contains the actual requests,
</SOAP-ENV:Body> formatted as child elements. Parameters for each
</SOAP-ENV:Envelope> request appear as children of that element.
Such elements may have arbitrary types (defined
Figure 1: A SOAP message (request). through an attribute) that conform to the encod-
Code Meaning
VersionMismatch The namespace on the SOAP Envelope is { Event signature for SOAP headers. }
unrecognized. TSOAPHeaderEvent = procedure(Sender: TSOAPDispatcher;
Name, Value: string; Attrs: TStrings;
mustUnderstand A header element with its mustUnderstand
MustUnderstand: Boolean;
attribute set to 1 is not understood, or
var Understood: Boolean) of object;
cannot be processed.
Client The message body is badly formed, or { The customised Web dispatcher for SOAP requests. }
does not contain enough information to TSOAPDispatcher = class(TCustomWebDispatcher)
process it. Some change is required to the private
body before the message can be re-sent. FName: string;
Server The request could not be processed due to FOnHeader: TSOAPHeaderEvent;
problems in the server. The message itself is FResponses: TList;
FSOAPFault: ESOAPFault;
fine and may succeed if re-sent at a later time.
FSOAPActions: TSOAPActions;
Figure 3: SOAP fault codes. FSOAPParser: TCustomSOAPParser;
procedure ClearResponses;
protected
ing scheme established in the envelope. Additional elements may function GetContent: string; virtual;
appear following the Body. procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
procedure SetSOAPFault(Fault: ESOAPFault);
Once a SOAP application processes a request, it sends a response procedure SetSOAPParser(Parser: TCustomSOAPParser);
in the form of another SOAP message. Again we have the SOAP procedure SOAPAction(Sender: TObject;
Envelope and Body tags with appropriate namespace declarations. Request: TWebRequest; Response: TWebResponse;
Within the body we have an element that contains the result of var Handled: Boolean); virtual;
public
the request. By convention, this element is named the same as the
constructor Create(AOwner: TComponent); override;
request element with the string “Response” appended. It surrounds destructor Destroy; override;
the elements that provide the actual reply. Figure 2 shows a possible property Content: string read GetContent;
response to the message in Figure 1. property SOAPFault: ESOAPFault
read FSOAPFault write SetSOAPFault;
procedure DoHeader(Name, Value: string; Attrs: TStrings;
If some error condition arises, a response is still sent, but instead it MustUnderstand: Boolean; var Understood: Boolean);
contains a SOAP-ENV:Fault element in the body. Within this appear procedure SetResponse(Name: string; Responses: TList);
a number of pre-defined elements that describe the problem. The published
faultcode element is intended for automated use, and consists of one property AfterDispatch;
property BeforeDispatch;
of a small number of generic types. The SOAP specification defines
property OnHeader: TSOAPHeaderEvent
those shown in Figure 3. We may define other types as necessary. read FOnHeader write FOnHeader;
property SOAPActions: TSOAPActions
In the faultstring element we find a human-readable description read FSOAPActions write FSOAPActions;
of the problem. Both these two elements must always be present. property SOAPParser: TCustomSOAPParser
read FSOAPParser write SetSOAPParser;
The faultactor element identifies the actor that generated the end;
error and is only required when multiple actors are used. Finally,
the detail element must appear whenever the error results from
processing the body of the request, and must not be present Figure 5: A SOAP dispatcher.
otherwise. It contains further details about the problem, such as
internal error codes, a stack trace, etc. Figure 4 shows an example the supplied URI identifying the request. Specifying an empty string
of an error response. means that the HTTP URL itself denotes the call:
When transmitted via HTTP, any error response must be sent back SOAPAction: "http://www.movies.com/findMovies.dll"
with an HTTP status code of 500, indicating “Internal server error”.
For valid responses the return code is the usual 200, for “OK”.
Incoming SOAP requests over HTTP must also include a header Processing SOAP
field that indicates the purpose of the message. This allows firewalls Using SOAP over HTTP is the only combination described in the
to easily filter requests. The header looks like the following, with specification, although other transport mechanisms could be used.
Figure 11: The example SOAP server data module. Figure 12: The example SOAP client at run time.
eter is set as a simple string value against its element’s name in a { Compile the SOAP request and parse the results. }
string list. The action handler then retrieves these by name from procedure TfrmMovies.btnFindClick(Sender: TObject);
var
the Values property.
Element: IXMLDOMNode;
Request: string;
These components are compiled and made available to Delphi by
including them in a package. A new unit, SOAPReg.pas, contains { Extract the list of matching movies. }
procedure ListMovies(Node: IXMLDOMNode);
the registration calls for them, while an associated resource file, var
SOAPReg.dcr, supplies the icons for the Component palette. Index: Integer;
begin
SOAP Server for Index := 0 to Node.ChildNodes.Length - 1 do
with Node.ChildNodes.Item[Index] do
To demonstrate these components in action, we can build a Web lbxMovies.Items.Add(Text);
server application that responds to a “findMovies” request. It takes end;
one optional parameter, the rating, and returns a list of matching
begin
movies from those in the database table. Typical request and Screen.Cursor := crHourglass;
response documents appear in Figures 1 and 2. try
pgcSOAP.ActivePage := tshMovies;
lbxMovies.Items.Clear;
Before we begin creating the Web server application, download the memRequest.Lines.Clear;
latest Extended Document Object Model components by Dieter memResponse.Lines.Clear;
Köhler at http://www.philo.de/xml/dom. These are required by the Request := '<?xml version="1.0"?>' + '<' +
SOAPEnvPrefix + ':' + SOAPEnvelopeTag + ' ' +
TSOAPOpenXMLParser class discussed shortly.
NamespaceAttr + ':' + SOAPEnvPrefix + '="' +
SOAPEnvNamespace + '">' + '<' + SOAPEnvPrefix + ':' +
To build the server, first create a new Web server application based SOAPBodyTag + '>' + '<findMovies>';
on ISAPI. Begin by selecting File | New to display the New Items if cbxRating.Text <> '' then
Request := Request + '<rating>' + cbxRating.Text +
dialog box, and select Web Server Application from the New page. Then '</rating>';
remove the generated Web module from the project without saving Request := Request + '</findMovies>' + '</' +
its contents, and add a data module to the project by selecting Data SOAPEnvPrefix + ':' + SOAPBodyTag + '>' + '</' +
SOAPEnvPrefix + ':' + SOAPEnvelopeTag + '>';
Module from the New page.
memRequest.Lines.Text := Request;
htpSOAP.Header := SOAPActionHeader + ': findMovies';
Place SOAPDispatcher and SOAPOpenXMLParser components htpSOAP.Post(FApplication, Request);
on the data module (they’re automatically linked). Then drop memResponse.Lines.Text := htpSOAP.Body;
if not FXMLDoc.LoadXML(htpSOAP.Body) then begin
Session and Query components on the data module. Set the MessageDlg(FXMLDoc.ParseError.Reason,
Session’s AutoSessionName property to True, point the query at the mtError, [mbOK], 0);
Movie-watchers database, and enter its SQL statement. It must be Exit;
end;
entered on different lines as shown here, so the WHERE clause Element :=
can easily be replaced later: FXMLDoc.DocumentElement.FirstChild.FirstChild;
if Element.NodeName =
SOAPEnvPrefix + ':' + SOAPFaultTag then
SELECT * FROM Movie
MessageDlg('Error in request'#13 +
WHERE Rating = '?'
Element.ChildNodes.Item[1].Text,mtError,[mbOK],0)
else
Press the ellipsis button next to the SOAPActions property of the ListMovies(Element);
finally
dispatcher in the Object Inspector, add a new SOAP action, and Screen.Cursor := crDefault;
name it findMovies. Create an OnInvoke event handler for it, and end;
enter the code shown in Figure 10. This procedure retrieves the rating end;
value sent by the client (if there is one), and queries the database for
matching movies. Those found are placed in TSOAPResponse objects Figure 13: The OnClick event handler for the example applica-
for use in the response document. tion’s Find button.
The event handler first clears the listbox, request, and response Resources
memo controls. It then constructs the SOAP XML as a string value, SOAP Specification: http://www.w3c.org/TR/SOAP
and copies it to the first memo field. An NMHTTP component Open XML: http://www.philo.de/xml/
supplies the connectivity to the Web application. After setting the
required SOAP header, its Post method is called to send the SOAP This article is adapted from material for the forthcoming book Delphi
request. Any response appears in its Body property, which is copied Developer’s Guide to XML [Wordware, 2001].
into a memo field and loaded into a DOM for interpretation.
Microsoft’s DOM is used in this case. The project and database referenced in this article are available on the
Delphi Informant Magazine Complete Works CD located in INFORM\
If an error response is found, it produces a dialog box showing 2001\AUG\DI200108KW.
the reason. Otherwise, each entry in the response (a movie title) is
added to the main listbox. To generate a SOAP error, we just pick a Keith Wood is an Australian working his way around the United States. He’s
rating that has no matching movies, such as “NR”. a systems engineer with Active Health Management, based near Boston, and a
freelance technical writer. Keith started using Borland’s products with Turbo Pascal
By default the client looks for the local Web server, and an applica- on a CP/M machine. His forthcoming book, Delphi Developer’s Guide to XML
tion on it named cgi-bin/SOAPServer.dll. The two menu options [Wordware, 2001], covers many other aspects of XML. You can reach him via
on the form let us change these settings. e-mail at kbwood@CompuServe.com.
By Mike Edenfield
Console Applications
Part I: Exploding the Myths, Exploring the Basics
C onsole applications are applications that run in the Win32 character mode,
or the CUI (character user interface) subsystem. And yes, they look just like
applications used to look before the GUI took over the world: a limited character set
on a black background, as shown in Figure 1.
Despite its unassuming exterior, however, the console Console applications run in a real-mode DOS
application is perfectly capable of performing myriad window. This is completely false. In fact, the
programming tasks, as this series will prove. More- reverse is true: DOS programs are run via a
over, it can often do the same job a GUI application console application named Winoldap, which
can, with an executable a tenth of the size. runs in a 32-bit console window. (Winoldap
is, however, a special type of console program
There are so many myths about console applications, that emulates a real mode by way of the x86
that I’d like to begin this series by debunking a few. “Virtual86” mode.)
Most of these misconceptions stem from the fact that Console applications can’t use the Windows
console applications look and behave like DOS pro- API. Again, while it is possible to write a
grams. Here are some of the more common myths: console application without a single API call,
Console applications aren’t really 32-bit. This when using only run-time library functions
myth arises because, in most Windows compil- the entire Windows API is available to your
ers, the standard “old” run-time input/output console application.
functions (e.g. Pascal’s Readln and Writeln) can This one has three parts: console applica-
be used to read/write to the console. This is just a tions can’t display windows; GUI applica-
convenience, however: internally those functions tions can’t read/write to a console; console
use the same file input/output functions that any applications can’t use the VCL. Because con-
other 32-bit Windows application does. sole applications run without forms, many
people assume they can’t use any graphical
controls — or indeed, the entire VCL! This
isn’t true. A console is merely a special kind
of window that the OS allocates to a process,
which maps its standard input/output/error
file handles to read/write from that console.
Any application can allocate a console of its
very own, and a console application certainly
can create a window if it chooses.
Basic I/O
The first step to creating a console application
in Delphi is to make sure the compiler informs
Windows that your application should run in the
console subsystem. This is done via the:
The child process’ standard file handles (Input and Output in Writeln('Hello, ', sName,
', welcome to the Console World!');
Delphi) are mapped to Read and Write from this new console Writeln('Hit <Enter> to exit');
window. Readln;
end.
The Pascal functions Readln and Writeln default to using Input
and Output as the files. In DOS, these file handles were mapped Figure 2: A simple “Hello World” application.
directly to the keyboard and screen buffers, respectively. In Win-
dows, they are mapped to the input and output buffers of the new As we touched on in “Basic I/O” earlier, when a new console is
console window. created, or a new process attaches to its parent process’ console, the
operating system automatically creates three “standard” file handles.
One important result of this close connection between Delphi’s read/ There are two API functions to manipulate these handles, which will
write functions and the Windows console is that source code from be used extensively in high-level console programming:
old DOS Pascal applications will generally compile with nothing
more than the addition of {$APPTYPE CONSOLE} somewhere in the function GetStdHandle(nStdHandle: DWORD): THandle; stdcall;
program file. The only caveats are that screen or graphical units such function SetStdHandle(nStdHandle: DWORD; hHandle: THandle):
BOOL; stdcall;
as CRT or BGI aren’t present (and, in fact, wouldn’t work on a
console), and that some of Pascal’s lower-level library functions (the
in/out functions, the direct register manipulation) are a no-no in These functions simply return a copy of the current standard file
protected-mode Win32. handle, or set the standard handle to a previously opened file handle
you supply. The three standard handles are:
In C, there is a difference between console and GUI processes, in STD_INPUT_HANDLE — Corresponds to Delphi’s Input
that GUI processes name their entry point WinMain, while console variable.
processes simply call their entry point main. In Delphi, of course, STD_OUTPUT_HANDLE — Corresponds to Delphi’s Output
the entry point is always the unnamed begin in the program’s main variable.
.dpr file. However, while typical Delphi applications simply call STD_ERROR_HANDLE — Standard output (no Delphi
Application.Run; from this procedure, a console application may keep variable).
all of its code here. It’s possible (just as in Turbo Pascal, etc.) to use
other units, but the simplest console applications will contain most A very simple use of these functions can be found in Figure 3, which
or all of their code in the .dpr file. A short example of this can be uses a stream object to write to the standard output console window
seen in Figure 2 — the ever-popular “Hello World” application. (All instead of Writeln. (The run-time result of this code is shown in
of the sample applications presented in this article are available for Figure 1.) While this example is somewhat contrived, it does demon-
download; see end of article for details.) strate the typical usage of GetStdHandle, which is to get a handle that
can be passed to other Windows API calls that expect a file handle.
When working with small console applications, it’s often conve- SetStdHandle is generally used to redirect the standard file handles of a
nient to edit and compile them outside the IDE. Calling the Delphi child process (an advanced topic that will be discussed later in this series).
command-line compiler requires less memory than the full IDE,
is slightly faster, and doesn’t generate all the additional option and Advanced I/O: Reading Input Events
symbol files the IDE does when building and saving projects. While it’s possible to use basic I/O routines to read and write to the
console, there’s much more to it. Because a console is just a special
To compile applications from the command line, simply make sure type of window, there are API calls you can make to manipulate that
your {$DELPHI}\Bin directory is in your path. Windows makes this window and receive input from it, including mouse input. In this
rather simple, as it allows you to specify a batch file to run whenever section, we’ll discuss receiving keyboard and mouse events from the
a command-line shell is run. Right-click any “MS-DOS” shortcut console at a lower level than simple Readln library functions.
and enter a batch file name, then put the correct “PATH=” line in
there. Then call the DCC32 program passing it the name of your There are two levels of console input/output that Windows recognizes.
.dpr file, as in: We have already looked at the basics of what is termed “high-level”
console mode. The API calls used for high-level console input are:
C:\> DCC32 Listing1.dpr
function ReadFile(hFile: THandle; var Buffer;
and you will generate a Listing1.exe file, ready to be run. nNumberOfBytesToRead: DWORD;
var lpNumberOfBytesRead: DWORD;
lpOverlapped: POverlapped): BOOL; stdcall;
Advanced I/O: File Handles function ReadConsole(hConsoleInput: THandle;
Before discussing the more advanced I/O features that consoles offer, lpBuffer: Pointer; nNumberOfCharsToRead: DWORD;
a brief discussion of what console handles are, and how to use them, var lpNumberOfCharsRead: DWORD; lpReserved: Pointer):
is necessary. BOOL; stdcall;
uses uses
Classes, Windows; Windows;
var var
strmOut: THandleStream; hInput: THandle;
hOutput: THandle; arrInputRecs: array[0..9] of TInputRecord;
dwCur, dwCount: DWORD;
begin cCur: Char;
{ Get the current output handle. } coorCur: TCoord;
hOutput := GetStdHandle(STD_OUTPUT_HANDLE);
{ Create a new handle stream with our output handle, begin
and use our stream object to write to it. } hInput := GetStdHandle(STD_INPUT_HANDLE);
strmOut := THandleStream.Create(hOutput); while True do begin
strmOut.Write('This is standard output.'+ #13#10, 26); ReadConsoleInput(hInput, arrInputRecs[0], 10, dwCount);
strmOut.Write('It shows up on the console screen!' + for dwCur := 0 to 10 - 1 do
#13#10, 36); case arrInputRecs[dwCur].EventType of
strmOut.Free; KEY_EVENT:
Writeln('Hit <Enter> to exit'); with arrInputRecs[dwCur].Event.KeyEvent do begin
Readln; cCur := AsciiChar;
end. if cCur = '' then
if bKeyDown then
Writeln('Unprintable key pressed.')
Figure 3: A simple example of the three standard functions. else
Writeln('Unprintable key released.')
These two functions, when used on a console window, behave almost else
identically. The differences are that ReadConsole can also read Unicode if bKeyDown then
character input from a console, while ReadFile can be used on non- Writeln('Pressed ', cCur, ' ',
console-handles, such as file and memory handles which may be redi- wRepeatCount, ' times.')
else
rected to standard input. (The console’s current code page determines
Writeln('Released ', cCur, ' key.');
if the input is ANSI or Unicode, a topic discussed later in this series.) end;
Unless you may need to read Unicode data, stick to Readln, which { Delphi has renamed MOUSE_EVENT to avoid a
maps to ReadFile and will handle redirected input handles properly. conflict with a Windows function mouse_event.
_MOUSE_EVENT:
with arrInputRecs[dwCur].Event.MouseEvent do
You’ll notice that both functions require a file handle as an argument. begin
This handle is extracted from the console via GetStdHandle, and in fact, coorCur := dwMousePosition;
nearly all of the API calls expect a console handle passed to them, either if dwEventFlags = 0 then
a handle to the input buffer or one of the output buffers. (Creating and Writeln('Button Pressed at ', coorCur.X,
',', coorCur.Y);
using multiple output buffers will be discussed later in this series.)
end;
end; // case
For even more control over the console input, there is a “low-level” end; // while
input method, which involves hooking into the console’s event end.
queue. This event queue behaves similarly to a window’s message
queue, but receives “input events” instead of messages. There are five Figure 4: Intercepting keyboard and mouse events.
key API functions used in low-level console input:
function ReadConsoleInput(hConsoleInput: THandle; is the Delphi alias for the INPUT_RECORD structure. The
var lpBuffer: TInputRecord; nLength: DWORD; structure is, in fact, a union of five types of input events. The first
var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
field, EventType, indicates which type of event record follows. A
function WriteConsoleInput(hConsoleInput: THandle;
const lpBuffer: TInputRecord; nLength: DWORD; complete discussion of each type of event is available on MSDN.
var lpNumberOfEventsWritten: DWORD): BOOL; stdcall; The two we’re most interested in are keyboard and mouse events.
function PeekConsoleInput(hConsoleInput: THandle; Briefly, the three less common types of events are:
var lpBuffer: TInputRecord; nLength: DWORD;
var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
WINDOW_BUFFER_SIZE_EVENT — Sent when the screen
function GetNumberOfConsoleInputEvents(
size changes.
hConsoleInput: THandle; var lpNumberOfEvents: DWORD): MENU_EVENT — Sent when the user uses the console’s menu
BOOL; stdcall; or toolbar.
function FlushConsoleInputBuffer(
hConsoleInput: THandle): BOOL; stdcall;
FOCUS_EVENT — Sent when the console gets focus.
The WINDOW_BUFFER_SIZE event is pretty uncommon, as most
The hConsoleInput parameter, used by all five functions, is the result people leave their consoles at the fixed size given when they’re cre-
of calling: ated. The WINDOW_BUFFER_SIZE event only occurs in certain
console modes, and is generally used to repaint the screen if needed.
GetStdHandle(STD_INPUT_HANDLE) The other two events, MENU_EVENT and FOCUS_EVENT, are
used internally by Windows, and are mostly undocumented (they’re
The remaining parameters should all be fairly self-explanatory, not difficult to figure out). In fact, Microsoft’s documentation advises
except for the newly introduced TInputRecord type. TInputRecord that they “should be ignored.”
Combine any of these eight attributes with or to form the 16 base In the meantime, detailed explanations of all the API calls in this
ANSI colors for foreground and background. (Note to old DOS article can be found as part of Microsoft’s Platform SDK. The latest
programmers: The high bit of the background sets the intensity version of the Platform SDK is available on the MSDN Web site
flag for the background color, not the blinking attribute as it did (http://msdn.microsoft.com), or the latest MSDN CD set. Console
under ANSI.SYS.) Of course, all three together make white, none applications are listed under “Base Services” as part of the “Files and
make black, etc. I/O” section. You should definitely take time to review the descrip-
tions of the console functions, and — as always — experiment. ∆
As you can see in Figure 5, which demonstrates both positioning
the cursor and changing the colors, we store the attributes and The projects referenced in this article are available on the Delphi Infor-
coordinates before we make any changes. It’s always a good idea mant Magazine Complete Works CD located in INFORM\2001\AUG\
to store these and restore them before your program executes. Your DI200108ME.
application may be running attached to the console of a parent
application, which will still be on screen when you are finished with
it. Any changes you make to the console will remain in effect, even
if your program terminates, which can cause problems, or at least a
difficult-to-read color scheme, for the parent process.
Conclusion
We’ve only scratched the surface of what console applications can do.
At its most basic, console programming allows you to port a large Mike Edenfield is an applications developer for Sylvan Learning Systems in
portion of DOS Pascal code into the Windows environment with Baltimore, MD, and an MCSD. He has five years experience with Delphi and Visual
little change. But, as we’ve seen, the console window is actually a Basic, and specializes in Microsoft SQL Server development. He can be contacted at
full-featured user interface. It just uses characters instead of pixels. Michael.Edenfield@educate.com.
By Rick Spence
Procedural Types
Putting Them to Use On Purpose
F ed up with writing yet another routine to search a StringList? Do you know about
procedural types? A procedural type is a native Pascal data type that you very well
may not know about, but are undoubtedly using.
In this article I’ll explain where you’re using them We’ll now declare a new type, which will allow
already, dissect the syntax, and suggest some situ- us to declare variables, which can reference one of
ations in which they are invaluable. Learn this these procedures:
and you’ll never need to write code to search a
StringList again. type TProcedurePtr = procedure;
procedure DisplayWarning;
begin In fact, as you will see, you can assign any proce-
ShowMessage('This is a warning'); dure that takes no arguments.
end;
Now we have a variable that references a pro-
procedure DisplayError;
begin
cedure. At some point we’ll want to run that
ShowMessage('This is an error'); procedure, which we do simply by using the vari-
end; able in a statement:
procedure DisplayMessage(s: string); The syntax to the right of the equal sign may take a little getting
begin used to. Think of it as a normal procedure declaration without the
ShowMessage(s); procedure name.
end;
// ...
You can now declare variables of type TProcedureMsgPtr:
var
MyMsgProc : TProcedureMsgPtr; var
begin MyMsgProc : TProcedureMsgPtr;
MyMsgProc := DisplayMessage;
end;
but you must assign a procedure with one parameter of type string,
Figure 1: Assigning a procedure with one parameter of type as shown in Figure 1. If you attempt to assign to MyMsgProc a
string to a procedure variable. procedure that doesn’t accept one parameter of type string, the
compiler will complain.
MyProc; Now, when you invoke the procedure using the procedural type, you
must supply an argument:
This simple statement calls the subroutine referenced by the variable
MyProc. Think of it as an indirect call. This does not call a subroutine MyMsgProc('This is my message');
called MyProc — there isn’t one. MyProc is a variable. It calls the
subroutine to which MyProc refers, in this case DisplayWarning. Run This calls the DisplayMessage routine, passing the string to the
the following through the debugger to convince yourself: subroutine’s formal parameter named s. Take a moment to make
sure you understand this. MyMsgProc is a pointer to a subroutine
var that accepts a single parameter of type string. When you call the
MyProc : TProcedurePtr; procedure to which MyMsgProc points, in this case DisplayMessage,
begin
you must pass the proper argument. In fact, the following two
MyProc := DisplayError;
MyProc; // Call DisplayError lines of code have the same effect:
That’s the basics of procedural types. You may not know how you The first is calling DisplayMessage indirectly, via a pointer to it. You
would use them yet, but by now you should understand the syntax. might ask, why bother? In this case you wouldn’t, you’d just call
DisplayMessage directly. Bear with me a moment longer.
Assigning Procedural Types
You can assign procedural types to each other in the same way you Procedural Types as Methods
assign other variables. You’re just copying the address of a procedure The procedural types you’ve seen so far have been references to
from one variable to another: stand-alone subroutines. You use a very similar syntax to work with
methods; in essence a method is a subroutine that can access an
var object’s data. To declare a procedural type that references a method
MyProc1 : TProcedurePtr; rather than a stand-alone subroutine, simply append the key words of
MyProc2 : TProcedurePtr;
object to the type declaration:
begin
MyProc1 := DisplayError;
MyProc2 := MyProc1; type TMethodMsgPtr = procedure(msg: string) of object;
...
MyProc1; // Call DisplayError var
MyProc2; // Call DisplayError MethodMsgPtr : TMethodMsgPtr;
As you’ll see in a moment, this is exactly what you do when you share
This is sufficient to inform the compiler that TMethodMsgPtr is a
event handlers among multiple components.
pointer to a method of an object, rather than a stand-alone proce-
dure. Then, when assigning it to a variable, use the object name
Procedural Types with Parameters followed by the method name:
What you just saw were two trivial procedures that did not receive
parameters. You can also use procedural types with subroutines that MethodMsgPtr := SomeObject.MsgMethod;
do take arguments, but you must declare the parameters along with
the type. Previously we used: and call it indirectly in the same way you’ve already seen:
to declare a new type, TProcedurePtr, which was a reference to a proce- Event Handlers Are Procedural Types
dure that did not take arguments. If the procedure has parameters, you Earlier, I said that you’re already using procedural types in your
must explicitly list them with their type as part of the type declaration: Delphi applications. Where have you been using them? With your
event handlers. Load Delphi’s help for TForm and look at the help for
type TProcedureMsgPtr = procedure(msg: string); one of the events, such as OnActivate. You’ll see it declared as:
procedure TForm1.FormCreate(Sender: TObject); Controls’ events are declared in a similar way. Load help and look at
var the OnClick event of TButton:
iniFile : TIniFile;
s : string; property OnClick: TNotifyEvent;
sl : TStringList;
i : Integer;
mnuItem : TMenuItem; OnClick is a property, which is a pointer to a method that accepts one
begin parameter of type TObject.
iniFile := nil;
sl := nil;
If you poke around for long enough in the VCL source, you can
mnuItem := nil;
try find the Pascal code that responds to messages from the Windows
iniFile := TIniFile.Create(ExtractFilePath( API and calls the programmer-assigned event handlers. Since you’re
Application.ExeName) + 'MruFiles.Ini'); not obligated to write code for events, the VCL must check whether
sl := TStringList.Create; you assigned a handler, and only call it if you have. You will see the
iniFile.ReadSection('MruFiles', sl);
for i := 0 to sl.Count - 1 do begin
following sort of code throughout the VCL:
s := IniFile.ReadString('MruFiles',
sl.strings[i], 'N/A'); if Assigned(FOnDblClick) then
mnuItem := TMenuItem.Create(Self); FOnDblClick(Self);
mnuItem.Caption := s;
mnuItem.OnClick := MruOpen;
File1.Insert(2 + i, mnuItem); This statement (from the TControl class) calls the event handler
end; for the double-click event, if that event handler has been assigned
// Add a separator at the end. a value.
mnuItem := TMenuItem.Create(Self);
mnuItem.Caption := '-';
File1.Insert(sl.Count - 1 + 3, mnuItem);
Most events, but not all, are declared as type TNotifyEvent. TForm’s
finally OnClose event, for example, expects two parameters. Here are the
iniFile.Free; pertinent declarations:
sl.Free;
end; TCloseEvent = procedure(Sender: TObject;
end; var Action: TCloseAction) of object;
...
property OnClose: TCloseEvent;
Figure 2: A main form’s OnCreate event handler.
If you follow the hyperlink to the declaration of TNotifyEvent, object Button1: TButton
you’ll see: Left = 304
Top = 104
Width = 75
type TNotifyEvent = procedure(Sender: TObject) of object; Height = 25
Caption = 'Button1'
which you should now understand. Variables of type TNotifyEvent are TabOrder = 1
pointers to methods (note of object in the declaration) that expect OnClick = Button1Click
It’s very common to have toolbar buttons use the same event as handlers, and how you can use them to assign event handlers
a menu item. You may have a menu item labeled Open File, for dynamically. Now we’ll look at a more general use of procedural
example, and a toolbar button that performs the same action. To types — as parameters to subroutines. Because procedural vari-
accomplish this, write the code for the OnClick event of one of ables are simply variables that contain pointers to pieces of code,
the components, then use the Object Inspector to associate the you can pass them as arguments just as any other variable.
same event with the other component. Delphi is simply making
entries in the .dfm file to have the two OnClick properties refer- To see why this is useful, consider how you search a StringList.
ence the same event handler. The TStringList class has a method named IndexOf, which searches
a StringList for a particular string, and returns its position in the
Note that the Object Inspector only allows you to assign events that list. IndexOf is useless, however, if you want to perform a case-
have the same parameter lists. For example, if you’ve written code for insensitive search, a substring search, or search a StringList’s objects.
the OnCloseQuery event, you cannot assign this event to a push button’s
OnClick property. OnClick is declared as a TNotifyEvent, which accepts Fortunately, writing your own search routine is pretty easy. For
one parameter of type TObject. OnCloseQuery is declared as a TCloseQuery example, the code in Figure 4 performs a case-insensitive search.
event that expects two parameters. The two types are incompatible. This is a basic StringList search. It loops through each element
of the StringList, terminating when the item is found, or the
Dynamically Creating Controls StringList is exhausted. The routine uses the expression:
Another example of using procedural types is when you’re creating
controls dynamically, i.e. at run time. In this situation, you must UpperCase(sl.Strings[i]) = UpperCase(s)
assign the event handlers; otherwise the controls won’t do anything.
to determine if it has found the desired element.
Consider the code in Figure 2, which is taken from a main form’s
OnCreate event handler. The handler is reading a list of most- Now consider Figure 5, which searches a StringList for a string in
recently-used files from an .ini file, MruFiles.Ini, stored in the same which the item being sought can be a left substring of the item in
directory as the executable. It’s adding the names of these files as new the string list. If you are searching for the string “Spence”, and an ele-
menu items to the bottom of an existing menu item named File1. ment in the StringList contains “Spencer”, that would be considered
a valid match. In contrast, the routine in Figure 4 is performing an
Note how the code assigns the procedure MruOpen to the OnClick exact comparison (except for case). This routine uses the expression:
property of the new menu item. When the user clicks on any of
these new menu items, Delphi will call the MruOpen event handler. Copy(sl.Strings[i], 1, Length(s)) = s
For this to work, you must declare MruOpen in the form’s class
declaration, as shown in Figure 3. Then, to implement it as you to determine whether this is the desired element. Otherwise, the
would any other method, you write: routine is identical. So how would you perform a case-insensitive
substring search? You’d duplicate the routine, except you’d use the
procedure TForm1.mruOpen(Sender: TObject); following expression to perform the comparison:
begin
OpenFile((Sender as TMenuItem).Caption);
UpperCase(Copy(sl.Strings[i], 1, Length(s))) = UpperCase(s)
end;
How would you search the StringList for an object whose class name
Note how MruOpen is declared as a method that accepts one is passed as a parameter? You’d use the following expression:
parameter of type TObject. It must be declared that way, because a
TMenuItem’s OnClick event is declared that way. sl.Objects[i].ClassName = s
Procedural Variables as Arguments The point is that the only thing that’s changing is the comparison.
You’ve seen how Delphi uses procedural types to implement event Therefore, it should be possible to parameterize this expression
So how do you call this routine? First you must write the routine
whose address you will pass to slSearch. As an example, here’s a case-
insensitive search routine, Insensitive, that looks for a string stored in
a global variable named s: Rick Spence is technical director of Web Tech training and Development
(http://www.WebTechCorp.com), a company with offices in the US and UK,
function InSensitive(sl: TStrings; i: Integer): Boolean; specializing in Delphi training and development.
begin
By Bill Todd
A Little Help
Adding Online Help to Your Applications
D elphi does a great job of giving you the tools to easily add Windows Help to
your applications. However, there’s limited documentation about how to use
them. This article will provide a complete overview of adding context-sensitive help
to Delphi programs.
Assigning a Help File to Your Program If you enter only the name of your help file in
The most obvious and easiest way to assign a help file the edit box, your application will look for the
to your program is to choose Project | Options from help file in the current directory, which is usually
the main menu, then select the Application tab in the directory where the EXE resides. You can
the Project Options dialog box, as shown in Figure also enter a relative path to the help file if you
1. Here you can enter the name of your help file in want to place the help file or files in a sub-
the Help file edit box, or click the Browse button to directory, e.g. help\helpdemo.hlp. In this case
locate your help file and add its path to the edit box. your program will look for the help file in the
The problem with using the Browse button is that it help subdirectory under the current directory.
inserts the complete path to your help file into the However, if your application changes the current
Help file edit box; this won’t work if a user installs directory, it won’t be able to find the help file.
your application in a different location. Even if your program doesn’t change the current
directory, what happens if a user decides to add
a shortcut to his or her desktop and, for some
reason, sets the Start In directory to some other
location? In either case, help will no longer work.
Application.HelpFile := ChangeFileExt(
Application.ExeName, '.HLP');
The Application object is not the only VCL object that has a integer. The value of the second parameter depends on the first. In
HelpFile property. Using TForm you can have a separate help file this case, the second parameter isn’t used, so zero is passed. Passing
for any or all of your forms, as well. All of the considerations HELP_FINDER as the first parameter displays the Help Topics
for assigning the Application.HelpFile property apply to assigning dialog box, as shown in Figure 2. When you start your program and
a form’s HelpFile property. The best way is to set the property in display this dialog box the first time, the Contents tab is selected.
code in the form’s OnCreate event handler. Because a form’s help file If you select the Index tab and close the dialog box, the Index tab
won’t have the same name as the application’s EXE file, you’ll always will be displayed the next time you open the dialog box. The same
want to use code similar to this: is true of the Find tab. Figure 3 lists the most useful constants
you can pass as the first parameter to HelpCommand and what each
Self.HelpFile := ExtractFilePath( does. For information about other HelpCommand constants, search
Application.ExeName) + 'help\custform.hlp'; for “WinHelp” in the Microsoft SDK help file.
When you implement context-sensitive help using the HelpContext The Delphi HelpCommand method is simply a wrapper around the
property of a form, your program will look first to see if the form Windows API function WinHelp. The WinHelp function takes four
has a help file assigned. If it does, that file will be searched for the parameters. The first two are the window handle of the window calling
context ID. If the form’s HelpFile property is blank, the application’s WinHelp and the path to the help file. These are supplied automatically
help file is used. when you call Application.HelpCommand. The second two parameters
are identical to the parameters of Application.HelpCommand.
Adding Help to the Menu
The first choice on an application’s Help menu usually displays the You might also want to have choices on your Help menu to
table of contents of the help file. To do this, add the following line to display a specific topic, such as Getting Started or Tutorial. To
the menu item’s OnClick event handler: display a specific topic, use the Application.HelpContext method,
or Application.HelpJump method:
Application.HelpCommand(HELP_FINDER, 0);
Application.HelpContext(10020);
This line calls the Application object’s HelpCommand method. The
first parameter is a constant of type Word, and the second is a long Application.HelpJump('TOPIC_ONE');
Another way to display help for a topic is to search for a topic by a key-
word that appears in the help file index using Application.HelpCommand,
as shown in Figure 4. Application.HelpCommand is called with the
HELP_KEY command constant. When HELP_KEY is passed as the first
parameter, the second parameter must be a pointer to a null-terminated
string that contains the keyword for which to search. In this code, the
Figure 2: The Help Topics dialog box. Figure 4: Displaying help by keyword.
You can also provide context-sensitive help in dialog boxes you Bill Todd is president of The Database Group, Inc., a database consulting and
create by calling the MessageDlg function. The fourth parameter development firm based near Phoenix. He is co-author of four database program-
in a call to MessageDlg is the context ID of the help topic you ming books, author of more than 80 articles, a Contributing Editor to Delphi
want to associate with the dialog box. If you pass a context ID Informant Magazine, and a member of Team Borland, providing technical support
and include a Help button in the third parameter, the help topic on the Borland Internet newsgroups. Bill is also a nationally known trainer and
is displayed automatically when the user clicks the Help button. has taught Delphi programming classes across the country and overseas, and is a
Select File | Message Dialog from the sample application’s menu to frequent speaker at Borland Developer Conferences in the US and Europe. Bill can
see an example of this. be reached at bill@dbginc.com.
By Rick Ross
Shared Objects
Creating and Using Linux Shared Libraries
L inux shared libraries can dramatically reduce memory and disk space requirements
for applications that share common code. Applications that require a plug-in
architecture also benefit from shared object libraries. And of course, existing libraries
can be leveraged to reduce development time, and therefore time to market.
Kylix allows you to create shared object libraries as where library name indicates the name of the file.
easily as Delphi creates DLLs. Linux, however, has For Kylix, it’s also the name of the project (.dpr)
certain naming and versioning conventions that file. major, minor, and micro refer to the release
can make them tricky to write and use. This article numbers of the library.
will give you the necessary background to write
shared object libraries efficiently and effectively; The major release number indicates compatibil-
we’ll also discuss exception-handling techniques. ity at the interface, or API level. Any interface
change in a library that requires applications to
Before shared libraries existed, applications were be recompiled in order to work should incre-
required to link in references to all of the functions ment the major version number of the library.
used at compile/link time. The linker resolved all For example, in major release one, a function
addresses when the files were linked. When two or requires an integer parameter, but in release
more applications share the same code, they need two, the same function now takes a string as a
to be linked to the same source files. Using this second argument. For the application to work
technique, the file sizes and memory requirements with release two, make the appropriate changes
of the applications increased. While there’s nothing in the application, recompile, and relink.
wrong with this commonly used approach, shared
object libraries were created to help reduce these For minor releases, any changes refer to addi-
memory and file-size requirements. tional interfaces that have been added, and are
completely compatible with previous releases.
A shared object library is a group of functions All existing interfaces remain unchanged.
that the linker does not resolve until run time. Finally, a micro release does not add functional-
Instead, these functions are specially marked, so ity; changes are made to the implementation.
they’re resolved (assigned an address) by loading Typical changes that affect micro releases are bug
the appropriate shared object library when the pro- fixes and performance tuning.
gram is executed.
Having a file-naming standard is certainly a step
Versioning in the right direction. But using all the version
Windows DLLs and executables can embed ver- numbers to identify a library is overkill. Also, any
sion information into the resource section of the changes to a file name would bring disastrous results.
file. Unlike Windows, Linux has a built-in mecha- Therefore, each library has an internal identifier that
nism for identifying the version of a shared library. refers to the name and the version. This is called the
The file-naming convention of a shared object soname. It is given to the linker when creating the
takes the form of: shared object. The soname takes the form:
The output contains three links that are indicated by the “->” char- <SOPREFIX><library name><SOSUFFIX>.so.<SOVERSION>
acters and the letter “l” as part of the permissions. There are two
major versions of libmenu: 4 and 5. By convention, the library name SOPREFIX defaults to “lib” for shared objects and “bpl” for pack-
without a version number points to the highest major version, 5 in ages. If the SONAME directive is used, it will create a symbolic link
this case. Then for each major version, there is also a link to the that points to the actual file name. For more information on these
most current release. Version 4 is a “zero release” (libmenu.so.4.0), directives, look in the Kylix help file.
while version 5 is “release one” (libmenu.so.5.1). Using a link to
point to the current version of a library can cause problems as well. Calling Conventions
Poorly written installation programs can change the link to point to a A calling convention is an agreement between the caller of a routine
version that’s incompatible with other installed applications. and the routine itself. All functions have a calling convention that
indicates how parameters are passed, how parameters are cleaned up,
When loading an application that uses a library, the program loader how registers are used, and how errors and exceptions are handled.
loads the shared object file name listed in the application’s function The default Kylix and Delphi calling convention is called register.
import table (the soname). If the program loader cannot find the For specifics regarding register and other calling conventions, search
library, an error message is displayed that indicates which version of the Kylix help under the topic “Calling Conventions.”
the library the application requires.
If a library is only going to be called by other Kylix applications,
If two or more libraries contain the same function definition, the first there’s no need to change it. However, applications written in another
library will be the one used. The search order used to look for librar- language normally use a different method of passing parameters. Two
ies is as follows: First, the directories specified by the environment of the most commonly used calling conventions are cdecl and stdcall.
library Project1;
uses
SysUtils, Classes;
begin
end.
Figure 2: The SO option in the New Items dialog box. Figure 3: Code generated for a new shared object.
function MyCube(x: Integer): Integer; stdcall; exports MyCube(x: Integer) name 'MyIntCube',
begin MyCube(x: Double) name 'MyDblCube';
Result := x * x * x;
end;
In shared object libraries, function imports and exports are bound by
exports name only, and are always case-sensitive.
MyCube;
Figure 6: How to use the MyCube function from a shared object. uses
Libc;
library SafeSO; for your application when calling routines in shared objects. Recall
that there are two ways of referring to routines in a shared object:
uses statically and dynamically. And make sure that the calling conven-
ShareExcept, SysUtils, Classes; tions are identical when calling routines in shared objects.
function SomeSOFunction: Boolean; stdcall;
begin Exceptions need to be handled properly with shared objects. The
try general rule is to not allow exceptions to escape exported functions,
// Do something. and to make ShareExcept the first unit in the uses statement.
Result := True;
except
on e : exception do Shared objects allow for the division of large applications into
// Handle any exceptions. several functional modules. When multiple applications need the
Result := False; same functionality, the result is increased reusability. Kylix pro-
end; vides the tools necessary to unleash the power of shared objects,
end;
quickly and effectively. ∆
exports
SomeSOFunction; The author would like to thank Danny Thorpe, Eric Whipple, and
Ken Faw for their help and suggestions with this article.
procedure DLLHandler(Reason: Integer);
begin
// 0 means unloading, 1 means loading. Additional Resources
if Reason = 0 then Kylix Help files
// Now we want to remove our signal handler. SysUtils.pas
end;
UnhookSignal(RTL_SIGDEFAULT);
Delphi for Linux Developer’s Guide by Eric Whipple, Rick Ross,
and Fletcher Bumpus, [Wordware Publishing, July 2001]
begin Advanced Programming in the UNIX Environment by W. Richard
// We want to map Linux Signals to Kylix Exceptions, so Stevens, [Addison-Wesley, 1993]
// we call HookSignal to hook all the default signals. ELF Standard, http://developer.intel.com/vtune/tis.htm
HookSignal(RTL_SIGDEFAULT);
// Install the Exit handler.
“Library Interface Versioning in Solaris and Linux” by
DLLProc := @DLLHandler; David J. Brown and Karl Runge, http://www.usenix.org/
end. publications/library/proceedings/als2000/full_papers/
browndavid/browndavid_html
Figure 11: A sample libray that maps Linux signals to Kylix exceptions. “Making Shared Libraries” by Luis Colorado,
http://www.linuxfocus.org/English/November1997/article6.html
When dealing with libraries, especially with non-Kylix applica- The files referenced in this article are available on the Delphi Informant
tions, make sure each exported library routine catches all software Magazine Complete Works CD located in INFORM\2001\AUG\
exceptions. Figure 11 shows a sample library that traps the external DI200108RR.
exceptions, and catches all exceptions that may occur in an
exported function. Remember that this example should only be
used if the calling application and any other libraries do not install
signal handlers.
Rick Ross is a Senior Consultant at PILLAR Technology Group, Inc. in Detroit, MI.
Conclusion He is a co-author of the upcoming book Kylix Development by Wordware Publish-
When designing and writing shared objects, remember to version ing. He has been developing utilities, database applications, and distributed
them properly. This will allow for the coexistence of new and exist- systems for DOS, Windows, Solaris, and UNIX for over 15 years. Rick is also an
ing applications that use them. Compiler directives play an impor- accomplished network designer and administrator and has worked with a number
tant role with versioning shared objects. Choose the best method of Fortune 500 companies.
By Alex Fedorov
T his series will show Delphi developers how to use the XML features of Microsoft SQL
Server 2000. There’s a lot of ground to cover, so we’ll take it in pieces. This month’s
article introduces the XML features.
Next month’s article will demonstrate how to use FOR XML Queries
Delphi to work with the Microsoft XML Docu- The XML-based data extraction is implemented
ment Object Model (DOM). Then the series will in SQL Server 2000 by the FOR XML clause
move on to XML templates, hooking up Delphi’s that’s part of the SELECT statement. Note that
data-aware controls to XML data, and other topics. this clause is only valid in a SELECT statement,
and can’t be used with other statements such as
This series assumes you’re familiar with the basics INSERT, UPDATE, etc. The syntax of the FOR
of XML and XSL. [See Ron Loewy’s “XML from XML clause is:
Delphi” and Keith Wood’s “XSL Transforma-
tions” articles in the July 1999 and November FOR XML Mode [ ,XMLDATA] [ ,ELEMENTS]
2000 issues of Delphi Informant Magazine.] [ ,BINARY Base64]
SQL Server 2000 XML Features The Mode argument specifies the shape of
Several new features introduced in Microsoft SQL the resulting XML document; it can be RAW,
Server 2000 make it an XML-enabled database AUTO, or EXPLICIT. The RAW mode returns
server. Among them are the ability to access SQL the XML document, where each row of the
Servers using the HTTP protocol; support for XDR resulting recordset is stored with the generic
(XML-Data Reduced) schemas, and the ability to <row /> element. Each column is mapped to an
specify XPath queries against these schemas; and the attribute of this element, and the attribute name
ability to retrieve and write XML data. The XML for equals the name of the column. For example,
Microsoft SQL Server 2000 Web Release provides this query:
even more functionality. It’s a free add-on for SQL
Server 2000, and is available for download at http://
SELECT *
msdn.microsoft.com/xml/default.asp. For example, FROM Customers
we can use the updategrams mechanism to easily WHERE CustomerID = 'AROUT'
update, insert, and delete data, as well as efficiently FOR XML RAW
bulk load large XML documents into SQL Server
2000 using XML Bulk Load functionality. returns the XML document shown in Figure 1.
Figure 3: Example result from a FOR XML query in AUTO mode Figure 4: Example result of a FOR XML RAW query, using the
using the ELEMENTS argument. XMLDATA argument.
General Tag
Figure 8: XML in Internet Explorer.
Virtual Directory Name Northwind
Local Path c:\inetpub\northwind
Security Tag Now that we’ve learned how to query SQL Server 2000 to return
Set the options according to the type of your access to SQL Server: XML documents, it’s time to try it in action. First, however, we
Windows-based, SQL-based, or mixed. should configure a virtual root on a Web site to communicate with
Data Source Tag SQL Server.
SQL Server local
Database Northwind Preparing Virtual Directories
Settings Tag Go to the Programs | Microsoft SQL Server menu, and select
Configure SQL XML Support in IIS to invoke the IIS Virtual
Allow URL Queries ✓
Directory Management for SQL Server MMC application.
Allow Template Queries ✓
Allow XPath ✓ Here, we can create virtual directories for SQL Server databases
Allow POST accessed through URL requests. First, click on your server name,
Virtual Names Tab and select the Default Web Site element. Next, right-click on
Virtual Directory: northwind dbobject type
this element, and select New | Virtual Directory. You’ll get the
Virtual Directory: templates template type, physical catalog
New Virtual Directory Properties dialog box with several tabs:
c:\inetpub\northwind\templates General, Security, Data Source, Settings, and so on. Figure 6
Advanced Tab shows which options you should specify to run the examples in
this article.
Here you can specify additional parameters for Microsoft SQL
Server OLE DB Provider. For more information see SQL
Server Books Online. Writing XML and SQL Statements
Using the virtual directory just created, we can start to query the
Figure 6: Options to set for the examples in this article.
Northwind database using URL-based queries. This can be done
directly from the Web browser, or from the client application —
Symbol Hex Value Description the one we’ll implement with the help of Delphi. Please note that
+ %20 Space. because the queries will be specified as a URL, we can’t use some
/ %2F Separator for directories and symbols. For example, spaces should be replaced with the %20
subdirectories. escape sequence, or with the + sign. There are also other symbols to
? %3F Parameters separator between watch out for, as shown in Figure 7.
URL and actual parameters.
% %25 Indicates special character. The format of the URL query looks like this:
& %26 Parameters separator.
Figure 7: Symbols that require substitutes in a query. http://server_name/vdir_name?SQL=SELECT+*+FROM+Employees+
FOR+XML+AUTO&Root=Northwind
SELECT EmployeeID, LastName, FirstName, Photo
FROM Employees where server_name is the name of the IIS server, and vdir_name
WHERE EmployeeID = 1
is the name of the virtual directory. The query itself follows
FOR XML AUTO, BINARY BASE64
SQL=, and the Root parameter is used to specify the name of the root
element of the resulting XML document. If you run this query in
Before using the FOR XML queries in URLs, you can try them Internet Explorer, you’ll get the XML document shown in Figure 8.
in the SQL Server Query Analyzer (see Figure 5). To see the whole
XML document returned by the query, click on Tool | Options. Please note how the ? symbol is used to separate the parameter (the
Then, in the Results tab, specify 8100 as the Maximum characters SQL query) from the name of the virtual directory, and how the
per column value. Root parameter is separated with the & symbol.
Windows Browser
client client
HTTP Request
Microsoft
SQL Server
Implementation Details
Before we switch to Delphi, let’s briefly look at how XML support
is implemented in SQL Server 2000. The diagram in Figure 9
shows the entire architecture, separated into three layers.
Conclusion
That’s the essentials of Microsoft SQL Server 2000 XML support.
Next month, we’ll learn how to use Delphi to work with the Micro-
soft XML Document Object Model (DOM). See you then. ∆
Alex Fedorov is a Chief Technology Officer for Netface SA, based in Lausanne,
Switzerland (http://www.netface.ch). He was one of the co-authors of Profes-
sional Active Server Pages 2.0 (Wrox, 1998) and ASP Programmer’s Reference
(Wrox, 1998), as well as Advanced Delphi Developer’s Guide to ADO (Word-
ware, 2000).
By Robert Leahey
B ased in the Netherlands, ModelMaker Tools offers two products to Delphi program-
mers. The first, ModelMaker, is a CASE tool for Delphi which supports reverse-
engineering, UML, documentation generation, etc. It’s a stand-alone application that
interacts with Delphi through the Open Tools API.
The subject of this review is ModelMaker Code You can create and edit classes and class mem-
Explorer (MM Code Explorer), a Delphi expert bers, copy or move members between classes, and
that exists in the Delphi IDE. (I reviewed Model- change the ancestors of classes — all within the
Maker version 5 in the October 1999 issue of MM Code Explorer window. As you make these
Delphi Informant Magazine. ModelMaker won Best changes, the code in the active unit is automati-
CASE Tool in Delphi Informant Magazine’s 2001 cally updated by the expert to reflect your edits.
Readers Choice Awards.) MM Code Explorer does all this in a quick and
responsive manner.
ModelMaker has become an invaluable part of my
toolkit. The product is so powerful and versatile A Closer Look
that I cannot imagine Delphi development with- The main window of MM Code Explorer is
out it. However, that power and versatility has shown in Figure 1. The upper pane contains a
made me dissatisfied with the Delphi IDE; many hierarchical representation of the classes in the
ModelMaker elements belong in the Delphi IDE. active unit. As you change active units, this list
updates to reflect the classes in the new unit. Note
My discontent is now waning, because Model- that the tree is displaying classes and interfaces,
Maker Tools has released a new expert for the and a Module node, which represents unit-level
Delphi IDE: ModelMaker Code Explorer. MM code elements.
Code Explorer, a direct replacement for Delphi’s
Code Explorer, brings many of ModelMaker’s This isn’t a static view; within this pane you can
award-winning editing features directly to the create and edit class and interface declarations,
Delphi IDE. navigate to class declarations, or drag and drop
classes to change their ancestors. One of the most
What Does It Do? powerful features of the expert is its ability to
MM Code Explorer is technically an expert, but copy and paste whole classes within this pane.
that term tends to minimize its value. MM Code Imagine you are refactoring your code, and need
Explorer integrates seamlessly with Delphi, only to change the location in which a class resides.
to make me realize how Delphi is supposed to You can select the class’ current unit, copy (or
work. Basically it parses the active unit to pro- cut) the class from within MM Code Explorer’s
vide an organized display of its code elements. Class Tree, select the new unit, and paste the
By Warren Rachele
Protection PLUS
A Software Licensing Toolkit
Simplifying the registration process on both design addresses these needs in unique ways.
counts increases user satisfaction, and — in the One unique aspect of the toolkit is that when
latter case — converts more tire kickers into reg- your application is compiled into an executable
istered owners. The developer wrestles opposing and distributed, Protection PLUS doesn’t display
interests: making it easier to register and license a fingerprint identifying itself as the locking
their software, while protecting their work from method of choice for the application. This fea-
unauthorized use. ture is designed to dissuade hackers who under-
stand the algorithms used on other competing
Protection PLUS tools, and therefore know how to crack the locks
Protection PLUS (Professional Licensing and on software products. Protection PLUS hides
Unlocking System) by Concept Software, Inc. is a itself to prevent this temptation. To extend this
software library that enables developers to seamlessly feature, the license files and keys are protected
integrate numerous software security and licensing and generated by a password and code seed pro-
functions into their products. The tool works alone vided by the user. This prevents purchasers of the
or with Concept Software’s companion product, Protection PLUS library from reverse engineering
Instant SOLO (Secure Online License Ordering), the codes to products they have purchased.
to add complete feature remote control to unlock
your product. Protection PLUS is designed to work Consider the creation of a demonstration version
in a variety of scenarios, from taking demonstration of your product as the simplest starting point
versions and converting them into full retail licenses in an examination of the library. The PLUS
to protecting network seat-licensed software. The system gives you a choice of disabling the copy
product has a vast array of abilities that include after an expiration date or a certain number of
enabling specific modules of your program, imple- uses. With its sophisticated monitoring of system
menting pay-per-use restrictions, and the ability to states, PLUS will prevent the user from gaining
rent or lease your product within specifically con- more usage by reinstalling the demonstration or
tracted time frames. backdating the system date. When the demon-
stration expires, you can provide a number of
Protection PLUS is a complex tool designed conduits for the user to register his or her copy.
for serious security needs. Its well thought-out A link to a Web site running SOLO can generate
the necessary keys, or the user can call you. With your distribution
of the trigger codes, the client will automatically regain use of your
application and all the features you’ve enabled. You can also offer
to demonstrate the application, or simply unlock it altogether.
Conclusion guess. Because the license files are protected via a programmer-
Protection PLUS provides all the tools necessary to protect your determined password and seed number, they’re virtually unbreak-
software. The wide range of options gives you the tools to meet able as well. All of this contributes to the tight control provided
the most complicated set of protection options. The design makes by Protection PLUS.
it as useful for the small developer who wants to make his or
her shareware into a trialware distribution as it is for the software This review has described only a few of the many features of Pro-
giant that wants to control every facet of the use of their software. tection PLUS. It’s a well-designed and well-implemented product.
Developers who rent or lease their software will appreciate the Software developers wanting to exercise some protection of their
ease with which they can now enable or disable their products. intellectual property would do well to consider this toolkit. ∆
Those who license their products on a seat-by-seat basis across a
network will also find it easier to control the use and distribution
of their product.
The transparency of Protection PLUS adds an additional layer of Warren Rachele is Chief Architect of The Hunter Group, an Evergreen, CO software-
security to your products in the current environment in which development company specializing in database-management software. The com-
hacking codes are publicly traded on Usenet. None but the most pany has served its customers since 1987. Warren also teaches programming,
sophisticated of users will be able to detect the signature of the hardware architecture, and database management at the college level. He can be
security system, so their cracking efforts will be at best an educated reached by e-mail at wrachele@earthlink.net.
T he first inductions into my Delphi Hall of Fame are now a year old, and it’s time to introduce the second class
of inductees. There are dozens, maybe hundreds, of developers who could seriously be considered for inclusion
into this elite set, but only a few have been influential enough to be nominated and approved. Without further ado,
here are the newest members listed in first-name alphabetical order:
Anders Ohlsson, Borland Developer Support guru, for his tireless Borland-certified Delphi developers than any other employer
promotion and support of Delphi, and for his involvement in Code- (http://www.marotzdelphi.com).
Central, such as his contribution to the ClientDataSet grid compo-
nent (ID#: 15099, co-written with John Kaster). Maxim Peresada, for Torry’s Delphi Pages (http://www.torry.net).
Bill Todd, for the two editions of his excellent database-centric book, Nick Hodges, for his leading role in pioneering the use of Delphi in
Delphi: A Developer’s Guide (published by M&T Books; co-written by creating Internet applications, and his inestimable TSmiley component.
Vince Kellen, et al.), and a host of well-written, wide-ranging articles
for Delphi Informant Magazine. Philippe Kahn, for founding Borland and infusing it with its distinct
flavor and attitude.
Cary Jensen, for his lead role in the book Delphi in Depth [Osborne/
McGraw-Hill, 1996], and his clearly written articles in Delphi Informant Stefan Hoffmeister, for his key role in GExperts, work on Kylix, and
Magazine, especially his “DBNavigator” series. Like Bill Todd, his articles superlative responsiveness on newsgroups.
frequently make important contributions to the Delphi literature.
Team JEDI (Joint Endeavor of Delphi Innovators), for making vari-
Chad Hower, for his Winshoes/Internet Direct (Indy) components, ous C-based APIs accessible to the Delphi masses by creating Object
http://www.nevrona.com/indypro. Pascal wrappers for them. They’re available via their free JCL, which
contains hundreds of utility procedures and functions, as well as non-
Dale Fuller, President and CEO of Borland, for bringing back the visual components (http://www.delphi-jedi.org).
pride, fire, and Borland name.
Wayne Niddery, for his Free Delphi Stuff Web site (http://
Dan Miser, for his above-and-beyond-the-call-of-duty contributions home.ican.net/~wniddery/freedelphistuff.html), and his book Bor-
to the MIDAS/multi-tier community, including his work on land Delphi How-To [SAMS, 1995], co-written with Gary Frerking
MIDAS itself, and support in related newsgroups, e.g. and Nathan Wallace.
http://www.distribucon.com.
Zarko Gajic, leader of the About Guide to Delphi Programming
Earl F. Glynn, for his efg’s Computer Lab Web site, which Web site (http://delphi.about.com) and its free newsletter. Sign up at
contains vast amounts of Delphi graphic examples and code http://delphi.about.com/library/blnewsletter.htm.
(http://www.efg2.com/lab).
That’s it for this year! If there’s anyone that you feel deserves to be in
Erik Berry, for his role as the current lead of the virtually indispens- the Delphi Hall of Fame, feel free to nominate him or her. To do so,
able open-source Delphi add-in GExperts (http://www.gexperts.org). provide me with all the pertinent details: name, Delphi-related feat, etc.
and message me at bclayshannon@earthlink.net. If you missed them, you
Gerald Nunn, for his role as the original lead of GExperts. can access last year’s Hall of Fame online at http://www.DelphiZine.com/
opinion/2000/09/di200009bp_o/di200009bp_o.asp. ∆
Jeff Overcash, for the InterBase Express components, which allow us
to bypass the BDE and use the world’s greatest development tool with — Clay Shannon
the world’s greatest database.
John Kaster, for his “Behind the Screen” series on the Borland
community site (http://community.borland.com), pivotal role in
and contributions to CodeCentral (http://community.borland.com/ Clay Shannon is an independent Delphi consultant, author, trainer, and mentor
codecentral), tireless promotion of Delphi and Kylix, and constant operating as Clay Shannon Custom Software. He is available for remote development
monitoring of the Delphi and Kylix newsgroups. or short-term on-site assignments in the Coeur d’ Alene, ID/Spokane, WA area. Clay
is the author of Developer’s Guide to Delphi Troubleshooting [Wordware, 1999],
Marotz Delphi Group, for their vision in adopting Delphi very which will soon be revised. To view his resume, go to http://www.sysadminsrus.net/
early on, their ASP Express components, and boasting more clayshannon/ClayShannon.doc. You can reach him at bclayshannon@earthlink.net.
D uring my first year of writing this column I devoted several articles to Delphi Internet sites, exploring author
sites, vendor sites, and more general (non-commercial) sites. I was inspired by one of Richard Wagner’s last
“File | New” columns (July, 1997) entitled “Delphi on the Web.” In it he concentrated on a handful of his favorite
sites. We’ll start here by taking a fresh look at two of the sites he praised that continue to flourish, sites I call
enduring classics. We’ll also discuss other good starting points. After that we’ll discuss educational sites and other
sources of components and tools. We’ll conclude with some newer sites that bear watching.
Enduring classics and other good starting points. Two sites that A more general site, Delphi and Pascal Programming Tutorials
Richard Wagner wrote about four years ago remain at the top of (http://ourworld.compuserve.com/homepages/TK_Boyd/Tut.htm)
many developers’ list of favorites. The Delphi Super Page (DSP) includes information on a variety of Delphi and Pascal topics, conve-
(http://delphi.icm.edu.pl), for which its creator Robert Czerwin- niently grouped by level of difficulty. The site is inspired by the book
ski won the 1998 Spirit of Delphi Award, continues to be one Borland Delphi How-To by Frerking, Wallace, and Niddery. As the author
of the best sources for Delphi components and other resources. describes them, most entries are “self-contained explanations of how
I doubt there is another single source with more components to accomplish a specific task, or use a particular component of the
and tools. The other perennial favorite, Torry’s Delphi Pages language.” This site appears most useful to the newer Delphi developer.
(http://www.torry.net) also won the Spirit of Delphi award, and
also includes a large number of tools and components. Its organiza- About Delphi Programming (http://delphi.about.com/compute/
tion and layout are superb, and its main page includes a link to the delphi/) is a rich site with much to offer. It includes a section
Delphi Programming Source Code (DPSC) Web site’s list of top for beginners, and covers many popular subjects, including database
Delphi sites (http://www.sandbrooksoftware.com/TS/index.shtml) development, graphics, multimedia, Object Pascal, the VCL, and
to which I referred while writing this column. more. There’s a small “How-to” section and examples of complete
Delphi projects. You can even explore the lighter side of program-
All of these sites are great places to begin a search for Delphi ming in the “Humor and Fun” section.
components and tools. If you are searching for Delphi informa-
tion and news, however, then a site with well-organized links such Robert Vivrette’s UNDU (Unofficial Newsletter of Delphi Users at
as DPSC is your ticket. One older site from which to launch is http://www.undu.com) remains one of my favorites, particularly for
Richey’s DELPHI-BOX (http://inner-smile.com/delphi5.htm). It the quality of material offered. In addition to the fine articles going
includes many links, all organized by categories such as news, tips back several years, you can find Robert’s favorite links, job sites, and
and tricks, tools, general Delphi, and more. The Research page user groups.
contains various search engines, some general, others devoted to
the Borland Web site or its newsgroups. An even better choice Delphi City (http://delphicity.net) is a small but attractive site. While the
is Delphi32.com (http://www.delphi32.com). One great feature of number of components available may not be as high as DSP or Torry’s,
this site is that it provides the latest Delphi news with appropriate there are some very specialized ones here. I was intrigued by some of the
links on its initial page. specific functionality covered in the multimedia collection; I’ll be coming
back to download some of them. You might also want to check out the
Delphi education and more. Some Delphi sites are general, covering MAS Delphi Page (http://w1.545.telia.com/~u54503556). The author
a variety of issues; others are more specific, concentrating on a single provides many of his own components, tools, and writings. The latter
topic. EFG’s Delphi Algorithms is one of the latter. Located at include some interesting topics.
http://www.efg2.com/Lab, it contains a plethora of links to sources
of information on Delphi algorithms, including just about every New sites to keep an eye on. As I was working on this column
category you can imagine from mathematical to sorting, game-related I received an e-mail from someone asking if I would contribute
to artificial intelligence. an article to his new site. Instead, I decided to check it out. It’s