Académique Documents
Professionnel Documents
Culture Documents
ON THE COVER
6
On the Net
FEATURES
13
OP Tech
16
Greater Delphi
21
30
Sleuth QA Suite 2
Product Review by Alan C. Moore, Ph.D.
33
Quick CLX
26
REVIEWS
At Your Fingertips
DEPARTMENTS
2
5
36
38
Delphi Tools
Newsline
Best Practices by Clay Shannon
File | New by Alan C. Moore, Ph.D.
Delphi
T O O L S
New Products
and Solutions
Book Picks
Learn Object Pascal with Delphi
Warren Rachele
Wordware Publishing, Inc.
ISBN: 1-55622-719-1
Cover Price: US$49.95
(358 pages, CD-ROM)
ISBN: 1-56592-098-8
Cover Price: US$34.95
(393 pages)
Starbase Corp.
Price: US$2,499 for a single user.
Phone: (888) 782-7700
Web Site: http://www.starbase.com
NAG Releases
NAG Components
for Delphi 1.4
NAG Software Solutions
announced the release of NAG
Components for Delphi 1.4,
xing minor bugs, improving
overall functionality, and
including new features and
components, such as the
TNAGSpinEdit component and
TNAGSpeedButton components, and a new TNAGButton
that is glyph-capable.
All registered users of NAG
Components for Delphi v1.3
are eligible for a free upgrade.
NAG Components for Delphi
is an easy-to-use, one-stop suite
of 100 percent native, VCLbased components for Borland
Delphi. The entire package
features over 40 components,
including visual controls for
enhancing the user interface
of applications and non-visual
components that allow developers to interact with the
end-users system without any
knowledge about the low-level
Windows API.
NAG Software Solutions
Price: US$69.95
E-Mail: sales@nagsoftware.com
Web Site: http://www.nagsoftware.com/
products/nagcomponents/
Delphi
T O O L S
New Products
and Solutions
Book Picks
Programming Microsoft
Internet Explorer 5
Scott Roberts
Microsoft Press
ISBN: 0-7356-0781-8
Cover Price: US$49.99
(511 pages, CD-ROM)
ISBN: 0-596-00025-1
Cover Price: US$34.95
(797 pages)
Delphi
T O O L S
New Products
and Solutions
Book Picks
Linux System Administrators
Survival Guide, Second Edition
Tim Parker
SAMS
ISBN: 0-672-31793-1
Cover Price: US$49.99
(740 pages)
ISBN: 1-55622-773-6
Cover Price: US$39.95
(314 pages, CD-ROM)
information systems. Model editors are complimented with verication and balancing reports, code
generation, reengineering capabilities and scriptable HTML
reporting features.
Logical data models often use
expressive names and various presentation options to communicate
information between developers.
The names of database objects like
tables, views, and columns are typically more constrained and cryptic in physical data models due
to the RDBMS limitations and
the legacy systems that they represent. WinA&D allows designers
to easily toggle between logical
and physical models and show
entities (tables) with full attribute
lists, primary and foreign keys
only, or customized to show
specic attributes.
WinA&Ds namespace concept
now supports data modeling, SQL
code generation, and reengineering of different database schemas.
Namespaces simplify team development, enable powerful organizational and reporting tools,
partition complex systems, and
Excel Software
Price: Standard, US$495; Educational,
US$845; Desktop, US$1,295; and Developer, US$1,995.
E-Mail: info@excelsoftware.com
Web Site: http://www.excelsoftware.com
Quasidata Announces
DbAltGrid Suite 1.2
Fractal Software
Price: US$65
E-Mail: info@fractal.nl
Web Site: http://www.fractal.nl
Quasidata
Price: US$145
E-Mail: info@dbaltgrid.com
Web Site: http://www.dbaltgrid.com
News
January 2001
Collingwood, Australia
The Australian Delphi User
Group (ADUG) has recently
created an e-mail job advertising service designed especially
for programmers and employers in Australia.
For those who would like
to keep an eye on the Delphi
job market in Australia, visit
http://www.adug.org.au/jobs/
programmers.htm.
On the Net
Internet Explorer / Delphi
By Bill Todd
nteracting with the Web is becoming a more common requirement for applications
every day. Part one of this two-part series looks at controlling Internet Explorer via
Automation from your Delphi application. Youll use a custom component to wrap the
IWebBrowser2 interface. In part two, youll look at embedding a browser in your application using the TWebBrowser control.
Using ShellExecute
On the Net
Method/Property
Description
GetIEWindowHandle
IEClose
IEGoBack
IEGoForward
IEGoHome
IEGoSearch
IEOpen
IEPrint
IEPrintPreview
IERefresh
IESave
IESaveAs
IEShow
IEStop
IsIEBusy
IsIEOpen
Description
IEAddressBarVisible
IEFullScreen
IEHeight
IELeft
IEMenuBarVisible
IEOffline
IEResizable
IESilent
IEStatusBarVisible
IEStatusText
IETheaterMode
IEToolbarVisible
IETop
IEVisible
IEWidth
IEURL
procedure TIEController.IERefresh;
begin
if not IsIEOpen then IEShow;
FIE.Refresh;
end;
The rst line of code requires some explanation. One of the problems
with writing a component that provides an interface to an Automation server is deciding what to do if a programmer calls one of
the methods or accesses one of the properties without rst opening
the server. There are two choices: The component can either create
an instance of the server if one does not exist, or it can raise an
exception. In this case, I decided to open the server and make it
visible by calling the IEShow method.
There is a second problem with Internet Explorer: how to determine
if an instance of Internet Explorer has already been created. Setting
the interface reference variable, FIE, to nil when the IEClose method
is called will not work because the user can close Internet Explorer.
This means the TIEController component must check if Internet
Explorer is still open before calling its methods or accessing its
properties. The IsIEOpen and GetIEWindowHandle methods shown
in Figure 5 meet this need.
The IsIEOpen method simply calls GetIEWindowHandle and
returns True if the window handle is not zero. GetIEWindowHandle
rst checks to see if the interface reference, FIE, is nil. If it is
nil, the method returns zero. If not, the IWebBrowser2
Get_HWND method is called to get the window handle. If
Internet Explorer is not open, calling Get_HWND will cause an
EOleException, which is handled by the try..except block by setting the result to zero.
There are some other surprising cases where error handling is required.
One would assume that the IWebBrowser2 GoForward and GoBack methods would simply do nothing if there was no page in the history list to
which to move. Instead, they raise an exception. Therefore, the IEGo...
methods of TIEController use a try..except block, as shown in Figure 6, to
swallow the exception if you try to go to a page that doesnt exist.
One more method deserves special mention: IsIEBusy. IsIEBusy returns
True if Internet Explorer is busy downloading a page, or engaged in
some other activity. You must check if Internet Explorer is busy before
you try to do anything to the current page, such as print it.
Many of the methods of the IWebBrowser2 interface are getter and
setter methods for properties. TIEController implements these as
Delphi properties whose getter and setter methods call the corresponding IWebBrowser2 methods. A typical example is the IEAddressBarVisible
property with its getter and setter methods, as shown in Figure 7.
Understanding ExecWB
If you look at the methods of the IWebBrowser2 interface, you will
notice that a lot of methods you would expect to nd are missing.
For example, there is no Print or SaveAs method. The reason is
that there are no corresponding functions in Internet Explorer
itself. Internet Explorer is just a simple application that hosts
the Microsoft Web Browser ActiveX control. It is the browser
control inside Internet Explorer that actually provides many of the
functions you nd on Internet Explorer menus and toolbars. To
allow Internet Explorers Automation clients to access the methods
of the embedded Web browser control, the IWebBrowser2 interface
includes a method called ExecWB.
On the Net
function TIEController.IsIEOpen: Boolean;
begin
if GetIEWindowHandle = 0 then
Result := False
else
Result := True;
end;
function TIEController.GetIEWindowHandle: HWND;
begin
try
if Assigned(FIE) then
Result := FIE.Get_HWND
else
Result := 0;
except
Result := 0;
end;
end;
On the Net
TIEController do not implement all of the available command IDs, but
do implement the ones youre most likely to need. You can easily add
any others you want to use.
Conclusion
If you need to give users the ability to view information on the Web,
controlling Internet Explorer through Automation provides an easy
solution. It lets you open the browser and take the user anywhere,
and then turn control over to the user to work as they wish. You can
also go to a page on the Web and save or print it without the user
ever seeing Internet Explorer. The sample software that accompanies
this article includes both the TIEController component, and a test
application that shows how to use it (see Figure 10).
The les referenced in this article are available on the Delphi
Informant Magazine Complete Works CD located in INFORM\01\
JAN\DI200101BT.
Bill Todd is president of The Database Group, Inc., a database consulting and
development firm based near Phoenix. He is co-author of four database programming books, author of more than 60 articles, a contributing editor to Delphi
Informant Magazine, and a member of Team Borland, providing technical support
on Borland Internet newsgroups. He is a frequent speaker at Borland Developer
Conferences in the US and Europe. Bill is also a nationally-known trainer and
has taught Delphi programming classes across the country and overseas. He is
currently a speaker on the Delphi Development Seminars Kylix World Tour. Bill can
be reached at bill@dbginc.com. For more information on the Kylix World Tour,
visit http://www.DelphiDevelopmentSeminars.com.
On the Net
procedure Register;
begin
RegisterComponents('DGI', [TIEController]);
end;
{ TIEController }
function TIEController.IEExecWB(CmdId, CmdOption: Integer;
VarIn, VarOut: OleVariant): Boolean;
var
q: Integer;
begin
if FIE.QueryStatusWB(CmdId) = OLECMDF_ENABLED +
OLECMDF_SUPPORTED then
try
FIE.ExecWB(CmdId, CmdOption, VarIn, VarOut)
except
raise EIEControlExecError(
'Command execution failed. (Id: ' +
IntToStr(CmdId) + ')');
end
else
raise EIEControlExecError.Create(
'The function you requested is not ' +
'available. (Id: ' + IntToStr(CmdId) + ')');
end;
procedure TIEController.IEClose;
begin
if IsIEOpen then begin
FIE.Quit;
FIE := nil;
end;
end;
function TIEController.GetIEHeight: Integer;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Height;
end;
function TIEController.GetIELeft: Integer;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Left;
end;
function TIEController.GetIEWindowHandle: HWND;
begin
try
if Assigned(FIE) then
Result := FIE.Get_HWND
else
Result := 0;
except
Result := 0;
end;
end;
function TIEController.GetIEURL: string;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_LocationURL;
end;
function TIEController.GetIEAddressBarVisible: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_AddressBar;
end;
function TIEController.IsIEBusy: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Busy;
end;
procedure TIEController.IEOpen;
begin
FIE := CoInternetExplorer.Create;
end;
procedure TIEController.SetIEURL(const Value: string);
var
Flags:
OleVariant;
TargetFrameName: OleVariant;
PostData:
OleVariant;
Headers:
OleVariant;
begin
if not IsIEOpen then IEShow;
FIE.Navigate(Value, Flags, TargetFrameName,
PostData, Headers);
end;
procedure TIEController.SetIEAddressBarVisible(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_AddressBar(Value);
end;
procedure TIEController.SetIEVisible(const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Visible := Value;
end;
function TIEController.GetIEOffLine: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Get_Offline;
end;
function TIEController.GetIEVisible: Boolean;
begin
if not IsIEOpen then IEShow;
Result := FIE.Visible;
end;
procedure TIEController.SetIEMenuBarVisible(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_MenuBar(Value);
end;
procedure TIEController.SetIEHeight(const Value: Integer);
begin
if not IsIEOpen then IEShow;
FIE.Set_Height(Value);
end;
procedure TIEController.SetIELeft(const Value: Integer);
begin
if not IsIEOpen then IEShow;
FIE.Set_Left(Value);
end;
procedure TIEController.SetIEOffline(const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_Offline(Value);
end;
function TIEController.GetIETop: Integer;
begin
On the Net
if not IsIEOpen then IEShow;
Result := FIE.Get_Top;
end;
procedure TIEController.SetIETheaterMode(
const Value: Boolean);
begin
if not IsIEOpen then IEShow;
FIE.Set_TheaterMode(Value);
end;
On the Net
begin
if not IsIEOpen then IEShow;
FIE.Refresh;
end;
procedure TIEController.IEShow;
begin
IEOpen;
IEVisible := True;
end;
function TIEController.IsIEOpen: Boolean;
begin
if GetIEWindowHandle = 0 then
Result := False
else
Result := True;
end;
procedure TIEController.IEPrint(ShowDlg: Boolean);
var
V: OleVariant;
begin
if ShowDlg then
IEExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DODefault, V, V)
else
IEExecWB(OLECMDID_PRINT,
OLECMDEXECOPT_DONTPROMPTUSER, V, V);
end;
procedure TIEController.IEPrintPreview;
var
V: OleVariant;
begin
IEExecWB(OLECMDID_PRINTPREVIEW,
OLECMDEXECOPT_DODEFAULT, V, V);
end;
procedure TIEController.IESave;
var
V: OleVariant;
begin
IEExecWB(OLECMDID_SAVE, OLECMDEXECOPT_DODEFAULT, V, V);
end;
procedure TIEController.IESaveAs(FileName: string);
var
VarIn,
VarOut: OleVariant;
begin
VarIn := FileName;
IEExecWB(OLECMDID_SAVEAS, OLECMDEXECOPT_DODEFAULT,
VarIn, VarOut);
end;
procedure TIEController.IEStop;
var
V: OleVariant;
begin
IEExecWB(OLECMDID_STOP, OLECMDEXECOPT_DODEFAULT, V, V);
end;
end.
OP Tech
By Mike Riley
hen asked about privacy in todays world, Sun Microsystems CEO Scott McNealy
remarked, You have zero privacy. Get over it. Of course, if this were really
the case, then Mr McNealy should have no qualms about posting his companys
confidential internal correspondence on public Internet sites. I havent seen this
happen yet. And thanks to the hard work of people like Phil Zimmerman, privacy
and data security can still exist in todays highly digitized world.
OP Tech
OP Tech
signed, the mail toolbar icon will become active. Clicking this icon
will transmit the read-only contents of the bottom memo eld to
the e-mail address you specied earlier. To validate the success of this
transmission, simply retrieve the e-mail message from the intended
recipient, decrypt, and verify.
Conclusion
To be a useful tool in a production environment, the PGP demonstration application could be automated to kick off a batch operation
via a Timer event, or Windows Task Scheduler operation. Better yet,
an NT Service could be created to ensure the automated operation
of an encrypted message transmission from within the Windows NT
security and remote management framework.
So what type of production applications can be written using this
combination of technologies? Plenty. Consider an extranet nancial or inventory reporting application that can be inexpensively
and securely transmitted from one companys Intranet to another
in an asynchronous fashion using an Internet gateway. How about
a condential project status report system that updates contractors
and external consultants, regardless of the type of e-mail platform
their respective companies utilize? And for a virtually distributed,
decentralized organization on a tight budget, secure communications can be sent to employees with free public e-mail service
accounts. These messages can be safely decrypted well after the
recipient has copied and pasted the contents from their browser
for decryption, regardless of whether SSL is used during the message retrieval process. Visiting a Web page that res off a Delphiauthored ISAPI DLL or COM object that fullls the entire request
can trigger this type of process as well.
So the next time someone says, You have no privacy, smile and tell
them Get with it.
Resources
PGP (Commercial Version) v6.5.8 or higher available from
The projects referenced in this article are available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\JAN\
DI200101MR.
Mike Riley is a Chief Scientist for RR Donnelley & Sons, one of North Americas
largest printers. He actively participates in the companys I-net and eMerging
Technology strategies using a wide variety of distributed network technologies,
including Delphi 5.0. Mike can be reached via his public e-mail address,
mike_riley_@hotmail.com.
15 January 2001 Delphi Informant Magazine
Greater Delphi
ast month we demonstrated how to use two of the Microsoft Office Web Components,
ChartSpace and Spreadsheet, in Delphi applications. The other Office Web Component
of interest for Delphi developers is the PivotTable component thats used to pivot, filter, and
summarize information from various data sources and present it in meaningful ways.
In this article, well rst briey discuss the PivotTable services implemented in Microsoft Excel, so
you can get an idea of how and when the PivotTable component should be used. Then well provide you with several examples of how to use the
PivotTable component in Delphi applications.
Row Fields
Column Fields
Greater Delphi
It should be noted, however, that the PivotTable
component behavior is not the same as the Excel
PivotTable services. While the latter hides all data,
the former allows them to be modied interactively
at run time.
Order Date
City
Product
January 5
January 5
January 5
January 6
January 6
January 6
January 6
January 6
January 7
January 7
January 7
January 7
January 7
January 7
January 7
Lyon
Lyon
Lyon
Marseille
Lyon
Strasbourg
Marseille
Lyon
Marseille
Lyon
Marseille
Strasbourg
Strasbourg
Marseille
Marseille
Ikura
Ipoh Coffee
Chocolade
Chang
Ipoh Coffee
Ipoh Coffee
Ikura
Chang
Chang
Ikura
Chocolade
Chocolade
Chang
Chocolade
Ipoh Coffee
$193.00
$782.00
$86.70
$1,061.82
$7,905.00
$938.40
$1,047.62
$190.00
$800.00
$95.00
$714.00
$437.50
$292.50
$465.00
$378.00
Michael Suyama
Janet Leverling
Michael Suyama
Janet Leverling
Nancy Davolio
Janet Leverling
Janet Leverling
Nancy Davolio
Michael Suyama
Nancy Davolio
Nancy Davolio
Michael Suyama
Michael Suyama
Nancy Davolio
Nancy Davolio
Lyon
Sum of
Extended Price
Product
Salesperson
Janet
Leverling
Michael
Suyama
Chang
Chocolade
Ikura
Ipoh Coffee
Grand Total
Nancy
Davolio
Grand Total
190
86.7
193
190
86.7
288
8,687
9,251.7
95
PivotTable
PivotView
PivotDataAxis
PivotGroupAxis
PivotData
PivotFilterAxis
PivotTotals
(PivotTotal)
PivotCell
PivotMember
PivotFieldSets
(PivotFieldSet)
PivotFieldSets
(PivotFieldSet)
PivotFieldSets
(PivotFieldSet)
PivotFields
(PivotField)
PivotFields
(PivotField)
PivotFields
(PivotField)
PivotTotals
(PivotTotal)
PivotFieldSets
(PivotFieldSet)
PivotMembers
(PivotMember)
Objects
Collections
Greater Delphi
const
DataSourcePath = 'c:\data\northwind.mdb';
...
PT.ConnectionString :=
'PROVIDER=MICROSOFT.JET.OLEDB.4.0;' +
'DATA SOURCE=' + DataSourcePath;
Next, we need to specify what kind of data we will use in our pivot
table. This can be done either through the DataMember property,
or through the CommandText property. In our example, well use
the Product Sales for 1997 view (this view returns the information
about the product sales from January 1, 1997 to December 31,
1997), that can be specied like this:
PT.DataMember := '[Product Sales for 1997]';
var
View : Variant;
...
View := PT.ActiveView;
// Move fields to the row, column,
// and filter axes for grouping.
View.RowAxis.InsertFieldSet(
View.FieldSets['CategoryName']);
View.ColumnAxis.InsertFieldSet(
View.FieldSets['ShippedQuarter']);
View.DataAxis.InsertFieldset(
View.FieldSets['ProductName']);
View.DataAxis.InsertFieldset(
View.FieldSets['ProductSales']);
or like this:
PT.CommandText :=
'SELECT
After that, we have two options. We can show the Fields list box
and allow users to drag and drop elds into the pivot table manually,
or we can do the same thing programmatically. Lets see how the
latter can be done.
The elds in
PivotTable Fields PivotTable Collection
the pivot table
Row fields
RowAxis collection
discussed earColumn fields
ColumnAxis collection
lier in this artiFilter fields
FilterAxis collection
cle have the
Totals or Details fields DataAxis collection
appropriate
Figure 5: PivotTable fields and their collections.
collections
within the PivotTable components object model. This is shown in the table in
Figure 5.
The rst three collections support the InsertFieldSet method thats used
to specify the data for this set of elds. To specify the data for totals or
detail elds, we can either use the InsertTotal method, or InsertFieldSet
method. The code snippet shown in Figure 6 species data for all parts
of the pivot table.
Here weve dened that the CategoryName eld is used to create
the row axis data, the ShippedQuarter eld is used to create the
column axis data, and the ProductName and ProductSales elds
are used to create details to summarize them interactively.
Specifying Colors
Now we can make our pivot table more attractive by applying
different colors to its various sections. To do this, we will use the
TotalBackColor, FieldLabelBackColor, MemberBackColor, FieldLabelFont,
and SubTotalBackColor properties of the PivotView object:
// Apply various color settings to
// different pivot table components.
View.TotalBackColor := 'CornSilk';
View.FieldLabelBackColor := 'DarkBlue';
View.FieldLabelFont.Color := 'White';
View.ColumnAxis.FieldSets[0].Fields[0].SubTotalBackColor :=
'LightSteelBlue';
View.RowAxis.FieldSets[0].Fields[0].SubTotalBackColor :=
'LightSteelBlue';
View.MemberBackColor := 'LightGrey';
In our rst example, we saw how to set the data source, dene
a pivot table layout, calculate totals and use formulas, and apply
various colors to the different parts of a pivot table. Lets now
move on to a discussion of lters.
Greater Delphi
Using Filters
To lter the data that appears in the pivot table, we need to use the
FilterAxis property of the PivotView object, for example:
View.FilterAxis.InsertFieldSet(
View.FieldSets['CategoryName']);
This allows users to lter the data appearing in the pivot table.
In other words, lter columns allow us to bring the third dimension to the at pivot table. Please note that instead of applying
lters programmatically, we can drag and drop an appropriate
column name to the lter eld area of the PivotTable component.
var
View : Variant;
// PivotTable view
Total : Variant;
// Totals
R, C : Integer;
// Row and column counters
Cell : Variant;
// One cell
Agg
: Variant;
// Aggregate value
LI
: TListItem; // ListView item
CI
: TListColumn; // ListView column
...
// Specify column headers.
CI := ListView1.Columns.Add;
CI.Caption := 'Category';
CI.AutoSize := True;
for C := 0 to PT.ActiveData.ColumnMembers.Count-1 do begin
CI := ListView1.Columns.Add;
CI.Caption := PT.ActiveData.ColumnMembers[C].Caption;
CI.AutoSize := True;
end;
// For each row...
for R := 0 to PT.ActiveData.RowMembers.Count-1 do begin
LI := ListView1.Items.Add;
LI.Caption := PT.ActiveData.RowMembers[R].Caption;
// For each column in a row...
for C := 0 to PT.ActiveData.ColumnMembers.Count-1 do
begin
// Get cell value.
Cell := PT.ActiveData.Cells[
PT.ActiveData.RowMembers[R],
PT.ActiveData.ColumnMembers[C]];
// Get aggregate value.
Agg := Cell.Aggregates.Item['Sales Total'].Value;
// Show it.
LI.SubItems.Add(IntToStr(Agg));
end;
end;
PT.ConnectionString :=
'PROVIDER = MSOLAP.2; DATA SOURCE = maindesk;' +
'INITIAL CATALOG=FoodMart 2000';
The Export method takes two arguments. The rst species the name
of the le; the second species the actions to be taken. We can specify
either plExportActionNone (its value is 0, indicating no action; just
save the le), or plExportActionOpenInExcel (its value is 1, save the
le and open it in Excel). If the le name isnt specied, a temporary
le named PivotTablexxxx will be created in the \Windows\Temp
directory. The pivot table is saved in XML format.
Another possibility is to save a static picture of a pivot table as a
GIF le. To do this, we use the ExportPicture method. All four of its
arguments are optional:
the name of the le to store the image in. If this argument isnt
specied, the pivot table image will be saved to a le named
Pivot.GIF.
Greater Delphi
// Copy the PivotTable data into the Clipboard.
PT.Copy(PT.ActiveView);
// Create an instance of Excel for Automation.
Excel := CreateOLEObject('Excel.Application');
// Add a new workbook to the Excel instance.
WBook := Excel.Workbooks.Add;
// Get a reference to the first worksheet.
WSheet := WBook.WorkSheets[1];
// Paste the contents of the Clipboard into that sheet.
WSheet.Paste(WSheet.Range['A1', 'A1']);
// Make Columns AutoFit; everything will be visible.
WSheet.UsedRange.Columns.AutoFit;
The following code saves the pivot table into the Customers.GIF le
in the c:\Data folder:
PT.ExportPicture('c:\Data\Customers.GIF');
:
:
:
:
Variant;
Variant;
Variant;
Variant;
//
//
//
//
gridlines. After that, we make the Excel instance visible, and let users
interact with it (see Figure 11). The Print Preview window that
results from executing this code is shown in Figure 12. To actually
print, we can call the PrintOut function, in which case we dont need
to make Excel visible.
Conclusion
As far as licensing is concerned its okay to use Microsoft Ofce Web
Components on computers where any edition of Microsoft Ofce
or Microsoft Access 2000 is installed. For more information on the
deployment issues of such applications, refer to the documents on the
Microsoft Web site at http://www.microsoft.com/ofce.
Weve seen how to use the PivotTable component that is part of Microsoft Ofce Web Components. We know how to dene the data source
for this component, how to set the row, column, and lter elds, how to
dene the data elds, how to create summaries, and how to apply colors
to the different parts of the pivot table. We have also studied how to
receive values from the pivot table cells and axes, as well as to make this
component more or less interactive in our application. Finally, we also
saw how to print the data from a PivotTable component.
The three demonstration projects referenced in this article are available
on the Delphi Informant Magazine Complete Works CD located in
INFORM\2001\JAN\DI200101AF.
An instance of Excel.
Workbook.
Worksheet.
PageSetup object.
Alex Fedorov is a Chief Technology Officer for Netface SA, based in Lausanne, Switzerland
(http://www.netface.ch). Hes one of the co-authors of Professional Active Server Pages
2.0 [Wrox Press, 1998] and ASP 2.0 Programmers Reference [Wrox Press, 1999].
Natalia Elmanova, Ph.D. is an executive editor for ComputerPress magazine published
in Moscow (http://www.compress.ru), and was a speaker at the 10th and 11th Annual
Inprise/Borland Conferences. Natalia and Alex are authors of Advanced Delphi Developers Guide to ADO [Wordware Publishing, 2000], and several Russian programming
books. You can visit their Web site at http://d5ado.homepage.com.
Quick CLX
By Robert Kozak
hen I wrote my previous article (Cross-platform Controls in the August 2000 issue
of Delphi Informant Magazine) I was overwhelmed by the sheer volume of things
to write about. There was so much, in fact, that I knew an ongoing column was the only
way to properly do CLX any justice. So naturally, I was excited when my proposal to do a
column on CLX was accepted. With this column, we are going to explore the internals of
CLX and discuss some of the issues of Kylix development. Its my hope that by reading this
column, you will walk away with a better understanding of the internals of CLX.
As I write this, Kylix is still being developed. And
its possible that it has been released by the time
you read this. This means that there might be differences between the specics Ill provide here and
the nal product. You take that risk when you write
about the latest and greatest.
One of the questions I bet you have is: What is
different about CLX? Specically, how does CLX
procedure TWinControl.UpdateShowing;
var
ShowControl: Boolean;
I: Integer;
begin
ShowControl := (FVisible or
(csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
not (csReadingState in ControlState);
if ShowControl then begin
if FHandle = 0 then CreateHandle; { <------- }
if FWinControls <> nil then
for I := 0 to FWinControls.Count - 1 do
TWinControl(FWinControls[I]).UpdateShowing;
end;
if FHandle <> 0 then
if FShowing <> ShowControl then begin
FShowing := ShowControl;
try
Perform(CM_SHOWINGCHANGED, 0, 0);
except
FShowing := not ShowControl;
raise;
end;
end;
end;
Quick CLX
procedure TWinControl.CreateHandle;
var
I: Integer;
begin
if FHandle = 0 then begin
CreateWnd; { <------- }
SetProp(FHandle, MakeIntAtom(ControlAtom),
THandle(Self));
SetProp(FHandle, MakeIntAtom(WindowAtom),
THandle(Self));
if Parent <> nil then
SetWindowPos(FHandle, Parent.PrecedingWindow(Self),
0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE +
SWP_NOACTIVATE);
for I := 0 to ControlCount - 1 do
Controls[I].UpdateAnchorRules;
end;
end;
procedure TWinControl.CreateWnd;
var
Params: TCreateParams;
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
CreateParams(Params); { <------- }
with Params do begin
if (WndParent = 0) and (Style and WS_CHILD <> 0) then
if (Owner <> nil) and
(csReading in Owner.ComponentState) and
(Owner is TWinControl) then
WndParent := TWinControl(Owner).Handle
else
raise EInvalidOperation.CreateFmt(
SParentRequired, [Name]);
FDefWndProc := WindowClass.lpfnWndProc;
ClassRegistered := GetClassInfo(WindowClass.hInstance,
WinClassName, TempClass);
if not ClassRegistered or
(TempClass.lpfnWndProc <> @InitWndProc) then begin
if ClassRegistered then
Windows.UnregisterClass(WinClassName,
WindowClass.hInstance);
WindowClass.lpfnWndProc := @InitWndProc;
WindowClass.lpszClassName := WinClassName;
if Windows.RegisterClass(WindowClass) = 0 then
RaiseLastWin32Error;
end;
CreationControl := Self;
CreateWindowHandle(Params); { <------- }
if FHandle = 0 then
RaiseLastWin32Error;
end;
StrDispose(FText);
FText := nil;
UpdateBounds;
Perform(WM_SETFONT, FFont.Handle, 1);
if AutoSize then
AdjustSize;
end;
procedure TWinControl.CreateWindowHandle(
const Params: TCreateParams);
begin
with Params do
FHandle := CreateWindowEx(ExStyle, WinClassName,
Caption, Style, X, Y, Width, Height, WndParent, 0,
WindowClass.hInstance, Param);
end;
Into CLX
The process is much simpler in CLX, and easier to follow. It starts the
Quick CLX
procedure TWidgetControl.UpdateShowing;
var
ShowControl: Boolean;
I: Integer;
begin
ShowControl := (FVisible or
(csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
not (csReadingState in ControlState);
if ShowControl then begin
HandleNeeded; { <------- }
if FWidgets <> nil then
for I := 0 to FWidgets.Count - 1 do
TWidgetControl(FWidgets[I]).UpdateShowing;
end;
if HandleAllocated then
if FShowing <> ShowControl then begin
FShowing := ShowControl;
try
ShowingChanged;
except
FShowing := not ShowControl;
raise;
end;
end;
end;
same way; setting the parent causes the UpdateShowing procedure (see
Figure 6) to be called. In CLX (just as in the VCL) you need a handle
to display a control, but its not the same as a Windows handle.
In CLX, a handle is an opaque reference to the underlying widget
control. To get this handle, UpdateShowing calls HandleNeeded (see
Figure 7), and then calls CreateHandle. As you can see from Figure 8,
CreateHandle rst calls CreateWidget, which will create the widget.
The next method I want you to notice is InitWidget. Because
InitWidget gets called after the widget is created, you can be sure
the handle is valid. Its at this point that you can set properties on
the widget. For example, say you have a property named Color. In
SetColor you can check with HandleAllocated to see if you have a valid
handle. If the handle is allocated, you can make the proper call to set
the color. If not, you can store the value into a private eld variable,
and in InitWidget you make the call to set the color.
Now our Button is created, parented, and ready to rock-and-roll.
Heres the list of methods called in order to create a Button
component in CLX:
UpdateShowing
HandleNeeded
CreateHandle
CreateWidget
InitWidget
procedure TWidgetControl.HandleNeeded;
begin
if FHandle = nil then begin
if Parent <> nil then
Parent.HandleNeeded;
CreateHandle; { <------- }
end;
end;
that we call the setText method of the widget control. Notice that
the handle is the rst parameter. Next, we store the accelerator, then
clear it from the QButton control. We do this because were going to
handle all of the accelerators in the CLX framework.
Next we call a dynamic method named TextChanged as a notication.
This equates to the CM_TEXTCHANGED notication message in
the VCL. In fact, most of the component messages from the VCL
arent a part of CLX, and are replaced with dynamic methods. The only
time we kept a CM_ type message was when it had to be broadcast to
multiple controls. Most of the time, however, the CM_ messages from
the VCL only need to notify a descendant that something happened.
The real fun begins when you press the button and the event
res. If you put a breakpoint on the ShowMessage call, and press
the button, you will stop in the middle of the user event handler
named ButtonClick.
In CLX, SetText also handles setting the Caption, but in this case its
overridden in TButtonControl. Take a look at Figure 9; you can see
Bring up the Call Stack debug window, and lets follow this back about
seven calls. At the top youll see you are in ButtonClick. Going backward
Quick CLX
TForm1.ButtonClick($BF30AC)
TControl.Click
TButton.Click
TButton.CNCommand((48401, 796, 0, 6226716, 0))
TControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TWinControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TButtonControl.WndProc((48401, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TControl.Perform(48401,796,6226716)
DoControlMsg(6226716,(no value))
TWinControl.WMCommand((273, 796, 0, 6226716, 0))
TCustomForm.WMCommand((273, 796, 0, 6226716, 0))
TControl.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TWinControl.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TCustomForm.WndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
TWinControl.MainWndProc((273, 796, 6226716, 0, 796, 0, 796, 95, 0, 0))
StdWndProc(10093556,273,796,6226716)
TWinControl.DefaultHandler((no value))
TControl.WMLButtonUp((514, 0, 45, 6, (45, 6), 0))
TControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
TWinControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
TButtonControl.WndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
TWinControl.MainWndProc((514, 0, 393261, 0, 0, 0, 45, 6, 0, 0))
StdWndProc(6226716,514,0,393261)
TApplication.HandleMessage
TApplication.Run
Project1
in time, you see that it was called by TControl.Click, which in turn was
called by TButton.Click. This could be VCL or CLX code; its the same
mechanism, the same code. You can see the output of the call stack for
the VCL in Figure 10, and for CLX in Figure 11.
program Figures;
destructor TWidgetControl.Destroy;
var
I: Integer;
Instance: TControl;
begin
Destroying;
if Parent <> nil then begin
RemoveFocus(True);
SetParent(nil);
end;
if FHandle <> nil then begin
DestroyWidget;
FHandle := nil;
end;
I := ControlCount;
while I <> 0 do begin
Instance := Controls[I - 1];
Remove(Instance);
Instance.Destroy;
I := ControlCount;
end;
FBrush.Free;
FPalette.Free;
inherited Destroy;
end;
As in the VCL, the Free method checks to see if the object is not
nil, and then calls the Destroy method. The TWidgetControl.Destroy
method is shown in Figure 12. Youll notice that it calls DestroyWidget,
which as I bet youve guessed destroys the underlying widget.
Conclusion
There you have it: the life and death of TButton. By following the
creation, use, and destruction of a control like TButton, weve seen the
differences in Borlands two component frameworks. CLX is a wonderful component framework. Its well designed and elegant. You can tell
by the code that the VCL is harder to read at this level. CLX makes
this much easier by separating the component notications from the
event-processing code. By calling dynamic methods, instead of passing
messages, the code path is easier to follow.
I would like to thank Adam Sparky Markowitz for his comments
and suggestions.
The projects referenced in this article are available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\JAN\
DI200101RK.
Involved as a user of Delphi since the initial beta, Robert Kozak is a member of the
Kylix R&D team and has been with Borland since the latter half of 1999. Since he
joined Borland, he has been involved in the development of C++Builder 5 and
Kylix. Robert was involved with the start of TaDDA! (Toronto Area Delphi Developers
Association), which later merged with TDUG (Toronto Delphi Users Group). Robert
continues to stay active in the user community, and on the Borland newsgroups.
Quick CLX
Begin Listing One Demonstration project
{ ******************************************************* }
{
Project source
}
{ ******************************************************* }
program ButtonProject;
uses
{ $IFDEF LINUX }
QForms,
{ $ENDIF }
{ $IFDEF MSWINDOWS }
Forms,
{ $ENDIF }
Unit1 in 'Unit1.pas' { Form1 };
{ $R *.res }
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
{ ******************************************************* }
{
Unit source
}
{ ******************************************************* }
unit Unit1;
var
Form1: TForm1;
implementation
{
{
{
{
{
{
$IFDEF LINUX }
$R *.xfm }
$ENDIF }
$IFDEF MSWINDOWS }
$R *.dfm }
$ENDIF }
interface
uses
{ $IFDEF LINUX }
QT, Variants, Classes, QGraphics, QControls, QForms,
QDialogs, QStdCtrls;
{ $IFDEF MSWINDOWS }
{ $ENDIF }
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
{ $ENDIF }
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
At Your Fingertips
By Bruno Sonnino
f you need to place images in the cells of a DBGrid component, you must accomplish
what is known as custom drawing. To do this, you must write an event handler for the
components OnDrawColumnCell event.
Its declared as shown here:
TDrawColumnCellEvent = procedure(Sender:
At Your Fingertips
To temporarily
store the icon to
Figure 5: Animated hourglass icon.
be shown, a
variable named
FCurrIcon is declared in the private section of the form. Each
time the OnTimer event handler is red, you retrieve the current
icon from the ImageList and set Application.Icon to it. Then, you
increment (or reset, if the value has reached the maximum value)
the current icon index. The result is shown in Figure 5.
The statement will be executed every time the OnTimer event handler
is red (the default is every second), changing the title in the taskbar
and displaying the current time, as demonstrated in Figure 3.
Justifying Text
One frequently asked question is how to justify text between margins.
The Label, Memo, and RichEdit components can align text on the
right or left, or center it, but theres no way to align text between
margins, as word processors do. The solution lies in a Windows API
function named SetTextJustication.
Heres its declaration:
function SetTextJustification(
DC: HDC;
// Handle of device context.
BreakExtra, // Length of extra space, in logical units.
BreakCount: Integer // Count of spaces in line of text.
): Integer; stdcall;
Once youve interactively placed the icons in sequence in the ImageList, the code shown in Figure 4 animates the icon. You should
set the Timers Interval property to a value that provides a smooth
animation effect (500, for half a second, was used in this example).
At Your Fingertips
Env := GetEnvironmentStrings;
with ListBox1 do begin
// If it's a double #0, Env[0] will be a #0;
// stop the loop.
while Env[0] <> #0 do begin
// Assign parsed string to ListBox Items.
Items.Add(string(Env));
// Make Env point to the next string.
Inc(Env, StrLen(Env)+1);
end;
end;
For example:
SetTextJustification(Canvas.Handle, 0, 0);
To get each word of text, you can process the whole text as a big
string, character-by-character, searching for break characters (e.g.
space, carriage return, line feed). As shown here:
At Your Fingertips
would exceed the line length. That word becomes the rst word of
the next line. To print the line, you must call SetTextJustication,
passing the break count found and the extra space to add. This is
determined by subtracting the actual width of the line from the
maximum width of the line.
Heres one way to implement it:
// Add extra space.
SetTextJustification(Canvas.Handle,
ClientWidth-Canvas.TextWidth(LineToPrint, Breaks);
// PosY has the current Y position to print the text.
Canvas.TextOut(0, PosY, LineToPrint);
// The new line to print begins with the word found.
LineToPrint := AWord;
// Restart break count.
Breaks := 0;
The second step is to determine if the line length, with the new word
included, exceeds the maximum line length. As the words t in the
line, they are included in a variable named LineToPrint.
This verication can be accomplished like this:
// Place current word in AWord variable.
AWord := Copy(Text, BegWord, i-BegWord);
// Verify if it's a line break.
if Canvas.TextWidth(
LineToPrint + ' ' + AWord) > ClientWidth then
...
To print the line if the end of the text or the end of the paragraph
has been reached, there is no special procedure; a simple call
to TextOut will accomplish the task. Figure 9 demonstrates my
JustPrint procedure, which prints justied text. You must pass
three parameters to it:
1) a canvas,
2) the text to print, and
3) the maximum width of the line.
Figure 10 shows the accompanying sample application with text
justied on a form.
Conclusion
This month I have placed ve programming pointers at your ngertips to assist you in your Delphi development efforts. Its now up to
you to put them to good use.
The projects referenced in this article are available on the Delphi Informant Magazine Complete Works CD located in INFORM\2001\JAN\
DI200101BS.
A Brazilian, Bruno Sonnino has been developing with Delphi since its first
version in 1995. He has written the books 365 Delphi Tips and Developing
Applications in Delphi 5, published in Portuguese. He can be reached at
sonnino@netmogi.com.br.
Sleuth QA Suite 2
Powerful, Flexible Debugging and Profiling Tools
Each tool comes with the outstanding documentation thats become a TurboPower trademark.
That documentation includes a manual with a
thorough tutorial for each application to get you
up and running fast. You can run either application from within the Delphi IDE, or as a standalone executable.
Description
Module
Shows information about the project modules used by the application being analyzed,
including name, when it was loaded, version
stamp, code, data size, etc.
Active only if one of the API function or
parameter options is selected. Provides a
report on errors resulting from API calls or
improper parameter values. You can also log
API calls receiving helpful information.
Shows debug information generated by an
application calling the Windows API function,
OutputDebugString. You can also show internal Sleuth QA audit messages.
Provides comprehensive view of the results
of an application run, summarizing the information displayed in other views. You can
print this report or select other options.
Parameters
& Failures
Debug Output
Report
Chart window at the lower left, which explains some of the resource
types being monitored. CodeWatch can conveniently take you right
to the line of code where un-released memory is allocated.
Data Shown
Routines
Profile
Comparison
Outstanding Documentation
If youve used other TurboPower products, youre aware of the excellent documentation that comes with each of its products. Sleuth QA
Suite is no exception. As mentioned, the manual includes a tutorial
for both tools (with all the les you need on disk). The manual
and online Help provide all the information you need to begin to
use these tools quickly. Sleuth QA Suite goes further by providing
a cogent introduction to dealing with memory issues and performing code optimization. Once the Suite has shown you where the
problems are, the manual gives you important clues about what to
do to solve those problems. The material is so good that Ive encouraged the folks at TurboPower to expand it in future versions. Even
without the extensive improvements in Suite 2, the rst version of
Sleuth QA Suite is an essential tool for every Delphi and C++Builder
developer. Before we nish, however, lets see what Suite 2 brings us.
ExpressQuantumGrid
and ExpressInspector
T
ExpressQuantumGrid
ExpressQuantumGrid includes two main components: a TreeList view combination named
The run-time data/report viewer supports multilevel hierarchy grouping based on key columns,
automatic sorting, and automatic summary at the
footer or group level (see Figure 2). Summary isnt
limited to the simple addition function, but supports functions like minimum, maximum, aver-
ExpressInspector
While a capable grid provides the ability to display
and edit multiple columns in more than one record of
data, sometimes its better to provide a single record
data form for editing. In this form you can display
more columns of data omitted from the grid view
because of screen real estate considerations, or provide
bigger areas for editing memo or graphic columns.
If there is something I hate about developing database applications, its creating single-record data-editing forms. These forms usually include numerous
data-aware controls, each with its unique editing
needs and layout requirements. As much as I like to
create visually interesting applications that provide
the end user with easy data-entering capabilities,
these forms seem to be nothing more than an exercise in using the IDE. Of course, when the application needs to be updated to support a new database
column, or a different visual representation or validation of another, the process is repeated.
ExpressInspector is a component set that mimics a
Delphi or Visual Basic object inspector (see Figure 3).
It allows you to connect a data source to a component,
and use a well-dened property editor to identify the
columns that you want displayed in the inspector,
including the type of editor to use. (Of the 18 in-place
cell editors in ExpressQuantumGrid, only the dropdown grid appears to be missing from ExpressInspector.) You can also create categories and hierarchies of
properties. Developer Express provides both a dataaware version of ExpressInspector, or a standard version where you can generate your categories and cells
programmatically.
In addition to the source, documentation, and samples, both products come with two additional libraries of code: a memory-based
dataset, and a collection of visual components that can be used in
your application. You should visit their Web site to get a full idea of
all the products available from Developer Express.
Conclusion
After my experience with ExpressQuantumGrid and ExpressInspector, I cant imagine developing a Delphi database application without these tools. The grid is the most capable VCL grid Ive found.
Just by using a small part of its capabilities and customization
options, my applications seem more professional, and the users
enjoy the editing capabilities.
In my opinion, a grid is the most important database UI control,
and ExpressQuantumGrid is a must-have tool in my database components arsenal. The ExpressInspector component is not as essential
for every application, but if your application can use an object
inspector-like interface for single-record editing, this component
is easy to use and capable. I dont hesitate to recommend it. Its
well built and provides the same exceptional technical support from
Developer Express.
Best Practices
Directions / Commentary
ike physicians, programmers cannot rest on their laurels. The industry advances far too quickly for anyone to pull
a Rip Van Winkle and snooze through the implementation of popular technologies such as XML, SOAP, COM+, ad
infinitum. You snooze, you lose is a maxim that applies very well to the programming profession.
It is just as necessary to lay a strong foundation on which to
fasten that knowledge. You need to acquire general principles of
programming before you can effectively apply the specics related to
new technologies and techniques. The principles are best acquired
through books, where you can commune with those older and wiser.
A list of books which every programmer should read and periodically re-read follows. Theyre separated into two categories:
old classics and new classics. The old classics certainly arent as
old as Stendhals The Red and the Black or even Steinbecks Grapes
of Wrath, but as far as programming books go, theyre not spring
chickens either. Although the original year of publication is shown,
many have enjoyed recent revisions. Why no Delphi classics you
ask? That topic was addressed recently by Alan Moore in his
November 2000 File | New column.
Old Classics
The Mythical Man Month by Frederick P. Brooks, Jr. [AddisonWesley, 1975] contains a series of seminal essays on software project management, and introduces Brooks Law: Adding manpower
to a late project makes it later. Also noteworthy is Brooks No
Silver Bullet essay, which is reprinted in the 1995 edition.
Programming Pearls by Jon Louis Bentley [Addison-Wesley, 1985]
is a series of essays on programming techniques and computer
science theory. The preface explains the reasoning for the books
name: Just as natural pearls grow from grains of sand that have
irritated oysters, these programming pearls have grown from real
problems that have irritated real programmers.
Peopleware by Tom DeMarco and Timothy Lister [Dorset House,
1987] is a people-centric, common-sense guide to software project
management. Besides being a practical and insightful book, its also
a downright entertaining one. It even relates some Dilbertesque
episodes, such as this: In my years at Bell Labs, we worked in twoperson ofces. They were spacious, quiet, and the phones could be
diverted. I shared my ofce with Wendl Thomis who went on to
build a small empire as an electronic toy maker. In those days, he
was working on the ESS fault dictionary. The dictionary scheme
relied upon the notion of n-space proximity, a concept that was
36 January 2001 Delphi Informant Magazine
New Classics
Code Complete by Steve McConnell [Microsoft Press, 1993]. If
you only read one general programming book, this should be it.
McConnell shows what to do and why, teaching good program-
Best Practices
ming practices throughout. Example code is in C, Basic, Pascal,
and other languages, but is always explained well enough that it
can be followed regardless of your level of multilingualism. If you
are serious about programming, you should read and apply the
information in this book. (By the way, I did not remember
consciously, anyway that the name of McConnells column in
IEEE Software was Best Practices at the time I suggested that title
for this column.)
Rapid Development by Steve McConnell [Microsoft Press, 1996]
elucidates how to responsibly speed up the development process,
why RAD often goes bad, and what to do to prevent it.
Software Project Survival Guide by Steve McConnell [Microsoft
Press, 1998] makes specic suggestions on how to give yourself
the best chance of success on your software projects. It shows
that there is another way; you dont have to bump your head on
every step each time you come down the stairs (read the book
for the full analogy). Why does the author go to such lengths to
tell readers how to avoid death march projects? A quote from
the book proves illuminating: As Thomas Hobbes observed in the
17th century, life under mob rule is solitary, poor, nasty, brutish,
and short. Life on a poorly run software project is solitary, poor,
nasty, brutish, and hardly ever short enough.
After the Gold Rush by Steve McConnell [Microsoft Press, 1999]
makes the point that the software profession needs to grow up
if it wants to be considered a true engineering profession. He
also discusses the possible ramications for individual programmers who are not progressive and alert: Programmers who arent
paying attention could easily nd themselves working as twentyrst century software janitors. Read the book to nd out how to
prevent this from happening to you or someone you love.
The preface of The Pragmatic Programmer by Andrew Hunt and
David Thomas [Addison-Wesley, 2000] says, This book will
make you a better programmer. I agree. It introduces the broken
window theory, explains why and how you need to continue to
invest in your knowledge portfolio, and contains (not unlike
the 201 Principles book previously noted) a listing of 70 tips
and a checklist of things to keep in mind as you practice your
profession.
The following three books are not programming books per se,
but are nevertheless very helpful in understanding how best to
approach and solve challenges.
How to Solve It by G. Polya [Princeton University Press, 1971]
describes itself: This ... was written by an eminent mathematician,
but it is a book on how to think straight in any eld. The work
was a major inuence on the revival of heuristics or the study
of the methods and rules of discovery and invention. In lucid
and appealing prose, it shows how the mathematical method of
demonstrating a proof or dening an unknown can be of help in
attacking any problem that can be reasoned out from building
a bridge to winning a game of anagrams ... deft, indeed brilliant
instructions on stripping away irrelevancies and going straight to
the heart of a problem.
It is amazing at times how much this book seems as if it was written specically about solving software challenges. As an example
of that, there is a How to Solve It list comprised of the following
steps:
37 January 2001 Delphi Informant Magazine
1)
2)
3)
4)
File | New
Directions / Commentary
elphi is more than an outstanding development tool. Its a community. Nowhere is that more apparent than in
Project JEDI, the Joint Endeavor of Delphi Innovators. JEDI is a project on the move.
A lot has changed since I last wrote about the Project in the June
1999 Delphi Informant Magazine. If youve visited the JEDI Web site
(http://www.delphi-jedi.org) lately, you know what Im talking about.
The site won About.coms Best of the Net award in March 2000.
In listing the award, the Projects goal was to extend Delphis native
access to the Windows API by translating C/C++ headers of popular,
but as yet unsupported, technologies into Delphi interface units.
That was JEDIs original goal, but the Project now encompasses
much more. I would describe it as an impressive organization of
international volunteers donating their efforts to improve Delphi and
help their fellow developers.
That spirit of community is accentuated by the third-party vendors
who have generously contributed their tools and resources, including:
Nevrona Designs, who donated licenses for ReportPrinterPro/Rave;
Digital Logikk AS, who donated copies of Time2Help for the Help
Team; and HREF Corporation, for hosting the Web site and providing their outstanding WebHub technology for the Web Team to use.
These are just three of many donations. Before discussing the exciting
new developments, I should go back and trace the Projects beginnings
for the benet of readers who may be just nding out about it.
One of those people was Helen Borrie (Australia), who has continued to be one of the most active and hard-working Jedians. On
the following Monday she wrote to the DDJ list, letting folks
know what had occurred over the weekend. She began: Ive been
astonished - amazed - heartened - all of that - to see how people
have rallied around a perceived problem in the Borland support
machine and come up so willingly with the beginnings of a strategy
to solve it. What a great weekend! Then she expressed one of our
initial dilemmas: We here (a team of six Delphi developers with a
variety of Windows and DOS backgrounds) are just having a little
bit of trouble understanding what the project is aimed at. Over the
days and weeks that followed the Project was dened and named,
a president and other administrators selected, and work began on
the rst translations.
Ups and downs, reorgs, Borland, and growth. The project had many
ups and downs in its rst year. At times members wondered if the
Project would survive. Several early members deserve special recognition for managing those challenging times of the rst year. Tim
Hayes, the rst president of the Project, was one of the main driving
forces. Keith Anderson, another founding member, made Internet
support services available to the project. Robert Love organized Birdsof-a-Feather sessions at the 1998 and 1999 Borland Conferences.
The project had one especially low period. In early 1999 it seemed
all but dead, with almost no discussions on the Internet and little
activity. Our current president, Thomas Guarino, took the initiative of
inviting three of us who were still active in Internet JEDI discussions
(Michael Beck, Helen Borrie, and me) to form a new ADMIN Team.
Our goal was simply to get the project moving again, and we were
remarkably successful.
Soon we learned that some folks at Borland had been watching the
Project from a distance, wondering if it would survive and whether
they should get involved. We soon entered into a discussion with
members of the Delphi R&D Team about ways we could work
together. (I covered much of this in my June column, so I wont go
into details here.) When Delphi 5 was released, the companion CD
included one of our conversions. There was also a JEDI Easter egg
included in Delphi 5 (type AJEDI in the About box). Charlie
Calvert was one of the most important people at Borland who helped
foster the new relationship in so many ways.
File | New
A new team emerges. For any organization to remain successful,
it must be willing to change. Project JEDI is no exception. In
August 1999 Tom announced the formation of a Steering Group to
enhance communication among the many new JEDI leaders who had
emerged. This new group was soon making more and more decisions.
A few months ago it became clear to Tom that it was time to dissolve
the ADMIN Team. That small group had served its purpose of resurrecting the project with its members already active in the new Steering
Group. The story does not end here. With the many new JEDI
Knights from all over the world, and an expanded leadership group,
the Project has delved into new areas to serve the Delphi community.
Not just header conversions. Header conversions remain a major
focus of the Project, and that area has improved considerably with
many new translations. The core activity of the Project was enhanced
further by the clean-up and grading of the API library, accomplished
by Rudy Velthuis (Netherlands), Phil Shrimpton (UK), and Marcel
van Brakel (Netherlands). Thanks to them, its now a lot easier for
developers to locate the translations they need. However, there are so
many new exciting JEDI projects that I can only scratch the surface. A
major new area of emphasis is education, with several projects providing a plethora of programming information in a variety of formats.
JEDI Voyager provides an online journey through a series of articles
such as one on programming and testing by Robert Marquardt
(Germany), a Font Users Resource tool by Alan Lloyd (USA), and
a series of articles entitled Making Forms Work and on creating
Help Files by Kevin Gallagher (USA). In addition to this online
educational initiative, there is also an Education Team whose goal is
to provide Delphi educational material in a variety of formats. We
have received a lot of donations, with the biggest one coming from
Greg Lief (almost 8MB of training materials). To make it easy to
read such material when ofine, Michael Beck redesigned the JEDI
Dolphin, an application that reads articles and tutorials ofine. Be
sure to download it and check it out. If you are an aspiring Delphi
writer and/or have a particularly cogent way of presenting a technical
topic, please join us and add to the knowledge base.
Early in the project, we had discussions about creating code examples,
classes, components, and sample units. Today that goal is becoming
a reality. The JEDI Code Library (JCL) is a set of thoroughly tested