0307
0307
0307
2003
Volume 9 Number 7
INSIDE
ON THE COVER
Lifecycle Management
Delphi Informant
Breaking Encapsulation
Greater Delphi
14
Last month, Bill Todd described the security features of the .NET
run-time environment. This month, he explains how to use those
security features in your Delphi for .NET applications.
The Model Is
the Application
FEATURES
On Language
www.DelphiZine.com
Breaking Encapsulation:
Easier than You Think
Programming .NET
with Delphi Page 20
Page 15
Security
.NET-ready Source
Page 25
$5.99 US
Page 30
$8.99 CAN
Foundation Class
19
Need to get your Delphi apps to .NET now? Then youll want to
read along as Fernando Vicaria explores the Dfm2Pas utility, a
program that converts .dfm files into .NET-compatible .pas files.
Delphi at Work
23
Warning Signs
REVIEWS
26 CDK for Delphi
29
InfoPower 4000
33
RemObjects SDK
36
Mastering Delphi 7
36
1
D E PA R T M E N T S
2 Toolbox
38 File | New by Alan C. Moore, Ph.D.
T O O L B O X
Nevrona Provides Many Editions of Rave Reports 5.0
T O O L B O X
DLite 1.0 Build 7.1 Released
Software by Martin released DLite 1.0
Build 7.1. DLite is a Delphi companion
tool that allows you to edit, compile,
and run pre-existing Delphi projects.
DLite gives you access to the DCC32
command-line compiler to compile any
project using any installed version of
Delphi 2-7, including the Delphi for
.NET preview included in Delphi 7.
The editor has the familiar look and
feel of the Delphi IDE.
DLite has incorporated icons from
GlyFx (www.GlyFx.com). You can now
configure DLite to use these colorful and aesthetically pleasing icons in
all menus and the toolbar. If you use
the GlyFx icons, you also have the
option to use large icons in the toolbar.
DLites classic icons are still available. Along with new GlyFx icons,
you can also configure which buttons
appear in DLites toolbar.
The search and replace functionality
has been rewritten and improved to
act just like the Delphi IDE, giving you
such options as Case Sensitive, Whole
Words Only, Prompt on Replace, Direction, and Origin.
In addition, an
editor option to
toggle Overwrite
Blocks has been
added. With this
option enabled, a
highlighted block
will be overwritten
if text is typed in.
Otherwise, typedin text will be
appended to the
highlighted block.
An environment
option to Load
desktop files for
projects, project
groups and packages has been added. With this option
enabled, DLite will read the .dsk file
associated with the project, project
group, or package being loaded and
automatically load any files that were
on the desktop.
A new menu option called Resize
and Center has been added to the
View menu. Depending on your current
screen resolution, this menu allows
MsgConnect Released
EldoS Group released MsgConnect, an
open source cross-platform framework
for data exchange.
MsgConnect encapsulates low-level
transport protocols (currently TCP and
memory-mapped files) and provides a
uniform API for exchanging binary information on a single system or across a
network.
MsgConnect uses the paradigm of
messages, which are well known to
Windows developers. MsgConnect
provides optional support of Rijndael
(AES) encryption, ZLib compression and
CRC32/Adler32/MD5 integrity checking.
MsgConnect is available for a variety of
platforms and development tools, including Windows (Delphi, C++, VB, DLL/
ActiveX, .NET), Linux/Unix/QNX (Kylix,
C++/gcc), Windows CE, and Java 2
SE/EE. Palm and Java 2 ME support is
under development.
MsgConnect can be used in both opensource and commercial applications. See
Web site for licensing information.
EldoS Group
Contact: [email protected]
Web Site: www.msgconnect.com
T O O L B O X
Nix Software Solutions Announces Nix
Components 2.5
Nix Software Solutions announced
the availability of Nix Components 2.5,
a multi-purpose suite of more than 77
visual and non-visual components for
Delphi and C++Builder. In addition
to improvements and bug fixes, this
version introduces two new features
that add great value to the product:
FlySize and FlyDrag.
FlySize enables supporting controls
or their parent forms to be dynamically resized at run time. One mode
delivers a turnkey solution for presenting the user with resizable panels
and bevels in graphical and layout
design applications, whereas another
mode causes the border of a FlySizeenabled control to mimic its parent
forms sizing border, even if there is
none. FlySize makes it easy to give
borderless forms resizing capabilities:
simply drop a TNixPanel control on a
borderless form, align it to alClient, and
set its FlySize property to fsForm.
FlyDrag complements FlySize by
allowing visual controls or their parent
forms to be dynamically dragged at
run time. One mode makes the surface
of a FlyDrag-enabled control mimic
its parent forms title bar, even when
there is no such thing. This makes it
possible for end users to move border
and captionless forms simply by dragging FlyDrag-capable controls as if they
were virtual title bars.
To give developers complete and precise control over FlySize and FlyDrag,
supporting controls publish events
that are triggered immediately before
Developer Express
Acquires Eagle Software
Delphi Programming
with COM and ActiveX
C# for Dummies
Mission-Critical
Security Planner
V. Ponamarev
Charles River Media
ISBN: 1-58450-254-1
Cover Price: US$44.95
(312 pages)
www.charlesriver.com
Eric Greenberg
Wiley Publishing, Inc.
ISBN: 0-471-21165-6
Cover Price: US$35
(416 pages)
www.wiley.com
David S. Frankel
Wiley Publishing, Inc.
ISBN: 0-471-31920-1
Cover Price: US$40
(328 pages)
www.wiley.com
LIFECYCLE
MDA
MANAGEMENT
MODELMAKER
UML
DELPHI 7
By Peter Morris
Model-driven Delphi
Part I: The Model Is the Application
What Is MDA?
Model-driven architecture is a difficult technology to
describe briefly. First, its based on an application
model described using Unified Modeling Language (visit
www.uml.org for more information). This technique
allows the application designer to specify a list of classes,
inheritance, properties, relationships between classes, and
constraints.
Borlands implementation of MDA, Bold for Delphi, is
derived from the UML model. Your applications business logic is defined using Bold for Delphi. In addition,
your database (known in MDA as the persistence storage)
is created or even evolved (more on this later). The
Delphi 7 Architect trial includes Bold for Delphi. Until
recently, Bold for Delphi was a product of BoldSoft, a
Swedish company. Borland purchased this company at the
end of 2002.
Getting Started
First, you need to get the software required to start modeling
your applications. Heres what youll need:
Delphi 7 Architect trial, available from
www.borland.com/products/downloads/download_delphi.
ModelMaker trial, available from
www.modelmakertools.com/download.htm.
Microsoft Access. Although numerous databases are supported, this article uses a local Access database.
The order of installation is important, because each product
tries to detect what software is already installed on your system so that it can add plug-ins, etc. Use the following order:
1) Delphi 7.
2) ModelMaker; this will install plug-ins into Delphi.
3) Bold for Delphi; this will install plug-ins into Delphi and
ModelMaker.
Lifecycle
Management
Model-driven Delphi
= True
Lifecycle
Management
Member:
DateJoined: Date
MembershipNumber:
Model-driven Delphi
Integer
Employee:
DateJoined: Date
StaffNumber: Integer
DateLeft: Date
Again, use the Bold Tagged Values Editor to set AllowNULL
= True on each of these new attributes. Finally, save this
model as ClubMan.
Importing the Business Model into
Delphi
If this were a real-life application, your business model
would be designed to completion and probably by
someone else. The final UML diagrams may even have
been approved by the customer. For the purpose of this
article series, the model will be imported at various
stages of development to demonstrate the various stages
of developing a Bold for Delphi application.
First, start Delphi 7. Create a new application and name
the main form fmMain. Next, create a new data module
and name it dmClubMan. Rearrange the creation order
in the Project | Options menu so that the data module is
created before the main form. Save the application as
Inheritance, the form as MainForm, and the data module
as ClubManModule:
From the Bold Handles component tab, drop a
TBoldModel onto the data module. This component
will hold your UML business model imported from
ModelMaker.
From the Bold Handles component tab, drop a
TBoldSystemTypeInfoHandle onto the data module.
This component holds information about the model
in an efficient form by use of hash tables, etc.
From the Bold Misc component tab, drop a
TBoldUMLMMLink component onto the data module.
This will import a specific ModelMaker project into
your TBoldModel.
From the Bold Handles component tab drop a
TBoldSystemHandle component onto the data module.
This component holds all instances of your business
objects at run time.
The following properties must now be set:
1) BoldSystemTypeInfoHandle1.UseGeneratedCode = False
2) BoldSystemTypeInfoHandle1.BoldModel = BoldModel1
3) BoldSystemHandle1.AutoActivate = True
4) BoldSystemHandle1.SystemTypeInfoHandle =
BoldSystemTypeInfoHandle1
5) BoldUMLMMLink1.BoldModel = BoldModel1
6) BoldUMLMMLink1.FileName = filename to the
ModelMaker model
Figure 3 illustrates the relationships between the default Bold
for Delphi system components. First, a TBoldModel is needed
to store the business model. Next, a TBoldUMLMMLink is
needed to import a model into the BoldModel component.
Although its possible to alter the business model using
7
Lifecycle
Management
Model-driven Delphi
Now close the application. You may ignore the Dirty Objects
exception which is displayed; this is the correct behavior.
Conclusion
This first article in this series has demonstrated how to create
a very simple business model, import it into Delphi, generate a
database, and enter data. The article also demonstrates a very
powerful form of inheritance. And not a single line of source
code has been written.
Although impressive, the features demonstrated so far are basic
bread n butter features of Bold for Delphi. This is just the
tip of a very large iceberg. In future articles I will introduce you
to various other abilities of Bold for Delphi, such as relationships, object constraints, object subscriptions, auto forms, and
object space synchronization. See you then.
The files referenced in this article are available for download
on the Delphi Informant Magazine Complete Works CD
located in INFORM\2003\JUL\DI200307PM.
O N
L A N G U A G E
OBJECT-ORIENTED PROGRAMMING
ENCAPSULATION
DELPHI 1-7
By Peter Gatis
Breaking Encapsulation
Its Easy and Dangerous
long time ago (circa 1977), before objectoriented programming came of age, we
programmers were taught to keep related
On
Language
Breaking Encapsulation
type
TForm1 = class...
TMyObject = class(TObject)
private
FValue: Integer;
public
constructor Create;
destructor Destroy; override;
property Value: Integer read FValue;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
constructor TMyObject.Create;
begin
FValue := 7;
ShowMessage('OnConstruction: ' + IntToStr(FValue));
end;
destructor TMyObject.Destroy;
begin
ShowMessage('OnDestruction: ' + IntToStr(FValue));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyObj: TMyObject;
MyAddr: pointer;
begin
MyObj := TMyObject.Create;
MyAddr := @MyObj.Value;
Integer(MyAddr^) := 9;
MyObj.Free;
end;
interference. What has happened here? A completely private variable, insulated properly by a well-defined interface
allowing nothing but read-only access, has changed its value
between creation and destruction. Nothing within the class
definition would suggest that this sort of thing would ever
be expected or allowed. The original architect of the class
would have no idea that the behavior of the object might be
materially altered at run time, nor would the coder responsible for the implementation perceive any risk.
Breaking encapsulation here involves an invasive manipulation of the ADT, that is, an adjustment of some private
object data by an agent other than the object itself. Such
manipulations are often well-intentioned, and often occur in
unfortunate flashes of brilliance in the heat of development.
No compiler warnings appear. No object is safe.
Figure 2 provides a second example of aggressive interference. The code represents a form with one button on it.
Click the button in the IDE and youll see an event with
the message Form1 Event. At run time, the message
becomes My Event. Without hard hunting, theres no
hint in the IDE as to how this could have happened. On the
single form in the IDE, you may place a breakpoint on the
ShowMessage('Form Event') statement a statement which
clearly compiles and find that this breakpoint is never
10
type
TMyObject = class;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
FMyObj: TMyObject;
end;
TMyObject = class(TObject)
procedure MyEvent(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
FMyObj := TMyObject.Create;
Button1.OnClick := FMyObj.MyEvent;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FMyObj.Destroy;
end;
procedure TMyObject.MyEvent(Sender: TObject);
begin
ShowMessage('My Event');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Form Event');
end;
encountered. Weve successfully altered a non-virtual method, without a re-introduction or re-declaration of any sort.
Note that the encapsulation was broken by altering a private
variable of the object in a way that was never intended, but
difficult to prevent. The actual variable at issue in this case is
the private variable FOnClick: TNotifyEvent, as defined in the
TControl class definition in Controls.pas. This goes significantly
deeper into the code than anyone would care to look, and finding the disposed variable would not help trace the transgression anyway. If this form were multiply inherited, the point of
substitution where the original TNotifyEvent was lost would
be so thoroughly disguised that its existence might never be
deduced without help from the original architect.
Weak Encapsulation
Examples of broken encapsulation arent always so nasty.
Weak encapsulation often occurs as an intermediate step in
the evolution of an object, when the demands on the object
arent entirely clear. It may be a shortcut to get things working. Weak encapsulation simply means that interdependencies exist between objects interdependencies that have
not yet been designed out.
On
Language
Breaking Encapsulation
Listing One (on page 12) shows events calling each other
instead of properly encapsulating the business logic in
business objects and methods. Because events are so easy
to create in the IDE, theres a temptation to bury business logic in the event itself, rather than taking the time
to encapsulate business logic in business class definitions.
The effect of this is to have events that call each other
to access various segments of logic. This is bad enough.
Taken to an extreme, however, we may have different
forms calling each others events.
Over-encapsulation
Over-encapsulation brings new light to the whole subject of
encapsulation, by demonstrating that its possible to hide
too much. Far from accessing an object outside its intended
interface, it may not be possible to figure out how to access
an object at all, even as it sits quietly in the IDE. Two
examples follow: one using inheritance, and the other using
sequence as a means to effectively conceal the inner workings of an object so completely as to render it unusable.
The fact of the matter is that class definitions rarely fit into
a clean hierarchical structure. Defining objects that work
together intimately, without killing each other, can be a
fierce exercise. The problem is similar to that of designing a
complex relational database without breaking normal form.
Imagine writing an application to assign students to portables. In the depths of design, it seems very natural to have
each student include a reference to his or her homeroom
portable, and to have each portable include a list of all its
homeroom students, as shown in Figure 3. This is a beautiful design strategy right up to the point that something
has to be deleted. Deciding to go from 8 portables to 7 one
month before school starts will leave 12.5% of the student
population without valid pointers. The interface is not yet
well-defined; the FHomeroom variable is vulnerable. The
encapsulation is weak.
There are a number of remedies to this particular difficulty,
the most common of which is to establish rigorous testing
procedures to catch all the cases where anything is deleted.
The onus is placed on all interlinked objects to have twoway links, and any deletion method must check all its sister
TStudent = class(TObject)
private
FHomeroom: TPortable;
public
property Homeroom: TPortable
read FHomeroom write FHomeroom;
end;
TPortable = class(TObject)
private
FHomeStudents: TList;
public
property HomeStudents: TList
read FHomeStudents; // List of TStudent.
end;
11
On
Language
Breaking Encapsulation
12
On
Language
Breaking Encapsulation
13
G R E A T E R
MICROSOFT .NET
SECURITY
D E L P H I
DELPHI FOR .NET
By Bill Todd
Safety .NET
Part II: Security in Delphi for .NET Applications
The security policy you establish for the .NET run-time environment is always enforced, regardless of anything you do
or dont do in your code. However, you can control
when security violations will be detected, and how your
application will behave if it cannot get the permissions it
needs. You can request permissions in code using declarative
security, imperative security, or a combination of the two.
Using Declarative Security
Declarative security uses attributes to request
permissions. The easiest way to understand how the
attributes work is with a simple example. Assume youve
created a new permission set called Custom Intranet. The
Custom Intranet permission set grants all permissions
except File IO. You have assigned this permission
set to the LocalInternet_Zone code group, so that all
applications loaded from servers on your local network
will have all permissions except File IO.
Create the simple console application shown in Figure 1.
This program displays the message, Press Enter to create
file., then waits for you to press R. The only purpose
of this prompt is to let you see that the program is running.
Next, the program creates an instance of the TTestObject class
shown in Figure 2. The program calls the WriteTextFile and
Hello methods of the TTestObject instance, and ends. Notice
that there is no call to TestObj.Free, because in the .NET
environment the garbage collector will free TestObj when the
program ends.
14
program AssemPerm1;
{$APPTYPE CONSOLE}
uses
System.IO, System.Security.Permissions,
Test in 'Test.pas';
var
TestObj: TTestObject;
S: string;
begin
Console.WriteLine('Press Enter to create file.');
S := Console.ReadLine();
TestObj := TTestObject.Create;
TestObj.WriteTextFile;
TestObj.Hello;
end.
Greater
Delphi
Safety .NET
unit Test;
interface
uses
System.IO, System.Security.Permissions;
type
TTestObject = class(TObject)
public
procedure Hello;
procedure WriteTextFile;
end;
implementation
procedure TTestObject.Hello;
begin
Console.WriteLine('File Written!');
end;
procedure TTestObject.WriteTextFile;
var
OutFileStream: FileStream;
Writer: StreamWriter;
begin
OutFileStream :=
FileStream.Create('c:\temp\junk.txt', FileMode.Create);
Writer := StreamWriter.Create(OutFileStream);
Writer.WriteLine('Hello World!');
Writer.Flush;
Writer.Close;
end;
end.
program AssemPerm1;
procedure TTestObject.WriteTextFile;
var
OutFileStream: FileStream;
Writer: StreamWriter;
begin
try
OutFileStream := FileStream.Create(
'c:\temp\junk.txt', FileMode.Create);
Writer := StreamWriter.Create(OutFileStream);
Writer.WriteLine('Hello World!');
Writer.Flush;
Writer.Close;
except
on E: SecurityException do
Console.WriteLine('Security exception.');
end;
end;
RequestOptional lets you request permissions that your assembly would like to have, but that arent required to run. For
example, your application has the option to allow the user to
save current settings to a disk file, but the settings cannot be
saved unless the assembly has File IO permission. The following attribute requests File IO permission optionally:
{$APPTYPE CONSOLE}
uses
System.IO, System.Security.Permissions,
Test in 'Test.pas';
[assembly: FileIOPermissionAttribute(
SecurityAction.RequestMinimum, All='c:\temp')]
var
TestObj: TTestObject;
S: string;
begin
Console.WriteLine('Press Enter to create file.');
S := Console.ReadLine();
TestObj := TTestObject.Create;
TestObj.WriteTextFile;
TestObj.Hello;
end.
informs the .NET run-time environment that this assembly needs all File IO permissions for the c:\temp folder,
and all the files and folders below it in the folder tree.
SecurityAction.RequestMinimum indicates that the assembly
must have this attribute to run. With this attribute in place,
youll get an immediate error when you attempt to run the
program, and it will never begin executing. In addition to
the All property, the FileIOPermissionAttribute class also has
Append, Read, and Write properties.
15
[assembly: FileIOPermissionAttribute(
SecurityAction.RequestOptional, All='c:\temp')]
Greater
Delphi
Safety .NET
procedure TTestObject.WriteTextFile;
var
OutFileStream: FileStream;
Writer: StreamWriter;
FioPerm: FileIOPermission;
begin
try
FioPerm := FileIOPermission.Create(
FileIOPermissionAccess.AllAccess, 'c:\temp');
if (SecurityManager.IsGranted(FioPerm)) then
Console.WriteLine('Permission Granted!')
else
Console.WriteLine('Not Granted!');
OutFileStream := FileStream.Create(
'c:\temp\junk.txt', FileMode.Create);
Writer := StreamWriter.Create(OutFileStream);
Writer.WriteLine('Hello World!');
Writer.Flush;
Writer.Close;
except
on E: SecurityException do
Console.WriteLine('Security exception.');
end;
end;
[FileIOPermissionAttribute(
SecurityAction.Demand, All='c:\temp')]
procedure TTestObject.WriteTextFile;
var
OutFileStream: FileStream;
Writer: StreamWriter;
begin
OutFileStream := FileStream.Create(
'c:\temp\junk.txt', FileMode.Create);
Writer := StreamWriter.Create(OutFileStream);
Writer.WriteLine('Hello World!');
Writer.Flush;
Writer.Close;
end;
SecurityAction
Description
LinkDemand
InheritanceDemand
Demand
Assert
Deny
PermitOnly
Only the resource for the specified permission can be accessed even if callers
have permission for other resources.
type
[FileIOPermissionAttribute(
SecurityAction.Demand, All='c:\temp')]
TTestObject = class(TObject)
public
procedure Hello;
procedure WriteTextFile;
end;
Figure 8: SecurityAction members that can be used at the class and method level.
Greater
Delphi
Safety .NET
Method
Description
Assert
Demand
Deny
PermitOnly
[PrincipalPermissionAttribute(
SecurityAction.Demand, Role='Administrators')]
procedure TTestObject.WriteTextFile;
var
OutFileStream: FileStream;
Writer: StreamWriter;
begin
try
OutFileStream := FileStream.Create(
'c:\temp\junk.txt', FileMode.Create);
Writer := StreamWriter.Create(OutFileStream);
Writer.WriteLine('Hello World!');
Writer.Flush;
Writer.Close;
except
on E: SecurityException do
Console.WriteLine('Security exception.');
end;
end;
Greater
Delphi
Safety .NET
procedure TTestObject.WriteTextFile;
var
OutFileStream: FileStream;
Writer: StreamWriter;
WinIdentity: WindowsIdentity;
WinPrincipal: WindowsPrincipal;
begin
try
WinIdentity := WindowsIdentity.GetCurrent;
WinPrincipal := WindowsPrincipal.Create(WinIdentity);
if (not WinPrincipal.IsInRole(
WindowsBuiltInRole.PowerUser)) then begin
Console.WriteLine('User is not in PowerUser.');
Exit;
end;
OutFileStream := FileStream.Create(
'c:\temp\junk.txt', FileMode.Create);
Writer := StreamWriter.Create(OutFileStream);
Writer.WriteLine('Hello World!');
Writer.Flush;
Writer.Close;
except
on E: SecurityException do
Console.WriteLine('Security exception.');
end;
end;
18
F O U N D A T I O N
DFM2PAS
WIN32
.NET
C L A S S
DELPHI 7
By Fernando Vicaria
This article explores the Dfm2Pas utility, a program that converts .dfm files into .NET-compatible .pas files. John Kasters
article, Using the Delphi for .NET Preview compiler in the
Delphi 7 IDE, provides a good primer for how to install
and use Delphi for .NET IDE Integration (see Further Reading at the end of this article for the specific reference). Its
important that the reader understands that although these
tools play an important role during the Preview (Beta) stages
of development, they wont be part of the final product and
have no official support from Borland.
Getting to Know the Tool
Make sure Dfm2Pas is added to your systems environment
path. Dfm2Pas doesnt have an installation program of its own,
so you need to add it to your systems path manually. On Windows XP you can do that by opening Control Panels System
icon, selecting the Advanced tab, then clicking the Environment
Variables button. Finally, add the location of Dfm2Pas to the list
in the Path variable. In a typical installation of the Delphi for
.NET Compiler Preview, Dfm2Pas is located at: C:\Program
Files\Borland\Delphi for .NET Preview\Demos\Dfm2Pas\, but
this can vary from one system to another.
To verify all is set correctly, open the DOS prompt and type:
dfm2pas. The result should look something like Figure 1. As
you can see, there are several command-line switches you
can pass to Dfm2Pas:
19
converts the Unit1.dfm file into code overriding the original contents of Unit1.pas. (Warning: If you use this option,
youll loose the original Unit1.pas file and wont be able to
recompile it in Win32.)
dfm2pas Unit1 Unit1_NET
Foundation
Class
program Project1;
uses
{$IFDEF CLR}Borland.VCL.Forms
{$ELSE}Forms{$ENDIF},
Unit1 in {$IFDEF CLR}'NET\Unit1.pas'
{$ELSE}'Win32\Unit1.pas'{$ENDIF};
{$R *.res}
begin
Application.Initialize;
{$IFDEF CLR}
Form1:= TForm1.Create(nil);
Application.MainForm:= Form1;
{$ELSE}
Application.CreateForm(TForm1, Form1);
{$ENDIF}
Application.Run;
end.
Foundation
Class
program TextEdit;
uses
{$IFDEF CLR}Borland.VCL.Forms
{$ELSE}Forms{$ENDIF},
MDIFrame in {$IFDEF CLR}'NET\MDIFrame.pas'
{$EL SE}'Win32\MDIFrame.pas'{$ENDIF},
MDIEdit in {$IFDEF CLR}NET\MDIEdit.pas'
{$ELSE}'Win32\MDIEdit.pas'{$ENDIF};
{$R *.res}
begin
Application.Initialize;
{$IFDEF CLR}
FrameForm:= TframeForm.Create(nil);
Application.MainForm:= FrameForm;
{$ELSE}
Application.CreateForm(TframeForm, FrameForm);
{$ENDIF}
Application.Run;
end.
To confirm that what you have now is a real .NET application, open ILDASM.exe (it comes free with the .NET
SDK) and select the application you just created. The
result is shown in Figure 6. ILDASM uses metadata (and
the Reflection namespace) to navigate the types imported
and referenced by Project1. To learn more about Reflection
check out the references listed in the Further Reading
section at the end of this article.
If you want to recompile your project to Win32, simply
click on makeWin32.bat. The result will be a Win32 executable (just as we did with the .NET batch file). We now
have the option to compile the project to two platforms
without having to change any code.
Converting an Existing Project
Like creating a new project, compiling an existing Delphi 7
project into a .NET application is very simple, and requires
only minor changes to the project source file. To demonstrate this well convert one of the demonstrations included
in Delphi 7; well use the TextEdit demo, which youll find
in the /Demos/Doc subdirectory. Here are the steps:
1) Open the TextEdit.dpr project using the IDE.
2) Open the project source file and modify the code in
the project source so it looks like Figure 7.
3) Now create a couple of subdirectories named /WIN32
and /NET, as we did with our first example.
21
4) Move the two Pascal files and their associated DFM files
into the /NET subdirectory.
5) Create two batch files (one for Win32 and one for .NET).
Figure 8 shows the .NET batch file created for TextEdit.dpr.
6) Run the batch files.
As you can see, the process is nearly identical to that of
creating a new project. However, there will be times when
the conversion wont be that simple, and youll need to
modify or add conditional defines to the source code.
Foundation
Class
Known Issues
There are a few things to be aware of when using Dfm2Pas
for converting an existing project or creating a new one.
Dfm2Pas is a tool created mainly to help Borlands QA
department debug the new VCL.NET, and is not meant to be
a commercial product. As such, it might not have the same
quality of other supported products.
22
Conclusion
Weve explored how to take advantage of one of the add-on
tools included with the Delphi for .NET Compiler Preview.
Weve learned how to create a new desktop application that
targets .NET using VCL.NET and Delphi 7s IDE. We also saw
how to quickly convert an existing Win32 project into .NET.
And finally, we learned how to easily compile a Delphi project
to Win32 and .NET.
To learn more, check out the excellent articles on the Internet
covering VCL.NET and the new Delphi for .NET Compiler Preview. A good place to start is at Borlands Developer Network.
Further Reading
Delphi for .NET compiler preview by John Kaster
and Danny Thorpe, http://bdn.borland.com/article/
0,1410,28972,00.html
Using the Delphi for .NET Preview compiler in the Delphi
7 IDE by John Kaster, http://bdn.borland.com/article/
0,1410,29159,00.html
Documentation files for Delphi for .NET Preview
The demonstration projects referenced in this article are available for download on the Delphi Informant Magazine Complete Works CD located in INFORM\2003\JUL\DI200307FV.
D E L P H I
EXCEPTIONS
A T
TROUBLESHOOTING
W O R K
REMOTING
DELPHI 2-7
By Simon Murrell
Warning Signs
Send Exception Notifications from Your Applications
Sample Application
The sample application that accompanies this article demonstrates the notification mechanism by generating an unhandled
exception, and then a handled exception. (The sample application is available for download; see end of article for details.)
Figure 1 shows the Sample Application form. The application
contains seven settings that store the configuration details. The
configuration details store the SMTP Server Name and Port for
the e-mail message destination. The configuration details also
store the e-mail address to where the e-mail message must be
sent, along with the details of the client who is sending the
e-mail message. Finally, the configuration details provide the
option to include a screen shot attachment.
Generating an Unhandled Exception
One button on the Sample Application generates an
unhandled exception; the code in its OnClick event tries
to divide an integer value by zero. Because Im not using
a try statement, the exception is not handled and, thereprocedure TfrmMain.cmdUnhandledExceptionClick(
Sender: TObject);
var
i: Integer;
j: Integer;
k: Integer;
begin
// Assign values.
i := 1;
j := 0;
k := i div j;
// Show result.
MessageDlg('Result: ' + IntToStr(k), mtInformation,
[mbOk], 0);
end;
23
Delphi
At
Work
Warning Signs
fore, the MessageDlg procedure will never be reached successfully. The code in Figure 2 displays the OnClick event
handler that generates an unhandled exception and then
tries to display an error message.
I overrode the OnException event of the TApplication class,
so it will use my newly defined event, named AppException.
I did this so that any unhandled exception generated in the
application will be redirected to the AppException procedure.
The AppException event then calls a locally defined procedure named ShowNotificationForm, which initializes the
Notification Form. ShowNotificationForm then assigns the
exception information received via the parameter listing to
the Error memo field found on the Notification Form.
Once assigned, the procedure takes a screen shot of the
current application display so the programmer can see
what generated the exception. Finally, the method displays the Notification Form so the user can send a report.
The following code is used to override the OnException
event of the TApplication class:
// Assign exception wrapper.
Application.OnException := Self.AppException;
procedure TfrmMain.cmdHandledExceptionClick(
Sender: TObject);
var
i: Integer;
j: Integer;
k: Integer;
begin
try
// Assign values.
i := 1;
j := 0;
k := i div j;
// Show result.
MessageDlg('Result: ' + IntToStr(k), mtInformation,
[mbOk], 0);
except
on E:Exception do begin
// Return error message.
ShowError('An error occurred during calculation - ' +
E.Message);
// Show notification form.
Self.ShowNotificationForm(E);
end;
end;
end;
Delphi
At
Work
Warning Signs
procedure TfrmNotification.ScreenCapture;
var
DC: HDC;
BitmapImage: TBitmap;
JpegImage: TJPEGImage;
begin
// If no attachment required...
if (ReadRegistry(RegistryKeyName,
'IncludeAttachment') = 'No') then
Exit;
DC := GetDC(GetDesktopWindow); // Assign device context.
BitmapImage := TBitmap.Create; // Initialize classes.
JpegImage := TJPEGImage.Create;
try // Assign values.
BitmapImage.Width := GetDeviceCaps(DC,HORZRES);
BitmapImage.Height := GetDeviceCaps(DC,VERTRES);
BitBlt(BitmapImage.Canvas.Handle, 0, 0,
BitmapImage.Width, BitmapImage.Height, DC,
0, 0, SRCCOPY);
JpegImage.Assign(BitmapImage); // Assign source.
JpegImage.CompressionQuality := 75;
// Save Jpeg image.
JpegImage.SaveToFile(ExtractFilePath(
Application.ExeName) + 'temp.jpg');
finally
// Release device context.
ReleaseDC(GetDesktopWindow,DC);
JpegImage.Free; // Dispose classes.
BitmapImage.Free;
end;
end;
erates the e-mail, along with the code that takes a screen
shot of the Main Application Form.
Capturing the Screen
I created a method named ScreenCapture (see Figure 6),
which is used to capture the current desktop display.
The procedure first gets the device context of the
desktop. After the device context has been assigned, the
procedure initializes the TBitmap and TJpegImage classes.
ScreenCapture then defines the dimensions of the bitmap
image and uses the BitBlt method from the Win32 API to
copy the image from the Desktops Device Context to the
TBitmap class. ScreenCapture then assigns the TBitmap class
to the TJpegImage class, and saves the image to a .jpeg file
on disk. The procedure finally disposes the Desktops Device
Context and the TBitmap and TJpegImage class instances.
Send Report
The Send Report button is used to generate a new email using the configuration details found in the Main
Application Form of the sample application and send the
e-mail to you to respond to the customer with a solution
to the problem. The code in Figure 7 displays how to
generate the e-mail message sent to you.
The procedure first initializes a new instance of the TIdMessage
class. Then it assigns the SMTP Server Name and Port that
will be used to send the e-mail message. The procedure then
assigns the recipient of the e-mail message along with details of
the sender of the e-mail message. The procedure then checks
whether an attachment must be added. Finally, the procedure
connects to the SMTP Server and sends the e-mail message.
25
procedure TfrmNotification.cmdSendReportClick(
Sender: TObject);
var
MailMessage: TIdMessage;
begin
try
// Initialize class.
MailMessage := TIdMessage.Create(nil);
// Assign SMTP client properties.
Self.SMTPClient.Host :=
ReadRegistry(RegistryKeyName, 'ServerName');
Self.SMTPClient.Port :=
StrToInt(ReadRegistry(RegistryKeyName,'ServerPort'));
// Assign message detail properties.
MailMessage.Recipients.EMailAddresses := ReadRegistry(
RegistryKeyName, 'NotificationEmailAddress');
MailMessage.From.Name :=
ReadRegistry(RegistryKeyName, 'ContactName');
MailMessage.From.Address := ReadRegistry(
RegistryKeyName, 'ContactEmailAddress');
MailMessage.Subject :=
'Unhandled Exception Notification - ' + ReadRegistry(
RegistryKeyName, 'CompanyName');
MailMessage.Priority := mpHighest;
MailMessage.Body.AddStrings(Self.txtError.Lines);
// Add attachment.
if (ReadRegistry(RegistryKeyName,
'IncludeAttachment') = 'Yes') then
// Add attachment.
TIdAttachment.Create(MailMessage.MessageParts,
ExtractFilePath(Application.ExeName) + 'temp.jpg');
// Disconnect if already connected.
if (Self.SMTPClient.Connected) then
Self.SMTPClient.Disconnect;
Self.SMTPClient.Connect; // Open connection.
// Send e-mail.
Self.SMTPClient.Send(MailMessage);
Self.SMTPClient.Disconnect; // Disconnect.
ShowInformation('Report Successfully Sent!');
Self.Close; // Close form.
except
on E:Exception do
ShowError(
'An error occurred while sending the Report - ' +
E.Message);
end;
end;
Conclusion
Of course, you dont have to use e-mail to submit an error.
You can also submit the error via a Web site or Web Service sitting out on the Internet. I hope you find this feature
useful. Ive found that if a customer experiences an error
because of incorrect input, or an error that has been unhandled, this feature allows them to communicate with me
more easily by sending the error along with the screen shot,
so I can minimize the time it takes to resolve any problems.
The sample application referenced in this article is available for download on the Delphi Informant Magazine
Complete Works CD located in INFORM\2003\JUL\
DI200307SM.
Simon Murrell uses a wide variety of languages, from C#, Delphi, Java, and Visual
Basic to JavaScript. You can reach Simon at [email protected].
N E W
&
U S E D
After youve made the preliminary decisions, CDK provides a variety of ways to work with properties, events,
and methods. Figure 2 shows the second page of the Class
Details portion of the main wizard, which allows you to
hide, publish, or override inherited properties, events, and
methods. As you can see, the main wizard provides two
ways to access the various tasks: by clicking on one of
the notebook pages, or by clicking on the task list in the
lower-left corner. The task list provides additional services.
As you move through the various stages, CDK keeps track
of your work. If you introduce errors or leave something
unfinished, CDK will place a warning icon by that task to
remind you to come back and deal with it.
You can also create a variety of component types using
CDK: visual, non-visual, and compound. The latter are
referred to as Super Components because they contain
sub-components that interact with each other. Earlier versions of CDK supported compound components, but they
required a bit more work. Those hard-coded composites
featured sub-components that were created dynamically
in the source code. Now the process is more automated.
Figure 1: CDK provides help at every stage. Here it provides a breakdown of the
VCL to assist in the selection of a class ancestor.
New
&
Used
Figure 3: CDK for Delphi allows you to use various design patterns in your
classes.
New
&
Used
28
N E W
&
U S E D
By Bill Todd
InfoPower 4000
Venerable Must-have Component Suite Gets Overhaul
oll2Woll Software has added an impressive set of new features to the InfoPower
grid and other components in their vener-
Noticeably New
Take a look at Figure 1 and youll immediately see some differences. Note that alternate rows in the grid appear in different colors. Showing the active row in a different color makes
it stand out so users can see at a glance on which row they
are working. It also makes reading across without jumping
lines much easier for the user. Implementing this feature is a
snap; simply set the grids AlternatingRowColor property.
Figure 1 shows another handy feature. Notice that the Company column is a hyperlink. You can display any number
of fields as links. When a user clicks a link, InfoPower
attempts to open the link in your browser. However, the
OnURLOpen event fires first, passing the URL string and
field object to your event handler, so you can take any
action you wish. For example, you could open another form
when the user clicks the link.
29
You may have noticed the plus sign (+) in the first column
of the grid. The InfoPower grid lets you show master-detail
relationships to any depth. Figure 2 shows the grid with
one of the customer records expanded to show the orders
for the customer. Notice that the order records are shown
in an embedded panel that contains a TwwDBNavigator
component, as well as the orders grid. Using this technique
you can provide any controls you need as part of the detail
tables display. Figure 3 shows one of the order records
expanded to show the items for that order. I really like the
way that each record expands vertically to contain the detail
grid or panel, without hiding other master records.
Youve Got the Power
The InfoPower grid lets you grant the user total control of
the row and column size and column order, simply by setting a few properties. Figure 4 shows the grid with the column order changed and the row height increased. You can
also specify the name of an INI file to hold the grid settings,
so a users grid layout will be preserved between sessions.
Another handy new feature you can enable simply by setting a property is double-click column sizing. With this feature enabled you can place the mouse cursor over the sizing
line for a column in the grids title bar and double click to
have the grid automatically adjust the column width to fit
the largest value being displayed.
If you need a more sophisticated user interface, you can
embed other data access components in an InfoPower grid,
such as a check box, combo box, or rich edit component.
In InfoPower 4000 you can allow embedded controls to
New
&
Used
InfoPower 4000
comes from the record above unless you are positioned on
the first record in the grid. In that case, the data is copied
from the record below.
Another new feature is the ability to group data that has a
one-to-many relationship, such as the result set returned
by a join query. For example, if you join customer and
orders tables, you can group by customer name to show
the customer with the order information. When you do, the
customer name is shown once for the first record for that
customer. For the second and subsequent records for that
customer, the name and grid lines are suppressed so your
grid looks like a grouped report. You can also add footer
cells to the grid to hold totals.
Figure 4: The grid after the user has changed the grid layout.
New
&
Used
InfoPower 4000
New
&
Used
InfoPower 4000
32
N E W
&
U S E D
By Ron Loewy
RemObjects SDK
Create Multi-tier Apps Using Delphi and Kylix
The RemObjects product is a combination of a designtime service builder (think of it as the RemObjects
equivalent of Delphis Type Library Editor), and a set of
components and interfaces used to create your servers
and clients. The installation program also features a
reference manual and several samples. Complete source
code is included, and the license allows you to deploy
your clients and servers with no royalties.
uses
...
library RemDirServer;
// RemObjects: Careful, do not remove!
{#ROGEN:RemDirServer.rodl}
New
&
Used
RemObjects SDK
In addition to the RODL file, and the
comment that ties it to the project, the
wizards create a module (TWebModule,
TDataModule, etc., based on the project
youre creating) with an instance
of a transport server component
TROWebBrokerServer in my case), and
an instance of a message type (SOAP
or RemObjects optimized Binary
message type). A unit is created to
define the interface published by the
service (this is the equivalent of Delphis
Project_TLB.pas file for COM Servers),
a unit used to invoke the service (the
code in this unit extracts the parameters
sent by the clients and dispatches the
actual services you write), and an
implementation unit where you will write
the code for the services you publish.
Figure 2: The service builder showing the interface published by the Remote Directory Service; the
ListFiles method is selected.
Component
Use
TROWinMessageServer
TROWebBrokerServer
TROIndyHTTPServer
TROIndyTCPServer
TROBPDXTCPServer
TROBPDXHTTPServer
TRODLLServer
34
New
&
Used
Component
RemObjects SDK
Use
applications.
35
T E X T F I L E
Mastering Delphi 7
M
Mastering Delphi 7
by Marco Cant, SYBEX,
www.sybex.com
ISBN: 0-7821-4201-X
Cover Price: US$59.99
(1,011 pages)
T E X T F I L E
nologies like SOAP and XML. Throughout, Cant presents complex issues in
a very understandable manner. The
highlight of this section was the final
two chapters covering Delphis .NET
preview. These 45 pages alone will be
sufficient for many to consider buying
this important work.
My opinion of Marco Cant grows
with each new edition of this excellent
book. A must for anyone new to Delphi, and a most valuable resource for
those developers working with database and/or Web applications, Mastering Delphi 7 is appropriate for all levels
of Delphi programmers, but especially
for those just getting started. Visit
Cants Web site, try out the code, read
his online books, and then buy this
book. You wont be disappointed.
Alan C. Moore, Ph.D.
F I L E
N E W
Kylix 2 Development
(Wordware, 2002) by Eric
Whipple and Rick Ross is
another fine introduction to
Borlands RAD Linux tool.
Similar to the two works
discussed above, it covers
Object Pascal, the IDE, the
component library, and
specific Linux issues, albeit
in a more concise way.
Its introduction to Object
Pascal is especially good.
More advanced but essential
topics, such as database
development and Internet
development, are also
included. I recommend this book to
anyone who has not used Delphi.
Marco Cant, a favorite author of
mine, has teamed up with Uberto
Barbini to write Mastering Kylix 2
(SYBEX, 2002). If youre a fan of
Marcos excellent Delphi books,
youll be pleased to find the same
writing style here, with clear stepby-step instructions to carry out
essential tasks. Theres a concise
introduction to certain elements of
File
New
39
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-2004. 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].