0305
0305
0305
2003
Volume 9 Number 5
INSIDE
O N
T H E
C O V E R
On the Net
IntraWeb Intro
Noel Rice introduces IntraWeb with nothing less than six complete example
projects. Bundled with Delphi 7, the AtoZed tool offers true RAD Web
development that makes building Web apps as easy as creating standard
Windows programs. Be careful, though; Noel warns that IntraWeb is addictive.
Delphi Informant
www.DelphiZine.com
F E A T U R E S
On Language
10
After Errors
If you dont already employ exceptions in your Delphi code shame on you!
and read Bill Todds exception primer immediately. And even if you already
regularly use try..finally and try..except blocks, Ill bet there are still some
valuable insights in Bills latest offering.
.NET/Win32 Interop
Building a .NET-ready
Greater Delphi
15
$5.99 US
Employing C#
Framework
Development
$8.99 CAN
Foundation Class
19
In Development
25
A .NET-ready Framework
Years in the making, Bob Fleischman and Seth Weiner take advantage of
Delphi interfaces to create a framework that allows Delphi developers to enjoy
the benefits of .NETs data-type object wrappers. They also offer compelling
arguments for the adoption of OOP, and of course example code.
30
R E V I E W S
32 PrintDAT!
34
1
D E PA R T M E N T S
2 Toolbox
36 File | New by Alan C. Moore, Ph.D.
T O O L B O X
combit Announces List & Label 9.0
FutureWare Releases
Palm Conduit Installer
Object
FutureWare announced Palm Conduit
Installer Object, an object used to examine, install, register, and uninstall Palm
conduits, programs, databases, and notifiers. It is a validator and wrapper for
the various Palm API calls. By hiding
the complexity and inter-dependence of
their use it allows an auto-installer to be
integrated quickly and easily, without
requiring any specialized knowledge.
The object is primarily used to
automatically install a Palm conduit
and the associated Palm programs
(and optional notifier) when the main
program is started, without requiring any user action. Documentation
includes full object description, tips
and tricks, references, and code samples from production products. Palm
Conduit Installer Object is compatible
with Delphi 5, 6, and 7.
For more information, or to download
a free evaluation copy, visit the FutureWare Web site.
FutureWare SCG
Price: US$29
Contact: (714) 446-0765
Web Site: www.futurewaredc.com/pcio
T O O L B O X
ModelMaker Code Explorer 1.14
ModelMaker Tools released ModelMaker synchronizes the editors method decCode Explorer 1.14 for Delphi 5, 6, and
laration header with implementation
7. This code browser and refactoring and header, and vice versa; Extract Methrestructuring tool is a substitute for the
od, which creates a new method in the
standard Code Explorer that ships with
current class using the selected text
Delphi. It shows classes (inheritance)
as main body; Extract Class, Interface,
and members (fields, methods, properwhich inserts a new class and converts
ties) in two views. It features InstantEdit- the selected members; Add field, mething and IntelliReplace; editing is as simod and property, which adds a new
ple as selecting options in dialog boxes
member and takes the word at the curand performing a drag and drop.
rent IDE editor position as input; Copy
There are many new features in
to/Move to, which copies the selected
this updated version. Most notably is
members to another class or interface;
IntelliReplace, which acts like a smart Create Factory Method, which creates
and instant whole words search
a new class method returning a new
and replace. For example, class and
instance of the class; Introduce Paramfield name changes are propagated in
eter Object, which converts method
source code.
parameters to a new class with one
Many new refactorings have
property for each parameter; Rename
been added, including Synchronize
Parameter, which renames a methods
Implementation/Declaration, which
parameter and propagates the new
Programming .NET
Web Services
C++Builder 6
Developers Guide
Programming
ASP.NET
UML in a
Nutshell
Sinan Si Alhir
OReilly & Assoc.
ISBN: 1-56592-448-7
Cover Price: US$24.95
(273 pages)
www.oreilly.com
O N
INTRAWEB
T H E
N E T
DELPHI 7
By Noel Rice
IntraWeb Intro
Truly RAD Web Development
P
A)
B)
C)
D)
E)
On
the
N e t
IntraWeb Intro
On
the
N e t
IntraWeb Intro
IntraWeb Components
Use a sampling of IntraWeb Standard components to get
a feel for the flow of IntraWeb development. For example,
the TIWRegion component is a substitute for TPanel. Set
the Align and Anchor properties to manage real estate on
the form. A favorite technique is to set all four anchors
to False, which causes the region to stay centered as
the browser window is sized. Setting the region color to
clGray, or another light, neutral color, provides a clean
entry area on the form. When displayed in the browser
the region resolves to a standard HTML table.
Does version 5.1 make things any easier? In version 5.0 you
must select from a slew of icons, then save all of your files
before you can even start. In version 5.1 you get a wizard to
simplify the process. Just choose File | New | Other | IntraWeb |
IntraWeb Application Wizard (see Figure A).
On
the
N e t
IntraWeb Intro
Figure 7: Iterate the controls array for a region looking for checkboxes.
On
the
N e t
IntraWeb Intro
<H1>TIWText Properties</H1>
Set <i>RawText</i> to <b>True</b> to pass formatted HTML to the browser.
Set <i>WantReturns</i> to <b>True</b> so that a line break in the Lines
property will show in the browser. Set WantReturns to <b>False</b>
if you want the browser to manage the line breaks.
IntraWeb also ships with three other component tabs. Components on the IntraWeb Control tab fall loosely into three categories:
Components that integrate with other non-IntraWeb
systems like WebBroker and WebSnap.
Components called Layout Managers can extend the
IntraWeb UI. For example, you could write an XML/XSL
Layout Manager and use it instead of the default layout.
Client side datasets hold lists of information in JavaScript
arrays on the client to prevent trips back to the server. The
two components implemented are IWClientSideDataSet and
IWClientSideDataSetDBLink.
IntraWeb Client Side components contain label, navigator, grid,
and charting components that read from IWClientSideDataSet
and IWClientSideDataSetDBLink. Data-aware components are
grouped on the IntraWeb Data tab of the Components palette,
and have counterparts for many of the standard Data Controls, including the database navigator, grid, label, edit, image,
checkbox, lookup lists, and combo boxes.
Figure 11: IntraWeb data-aware controls displaying live data in the browser.
On
the
N e t
IntraWeb Intro
procedure TformMain.IWDBGrid1Columns0nClick(
ASender: TObject; const AValue: string);
var
SDecodedValue: string;
begin
SDecodedValue := HTTPDecode(AValue);
if SDecodedValue <> '' then
tblEvents.Locate('Event_Name', SDecodedValue, []);
end;
Figure 13: Using the columns OnClick event handler for navigation.
O N
L A N G U A G E
ERROR HANDLING
EXCEPTIONS
DELPHI 1-7
By Bill Todd
After Errors
Using and Handling Exceptions
You dont
There are two reasons you need
to interrupt and take control of
a try..finally
this process. First, you may need
you create an object.
to take action to return your
application to a safe and stable
Figure 3 shows a snippet of
state. Second, you may want to handle the exception in
code that creates an instance of a TEdit component.
some way other than letting the call stack unwind and an
The owner, a form in this case, is passed as a paramerror message appear.
eter to the constructor. This means that when MainForm
is destroyed, it will first free all the components that it
Ensuring That Code Executes
owns. Because you know that the TEdit instance to which
Figure 1 shows a code snippet that creates an instance
MyEdit points will be freed when its owner is destroyed,
of the TStringList class, does some stuff, then frees
you dont need a try..finally block to ensure it will be
the instance pointed to by the MyList variable. If the
freed. You can use a try..finally block if you wish, but
code that does some stuff raises an exception, your
theres no potential for a memory leak if you dont.
application will have a memory leak, because the function
that contains this code will return without executing the
var
MyList: TStringList;
call to MyList.Free.
Figure 2 shows the same snippet with a try..finally block
added. The try..finally construct tells Delphi to try to
execute the code between the try keyword and the finally
keyword, and no matter what happens always
10
begin
MyList := TStringList.Create;
...do some stuff...
MyList.Free;
On
Language
After Errors
var
MyList: TStringList;
begin
MyList := TStringList.Create;
try
...do some stuff...
finally
MyList.Free;
end;
var
NameEdit: TEdit;
begin
NameEdit := TEdit.Create(MainForm);
NameEdit.Parent := MainForm;
NameEdit.Top := 10;
NameEdit.Left := 10;
var
Buff: PChar;
begin
GetMem(Buff, 512);
try
...do some stuff...
finally
FreeMem(Buff);
end;
procedure TForm1.MathErrorWithMessageClick(
Sender: TObject);
var
i, j, k, l, m: Integer;
begin
i := 23;
j := 0;
l := 2;
m := 4;
try
k := i div j * (l div m);
except
on EDivByZero do begin
k := 0;
MessageDlg('Divide by zero error',
mtError, [mbOK], 0);
end;
on EIntError do begin
k := 0;
MessageDlg('Integer math error.',
mtError, [mbOK], 0);
end;
end;
{ Display the result. }
Result.Caption := IntToStr(k);
end;
On
Language
After Errors
procedure TForm1.MathErrorWithMessageClick(
Sender: TObject);
var
i, j, k, l, m: Integer;
begin
i := 23;
j := 0;
l := 2;
m := 4;
try
k := i div j * (l div m);
except
on EDivByZero do begin
k := 0;
MessageDlg('Divide by zero error',
mtError, [mbOK], 0);
raise;
end;
on EIntError do begin
k := 0;
MessageDlg('Integer math error.',
mtError, [mbOK], 0);
raise;
end;
end;
{ Display the result. }
Result.Caption := IntToStr(k);
end;
type
EMyCustomException = class(Exception);
EDeleteNotAllowedException = class(EMyCustomException);
EInsertNotAllowedException = class(EMyCustomException);
On
Language
After Errors
try
k := i div j * (l div m);
except
on e: EDivByZero do begin
k := 0;
e.Message = Both j and m must be greater than zero.;
e.HelpContext = 123;
raise;
end;
end;
13
On
Language
After Errors
Application.OnException := AppOnException;
14
G R E A T E R
COM+
D E L P H I
INFORMATION SHARING
DLLS
DELPHI 5
By Matthew Hess
S
P
M
15
Greater
Delphi
Task
Compute SPM
15
47
172
547
Is there anything for which you shouldnt use the SPM? Its
important to understand that the SPM is not a replacement for a
database. The SPM has a simple locking scheme that is fast and
well suited for quick reads, but its not really up to database
standards. Second, the SPM isnt transactional. If something
goes wrong, you have no way to roll back a change to the SPM.
Most importantly, the SPM isnt persistent. When your COM+
application shuts down, the contents of the SPM are lost.
I should mention in this context that the default timeout of
a COM+ application is three minutes. This means that three
minutes after your last object is freed from memory, your
COM+ application will shut down and the SPM will be reset.
You may want to change the default timeout, or take other
measures (such as guaranteeing that at least one object will
be created every three minutes) to make sure your application
doesnt timeout. Conversely, you may want to take advantage
of this behavior to force the SPM to refresh.
Setting Up
Enough with the theory, lets dig into the details. The code
in this article references the COM+ Services 1.0 type library.
To import the type library in Delphi 5, go to Project | Import Type
Library, and select COM+ Services Type Library (Version 1.0), which is
based on the COMSVCS.DLL. Create a unit somewhere (see
Figure 3), add the resulting unit (COMSVCSLib_TLB) to your
projects and units that use the SPM and other COM+ services.
Getting a Shared Property
A piece of information in the SPM is called a shared property.
Properties are organized into shared property groups for locking
16
Greater
Delphi
ISharedPropertyGroupManager = interface(IDispatch)
['{2A005C0D-A5DE-11CF-9E66-00AA00A3F464}']
function CreatePropertyGroup(const name: WideString;
var dwIsoMode: Integer; var dwRelMode: Integer;
out fExists: WordBool): ISharedPropertyGroup; safecall;
function Get_Group(const name: WideString):
ISharedPropertyGroup; safecall;
function Get__NewEnum: IUnknown; safecall;
property Group[const name: WideString]:
ISharedPropertyGroup read Get_Group;
property _NewEnum: IUnknown read Get__NewEnum;
end;
Notice also that this function requires that the caller provide a
default value to use, in case the group or the property is
not found. This is convenient, because it saves the caller
from having to test the result for an unassigned condition.
Setting a Shared Property
Not surprisingly, to set a shared property we must
also first reference its group. In this case, well use
the CreatePropertyGroup function, which is a bit
more complicated. CreatePropertyGroup expects four
parameters, some of which deserve a little explanation.
First is the in parameter of the name of the group. Last
is a Boolean out parameter which will be True if the
property group exists, and False if it doesnt. In between
come two interesting in-out integer parameters,
dwIsoMode and dwRelMode.
dwIsoMode sets the isolation or locking behavior for the
group. It has two values, LockSetGet, which is the default,
and LockMethod. LockSetGet indicates that locking happens
17
Database
Database
DBName
DBName
UserID
Password
x UserID
Password
LockMethod
LockGetSet
Greater
Delphi
procedure SetSharedProperty(
const sGroup, sProp: WideString; vValue: Variant);
var
pSPGM: SharedPropertyGroupManager;
pGroup: ISharedPropertyGroup;
pProp: ISharedProperty;
lExists: WordBool;
i, j: Integer;
begin
try
pSPGM := CoSharedPropertyGroupManager.Create;
i := LockMethod; // Accessing group will lock it.
j := Process;
pGroup :=
pSPGM.CreatePropertyGroup(sGroup, i, j, lExists);
pProp := pGroup.CreateProperty(sProp, lExists);
pProp.Value := vValue;
finally
if pProp <> nil then pProp := nil;
if pGroup <> nil then pGroup := nil;
if pSPGM <> nil then pSPGM := nil;
end;
end;
the SPM and the various functions that are used to calculate the
shared properties in the first place (see Figure 8).
Note that no client will ever call GetDomain,
GetSimpleSharedProperty, or QueryDomainfromAD. These
functions are all hidden behind the Domain property.
Conclusion
As weve seen, the COM+ SPM is fairly easy to use and
can provide significant performance gains for your COM+
applications. Download the accompanying sample project
to see it in action. Like the sample project from last
months article on COM+ Queued Components, this one
requires that you set up a COM+ application and launch
it from a simple ASP page. Please follow the directions
from last month for creating the application, installing the
components, running your code in the Delphi debugger,
and launching it all from an ASP script. I think youll
find that the COM+ SPM is an indispensable part of your
COM programmers toolkit.
The demonstration project and DLL referenced in this
article is available for download on the Delphi Informant
Magazine Complete Works CD located in INFORM\2003\
MAY\DI200305MH.
18
F O U N D A T I O N
C# ASSEMBLIES
C L A S S
By Fernando Vicaria
.NET/Win32 Interop
Part II: Clearing Common (and Uncommon) Interoperability Hurdles
In this final installment, well discuss some of the more frequent issues concerning platform and development language
interoperability. Well also continue with more examples of how
to dynamically load and use an unmanaged DLL in your .NET
apps, i.e. Dynamic PInvoke. Well also learn how to use managed code from your Win32 applications (Inverse PInvoke),
demonstrate a few techniques to access Win32 code from .NET
(and vice versa), demonstrate some of the language-independent capabilities of .NET, and show how Delphi developers
can take advantage of the enormous amount of code thats
already out there for the .NET platform.
Lets start with the simplest case possible: We need to consume some type residing in an assembly (DLL) designed in
C#. Then well do the opposite and have C# use code from
an assembly designed in Delphi for .NET. Well also see
how to make Win32 applications (designed with Delphi 7)
consume assemblies designed for the .NET platform. Finally, well see two different techniques to dynamically load
19
Figure 1: Factorial.cs.
Foundation
Class
.NET/Win32 Interop
program UseCSharpDll;
uses Functions;
{$APPTYPE CONSOLE}
var
temp: string;
i, j: Integer;
begin
Writeln;
WriteLn('Enter an integer: ');
Read(temp);
i := StrToInt(Temp);
j := Functions.Factorial.Calc(i);
WriteLn('Factorial of ' + IntToStr(i) + ' is: ' +
IntToStr(j));
Writeln;
end.
Figure 2: UseCSharpDll.dpr.
not going to use any of its types in our Morpheus example. I included it to show you how to combine multiple
C# files into a single namespace inside an assembly. Both
files (Factorial.cs and DigitCounter.cs) contribute to the
same namespace (Functions) and are linked into the same
assembly (Functions.dll).
Now lets take a look at the code for our project; its shown
in Figure 2. As you can see, this example is extremely simple. It asks you to enter an integer as input, then outputs the
factorial of that integer. The project uses the Functions DLL,
so the compiler needs to be able to find it at compile time.
To compile the DLL we use the C# compiler that comes free
with the .NET SDK; simply type the following at the DOS
prompt (without the line break of course):
csc /target:library /out:Functions.dll Factorial.cs
DigitCounter.cs
library Functions;
type
TFactorial = class(TObject)
public
class function Calc(i: Integer): Integer; static;
end;
{ TFactorial }
class function TFactorial.Calc(i: Integer): Integer;
begin
Result := 1;
if i > 1 then
Result := i*Calc(i -1);
end;
begin
end.
The first line generates the Morpheus DLL, and the second
the C# application. I also added a batch file for this example,
so you dont need to open the command prompt.
Consuming .NET Assemblies from
Win32 Applications
Now lets look into the situation where we have a Win32
application that needs to call code inside a .NET assembly.
Its unlikely that youll run into this, but Ive decided to
cover it for the sake of completeness.
Foundation
Class
.NET/Win32 Interop
Member Name
AutoDispatch
AutoDual
None
Description
Indicates that the class only supports late
binding for COM clients. A dispinterface
for the class is automatically exposed to
COM clients upon request. To prevent
clients from caching the dispid of the
interface, the type library produced by
TlbExp doesnt contain type information
for the dispinterface. The dispinterface
doesnt exhibit the versioning problems
described in ClassInterfaceAttribute,
because clients can only late-bind to the
interface. This is the default setting for
ClassInterfaceAttribute.
Indicates that a dual class interface is
automatically generated for the class
and exposed to COM. Type information
is produced for the class interface and
published in the type library. The use of
AutoDual is strongly discouraged, because
of the versioning limitations described in
ClassInterfaceAttribute.
Indicates that no class interface is
generated for the class. If no interfaces
are implemented explicitly, the class will
only provide late bound access through
IDispatch. Users are expected to expose
functionality through interfaces that
are explicitly implemented by the class.
This is the recommended setting for
ClassInterfaceAttribute.
Foundation
Class
.NET/Win32 Interop
any additional
parameter that
might be on the
stack.
library Invoke;
procedure Call(FuncPtr: Longword); stdcall;
asm
pop ebp
pop edx
pop ecx
push edx
jmp ecx
end;
exports
Call;
begin
end.
program UseDll;
uses
System.Runtime.InteropServices, Invoke;
//
//
//
//
//
[DllImport('Invoke', CharSet=CharSet.Unicode,
EntryPoint = 'Call')]
function ProxyFunc(funcptr: IntPtr; hWnd: Integer;
lpText, lpCaption: string; uType: Integer): Integer;
external;
var
hDLL, FuncAddr: IntPtr;
begin
// Dynamically linking to an API.
hDLL := LoadLibrary('user32');
FuncAddr := GetProcAddress(hDLL, 'MessageBox');
ProxyFunc(FuncAddr, 0,
'Unmanaged call to MessageBox inside User32.dll',
'.NET -> Win32', 0);
FreeLibrary(hDLL);
end.
Figure 8 shows
a Morpheus
application that
uses the Invoke DLL to forward a call to an API function at
run time. I used a console application to demonstrate this
technique, but you could easily create a GUI application,
and pass the DLL and function name via a TOpenDialog and
a TEdit control.
Foundation
Class
.NET/Win32 Interop
program APIWrapper;
{$APPTYPE CONSOLE}
uses
APIWrap;
var
RetValue: Integer;
Args: array of TObject;
begin
// Prepare agruments array...
SetLength(Args, 4);
Args[0] := TObject(LongWord(0));
Args[1] := string(
'Unmanaged call to MessageBoxW inside User32.dll');
Args[2] := string('.NET -> Win32');
Args[3] := TObject(LongWord(0));
// Call API...
RetValue := Integer(InvokeWin32API('MessageBox',
'user32.dll', TypeOf(Integer), Args));
Console.WriteLine(
'MessageBox returned: ' + IntToStr(RetValue));
end.
Summary
Concluding our two-part series delving into .NET/Win32
interoperability, we looked into some of the techniques
available to get hold of existing Win32 code from inside
a .NET application, as well as how to design a Win32
application capable of calling code that resides inside a
.NET assembly. We also saw how Morpheus users can use
the available code base written in a variety of languages
that also target the .NET platform.
Hopefully youve seen enough here to pique your interest. If
you havent already, pick up a copy of Delphi 7 and dive in.
Foundation
Class
.NET/Win32 Interop
Further Reading
.NET Interoperability: .NET Win32 by Brian Long,
http://www.blong.com/Conferences/BorConUK2002/
Interop1/Win32AndDotNetInterop.htm#InversePInvoke
Introducing Microsoft .NET by David Platt (ISBN 0-73561571-3).
Microsoft .NET/COM Migration and Interoperability,
http://msdn.microsoft.com/library/default.asp?url=/
library/en-us/dnbda/html/cominterop.asp
Eight sample projects accompany this article and are available for download on the Delphi Informant Magazine Complete Works CD located in INFORM\2003\MAY\DI200305FV.
}
// Indicates that a dual class interface is automatically
// generated for the class and exposed to COM. Type
// information is produced for the class interface and
// published in the type library. The use of AutoDual is
// strongly discouraged because of the versioning
// limitations described in ClassInterfaceAttribute.
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ClassB
{
public ClassB() { }
24
I N
.NET
D E V E L O P M E N T
OOP
INTERFACES
DELPHI 5-7
A .NET-ready Framework
Forward-looking Classes Embrace OOP
In
Development
A .NET-ready Framework
IADS_Readable = interface(IADS_Comparable)
['{1169DD10-5361-45DA-BC46-35C1E647A702}']
function stringValue: string;
function toString: IADS_String;
function toVariant: Variant;
function hashCode: Integer;
end;
26
In
Development
A .NET-ready Framework
person.setPrefix(TADS_String.valueOf(
tblPersons.FieldByName('prefix').Value));
person.setFirstName(TADS_String.valueOf(
tblPersons.FieldByName('first').Value));
person.setMiddleName(TADS_String.valueOf(
tblPersons.FieldByName('middle').Value));
person.setLastName(TADS_String.valueOf(
tblPersons.FieldByName('last').Value));
person.setSuffix(TADS_String.valueOf(
tblPersons.FieldByName('suffix').Value));
person.setSiblingCount(TADS_Integer.valueOf(
tblPersons.FieldByName('siblings').Value));
person.setBirthDate(TADS_DateTime.valueOf(
tblPersons.FieldByName('birth_date').Value));
In
Development
A .NET-ready Framework
txtFullName.Text := person.getFullName.stringValue;
txtFirstName.Text := person.getFirstName.stringValue;
txtHasSiblings.Text := person.getHasSiblings.stringValue;
txtSiblings.Text := person.getSiblingCount.stringValue;
txtAge.Text := person.getAge.stringValue;
txtBirthDate.Text := person.getBirthDate.stringValue;
txtLastFirst.Text :=
person.getFullNameLastFirst.stringValue;
Memo1.Lines.Text := pPerson.asXML.stringValue;
if stringOne.subString(2,4).compareToIgnoreCase(
TADS_String.valueOf('hi')) = 0 then
You must instantiate a TADS_String with that value to pass the parameter. If youre not paying close attention, however, you
could miss that this line of code causes a memory leak! Theres no handle to the instance created by TADS_String.Create,
so theres no way to free it! Therefore, all our objects hide their constructors by making the methods protected. We then
implement valueOf functions, which return an interface to a newly instantiated object rather than the object itself. Using
the line below, the temporary hi object is cleaned up as soon as there are no more references to it, which is immediately
after the compareToIgnoreCase function returns:
if stringOne.subString(2,4).compareToIgnoreCase(
TADS_String.valueOf('hi')) = 0
A similar issue exists with the string-handling routine return values. Because these objects are immutable, each of these
methods returns a new instance of the relevant object. If the subString function were to return a TADS_String object, for example,
there would again be no way to free it. Thus, it is necessary for these functions to return reference-counted interfaces as well.
28
In
Development
A .NET-ready Framework
T O W A R D
HUMOR
T H E
L I G H T
DELPHI 1-7
By Loren Scott
Time Management
As a software developer, the concept of time takes on an
entirely different dimension from that of a normal person
including all non-programmer spouses.
Its a safe bet that in every household that is currently occupied by a programmer and their non-coding significant other,
the following short sentence has been spoken more than once
usually between the hours of midnight and 2:00 am:
This program is a piece of crap! Hell, I could write a program better than this in two days!
This is then followed by five minutes of fierce typing followed by either: A) a shout of Yes! and the sound of the
30
To w a r d
the
Light
Start Coding
Complile
Continue Coding
Link
Run
GO TO BED
NO
Does it
work?
YES
Loren Scott is the co-author of Delphi Database Development (M&T Books, 1996)
and has served in the trenches in software tech support, development, marketing,
consulting, and management since 1990. Currently serving time as a Senior
Software Engineer for a large Southern California company, Loren can be reached
via e-mail at [email protected].
31
N E W
&
U S E D
By Clay Shannon
PrintDAT!
Simple Printing Tool Delivers Quick Solutions
New
&
Used
PrintDAT!
Figure 3: The General tab of the Report Options dialog box. There are also tabs
for Output, Style, Titles, and Setup that provide end users enormous opportunity
to customize reports.
33
Documentation
PrintDAT! is very well documented, especially for such
an intuitive tool. It comes with an Acrobat tutorial
(TUTORIAL.pdf) and a developer help file (PD_DEV.hlp), as
well as an end-user help file (PD_USER.hlp), which provides
context-sensitive help from within the built-in previewer. Of
additional benefit to the developer are the comprehensive
demonstration programs that ship with PrintDAT!
Deployment
If you want to set the report users report settings,
deploy the .pxt file, which PrintDAT! automatically
creates when you modify any of the report settings as
different from their default values (as long as you answer
in the affirmative when asked if you want to save the
modified settings). These settings are not global, or even
application-specific, but are report-specific, so each report
can retain unique settings.
The Bottom Line
PrintDAT! is an awesome product. Its not as flexible
and full-featured as some other reporting tools, such as
ReportBuilder and Ace Reporter, but if all you need are
simple reports, and dont require full control over every
aspect of visual presentation, dont hesitate to use PrintDAT!
Clay Shannon is a Borland and PDA-certified Delphi 5 developer and the author
of The Tomes of Delphi: Developers Guide to Troubleshooting (Wordware,
2001) as well as the novel he claims is the strangest one ever written, The
Wacky Misadventures of Warble McGorkle (see http://www.winsite.com/bin/
Info?12500000036639 for more information about this and other novels he has
written). You can contact him at [email protected].
N E W
&
U S E D
By Lee Inman
New
&
Used
35
F I L E
N E W
File
New
37
Alan Moore is a professor at Kentucky State University, where he teaches music theory and humanities. He
was named Distinguished Professor for 2001-2002. He has been named the Project JEDI Director for 2002-2003.
He has developed education-related applications with the Borland languages for more than 15 years. Hes the
author of The Tomes of Delphi: Win32 Multimedia API (Wordware Publishing, 2000) and co-author (with
John C. Penman) of The Tomes of Delphi: Basic 32-Bit Communications Programming (Wordware Publishing,
2003). He also has published a number of articles in various technical journals. Using Delphi, he specializes in
writing custom components and implementing multimedia capabilities in applications, particularly sound and
music. You can reach Alan on the Internet at [email protected].