y ‘i y
DELPHI
IN A NUTSHELL
A Nesktop Quick ReferenceA Desktop Quick Reference
Ray Lischner
Beijing + CumirigDelphi in a Nutshelt
by Ray Lisehner
Copynght © 2000 OReilly & Associates, Inc. All nghts reserved,
ranted in the United States of America
Published by O'Reilly & Associates, Inc, 101 Mortis Street, Sebastopol, CA 95472,
Editor: simon Hayes
Production Editor: siuiclense Newell
Cover Designer: ‘lie Volckhausen
Printing History:
March 2000: First Edition.
"Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are
registered trademarks of O'Reilly & Associates, Inc. The assocration between the
image of a Ivnx and the topic of Delphi isa trademark of O'Reilly & Asseerates, Tne
Many of the designations used by manufacturers and sellers to distinguish their
products are clasmed as trademarks. Where those designations appear in this book,
and O'Reilly & Associates, Inc. was aware ofa trademark claim, the deagnations
have been prnted in caps of initial caps.
While every precaution has been taken inthe preparation ofthis book, the publisher
assumes no responsibilty in exits or umssions, of for damages resulting from the
Use of the information contained heresn,
Library of Congress Cataloging-in-Publication Data
Lschner, Ray, 1961—
Delph un a nutshell / by Ray Lischner
ecm
ISN 1-56592-059-5 (alk. paper)
1. Delphi (Computer file) 2. Computer software—evelopment I. Title
(QA7676.D47 156 2000
005.208-de2 99-086244
ISBN: 1-56592.059.5 (7/001
wmTable of Contents
Preface
Chapter 1—Delpbi Pascal
Units
Programs
Libraries
Packages
Data Types
Variables and Constants
Exception Handling.
File YO.
Functions and Provedures
Chapter 2—The Delphi Object Model
Classes and Objects
Interfaces,
Reference Counting
Messages soe
Memory Management...
Old-Seyle Object Types
Chapter 3—Runtime Type Information
Virtual Method Table
Published Declarations
‘The Typlnfo UnitVirtual and Dynamic Methods
Initialization and Finalization
Automated Metiuds
Interfaces
Exploring RTT
Chapter 4—Concurrent Programming
‘Thread and Processes
The TThread Class
‘The BeginThread and EndThread Functions ..
Thread Local Storage
Processes
Futures
Chapter 5—Language Keference
Chapter 6—System Constants
Variant Type Codes
Open Array Types
Virtual Method Table Ottsets
Runtime Error Codes,
Chapter 7—Operators
Unary Operators
‘Muldplicaive Operators
Additive Operators
Comparison Operators
Chapter 8—Compiler Directives
Appendix A—Command-Line Tools
Appendix B—The SysUtils Untt
Index
90
n
a
oo)
35
103
108
109
109
19
127
422
22
23
424
25
428
428
430
81
432
435
473
488
543Preface
Borland’s Delph is a combination of a modem programming language, an inte-
trated development environment (IDE), and the visual component library (VCL)
Delphi's IDE i readily familiar 19 anyone who has wee smi tools. Ror example,
4 WYSIWYG form editor lets you design a window visually, with drag-and-drop
cape, Mute inporany, tie framework 18 objectonented, extensible and custom
able, due to the power and flexibly of the Delphi programming language.
The hear of Delphi is the Delphi Pascal programming language, which has key
features to support the IDE and VCL. It has all the power of a’ modem object
nented langsinge, along withthe elegance and simpliciyy of Pascal
Debbi in a Nutsbell is a comprehensive reference manual for Delphi Pascal t
covers the entice language, and it also highlights ways to use the language efec-
tively Eapctieuced Delphi prugraumers cat use dhs book as an alphabeical
reference, Newcomers to Delph should spend exta time with the first few chap-
ters. [hope that everyone wil find something of value in these pages.
Not Your Father's Pascal
Delphi Pascal is one of many object-onented variants of Pascal. Over the years
Delphi has evolved and is no longer recognizable as the Pascal you used in school
all those many years ago. In addition wo unitbused audular programy andl
robust class model, Delphi Pascal has a number of other modem language
features, including the following:
© Intertaces (similar to Java™ and COM sntesfaces)
+ Unicode stangs
+ Properties
+ Exception handlingDelphi started as a Windows programming language and envitonment, and many
Delphi programmers (myself included) consider Delphi to be the best Windows
development tool available. Deiphr includes full support for COM and ActiveX, an,
objectonented widget library (called the Visual Component Library, or VCL). and.
4 rapid-application development environment that 1s extensible and customizable,
Delphi for Linux
AAS Lite this, Borland is hard at work porting Delphi to Linux. Peshaps when you
‘ead this, Delph for Linux will be available, bringing its integrated development
enuonmient fo X-Windows, including ts WYSIWYG form edlfor, multtier data
base support, and full CORBA suppor.
Until Borland finishes this work and releases Delphi for Linux, can only specu-
late abour how the final product will look. (No, T don't get any specal msde
snformation.) You can rely on the core language heing the same in both Delphs for
lunux and Delphi for Windows, including classes, objects, interfaces, strings,
dynamic arrays, exception, aut the basic data types. Most of the builtin subrou
tines will work the same under Linux as under Windows
Some language features described in this book are clearly Windows specific, such
as the CéShow and Dl1Proc vanables or the Pindifinstance function. If you
Want to write code thar 1s portahle herween Windows and Linux, you must avoid
these Windows-specific features,
Delphi for Windows 1s the best development environment for writing Windows
applications and libraries. To attaun this preaues poston, Delph has incorporated.
a number of Windows-specifc features. Borland has a goal of making Delph for
Linux the best Liqux development environment. To achieve that goal, we can
expect Delphi ta include some Linux-specfic features,
‘Ym just guessing. but I believe ic will he foasihle to write code that 1s portable
between Windows and Linux. However, you will have to sacrifice some features
that are unique to each envirumuent. Wrtung components that are easily portable,
specially interactive controls, will probably be a daunting task. Making an appli-
cation that is portable will most likely be easier,
About This Book
‘The first four chapters of this book present information om haw
effectively, and subsequent chapters form the language reference proper.
Chapter 1, Delpbi Pascal, discusses the differences between Delphi Pascal and
‘sandard Pascal. If you have used Tusbo Paceal or other variants of Object Pascal,
you should ge Chapter 1 a quick read to lear about the new features that are
unique t0 Delphi Pascal. Similarly, if you haven't used Pascal since your college
days (all those years ago). you must rearl Chapter 1 to leatn about the new and.
nifty features in Delphi Pascal. You mught be surprised at how far the language
hhas come over the yeas
Chapter 2, The Dolpbr Object Model, discusses classes an Ulyeuts m greater depth.
If you have used other vanants of Object Pascal, you must read this chapter
ise Delph
bill Preface ae —_ abecause Delphi's object model 1s quite different. IF you have experience with other
‘object-onented programming languages, read Chapter 2 to learn the differences
between Delp sind valet languages, sult a9 Java snd Or
Chapter 3, Runtime Type Information, covers the key to Delphits integrated devel
‘opment environment. RTTI 1s not documented in Borland!’ offical help files, but
anyone writing or using components (that 1s, every Delphi programmer) should
understand the nature of RTTT, including its imitations and proper uses. Chapter 4
tells you everything there is to know about RTTI, and then some.
Chapter 4, Concurrent Programming, 36 about using Delphi in a modern, muli-
threaded, multiprocessor world. Delphi includes several language fearues to help
‘you write multithreaded applicauons, but these features can be difficult to use if
you do not have much expenence with the ticks and traps of multithreaded
programming. This chapter gets you started uting Delphi elfectively to write
‘modern applications,
Chapter 5, Language Reference, 8 the bull of the book. The alphabetical refer-
cence lists every keyword, direcuve, subrouune, type, and vanuble an the Delpla
Pascal language and its system units. Full examples show you how to use the
language correctly and effectively
Chapter 6, System Constants, contains tables of related constants, Chapter 51s large
fenonigh without adding these literals Maving them 10 a separate chapter makes
the complete reference easier to use.
Chapter 7, Operators, describes all the arithmetic and other operators in Delp
Pascal. Symbols do not alphabetize wel, so listing the symbol operators in thet
con chapxer makes i ease find information about a particular operator.
Chapter 8, Compiler Directives, lists all the specal comments that you can melude
sn yout source code to control how Delphi compiles and links your program.
Appendix A, Command-time Tools dscns the usage and options for the vanious
‘commandline tools that come with Delphi, These tools are not related to the
Delpin Pascal language, but they are often overlooked and can be extremely
‘seful for the Delph profesional
Append B, Tho Syst: Uni list all the subroutines, ypes, nd varables in the
SyaVtils unit. This unit not bul nto the compiler (asthe System uni 1). Its
‘not part of the Delpht Pascal language, but 13 part of Deiphi's euntime lbeary
Nonetheless, many Delphi professionals have come to rely on SysUtils as though
ic were part of the language, and indeed, many subroutines in SysUeils are supe-
tiv dew eyuivaeuts is Use Syste uk (oul ay AusiPus used Uf FOS)
Conventions Used in This Book
‘The following typographical conventions are used sn this book
Constant width
Used for Delph entifcrs sud symbols, uicludling all keywords aud dite
tives. In the language seference, constant wicth shows the syntax elements
that must be used exactly as Shown. For example, the array declaration
Prefacerequires the square brackets and other symbols. and the tyne, array, and of
keywords to be used as shown:
type name = array(Index type, ...] of nase type:
Constant width italic
Used in the language reference for syntax elements that must be replaced by
your code. In the previous example, you must supply the type Name, the
Index type, and the Rase type.
Constant Width Bold
Used in longer code examples to highlight the lines that contain the language
‘element being described.
alte
Used to indicate variables, filenames, directory names, URLs, and glossary
Note Icons
the ourrounding text.
g ‘The owl icon designates a tip, suggestion, or general note related to
& ‘The turkey icon designates a warning related to the surrounding tex.
For More Information
‘When you have a question about Delph, you should fist consult the Delphi help
files. Delphi also comes with numerous examples (in the Dems directory) that are
‘often more helpful than the help files,
1f you still cannot find the answer you seek, try posing your question to one of the
many Delphi newsgroups. Several standard newsgioups exist, and Borland mai.
tains its own newsgroups on its server, forums.borland com. In panticula,
oriand pubic. delphi.objectpascal 1s the appropriate newsgroup for questions
related to the Delphi Pascal language
If you want to know about Delphi's integrated development environment, about
the visual component library, or other topics on Delphi programming, the two
‘most popular books are Mastering Delphi 5, by Marco Cann (Sybex, 1999) and
Delph 5 Developer's Guide, by Steve Terxesra and Xavier Pacheco (Sams, 1999),
If you find errors or omissions an this book, please bring them to my attention by
sending email to nussbell@iempest-sw:com. I receive t00 much email to answer
© Preface‘every message indivdually, but be assured that I tad everything (everything that
makes it past my anti-spam fiers, anyway).
How to Contact Us
“The information in this book has bon tested and verified to the best of our ability,
Dut mustakes and oversights do occur. Please let us know about ettors you may
find, as well as your suggestions for future editions, by writing to:
Oxeilly & Associates, Inc
101 Morns Street
Sebastopol, CA 95472
(800) 998-9938 Gin the U.S. ot Canada)
(707) 820-0515 (international or local)
(707) 829.0104 (fax)
‘You can also send us email messages. To be put on our muzling list or to request a
catalog, send mail to:
‘
[email protected]
To ask technical questions or to comment on the book, send email to:
bookquestionstorelly com
To find out about errata and any plans for fuure editions, you can access the
ook’s web site at
eww oreily.comieatalog/delphs
For more information about ths book and others, see the O'Reilly web site
sun oreilly.com
Acknowledgments
I thank Tim O'Reilly for taking a chance with his fist Delphi tile. I look forwaed
to reading and writing other Delphi boolis published by O*Relly
“The technical editors—Allen Raner and Hallvard Vasshotn—did an excellent ob of
spotting my mistakes. Hallvard's copious and detailed comments were snvahuable.
Any remaming mistakes are ones that Cade afl Une ediors finshed thes ao
cough work.
1 thank my editor, Simon Hayes, and the entice team at 'Reily—Bob Herbstman,
‘Troy Mot, and the design and production staff—for turning my humble manu-
scp soto this polished book you tee now
None of my books would be possible without the suppor of family 1 thank my
wife, Cheryl, and the once-and-future programmer, Anus, who makes it all
wworthtie
PrefaceCHAPTER 1
Delphi Pascal
Delph Pascal 1 an objectonented extension of wadtionl Pascal. It is not quite a
proper superset of ISO standard Pascal, but if you remember Pascal from your
School days. you will easy pick up Delphi's extensions. Delph not ps a fancy
Pascal, though. Delphi adds powerful object-oriented feaures, without making the
lungusge woo complicated. Delph tas asses and objects, exception handling,
multithreaded programming, modular programming, dynamic and static linking,
OLE automaton, and much, much more
‘Thus chapter describes Delphi's extensions to Pascal, You should already be
familar woth tridtional Pascal or one of the other poplar extensions to Pascal,
such as Object Pascal. If you already know Borland's Object Pascal from the Turbo
Fascalproduets, yOu Will need to leam a new object model (detailed in Chapter 2,
‘The Delpbs Obyct Model), plus other new Features,
Bottand uses the name “Object Pascal” to refer to Delphi's programming language,
Dut many other languages use the same name, which resuls in confusion. This
book uses the name "Delphi Pascal” to refer to the Delph programuning language,
leaving Object Pascal forthe many other obyct-orented variations of Pascal
Units
Delphi Pascal is a modular programming language, and the basic module is called
‘unit To compile and link a Delphi program, you need a program source file
and any number of additional units sn sousce or abject form. The program source
fle w usually called a project source file because the project can be a program or a
library—that is, a dynamucally linked library (DLL).
When Delphi links a program or library, it can statically like all the units into a
Single exe ur all Ble, or Gu dymaanically link wo unis dhat ure an packages.
package 1s a special kind of DLL that contains one or more units and some extra
logic that enables Delplu to hude the differences between a statically linked unitand a dynamically linked unit in a package See the section “Packages,” later in
thus chapter, for more information about packages,
Forms and Files
Some units represent forms. A form is Delphi's term for a window you can edit
with Delphi's GUL builder. A form descnption is stored in a .dfm file, which
contains the Forme layout, contents, aad prupeties
Every fm file has an associated pas file, which contains the coe fur dia form,
Forms and .dfm files are part of Delphi's integrated development environment
ADE), but are not part of the tormal Delphi Pascal language, Nonetheless. the
language sncludes several features that exist solely to support Delphi's IDE and
form descriptions. Read about these features sn depth in Chapter 3, Runtime Type
Information.
‘which maintains compatibility with the fist version of Delph, Ver.
sons 2 and later produce only 32-bit programs, so Delphi's linker
convers the df resource to a 32-bit resewece automatically Th
binary .dfm files are usually compatible with all versions of Delphi
Delphi 5 also suppunes extual .dfn files, Ihese files are plain text
and. are not compatible with por versions of Delphi, at least not
without conversion back to the binary format. The only way to tell
whether a dfn file ie binary of text ia to ope te file and check the
contents. An easy way to do this programmatically 1s to test the frst
{tee bytes, which are always SFF SOA $00 sn a bunary .djin file,
g A binary fm file w actually a 16 bit eo (Windows resuuiee) fe,
Table 1-1 beefy describes the fles you are likely to tind in Delphi and what they:
are used for. Files marked with “(IDE)" are not part of Delphi Pascal, hur ane used
by the IDE,
Table 1-1: Delp Files:
Extension | Description
pg) Proc group TD
tet | Compiled package (Opec! hand uf DED
ft | Options forthe command line compiler
dep | Compiled package efomation, needed lnk with a package
der | Component bmap resource (DE)
dou | Cale yet code
jm | Porn descnption ADE)
dof | Project options fle (DED
dpe} source te tor bulding a package
dpe | Main source file fora program o rary
dire | Resource snpt for resourcestring decantions
Chapter 1 Delbbs PascalTable 1-1. Delphs Files (continued)
Extension
sk Desktop layout UDB)
‘pas Unit source code
Windows resource (every dpirhas an associated rms file)
Separating Interface from Implementation
‘A unit has two pans: interface and implementation. The interface part declares the
types, vanables, constants, and routines that are visible to other unite. The imple
‘mentation section provides the guts of the routines declared in the interface
section. the implementation section can have additonal declarations that are
private 10 the unit's implementation. Thus, units are Delphi's primary means of
information hiding,
(One unit can tse another unit, that is, import the declarations from that other unit
A change to a unit interface requires a recompilation of all unite that woe the
changed declaration in the modified unit. Delphi’s compiler manages this automat-
scally, o you don’t need to use makeliles to compile Delphi project.
‘You can use a unit in the interface or implementation section, and the choice 1s
important when building a project
If unit A uses unit B sn its interface section, changes to unit B's interface are
propagated as changes to unit A's interface. Delpht must recompile all the
unite that use unit A.
‘+ Ifunit A uses unit B in its implementation section, only unit A must be recom-
piled to use the new declarations in unit By
Units cannot have circular references in thes interface sections. Sometimes, you
will run into wo clase declarations that contain mutually dependent declarations.
‘The sumplest solution is to use a single unit, but if you have reasons to declare the
classes in separate units, you can use an abstract base class in one or both units to
climinate the circular dependency (See Chapter 2 for more information.)
Inittalizing and Finalizing
Every unit can have an sniualzation and a finalization section. Code an every
initialization section runs before the program or library's main begin-end block.
Code in the finalization section runs after the program terminates or when the
brary is unluaded. Delphi runs the inialization sections using « deptifist
traversal of the unit dependency tree, In other words, before a units snitiaization
‘code runs, Delpht runs the initialization section of every unit it uses, A unit 1s
initialized only once, Example 1-1 demonstrates how Delphi initializes and final-
Example 1-1. Showing the Order of Unis Initialtzation
program Bamplel_t;
‘ses unitay
(Steptype Console)
mis 3Example 1-1. Showing the Order of Unit intalization (continued)
agin
Weltetas("Reawpie LoL main program)
end.
interface
inplenantation
inieializatioas
Weiteln ‘unica initialization’);
tanalization
Weitetan(unita finalization’):
en.
unit units;
sntertave
inplesentaticn
anitialization
Weitain("mniep initializati
finalization
lwrfretn( ‘inst finalisation’)
ena.
When you compile and run Example 1-1, be sure to nin it from a command
[promps, not the IDE, of else the console will appear and disappear before you can
see the output. which is shown ax Fxampte 1-2
Example 1 2- The Output from Running Example 1-1
We \nsshet Sema
units initialisation
sunita iniesalization
Banple 1-1 main progran
units finalization
The System and Systnit Units
‘The System und Sysmnit units are automatically included in every unit, o all of
the declarations in these units are effectively part of the Delphi Paseal language,
and the compiler has special knowiedge about many of the functions and proce.
dures in the Syotem and Oystnit units Chapter 5, Language Reference, 1s a
complete reference to the system routines and declarations meant for your use
Programs
‘A Delphi progiau looks sunllar to a traditional Pascal program, stating with the
rogram keyword and using a beginend block for the main program Delphi
programs are usually short, though, because the real work takes place in one or
‘more separate unite, In a GUL application, for example, die una program usually
7
Chapter 1~ Delphi Pascalcalls an initialization procedure, ereates one or more forms (windows), and calls a
procedure for the Windows event loop.
or compatibility with standard Fascal, Delphi allows a parameter list after the
program name, but—like most modern Pascal compilers—it notes the identifiers
listed there,
In a GUT application, you cannot use the standard Pascal /O procedures because
there ie no input device to read from and no output device to write to. Instead,
‘you can compile a console application, which can read and wete using standard
Pascal VO routines. (See Chapter 8, Compiler Directives, to leam about the
‘Shox/Type directive, which tells Delph to build a console or a GUI application.)
|A program's uses declaration lists the units that make up the program. Each unit
rname can be followed by an in directive that specifies a filename. The IDE and
‘compiler use the filename to locate the units that malee up the project. Unit
‘without an in directive are usually library units, and are not part of the projects
source code. If 2 unit has an associated form, the IDE also stores the form name in
‘3 comment, Example 1-3 shows a typical program source fle
Example 1-5: Typical Program Pile
progean Typical)
Fors,
Main in ‘Vain.pas' ainForm),
Movestusé in ‘MoreStuff.oae' (Forn2}.
els in “Ueits.pas
(gr +885)
begin
‘Aophication Initialize;
‘oplication,CreateForm(MainForm, Mainform)
‘pplication. CreateForm(1Form2, Forn2);
deplicaticn. amy
end.
The Forms unit part of the standard Delphi library, so it does not have an in.
directive and source file, The other units have source filenames, so Delphis IDE
manages thoce files ao part of the project. To learn about the $R compiler direc-
tive, see Chapter 8, The Application object 1s part of Delphi's visual component
library and is not covered in this book. Consult Delph’s online help tor informa:
tion about the Application object and the rest of the VCI
Librartes
‘A Delphi library compiles to a standard Windows DLL. A library source file looks
the same ae a program source file, except that it uses the Libvary keyword
instead of progran A Lbrary yprcally has an exports declaration, which lists the
roulines that the DLL exports. Ihe exports declaration 1s optional, and if you
intend to use @ unit in a library. is usually best to put the exports declaration in
Libraries 3the unit, close to the subroutine you are exporting. If you don't use the unit in a
Iibrary, the exports declaration has no impact.
‘The min body of the brary—its begin-end block—executes each time the
library is loaded into an application. Thus, you don't need to write a DLL proce
dure to handle the DLL_PROCESS_ATTACH event, For process detach and thread
‘events, though, you must write hauler. Assign the handler to the DLIProe var-
able. Delphi takes care of registering the procedure with Windows, and Windews
calls the procedure when a process detaches or when a thread attaches or
detaches. Feample 1-4 shows a simple DLL procedure.
Esample If DIL Auach and Derach viewer
we none
rocedure Log const Mog: string);
begin
MessageBox(0, Phar (tog), ‘Attacher', tb toontnformation + Hb.OK);
euvedure actachuetacnroc|Reagon: Integer) ;
begin
ase Reason of
DiL_Process Detach: Log(‘nararh Procees!)
DiLtread Attach: tog(‘Attach Thread’);
Di_stread Detach: tog( ‘Detach Tawa")
else og (nim resent");
ena;
begin
11 This code runs each tine the DU, ie Loaded into a new process.
Lagl'Retach Proceea')
DiiPrec += GhteachDetachProc:
Using Dynamic Memory
‘When using a DLL, you must be careful about dynamic memory. Any memory
allocated by a DLL fs freed when the DLL 1s unloaded. Your application might
fetain pointers to that memory, though, which can cause acess violations of
‘worse problems if you aren't careful. The simplest solution is to use the ShareMen
unit as the fst unit in your application and it every Mbrary the application loads,
‘The ShareMe unit redirects all memory requests to a single DL (BorlndM Mtl,
‘which is loaded as long as the application is running, You can load and unload
DLLs without worrying ahout dangling pointers
Sharing Objects
‘Sharettem solves one kind of memory problem, but not another: class identity If
lass A is used in the application and in a DIL, Delphi cannot tell that both
© Chapter 1 Delph Pascalsmodles nse the same class. Although hath modiles use the same class name, this
doesn't mean the classes are identical, Delphi takes the safest course and assumes
the classes are different, f you know better, you have no easy way 10 form
Delph
Sometimes, having separate class sdentites does not cause any problems, but if
‘your program ines to use an object reference across a DLL boundary, the is and
fas operators will not work the way you expect them to, Recause the DLL thinks
class A is different from the application's class A, the is operator always returns
alse.
‘One way wo crcurvert das problemas uw pass obyecisweruss DLL boundaries,
IF you have a graphic object, for example, don't pass a TBitmap object, but pass a
‘Windows handle (XBTTMRP) instead. Another solution 1s to use packages. Delphi
3c entities in packages to avoid thie problem.
automatically manages the c
Setting the Image Base
When you create a library, be sure to set the Image Base option, Windows must
load every module (DLL and application) at a unique image base address. Delphi's
default is $00400000, but Windows uses that address for the application, so it
‘cannot load a DUL at the same address, When Windows must move @ DLL. to a
different address. you mcur a performance penalty. because Windows must rewrite
a relocation table to reflect the new addresses. You cannot guarantee that every
DLL will Lave « uunque addiess because you Cannot cool the audesses Uther
DLL authors use, but you can do better than the default. You should at least make
sure your DLLs use a different umage base than any of the standard Delph pack-
ages and Windows DLLs. Use Windows Quick View to check a fle’ image base,
Packages
Delphi can link a unit statically with a program or library, of it can link units
dynamically To link dynamucally to one of more units, You must put those units sn
a package, which is a special kind of DLL. When you write a program or library,
you don't need to worry about how the units will be linked. IF you decide to use &
package, the unit in the package are not linked into your .exe or ll, but snstead,
Delphi compiles a reference to the package’s DLL (which has the extension bp!
for Borland Package Library)
Packages wvold dhe problems of DLLs, namely, mimagmy memory and class alent:
ties. Delplu keeps track ofthe classes defined in each unit and makes sure that the
application and all associated packages use the same class identity for the same
clace, 20 the 49 and aa operators work correctly
Design-Time Versus Runtime
Delphi's IDE uses packages to load components, custom forms, and other design-
time units, cuch as propery editors, When you write components, keep thet
designstime code in a design-ime package, and put the actual component class ia
4 runtime package. Applications that use your component can link statically with
the component’s dew file or link dynamically with the runtime package that
Packages 7Contains your component. By keeping the design-time envle in a separate package,
you avoxd linking any extraneous code sto an application,
Note that the designstime package requires the muntime package because you
‘cannot link one unit sto multiple packages. Think of an application ut library a5 4
collection of units, You cannot include a unit more than once ina single
rogram—at doesnt matter whether the units ae linked statically or dynamically
‘Thus, if an application uses two packages, the same unit cannot be contained in
both packages. That would be the equivalent of linking the unit rwice,
Building a Package
To build a package, you need to create 2 dl file, or package source file, The
// mide TACSOUNC. AMA
end;
Tsavingexccount = clase(TAccount)
public
1/ Can call TSavingsAccount.Audlt and Taccount AUG
procedure Audit kind: Tandireind); ewerload,
end
Checking: Theckingkecount;
savings: Teavingsscccunt;
begin
‘Checking 1= Theckingtocount Creat
Savings := TSavingsdcccunt Crear
Checking. adit; 11 Bere because Thecount Audit is hidden
‘Savings Pasty 10 Okay because Auli iv Overloaded
Savings Audit (autiagty); 7 Okay
Secking.auaic auineernan); // okay
Constructors
[Every class has one or more constructors, possibly inherited from a base class. By
‘convention, consiructors are usually named Create, although you can use any
‘name you like, Some constrictor names start with Create, but convey additianal
information, such a CreateFromPile or CreatePronStrean Usually, though,
the simple name Create is sufficient, and you cat use mietiod overloading 10
define multiple constructors with the same name. Another reason to overload the
name Create is for compatibility with Cr+ Builder. C++ does not permit different
Consinictor names, you must use overloading to define multiple constructors.
4 Chapter 2— Toe Delphi Object ModelCalling a constructor
A constructor 36 2 hybndl of object and class methods. You can call using an
object reference or a class reference. Delphi passes an additional, hidden param-
eter w indicate how it was called, If you call a constructor using a class reference,
Delphr calls the class's NewInstance method to allocate a new instance of the
class. After calling NewEnstance, the constructor continues and initializes the
fobject. The constructor automatically sets up a try-except block, and if any
exception occurs inthe constructor, Delphr calls the destructor,
‘When you call a constructor with an object reference, Delph does not set up the
exy-except block and does not call NewInstance. Instead, st calls the constructor
the same way it calls any ordinary method. This lets you call an inherited
‘constructor without unnecessary overhead.
with an object reference, rather than calling t with a clase reference
‘A common error sto try £0 create an object by calling a constructor
and assigning it to the object variable.
begin .
eceunt Create; 1 wecoos
Account = TSavingsAccount Create; // right
‘One of Delphi's features is that you have total control over when, how, and
‘whether to call the inhesited constuccut. This les you waite some powerful and
interesting classes, but also introduces aa area where it 1s easy to make mistakes.
Delph always constructs the derived class first, and only if the derived class calls
the inherited constructor does Delphi construct the base class. C++ constructs
classes in the opposite direction, stating from the ancestow class ancl coasteting,
the denwved class last. Thus, if class C inherits from B, which inherits from A,
Delphi consiructs © firs, then B, and A last. Crt consttuets A first, then B, and
finally C
Virtual methods and constructors
Another significant difference between CH¥ and Delphi is that in C+, a
constructor always runs with the virtual method table of the class being
constructed, but in Delphi, the virtual methods are those of the derived class, even
when the base class is heing constructed. As a result, you must be careful when
‘writing any virtual method that might be called from a constructor. Unless you are
careful, the object might not be fully constructed when the method is called. To
avoid any problems, you should override the AfterConstruction method and
‘use tat for any code that needs to wait until the object is fully constructed. If you
override AftexConstruction, be sure t call the inherited meta, wu.
‘One conetructor can call another conetructor. Delphi can tell the call ¢ from on.
object reference (namely, Sef), soit calls the constructor as an ordinary method.
Glasses and Objects 4‘The most common reason to call another constmictor is to put all the initialization
‘code ina single constructor. Example 2-7 shows some different ways to define and
call constructors
‘Example 2-7- Declaring and Calling Constructors
‘Teustoner = class... end
‘Taccount = class
private
‘Balance: currency;
‘tamper: cardinals
fOustonar: TOustoner;
public
constructor Create Customer: TOustonet]; virtual;
destructor Destroy; override;
ena;
‘TavingsAccount = clase tRacount)
€InterestRate: Integer; // Scaled by 1000
patie
constructor Craate|Custoner: TCustemer) ; override; overload:
constructor Create(Custaner: Toustoner; ‘interestRate: Tteyer)
‘overload:
11 ote that TSavingstecount does not need a destructor. Te simply
end)
Accountitber: Cardinal
L
‘constrictor TAccount Create(Custoner: TOustoner)
featm
“inherited create; 11 Call Tobjece.ceeate,
Bhebe: := Aowwntmmber; —// Assign a Unigie accoMnE riber.
ne (Accountitmber) ;
roustoner = Customer 1) Wotity custerer of new account.
Ccustoner-Attachtooount (Self):
end
destructor Thocount Destroy;
begin
(1 TE the constructor fails before setting fOustener, the field
17 Will be nil, Release the accomt only 1f Customer {8 not nil,
Af Custener © nil then
‘Customer Releasetocount (Self);
11 eat Tehject. Destroy,
inherited Destroy
const
DefaultinterestRate = 5000; // St. scaled ty 10n0
‘D Chapier 2 The Delphi Object ModelExample 2-7: Declaring and Calling Constructors (continued)
constructor TSavingstccout.Create(Custener: Teustower)
beain
7) Call a sibling constructor.
Create(Custoner, DefaultinterestRate);
end:
constnuctor TeavingeNovount (Customer! TOuctonary Intarcothatortntogee)
begin
inherited Create(custener)
FintereatRate := Interesttates
ea
Destructors
Destructor, lke Consuuciurs, Gahe att estes hidden parameter, The Ort call wo a
destructor passes True for the extra parameter. This tells Delphi to call
FreeInstance (o free the object. I the destructor calls an inherited destructor,
Delphi passes False as the hidden parameter to prevent the inherited dectructor
from trying to free the same object.
declare additional destructoxs, but you should’ take advantage of
that feature. Declaring multiple destructors 4s confusing and serves
‘no useful purpose,
g A class usually has one destructor, called Destroy Delphi lets you
Betore Delphi stants the body of the destructor, it calls the virtual method,
BoforeDestruction. You cin overide Reforebeatruction to assert program
state or take care of other business that must take place before any destructor
stay, This leo you write a class safely without worrying abou how oF whether
any denved classes will call the base class destructor,
destructor, but You must not redeclare the Free method. When iree
ing an object. you should call the Free method and not the destruc
tor. The distinction is important, because Pree checks whether the
cubject reference iy nil and calls Destroy only for non-nd2 refer-
‘ences. In extraordinary circumstances, a class can redefine the Pree
method (such as TInterface in the seldom-used VirtIntf unid,
‘which malee it that much more important to call Free, not Deatoy.
& ‘When writing a class, you might need to override the Destroy
Ifa constrictor of Afterconatniction method mises an exception, Delphi ate
‘matically calls the object's destructor. When you write a destructor, you must
remember that the object being destroyed might not have been ‘completely
constructed. Delphi ensures that all fields start out at zero, but if the exception
Glasses and Objects HB‘occurs in the middle of your constructor, some fields might be initialized and
some might still be zero, Ifthe destructor just frees objects and pointers, you don't
need to worry, hecanse the Pree method and ExeaMem procedure both check for
‘nil pointers. Ifthe destructor calls other methods, though, always check first for a
‘add pouster,
Object Life Cycle
For most objets, you call a constructor to create the object, use the object, and
then call Free W fice the olyeu. Delph handles all te other detals Tor you
Sometimes, though, you need to know a litle more about the inner mechanisms
of Delphi's object model. Example 28 shows the methods that Delph calls or
sionulates when it creates and frees en object.
Example 2-5: The Life Cyele of an Object
ope
‘TSomething = class
rocedire DeSanething;
end;
Refs rermathing:
begin
ef. TSaethiun.Craatey
Ref. Dosoneting?
Ret Free;
end;
11 he hidéon code in the constructor locke comcthing 2ike this:
function TSenething.Create(ToClassRef: Boolean): “Sanething;
begin
TE TeClasate€ then
uy
1/ Bocate the new abject.
Self := TSonething.tewinstance;
1/ Mestostance initializes the object in the came way that
U7 InitInstance does. Tf you uverside Mewlastance, oth,
U1 az do not cal the inherited Newinstance, you mt call
17 Initinstance, The call is shom below, 20 you know wt
1/ happens, bot renenber that ordinarily Delehi does not
11 actually call Tnitinstance.
ntetnarancn (S16);
11 00 the eal work of the wuustewctor, Lat wlthoue all che
V/ class reference overhead. Delphi does not really call the
17 constructor recursively.
SelE.Create (Falco);
Self Retertonaemict ion:
except
11 16 any exception scours, Delph culuiaticaliy cats che
1/ object's destructor.
F_ Chapier 2 The Delph Ohyect ModelFxample 2-8: The life Cycle of an Object cominued)
self. Desteoy;
«a
Self.Cronte Pale) ;
ena:
// Tho biden code in the deetructor locks something 1ike this:
‘procedure TSonething.Destroy Deallocate: Boolean)
tain
EE Deallocate then
17 Delphi dosen't really esl) the destructor recursively, but
1/ this is whore the destrictor's real work takes place
Self. Destroy False);
4 Deallocate then
begin
71 Delph doesn’t really call Cleampinstance, Instasd, the
71 Breetnstance method dees the cleansp. Tf you override
/| Weeetnstance and do not call the inherited Freelnstance,
1/ you most call Cleampinstance to clean up strings,
11 Ayramia arraye, ard Varant-type fel
Self. Cleanpinstance;
(Cali Prestnstance bo fee the Uujavt's wauey.
Self Freetnstance
Access Levels
Like C4 and Java, Delph as difcrcat access levels that determine which vbjeus
can access the fields, methods, and properties of another object, The access levels
ae as follows:
provate
Declarations that are declared prwvate can be accessed only by the class's own
‘methods or by any method, procedure, or function defined sn the same units
implementation section. Delphi dues ut have Cr+-siye fiend declarations of
Java-style package level access. The equivalent in Delphi 1s to declare
package or friend classes in the same unit, which gwes them access to the
pprvate and protected parts of every clase defined in the came unit.
protected
A protected declaration can be accessed from any method of the class or its
descendants. The descendent classes cant resile mi dilferet uns.
public
Public methods have unrestncted access. Any method, function, oF procedure
‘can access a public declaration, Unless you use the M+ compiler directive
Gee Chapter 8, Compiler Directives for details), the default access level
public
Classes and Ohjoote 75published
Published declarations are similar to public declarations, except that Delphi
‘sores runtime type information for published declarations. Some declarations
cannot be published: see Chapter 3 for details. Ifa class or a hase class uses
the $0 directive, the default access level is published.
Delphi's IDE declares fields and methods in the intial unnamed sex
tion of a form declaration, Because Porm inherits from
‘Tersistent, which uses the $M+ directive, the initial section is
Published. In other Words, the LUE declares its tields and methods as
published. When Delphi loads a form description (dfn file, it relies
‘on the published information to build the form object. The IDE relies
fon the initial, unnamed section of the foun cass. I you modify that
section, you run the nsk of disabling the IDE's form editor.
automated
‘Automated declutatious ure sunalar w public declarations, except that Delphi
Stores adaltional runtime type information to support OLE automation servers.
‘Automated declarations are obsolete; you should use Delphi's type library
‘editor instead, but for now, they remain a part of the language for backwaid
compatibility A future release of Delphi might eliminate them entirely.
(Chapter 5 describes automated declarations in more depth,
‘A derived class can increase the access level of a propery by redectaring the prop-
ety under the new access level (e.g. change protected to public). You cannne
decrease a property's access level, and you cannot change the visibility of afield
for method. You can override a viral method and declare he uvenudden method
a the same or higher access level, but you cannot decrease the access level,
Properties
‘A propery looks like a field but can act like a method. Properties take the place uf
accessor and mutator methods (sometimes called getters and setters), but have
‘much more flexibility and power. Properties are vita to Delphi's IDE, and you can
also use properties in many other sinations
A property has a reader and writer to get and set the property’ value. The rearer
can be the name of a field, a selector for an aggregate field, or a method that
‘etums the property value. The writer can be a fick! naue, a selewior for an aggre-
gate field, or a method that sets the property value, You can omit the writer to
‘make a read-only property You can also omit the reader to create a wrte-only
Dpropenty, hut the nses for such a beast are limited. Omitting both the reader and
the writer is pointless, so Delphi does not let you do so,
‘Most readers and writers are field names or method names, but you can also refer
to part of an aggregate fill Geuurd or array). If a reader or writer refers to an.
array element, the array index must be 4 constant, and the field's type cannot be a
dynamic array Records and arrays can be nested, and you can even use variant
G6 Chapter 7~ The Delphi Object ModalHiding a Constructor
Sometimes, a class is not for public use, but is a helper class whose use 1s
entirely subservient to another class. In that case, you probably want to
make the constructors for the helper class pavate or protected, but ths is
tricky TObject declares a public constructor: Create. Even though the
helper class's constructors are private or protected, you can call the public
(Create constructor inherited from TObject.
Although you cannot change the access level of the inherited Create
constructor, you can hide it with another public constructor. Because the |
denved constructor should not be called, it can raise an exception. For
example:
bine
‘wrivateielper = class
11 Teublic 1s the only class allowed to
11 call the real constroctor: }
constructor Create(Oxner TPublie); |
‘vecload
public
1/ Bide Thject.Create, in ease someone
1/ Terivateielper instance.
reintroduce; overload,
end;
‘Teublic = clas |
palic
Gestiuctor Destroy;
cna;
constructor "Privateelper Crea
begin
aise Bxcepticn. Create Programing error’)
constructor TPublic.creste;
becin
1/ This Se the only place where
11 Trivatellelper is created,
{lelper := TerivateHelper.Create(Self) ;
Glasses and Objects 47records. Example 29 shows an extended rectangle type, similar to the Windows
‘TRect type, but because its a clas, it has properties and methods.
Example 2-9: Properties Readers and Writers
‘ects = class IPersistent)
Re TRect;
function vecweiont: integer;
function Getiidth: Integer;
Procedure SetHeight const Value: Integer);
Drocedure Setitideh (const. Valves Toragsr):
public
‘constructor Cresto(conat Ri; ect), overtoats
constructor Create(Left, Top, Right, Botan: Integer); overload
constructor create(censt Topiett, BottonRight: Tpoint]; overload;
procedure Assign(Scurce: TPersiatent); overrige;
procedure Inflate(X, ¥: Integer)
Drocodura Ineoroect (const Ry ects
function tstipty: Boolean;
funccion isuqual (const R: TRectR) Boolean,
procedure Offset (x, ¥i Integer);
Procedure Union(const R: TRECtE) +
Property Topleft: TPoint read R.topLett write R.tepLeft;
Property Bottonfight: Teint read R.DuttiaRiyhl, write R-BOCtemKghc:
property Rect: TRect read R write Ry
[property eight: integer read Getieight write Setieight;
roperty Width: Integer read Getiideh weite Setiicth;
published
Droperty Loft: Incager vend R.taft write R.taft defauite 0p
property Right: Integer read R.Right write R.Right default 0;
[reperty Tops Intoger vend Mofop waite Ritoy default 0;
[property Botton: Integer read R.Bottoa write R.Bottom detault 0;
Array properties
Properties come in scalar and array flavors. An array property cannot be
Published, but they have many oiler wes. The array index can be any type, and
you can have multidimensional arrays, too. For array-type properties. you must
Use read and write methods—you cannot map an aray-type propery directly to
an aeray-type field
‘You can designate one array property 2s the default property. You can refer to the
default property by using an object reference and an array subscript without
mentioning dhe property name, as shown in Example 2-10.
Example 2-10- Using a Default Array Property
te
‘TBample = clase
‘GB Chapter 2— The Delphi Object ModelExample 2-10- Using a Default Array Property (continued)
beoperty Teeus(1: Integer] mnceger resa vetiten write Setitem;
Droperty Chars[C: Char): Coar read Gotchar write SetChsr; defal®;
end
ample: Temple,
cr chars
desl
Brample := Tample.create;
Y= Brample.ttors(d}; // must vention property nane explicitly
t= Bamplel'#°1; 1) aeeay property is default
© i= Bemple.chars["K']; // Same a8 previous line
Indexed properties
You can map many properties to a single read or waite method by specifying an
dex umber tor each property. The index value 1s passed to the read and write
methods to differentiate one property from another.
‘You can even mix array indices and an index specifier. The reader and writer
‘methods take the array indices as the first arguments, followed by the index
copecifier.
Default values
A property can also have stored and default directives. This information has no
semantic meaning to the Delphi Paseal language, but Delphi's IDE uses this infor
‘mation when storing form descriptions. The value for the stored directive is a
Boolean constant, a field of Boolean type, of a method that takes no arguments
and returns a Boolean result. The value for the default directive is a constant
value of the same type as the property. Only enumerated, integer, and set-type
properties can have a default value. The stored and default dircttives lave
meaning only for published properties
To distinguish a default array from 2 default value, the default array directive
‘comes after the semicolon that ends the property declaration. The default value
directive appears as part of the property declaration. See the default directive ip
Chapter 5 for details.
Using properties
A common approach to writing Delphi classes 1s to make all fields private, and
declare public properties to access the fields. Delphi imposes no performance
penalty for propertios that access fields direcly. By using properties you get the
added benefit of being able to change the implementation at a future date, say t0
‘add validation when a field's value changes. You can also use propenies 10
enforce restricted access, sich ag sising a readonly property to access 4 field
whose value should not be changed, Example 2-11 shows some of the different
‘ways to dectare and use properties
‘Classes and Objects 49‘Example 2-11. Declaring and Using Properties
pe
ane: string;
TextiNmber: string(9];
ena:
‘Tecomt = class
foustener: Tousteners
halance: currency:
Suwnber: Cardinal,
‘procedure SetBalance(Newalance: Currency)
vublished
‘property Balance: Currency read flalance write SetBalance;
property Manber. Cardinal Feod Ohaver; /7 Cal caange account #
property Custilane: string read {Custoner. Rane
TSavingsAcceunt = clase(TRocaunt)
private
‘EInterestRate: Totegers
published
property Intereathate, Integer Koad Cuvcresthate
write flnteresthate default Detaultinterestiate;
TLinkedtecout = clase(TObject)
Accounts: array[0..1) of there:
Function Getkcooint (Index: Integer); TAcoout;
public
11 Two ways for properties to access an arzay: using an index
11 of reterring to an array elenent.
reparty checking: TAcount index 0 read GetAccotnt
Property Savings: TRocount read flccount(2};
ea
‘TocountList = clase
‘fist: Mist:
function GetAccoune index: Integer}: TAecount;
[Procedure SetAccount (Index: Integer Account: TMocount)
function Getcont: Integer;
protected
property List
public
property Comt: Integer read GetCount
Droperty Acoounts|undex: integer]: TAccount read GetRecount
write SetAccount; default;
‘List read fhist;
ond;
procedure TAccount.SetBalance (NewBalance: Currency!
Begin
{LE MewBalance < 0 then
raise Riveraramncepeion,veates
‘palence := Nevialance;
sapter 2~ The Dolpa Objeci Model‘Example 2-11, Declaring and Using Properties (continued)
ena)
function TLinkéAccount.GetAocount Index: Inteser); Thocount:
begin:
ena;
Arcomte( see)
‘fnetion Taccountbist atcount: Integer;
begin
Result. := List.Count
end
function TecountList.Getaccount Index: Integer): Thacount;
begin
Result := List Index)
‘rocedute TAccountList.SetAccount (Index: Integer Account: TAccount)
begin
fist [Index] := Account
Glasstype properties
Properties of class type need a litle extra attention. The best way to work with
classtype properties is to make sure the owner abject manages the property
object. In other words, don't save a reference to other objects, but keep a private
copy of the property object. Use a write method fo store an abject By copying i.
Delphi's IDE requires this behavior of published properties, and it makes sense for
‘unpublished properties, too.
‘The only exception to the rule for classtype properties is when a
property stores a reference to a component on a form In that e252,
the property must store an object reference and not a copy of the
component
elphi'e INF stores camponent references in 2 .dfn file by storing
only the component name. When the .dfm is loaded, Delph looks
Lup the component name fo restore the object relerence. If You must
store an entire component within another component, yous must cel.
cegate all properties ofthe inner component.
Make sure the property's class inherits from TPereistent and that the class over-
rides the Assign method. Implement your property’ write method to call Assign.
(tersistent—in the Classes unit—is not required, but its the easiest way to
‘copy an object. Otherwise, you need to duplicate the Assign method in what.
ever class you use.) The read method can provide diet access tothe field If the
property object has an OnChange event, you might need to set that so your object
Glasses and Objects 314s notified of any changes. Example 2-12 shows a typical pater for using a class
type property The example defines a graphical control that repeatedly displays 4
bitmap throughout its extent, ling the bitmap as necessay The BiLuay prupenty
sores a TBitnap object.
‘Example 2-12: Declannng and lang a Classe Property
vit the;
ncartace
vweco yaUtile, Classes, Controls, Oxaphics,
vine
7) tile a bitmap
‘Tile = clasa(TGzaphictontzol)
‘iteep: Teitmap:
procedure cetDitmap Qiewitmap. ZDitnap)
procedure Bitnapchanged|(Sender: TUbject)
protected
prooadure Faint; override;
public
constrictor Creste(Ouner: Trmpenent): evarriAe
Gestructor Destroy; override,
ubliohea
property Align;
Droperty sitnap: Tustmap resa taitnap write SetAtmap;
property OnClick;
‘Property OncbiClick;
1 Many other properties are useful. tot wera emitted te ane spare,
11 Soe Neontrol for a full List.
smptenencacion
(mue)
1/ Create the bitmap when creating the control
‘construoter Mile.Croate(Owmers Tsepenent)
begin
Iohericea:
Bitmap := Teitnap. Create
‘Bitmap crchange i= Bitmapchanged;
ea;
1) Pree the bitmap when dcotzoying the control.
Sestructor Mile. Destroy;
egin
Freche (£510)
inherited
end:
1/ nan the hemp changeo, redraw the conteel
52 Chapter 2 The Delpbi Object Model‘Example 2-12: Declaring and Using a Class-type Property (continued)
procedure TPile.BitmapChanged( Sender: TObject)
Degin
Tnvalidate;
ea;
1) Paint the control by tiling the bitmap. I€ there 12 no
17 wienep, don't paint anything.
procedure Trile.Paint;
X, Ys Integer:
begin
Af (Bitmap.mideh = 0) or (Remap. Height = 0) Phen
Y= 0;
while ¥ < clientheigne 30
begin
Xi 0;
while X < clenentath do
begin
Camrao. Dea, > Bitmap
Inet, Bitmap. Width) 7
Ino(Y, Bitmap.teigh
ena,
ena
11 Sot a noe biteap by copying the TBitmap cbject.
procedure Mile, SetBiteap NewBitmap: TBitmep);
Degin
‘Bitmap. Assign (ewBitxap) +
a;
ea.
Interfaces
‘An interface defines a type that comprises abstract vitual methods. Although a
lass inherits from a single base class, it can implement any number of interfaces
‘An iateeface is similar to an abstract class (hat i, a class that has nv fields and all,
‘of whose methods are abstrac), but Delphi has extra magic to help you work with
interfaces. Delphi's interfaces sometimes look like COM (Component Object
Model interfaces, hut you don't need to know COM to use Delphi interfaces, and
you can use interfaces for many other purposes.
‘You can declare a new interface by inheriting from an existing interface. An inter-
face declaration Coutaing metludl and property declarations, but no elds. Just as
all classes inherit from TObject, all interfaces inherit from Unknown The
Tonknown interface declares thee methods: _AddRef, Release, and
ueryinterface, If you are familiar with COM, you will recognize these
methods. The frst two methods manage reference counting for the lifetime of the
Tnuerfaces 53‘object that implements the interface. The third method accesses other interfaces an
‘object might implement.
‘When you declare a class that implements one or more interfaces, you must
provide an implementation of all the methods declared in all the interfaces. The
‘lass can implement an interface’s methods, or it can delegate the implementation
to a property, whoce value i an interface. The simplest way tw implement the
~AddRef, Release, and Queryinterface methods is to inherit them from
‘Tinterfacedobject or one of its derived classes, but you are free to inherit from
any other class if you wish to refine the methods yoursal
‘A cass implements each of an interface's methods by declaring a method with the
same name, arguments, and calling convention. Delphi automatically matches the
class's methods with the interface’s methous. If you wnt to use « different method
ame, you can redirect an interface method to a method with a different name.
‘The redirected method must have the same arguments and calling convention as.
the interface method. Thi feature ic ecpecally important when @ class imple
‘ments multiple interfaces with identical method names. See the class keyword in
{Chapter 5 for more information about redirecting methods
A class cant delegate the mmplementation of an interfice 10 a property that uses the
implements directive. The property's value must be the interface that the class
\ants to amplement. When the object cast to that interface type, Delph: automat-
ically fetches the property’s valuc and returns that interface, See dhe Lxylemenits
directive in Chapter 5 for details.
For each non-delegated snterface, the compiler creates a hidden field to store
pointer (0 the interfice's VM. the interface field or tields follow immediately after
the object's hidden VMT field. Tut as an object reference ss really a pointer to the:
cbject’s hidden VMT field, an interface reference is a pointer to the interface's
hidden VMT ficld. Delphi automatically snitalizes dhe laden felds when the
‘object 1s constructed. See Chapter 3 to learn how the compiler uses RTTI to keep.
track of the VMT and the hidden field.
Reference counting
‘The compiler generates calls to _AdARef and _Release to manage the lifetime of
interfaced objects. To use Delphi's automatic relerence counting, declare a vari-
able with an interface type. When you assign an interface reference to an mterface
vanable, Delphi automatically calls _AddRe#. When the variable goes out of scope,
Delphi automatically calls Relea.
‘The behavior of _AAARof and Release is entirely up to you. If you mile from
‘inter faceddbject, these methods implement reference counting, The _AddRef
‘method increments the reference count, and _Release decrements it, When the
reference count goes to 7er0, Release frees the object. If you inherit from a
different class, you can define these methods to do anything you want. You
sliould implemen queryinterface correcily, though, because Delphi relies on it
{0 implement the as operator.ypecasting
Delphi calls Quexyinterface as part ofits umplementation of the a operator for
interfaces, You can use the as operator to cast an anterface to any other interface
type. Delphi calls Queryinterface to obtain the sew huterface reference, If
Queryinterface retums an error, the as operator raises a runtime error. (The
‘SysUtils unit maps the runtime error to an EInt £CastBrrox exception.)
‘You can implement Queryinterface any way you want, but you probably want
to use the same approach taken by TInterfacadbjact. Fxample 2-13 shows a
class that implements QueryInterface normally, but uses stubs for _AddRef and
Release. Late ut this secon, you'll see how useful this class can be
Example 2-13: Interface Base Class Without Reference Counting
function mbRefCout.queryInverface const IID:TGI2D; out Obi): Hesult
begin
Af Getintertace(TID. Obs) then
Result
oles
Result :+ Windows. Nointerface;
function awokes oune. telesst
begin
Repult
cea:
integers
Interfaces and object-oriented programming
‘The most important use of interfaces 1s to separate type inheritance from class
‘mheritance. Class mheriance is an effective tool for code reuse A derived class
casily inherits the fields, methods, and properties of a base class, and thereby
avoids rcimplementing common meilwals, In a strongly typed language, such as
Delphn, the compifer treats @ class as a type, and therefore class inheritance
‘becomes synonymous with type inheritance. In the best of all possible worlds,
thonigh, types and classes are entirely separate.
Textbooks on objectonented programming ofien desde an inheritance relation.
ship as an “isa” relationship, for example, a TSavingsAccount “is” TAccount.
Tnierfaces 33You can see the same idea in Delphi's is operator, where you test whether an
Account vanable is TSavingsAccount.
‘Uutside of textbook examples, though, simple 1s relationships break down. A
square is a rectangle, but that doesn't mean you want 10 derive TSquaze from
‘Rectangle. A rectangle 1s a polygon, but you probably don’t want to denve
‘TRectanyle fiom TRolygon. Class inheritance forces a derived class to store all
the fields that are declared an the base class, but in this case, the dened class
doesn't need that information. A ‘Square object can get away with storing a
single length for all of ite cides. A Tectangle object, however, unust siore {WO
lengths. A TPolygon obyect needs to store many sides and vertices.
‘The solution 1s to separate the type inheritance (a square 1s a rectangle 1s a
polygon) from class nhentance (class C anhents the fields and methods of class B,
Which inherits the fields and methods of class A). Use interfaces for type inherit
lance, so you can leave class inheritance to do what it does best inheriting fields
and methods,
In other words, ISquare inherity from Rectangle, which wihevils fuss
Polygon. The interfaces follow the “isa” relationship. Entirely separate from the
Interfaces, the class TSquare implements TSquare, TRectangle, and TPolygon.
‘Rectangle implements IRectangla and TPalygon.
inital 1. Delphi follows this convention for all interfaces. Note that it
‘The convention in COM programming 1s to name anterfaces with an
|s a useful convention, but not a language requirement.
(On the implementation side, you can declare additonal classes to implement code
‘euse. For example, Teaseshape implements the common methods and flds for
all shapes. TRuctangle mers from iBaseshape and implements the methods in
8 way that make sense for rectangles. "Polygon also mherits from TBaseShape
and implements the methods in a way that make sense for other kinds of
polygons
A drawing program can vse the shapes by manipulating TPetygon interfaces,
Example 2-14 shows simplified classes and interfaces for this scheme. Notice how
ach interface has a GUID (Globally Unique Identitier) in its declaration. The
GUID is necessary for using Queryinterface. If you need the GIT af an inter.
face (in an explicit call to Queryinterface, for example), you can use the
interface name. Delphi automatically conven ut haterfuce ame 10 fs GUID.
‘example 2-14: Separating Type and Class Hierarchies
‘shape = interface
1 (Sor6De51-P4ES-1102-s8nc-o0104BcRCH4D) *)
‘procedure Dra (Canvas: Tanvaa):
function Getroaition: Point;
proseaire SetDooktion|Valust ‘Peint)s
Droperty Position: TPoint zead GetPosition write setPosition:
56 Chapter 2— The Delphi Object ModelExample 2-14: Separating Type and Class Hierarchies continued)
Polygon = interface (1shape)
((S0reD852-Pamp-11n2~88A0-0010¢eCAckAB}')
turetion Maertices: Integer
function thmcidees Integers
function Sidetength Index: Integer) : Integer;
function vertex(index: integer): 1HOint;
ea:
Rectangle = interface(IPelygan)
((S0R6D853-P4RB-1102-8830-001068CRC48R) *)
eed
Togisce ~ intertaco(tRectangle)
( (SoreD9s4-r4ae-1102-28nc-0010¢Bcace4B)"}
‘unecion sider unteger;
end:
‘aseshave = class(MoRefCount. TShape)
private
ePoeitions Teint,
function GetPosition: TPoints
public
constructor Create; virtual;
‘procedure Draw (Canvas: TCanvas): virtual: abstract:
Droperty Fosition: TPoint read fFositicn write SetPesition;
swoint)
‘olygon = class(TBaseshape, Polygon)
‘vertices: array of "Point;
mblic
procedure Draw (Canvas: Tamas): override:
function Nawercices: Integer?
Emotion tmbidees Integers
function SideLength Index: Integer): Integer;
fwcclon Vervex(Intex: anceger): 1e04nC?
end
‘TRactangle = clase(Taseshape, TFelygen, IRactangle)
private
‘Rect TRect;
panic
procedure Draw (Canvas: TCanvas); override:
function tmvertices: anceger;
fmction Mansides: Integer;
function sidetength(Index: Integer) : Integer;
function Vertex(Index: Integer): TRoint:
end:
‘Tequare = slase(sBacoshape, TPolygen, TRectangle, Tequare)
public
‘Procedure Draw Canvas: Tamas]; override
function Side: Integer:
interfaces 57Bxample 2-14: Separating Type and Class Hierarchies (continued)
function mawvertices: Integer;
function Masides: Integer
function SideLength(Index: Intever) : Inteoer
function Vertex (Index: Integer): TPoint,
A deswed clase inherits the interfaces implemented by the ancestuns’ disses. Thus,
‘Rectangle inherits ftom TBaseshape, and THaseShape implements TShape so
‘TRectangle implements IShape. Inheritance of interfaces works a litle differ-
cently Interface inheritance is merely a typing convenience, co you don't have 10
retype a lot of method declarations. When a class implements an interface, that
does wot autuuatially mean the class implements the ancestor interfaces. A class
implements only those interfaces that are listed in its class declaration (andl vn the
declaration for ancestor classes). Thus, even though TRectangle inherits from
Polygon, the Mectangle class must list TRecuaiisLe und TPOLygon explicit
To implement a type hierarchy, you might not want to use reference counting.
Instead, you will rely on explicit memory management, the way you do for normal
Delphi objects. in this case, i's est to implement the _AddRef and _Release
‘methods as stubs, such as those in the MoRafCoant class in Example 2-13, just
be careful not to have any variables that hold stale references. A variable that
refers to an object that las been freed can cause problems if you use the vanable.
‘An interface variable that refers to an object that has been freed will certaunly
cause problems, because Delph will automatically call ss Release method. In
‘other words, you never want to have vanables that contain invalid pointe, td
‘working with interfaces that do not use reference counting forces you to behave.
COM and Corba
Delphi mnterfaces are also useful for implementing and using COM and Corba
objects. You can define 2 COM server that implements many interfaces, and
Delphi automatically manages the COM aggregauon for you. the runtime library
contains many classes that make it easier to define COM servers, class factones,
and so on. Because these classes are not part of the Delphi Pascal language, they
are not covered in thie book. Consult the product documentation learn mvre,
Reference Counting
‘The previous section discusses how Delphi uses reference counting to manage the
lifeime of interfaces. Stunys aud dyuatmc arrays also use reference counting to
‘manage their lifetimes. The compiler generates appropriate code to keep track of
‘when interface references, stamgs, and dynamic arrays are created and when the
variables go out of scope and the objects, strings, and arrays must be desuuyed
Usually, the compiler can handle the reference counting automatically, and every:
thing works the way the you expect it to, Sometimes, though, you need to give a
hint w the compiler. For example, sf you declare a record that contains a refer.
ence counted field, and you use GetMem to allocate a new instance of the record,
you must call In{tialize, passing the record as an argument. Before calling
Froatfem you muct call Finalize.
58 Chapter 2— The Delph Object ModelSometimes, you want to keep reference to 9 string of interface after the variable
{goes out of scope, that is, at the end of the block where the variable declared.
For example, maybe you want £0 associate an interface with each item in 2
TMListview. You can do this by explicitly managing the reference count. When
stonng the interface, be sure to cast it to TUnknown, call AddRef, and cast the
Unknown reference to & raw pointer, When extracting the data, type cast the
pointer o TUnknown. You can then use the as operator to cast the interface to any
desired type, or just let Delphi release the interface. For convenience, declare a
‘couple of subroutines to do the diny work for you, and you can reuse these
subroutines any time you need to retain an interface reference. Example 215,
shows an example of how you can store an interface reference as the data associ
ated with a list view item,
Example 2 15: Storing Interfaces in a List View
1/ count 4 incremented and the interface will not be freed
function RefTlkinown const Intf: Tnknown): Folnter
penn
att. _pdaret 11 Tecrement the reference cout.
Result := Pointer(Intt); // Save the interface pointer.
ea:
11 Release the interface whose valve is
Drocedure ReleaseTUnkom(P: Pointer):
nels Timon;
begin
Pointertanté) i= P:
1/ Delph releases the interface when Intf goes cut of scope.
stored in the pointer Pr
17 yen the user clicks tne button, ade an interrace to tne Last.
procedure TFornl..uttonlClick(Sender: TWbject):
begin
Feen.caytion := "Stuff;
een. vate := Ketauninown(GetIntE as JUneNown) :
ena
17 then the List view is destroyed or the List {ten {a destroyed
1) fer any other reason, release the interface, too.
procedure MPorad.LiotvicwDeletion|Gender: TObject; Tem: TListrtem ;
begin
ona
11 then the user selects the List view item, do somthing with the
11 sesociated interface,
provedise TPomd.LictVioviGlick(Gsndces Tobject);
Rijoronce Crating 50Example 2-15: Storing Interfaces in a List View (combined)
= TUnknown(ListView! selected.Oata) az yinterface;
Ent Dosonathinglooful;
ena
You can also store stnngs as data. Instead of using _A@ARef, cast the string to 2
Pointer to store the reference to the string, then force the vanable to forget about
the sinng. When the variahle goes aur of scape, Delphi will nat free the string,
because the variable has forgotten all about it, After retneving the pointer, assign it
tw a suing vatrable that f$ Gist 10 4 pointer. When the subroutine returns, Delphi
automatically frees the string’s memory Be sure your program does not retain any
pointers to memory that 18 about to be freed. Again, convenience subroutines
simplify the tack, Example 2-16 shows one way to stove sigs
‘Example 2-16: Storing Sirings in a Lst View
17 save a sefecewe Co a string and recum a raw pointer
U1 to the string.
function Refstring const §: string) Pointer;
ocal: string:
bain
Toca t= $3 // Tncrenent the reference cmt.
Resull c+ Pointer (tocal); // Save che scring pointer.
Pointer (tocal) i= nil; // Prevent decresent ing the ref cout
ena)
1/ Release a string that wis referenced with RefSteing,
rovedce Releasastring 2, olnter);
begin
Poamter tocal)
11 Delphi frees the string when Local qoes out of scape.
end
1/ Ween the user clicks the button, add an item to the list view
17 apd save ah ellie, Mice Lela,
procedure TFornl ButtoniClick (sender: Tbyect);
Teens TListTtem;
begin
Tram = Tatiewt Troms. A84:
Tten.Caption := Balti Text;
eam Data = RefStalin (BUL2. Text);
ea:
// Release the string when the List view item is desteoved
11 for aay ren800,
Drocedtire Tena! [4crWeviteletinn(Sendar: object) Team TLictTtan) +
60 Chapter 2 The Delph: Obyect ModelExample 2-16: Storing Stings in a List View (continued)
begin
Releasestring(Tten.Datal;
end:
1) Retxiorg the string wen the user elects the Hist view Sten
procedure Trorml ,LetVieviClick(Sender: Tbject)
ser: string:
begin
Sf ListView Selected © nil then
begin
Ber «~ steing(Listvieut colacted tata);
Stonttessage (St)
Messages
‘Yow should be familiar with Windows messages: user interactions and other events
generate messages, which Windows sends 10 an application. An application
processes messages one at a time to respond to the user and other events. Each
Jnd of message has a unique number and rwo integer parameters. Sometimes 2
parameter is actualy a pointer to a string or structure that contains more complex.
Information. Messages form the heart of Windows evenclnven architecure, and
Delpht has a unique way of supporting Windows messages.
In Delphi, every object—not only window controls—can respond (0 messages. A
‘message has an unteger identifier and can contain any amount of additional infor-
‘mation. In the VCL, the Application object receives Windows messages and
‘maps them to equwalent Delphi messages. In other words, Windows messages are
8 special case of more general Delp messages.
‘A Delphi message Is 4 record where the fist avo bytes contain an integer mesage
‘identifier, and the remainder of the record is programmer-defined. Delphi's
message dispatcher never refers 10 any part of the message record past the
micssage aumber, 30 you are free to store any amount or kind of information in a
message record. By convention, the VCL always uses Windows-siyle message
records (Message), but if you find other uses for Delphi messages, you don't
need to feel sa ennstrained
To send a message to an object, fill in the message identifier and the rest of the
message record and call the objects Dispatch method, Delphi looks up the
imessage number an the object's message table. The message table contains
pointers (0 all the message handlers that the class defines. If the class does not
define a message handler for the message number, Delphi searches the parent
clases message table. The search continues until Delphi finds a message handler
or it reaches the TObject class. If the class and its ancestor lasses do not define a
message handler for the message number, Delphi calls the objects
DefaultHandler method, Window controls in the VCL override Default
Handler (0 pass the message co the window procedure, other classes usually
“Messages 61ignore unknown messages. You can overtide Defaultiandler to do anything
you want, perhaps raise an exception,
Use the message directive to declare a message handler for any message. See
Chapter 5 for details about the message directive
Message handlers use the same message table and dispatcher as dynanuc methods
Each method that you declare with the dynamic directive 1s assigned a 16-bit
negative number, whuch io really a message number. A call ty « dynamic method
uses the same dispatch code 10 look up the dynane method, but ifthe method is
‘not found, that means the dynamic method is abstact, so Delphi calls
AbstractErrorProc to repowt call an abstract method,
Because dynamic methods use negative numbers, you cannot write 4 message
handler for negative message numbers, that ss, message numbers with the most-
‘ignficant bit set to one, This limitation should st cause any problems for normal
applications. If you need to define custom messages, you have the entire space
above WHLUSER ($000) available, up to $7FFF Delphi looks up dynamic methods
and messages in the same table using a linear vearch, 20 with large message
tables, your application will waste time performing method lookups,
Delphi's message system 1s entirely general purpose, so you might find a creative
use for it. Usually, uiterfuces provide the same capabilty, but with beter perfor.
‘mance and increased type-safery
Memory Management
Delphi manages the memory and litetime of strings, Variants, dynamic arrays,
and interfaces automatically For all other dynamically allocated memary, you—the
programmer—are in charge. Its easy to be confused because it seems as though.
Delphi automatically manages the meinory of components, too, but thats just a
tack of the VEL.
‘Memory management 1s thread-safe, provided you use Delphi's classes or func-
lions t0 create the threads. If you go straight to the Windows API and the
CreateTfread function, you must set the Tawyltithresd variable to True, For
‘more information, see Chapter 4, Concurrent Programming,
Ordinarily, when you construct an object, Delphi calls Newinstance to allocate
and initialize the object. You ean overnde Newiustauve Ww cliange dhe way Delphi
allocates memory for the object. For example, suppose you have an application
‘hat frequently uses doubly linked lists. Instead of using the general-purpose
‘memory allocator fr every node, its much faster to keep a chain of available
‘nodes for reuse. Use Delphi's memory manager only when the node lis is empty. IF
your application frequently allocates and trees nodes, this special-purpose allocator
‘ean be faster than the general-purpose allocator. Example 2-17 shows simple
amplementation of this scheme, (See Chapter 4 for a thread-safe version of this
lace)
D Chap 2~ Te Dap Objet ModelComponents Versus Objects
The VCU's Component class has two fancy mechanisms for managing object
lMfeumes, and they offen confuse new Delphi programmers, incking them
toro thinking that Delph always manages object lifetimes. 1's umporant that
you understand exactly how components work, so you won't be fooled.
Every component has an owner. When the owner is freed, it automatically
frees the components that it owns. A form owns the components you drop
‘on it, so when the form is freed, it automatically frees all the components on.
| the form. Thus, you dont usually need to be concerned with managing the
} lifetime of forms and components.
When a form or component fies a component st owns, the owner also |
checks whether has @ published fie of the same name as the compo-
heat I 20, the owner acts that ld to mid. Thuo,f your form dynamealy
fads or removes components, the forms fields always contain valid objet
references of are nil. Don’t be fooled into thinkang that Delphi does ths for
any ather el or obyert referee tre anes nly republished Beli
(such 38 those astomatilly created when you drop a component on a form |
sn the IDES form edo), and only when the field name matches the compo |
Bxample 2 17; Custom Memory Managomont for Linked lasts
"Diode
‘Miext, fPrevious: Mode;
// Vodsa are under control of Thinkedist.
procedure Relinkitetiext, NewPzevious: Tiode):
constrictor Creste/Ment: ‘Thoda = nil: Previcus: Mode = nit);
procedure RealPree;
ass
public
(estrector Destroy; override:
clase function Newinstance: Tobject; override;
procedure Freelnstace; override
Peonerty Next: Mle read Mt
property Previous: Mode read fPrevious;
17 singly Janked Last of nodes that are tree tor reuse.
1/ caly the Next fields are used to maintain this List
11 pitocate @ nov node by getting the head of the Nodebist.
11 Reomsber to call InitInstance to initialize the node that was
“Memory Management 3‘Bxample 2-17: Custom Memory Management for inked Lists (continued)
11 Tf the NodeList is expty, allocate 2 node normally.
clase function Teade.ewtnstance: subjects
begin
IE MoceLsst = nil then
Result := inherited Nowinetance
else
begin
Result := NodeList;
Initinstance(Resule) ;
11 ects the Nndetist wees only the Mont field, set the Previuus
1/ Held to a special value. If a program erroneously zefers to the
1/ Teevicus field uf a Exee node, you can see the special valve
11 aod fnew the cause of the error.
BadPointerValueToPlagerrors » Pointer (SPOEENRAD)
11 Bree a node by aAAing £© to the head of the Medel
11 faster than using the general-purpose meviory manager
12 26 you want to clean yp Une List propery when the application
1/ Hinishes, call RealPree for each node in the List. the inherited
11 wreeinstance method frees and cleana up the node for real.
procedure Mode. Real Pree;
begin
Inherited Preetnetanes,
nds
You can also replace the entire memory management system that Delphi uses.
Install a new memory manager by calling SetMenoryManager. For example, you
_mught want to replace Delphi's suballocator with an allocatnr that performs addi-
tional error checking, Example 2-18 shows a custom memory manager that keeps
a list of pointers the program has allocated a explicly checks each attempt to
free a pointer against the lis. Any attempt to free an invalid pointer is refused, and
Delphi will repor a runtime error (which Systtiis changes to an exception). As
a bomus, the memory manager check that the list i» empty when the application
‘ends. If the list is not empty, you have a memory leak.
Example 2-18: Installing a Custom Memory Manager
unit checiaeaige;
interface
The Delps Object ModelFxample 2-18: Installing a Custom Memory Manager (continued)
uses Windows
function Chacktat (ize: Totegar): Pointers
function Check#ree(Men: Pointer): Integer:
function checkRealloc (ten. Pointer; Size, tnteges) Pointer,
espFlags: Diord; // Tn a aingle-threaded application, you might
1/ went to set this vo Heap No Seriatize,
Ixplenmntatton
MaxSize = Maxnt av 4;
te
‘TPointerarcay = arvay[1..MaxSize] of Pointers
PPointerkeray = “TPoineerkrray:
Heap: Mande; 1 Windows heap for the pointer List
List. Ffoineeracray; —_// Let of allocated pointers
Listsize: Integer; 11 Mhaber of pointers in the List
astaliect ancegers 417 capacaty of ene poanter List
11 XE the List of allocated pointers is not empty when the program
1) Cimishes. that means you have a menory leak. Handling the memory
1) leak ip left a2 an exerciae for the reader
procedure Henorvtesky
11 Meport the leak ¢0 the user, but remember that tne program is
1/ sting down, so you should probably stick to the Windows APE
1/ and not use the VoL.
ena
1/348 a pointer t2 the Let.
procedure Aédten(Nen: Pointer)
begin
if List © nil then
begin
71 New List of pointers.
Listaloe *= 8)
[Mot += Hesphiloe(tenp, HospFlage, LigtAlice * eiacoE(tointce))y
ea
else Af Listsize >= Liscalloe enen
begin
1/ Make the List bigger. Try to do it sommhat intelligentiy.
SE ListAlloo < 256 then
Listalloe := Listhlloc * 2
LatAllec = Letallec + 256;
isc = Heapkeatioc (weap, Neapriags, List,
Listalloe * Sizeot(Pointer));
ea)
11 2d a pointer to the List.
“Memory Management 6%Example 2-18: Installing a Custom Memory Manager (continued)
Ino(Lletsize)
Est (uistsize) := mm;
end;
1 took for a pointer in the List, and renwe it. Ratum true for
// success, and False if the pointer is not in the list.
function Rémrweten(Man: Pointer): Boolean,
begin
tor i= 1 to Listsise do
4if List (1) © Mon then
agin
Merwniimory(@List (51, OLee(r+1], (bisteie-2) * sizeot(roincer) 7
Dec(Listsiza);
Reoule = Tou
Baty
11 Replacenmnt wanzy sllocacor.
function Checktet (size: Integer]: Pointers
begin
Result = Syacetsen(Size)
deen (Result);
ea
U/ X€ the pointer isw't tn che 14t, don't cal the real
1/ Free functicn. Return 0 for success, and non-zero for an @rEOr
1/ Remove the old pointer and add the new one. which might he the
1 ane as the old one, or it might be different. Return nil for
U1 an error, and Peiphi will xaiee an ewception.
function CheckRealloc (en: Pointer; Size; Integer): Pointers
bogin
4 not Renovelisn (em) Chen
else
begin
Ragult :afyahan!Ieem(en, 320) 1
aan (Ret);
ena
66 Chapter 2= The Delphi Object ModelFeample 2-18: Installing a Custom Memory Manager continued)
procedure SetNetanager
gre Mennrysanagaes
begin
Yar etien = checket,
‘ae. Fresiem = CheckFree;
ge_RealLocien += CneceReal Lon
SetMenorymanager gt)
ene;
esp = HeapCceate|0, teapPags, 0);
Sebiieianager
if Listsize © 0 then
MenoryLeak;
earner ey a +
ea
If you define @ custom memory manager, you must ensure that your memory
‘manager Is used forall memory allocation. The easiest way (0 do Us ist set te
memory manager 0 2 units snitilization section, as shown in Example 2-18. The
memory management unit must be the first unit listed in the project's uses
declaration,
Ordinarily, fa unit makes global changes sn its snitalization section, it should
clean up those changes in its finalization section. A unit in a package might be
loaded and unloaded many umes sn a single application, so cleaning up 1s impor
tant. A memory manager is different, though. Memory allocated by one manager
cannot be freed by another manager, so you must ensure that only one manager is
‘active un an application, and that the manager 1 active for the ensie duration of
the application, This means you must not put your memory manager in a package,
although you can use @ DLL, as explained in the next section
Memory and DLLs
If you use DiLs and ty to pass objects between DLLs or between the application
and a DLL, you fun into a number of problems. Hirst of al, each DLL and EXE
keeps its own copy of its class tables. The is and as operators do not work
correctly for objects passed berween DLLs and EXEs. Use packages (described in
‘Chapter 1) to solve dhis problet, another pruble i that anny memory allocated ia
DIL 1s owned by that DLL. When Windows unloads the DLL, all memory allo-
‘cated by the DLL is freed, even if the EXE or another DLL holds a pointer to that
‘memory This can be a major problem when using strings, dynamic arrays, and
variants because you never know when Delphi will allocate memory
automatically
‘he solution 1s to use the sharetiem unit as the first uni of your project and every
DLL. The ShareMem unit insalls a custom memory manager that redirects all
memory allocation requests t0 4 special DLL, Borlnd\Mdll The application
docen't unload DortidMM until the applicstion exits. The DLL magic takee place
‘Memory Management 67transparently, so you don't need to worry about the details. Just make suce you
use the ShareMen unit, and make sure itis the first unit used by your program
and libraries. When you release your application to your clients or Customers, You
will need to unclude BorlndMM al
If you define your own memory manager, and you need to use DLLs, you must
duplicate the magic performed by the Sharetiem unit. You can replace ShareMem
‘with your own unit that forwards memory requests to your DIE, which uses your
custom memory manager. Example 2-19 shows one way to define your own
replacement for the Ghasetfem wait.
‘txample 2-19: Defining a Shared Memory Manager
1/ Use this nit first so all memory allocations use the shared
11 memory maager. The exclication ant nt Mite must use thie untt.
1/ You canmot use packages because those DLLe use the default Borland
1) onared mano manager
ater cave
fumetion Checktet (Size: Integer): Pointer
function CheckFree (Men: Pointer): Thtege
fmetion ChectRealloc Wen: Pointer; Size: Integer); Pointer;
{nplenentation
BLL = "Chechaat.alD'
fmction Checkiet (Size: Integer): Pointers external EL;
fncticn Cheskrree ems Pointer): Integer, external DLL;
fumetion CheckRealloc (Man: Pointer; Size: Integer): Pointer
Procedure Sethewanager;
Mgr: Dlenorymanager
begin
Mgr. Gaten + Checkcet;
Myc. Peeetew i= ClsckF ree;
Mgr Reallocten := Checkealloe;
Settanoranager (ge);
ea)
inirtarizatson
Setilanager;
‘he Check¥@4 DLL uses your custom memory manager and exports is functions so
they can be used hy the Checkshareden unit, Example 220 chows the source
code for the Chacka library.
(68 Chapler 2= Toe Delphi Object Modelxample 2-20: Defining the Shared Memory Manager DLL
brary checlan;
11 Replacement, for BoxIndMi.dl1 to use a custom nenory manager.
Chechen:
egorts
Checkset, CheckFree, Checkealloc:
besin
‘Your program and library projects use the CheckShareMem unit first, and all
memory requests go 10 CheckMAL dll, which uses the errorchecking memory
‘manager. You don't often need to replace Delphi's memory manager, But as you
can see, it sn difficult to de
The memory manager that comes with Delphi works well for most
applications, but it does noc perform well in some cases. The aver-
‘age application allocates and freee memory in chunks of varying
sizes. If your application is different and allocates memory in ever-
sncreasing sizes (say, because you have a dynamic array that grows
fn small steps t0 a very large size), performance will sufer. Delphi's
‘memory manager will allocate more memory than your application
needs, One solution 1s 10 redesign your program so it uses memory
1m a different pattern (say, by preallocating a large dynamic array).
‘Another solution is to weite a memory manager that better meets the
‘apceniized aceds of your application. For example, the new mem
‘ory manager might use the Windows API (GleapAlLocate, etc).
Old-Style Object Types
1m adiion w css ypes, Delp supports an obsolete eype that uss the object
keyword. Old-atyle objects exist for backward compatibility with Turbo Pascal, but
they might be dropped entirely from future versions of Delph.
Old-style obiect types are more like records than new-style objects. Fields in an
fleLayle object are laid nur in the same manner as in records. If the object type
does not have any virtual methods, there 1s no hudden field for the VMT pointer,
for example. Unlike records, object types can use mhertance. Derived fields
appear after inherited fields. Ifa class declares a vitwal method, is first field is the
YMT pointer, which appears after all the anherited fields. (Unlike a new-style
object, where the VMT pointer is always firt because Tobject declares virtual
methods)
Old-Shle Object Types 65An oldstvle object ype can have private, protected. and public sections, but aot
published or automated Sections, Because it cannot have @ published section, un
old abject type cannot hive sy runtime re Information An old ebsect type
cannot implement interfaces.
Constructors and destuctors work diferenty in old-style object types than in
ew-syie clase types. To create an instance of an old Object ype ell the New
procedure. The newly allocated obyet is intaized to all sero. I you declan»
constructor, you can calli 28 part of the eal to Rew, Pass the constructor name
sd arguments wo ie secon argument o New Simualy you cas call a destuctor
‘when you cll Dispose to fee the object instance. The destctor pamela
‘ments are the second argument to Diepose
You don' have to alloite an old-syle abject instance dynamically You can
treat the object type ae record type and declare object ype vanables ss vale
level or local variables. Delphi automaticaly intalzes sting, dynamic ary, and
Vara flelds, but doesnot maize oer tes inthe obec instance
Unlike newsiyle class types, exceptions in old-style constructors do not automat-
‘cally cause Delphi to free a dynamically created object or call the destructor
‘70 Chapter 2— The Delphi Object ModelCHAPTER 3
Runtime Type Information
Delphi's Integrated Development Environment (IDE) depends on snformation
provded by the woupiler. This formation, called Runtime Type information
(RTTD, describes some aspects of classes and other types. It's nota full reflection
system such as you find in Java, but i's more complete than type identifiers sn
Ces For ordinary, everyday use of Delphi, you can ignore the detaile of RTTI and
just lec Delpiu do its thing. Sometimes, though, you need to look under the hood
and understand exactly how RTTI works,
‘The only difference between a published declaration and a public declaration is
RITI. Delphi stores RTTI for published fields, methods, and properties, but not for
Public, protected, or private declarations. Although the primary purpose of RTTL 1s
to publish declarations for the IDE and for saving and loading df files, the [UTI
tables include other kinds of information, For example, virual and dynamic
methods, interfaces, and automated declarations are part of a class's RTTI, Most
1 called type myformation This chapter explains all the details
types also have R
of RTTL
Virtual Method Table
‘The Virtual Method Table (YM) ctores pointer to all the virtual methods declared
for a class and its base classes. The layout of the VMTT 1s the same as in most C++
‘implementations (inchiding Borland C++ and C++ Builder) and is the same format
equived for COM, namely 2 list of posters tr methods Rach veal method of a
class or its ancestor classes has an entry in the VMT
Fach class has a unique VMT. Even if a class does not define any of its own viral
‘methods, but only inherits methods from its base class, it has its own VMT that
lists all the virual methods it inherits, Because each VMT lists every virtual
‘method, Delphi can compile calls to virtual methods as quick lookups in the VMT.
Because each class has is own VMT, Delphi uses the VM to identify 4 cass. In
nfact, a class reference 1s really a pointer to a class's VMT, and the Classmype:
‘method retuins a pointer to the VMT.
In addition to a table of vutual methods, the VMT includes other information
bout a lass, such as the class name, a pointer to the VMT for the base class, ral
pointers to many other RTTT tables. The other RTTT pointers appear before the fist
Viral method in the VMI. Example 3-1 shows a record layout that 1s equivalent
to the VMT. The actual list of virual methods hegins after the end of the ‘TUme
record, In other words, you can convert a Telase class reference t0 a pointer to a
"Wine record by subtracting the size uf te record, as shown in Example 31
‘Example 3-1, Structure of a VMT
Pim: = “cv
‘Tint = record
SelfPtr: aelane: U1 Boinee fanaa to the start
11 of the vie
1/ he following pointers point to otlet RITE tales. Tf a class
1/ oes not have a table, the pointer is nil. this, moet classes
71 pave a nit intrvapie and AuteTable, for example,
Intetable: PinterfaceTable; // Tnterface table
antoTable Pautotable 11 Mutewation table
Initrable: Pilea 1) Welds needing finalisation
‘ypemnto: Prypetnfo; _J/ Proparties & other info
Bielaebles reieiafeble, —// Ptinte Letts
PlethoaTable; —// Published methods
woymetnodtable; // List of éymanic methods
Pshortstring; // Points to the class nane
Instancesize: Lengine:——// Stxa of each abject, im bytes
Classtarent: “qlass; ——// immediate base class
1/ The following fielés point to speclal virtual methods that
U7 ave hwerived fxem iubject.
Safecallniception: Pointer;
Afterconstruction: Pointer
Beforedestruction: Pointer:
Dispatch: Pointers
Yewinatance: Pointer;
Destroy:
1/ Mere begin the virtual meted pointers
11 Bach virtual method is stored as a code pointer, 0.9.,
1) Visessidathoarabler array[L..Count] of Pointer,
11 Bit the compiler does not stare the cout of the mnber of
77 smite pointers in the table,
end;
ees Pint
begin
11% get a Wine pointer from a clase reference, cast Ue Lia
D Ghapir J Ranta Hip toon —Example 3-1. Structure of a VMT (continued)
1/ reference to the Punt type and subtract the size of the TMM
17 record, This 18 easily done with the Dec procedure:
Vat = Pint (SoneObject.Clasetype}
Dec it
As you can see, the VMT includes pointers to many other tables. The following
‘sections deseribe these tables in more detail,
Published Declarations
‘The only difference between a published declaration and a public one 1s that a
published decluation tells die Compiler w store information in de VMT. Only,
certain kinds of information can be stored, so published declarations face a
number of restrictions:
+ In order to declare any published fields, methods, or properties, a class must
have RTTT enabled hy using the $e directive or by inheriting from a clase
that has RTL. (See Chapter 8, Compiler Directives, for details.)
+ Fields must be of class type (no other types are allowed). The class type must
have RTT enabled.
+ Array properties cannot be published. The type af a published propery cane
‘not be a pointer, record, or array If it isa set type, it must be small enough t0
bbe stored an aur uneyer. In de current release of Delphi, that means the set
can have no more than 32 members.
‘+ The published section cannot contain more than one overloaded method with
each name. You can overioad methods, but only one of the overloaded meth-
cds can be published.
‘The Classes unit declares TPersistent with the $+ directive, TPexsistent is
usually used as a base class for all Delphi classes that need published declara
tione, Note that "Component inherits from Persistent.
Published Methods
Delphi stores the names and addresses of published methods in a class's RTL
‘The IDE uces thio information to ctore the valuee of event properties in a dfn fie
In the IDE, each event property is either nil or contains a method reference. The
‘method relerence includes a pointer to the method's entry point. (At design time,
the IDE has no tre entry point, so it makes ane up. Ar mintime, your application
uses the method's real entry point.) To store the value of an event propery,
Delphi tooks up the mediod address in the class's RTTL, finds the corresponding
‘method name, and stores the name in the .dfm file. To load a .dfm file, Delobi
reads the method name and looks up the corresponding method address from the
lace RFT,
A class's RTT stares only the published methods for that class, and not for any
ancestor classes. Thus, to look up a method name or address, the lookup might
fail for a denved class, in which case, the lookup continues wrth the base class
‘The MethodName and Methodaddress methods of TObiect do the work of
Published Declarations 73searching a class's RTTI, then searching the hase clases RTTT, and so 09, up the
mhertance chain, (See the TObject type in Chapter 5, Language Reference, for
details about these methods.) The published! method table contains only’ the
‘method name and address.
You can declare any method in the published section of a class dectaration.
‘Usually, though, Delphi's IDE creates the methods for you, When you double-click
fan event property. for example, the IDF creates a method in the inital, unnamed
section of the form class. Because a form class has RTTI enabled, the inital,
lunnamed section is published. (Form classes have RIM because TPersistent is
din ancestor class)
‘The method table starts with a 2-byte count of the number of published methods,
followed by a record for each method. Each method record starts with a 2-byte
size of the method coco, followed by the method address (4 bytes), and then,
followed by the method name as a shor stung, that is, as a L-byte stnng length
followed by the text of the sting.
More Method RITI
‘The record size for a method is usually 2 + 4 + 1 + Length(Name), but some
method records luave audlidonal information, The additional information is
‘not part of the official RTT for the class, Future versions of the compiler
‘might not generate this information, so you should not write any code that
relies on it. The information ie anteresting, though, so take « luk at what
Delphi hides in its method records.
Delphi stores additional information for methods that use the stdcall.
calling convertion and that have parameter and return types for which
Delphi ordinarily stores tyne mformation, The extra information 1 stored
afer the method name and mcludes the names and types of the parameters.
‘You caa tell thi extra information 1s preseut whit ie size of the method
record is larger than it needs to be to store the record size, method address,
‘and method name. If the size 1s 4 bytes larger than it needs to be, that
‘means the methor! takes nn parameters. The extra 4 bytes are always eer.
If the size is more than 6 bytes lager than it neers tae, the anformation for
the method's parameters 1 stored following the sixth byte, (The extra 2
bytea do not scem to serve ay useful purpose, but they are not always
zero.) Each parameter has a pointer to a TIypelngo record for the parant-
eters type, followed by the parameter name (as a short string), followed by
a teiling #0 byte. (See "The Typlafo Unit later in this chaptes, wy Tea
about the Typetngo record.)
Example 32 depicts the logical structure of the method table, Note that Deipl
‘cannot use these declarations verbatim because the record size varies t fit the size
of the stangs.
74 Chapier 3 Runtime Type Information ~Example 3-2: The Layout of the Published Method Table
ype
"MiethodFaran = packed record
‘aypeinfo: PPIypeTnto;
Tape: shortstring;
11 ha nana 18 foltoumd ty a Praiting #0 Hye
ena:
Size: Word: 1/ S20 of the TneNethod record,
‘scrass: Poanter: 11 Pointer to the nethed entry point
Nane: packed shorestring; // Nane of the published method,
1/ Seve methods have sn edditional 4 zero bytes, which means the
1/ Some methods have an adaitional 6 bytes, followed by a series of
1/ Te seems that enly steal) methods have this extra information,
17 You can identity the extza into by the "Method. Size value being
1/ too big for just the Size, Addzess, and Name nenbere. The only
11 way to know how many paraneters are stored here is to check
Bxtrastuft: array(l. FourOrsix] of Byte;
Rarone: array(i.-Darantount] of TathodParcs,
end
(Crupiished method tabte 7
‘TiathodTable = packed record
count: word,
Methods: array! 1. camt) of Method
ea,
Published Fields and Field Types
Each published field has a name, a type, and an offset. The type is a class refer-
‘ence for the field's type. (Published fields must be of class type.) The offset is an
offset (in bytes) nto the object's storage, where the field 1s stored
‘The published field table stars with a 2hyte count of the number of fields,
followed by a d-byte pointer to a class table, followed by the field definitions
Each field Uefaidon tsa record contuming a +-byte offer, and a 2-byte index into
the class table, followed by the field name as a shor sting.
‘The class table list all the classes used by the published fields. Each field contains
an index into this table. The class table starts with a 2-byte count, followed by a
lic of class references where each class reference is 4 bytes. A class reference is a
pointer to the clases VMT.
Example 3-3 shows the logical layout of the field table. Because the records are
Variable length, you cauiol use lisse declarations ai « Delp prograt,
Example 3-3: Layout of the Published Field Table
( Piet class table }
Prielaciasefuble = “[Picléclassrable
‘eielaclaseTable = packed record
Published Declarations 73Example 3-3: Layout ofthe Published Field Table (continued)
‘count: Word
Classes: packed arcay{1..count] of “Ilass;
ed;
( Published fed xeooed )
‘Wield « packed recor’
OEfset: tocamord: 11 yee offset of taeld sn the object. )
Classindex: Word; // Index in the Fialé-lasotable of the
1 elas type.
Name; packed Shortstrina: // Nae of the gibtiehad flat. 5
ex
( pantished Heid table }
‘Tieumreble = packed record
‘count: Wore;
MieldclascTable: PPieléClaseTable;
Fields: packed array [1,.Count] of TField:
eo
Published Properties
Published properties have lots of information stored about them: name, ‘ype,
reader, writer, default value, index, and stored flag. The type is a pointer to a
‘Tiypatnfo ervord (discussed sn the next section). The reader and writer can be
fields, methods, or nothing. The default value 1s an ofdinal value, non-ordinal
properties don’ have default values the stored tlag can be a constant, «field ora
method reference. The Object Inspector in Delphi’ IDE elles cm published peop.
ferties, and Delphi uses the default and stored information wen saving and
loading fn feo
‘The reader, writer, and stored fel cin be pointes to static methods, byte oles
of vitual methods, or byt offsets of fields. Dynamic methods are not allowed, and
‘sav of vital methods must use the register calling convention (which 1 the
default). Additionally. the stored value can he a constant True or False. Delphi
stores these diferent kinds of values as follows
+ A constant True or False is stored a8 a literal zero or 1. Only the stored
directive can have a constant Teve of Fase. Ir a veader ot wilet 2240, at
‘means the property does not have that particular directive, that 1s, the prop
eny 18 write only or read-only, respectively
+ A fickd uffet stored with SRF in the most signicant byte, For example, a
field stored at offset 42 ($2A) would have the value $FFOQOO2A. Note that
published fields are rarely used to store property values, so its unlikely that
you could look up the name of the field in the published fet table
+ A virtual method is stored with $FF 1m the most significant byte and the byte
offset of the method 25 a Snallint in the low order 2 bytes. For example,
the dnd vutual method is sored a8 SFEOOUDNE, (ihe frst virtual method has
offset 0.)
76 Chapter 3 Runtime Type InformationPublished Fields and Components
‘When you drop a component on a form in Delphi's IDE, the IDE creates a
published field declaration for that component. Delphi takes advantage of
published fies sn the form class when saving and loading .dfm files, but
the mechanisms Delphi uses are common to any component because they
are implemented as methods of the Component. class, these tncks are not
part ofthe Delphi language, but they affect most Delph programs.
When a component (call it Owner) becomes the owner of another compo-
nent (call it Chi), the child looks up its name (that is, the value of the
Nave propery) in the owner. If Oumex has a published field with the came |
‘name as Child, the owner sets the value of its field to be a reference to the |
child object. When Child is destroyed, Owner checks again for a published
field of the same name, and if it finds a match, it sets the eld to ni
Usually, the only time a Delphi programmer encounters this behavior 1s for
the published fields of a form, When Delphi loads a fm, it creates the child
components, The form class (which 10 the owner) notices that» compor
nnent’s name matches that of a published field and automatically sets the
Field to refer to the newly created component,
Im other words, the Delph language does not treat components or forms
specially Instead, the Tomponent class knows about published fields and
‘ses that information to manage the components it owns. Don't be fooled
nto thinking that Delphs automatically manages the lifetime of all compo-
nents just because it manages some components, |
‘The published field table is especially umpostant when loading a dim. When
Delphi loads a component Irom a fm, it reads the name of the compo:
nnent’s type as a string. Delphi needs a class reference for the class name,
which it can look up an the field class table. If you write a component that
stores a subcompuneut wind daa subcomponent is nue declared an a
Published field, you will need to register its class explicitly by calling
RegisterClass or RegisterClasses (both in the Classes uni). Regis:
tering classes ie not a feature ofthe Delphi language.
[A static method ss stored as an address, ¢ ., SOOI01BA2, The memory architec-
ture of Windows prevents any method fiom having an address with $FP or $FE
1m the most significant byte, so there is no danger of conflicts or ambiguities.
The default value can be stored only for integer, character, enumeration, or see
types. If the programmer declares a property with the nodefault directive (which
Js the same as omitting the default directive), Delphi stores the most negative
integer ($80000000 or ~2,147,483,648) as the default value, In other words, you
cannot have an mteger property whose default value i ~7,147 483,648 because
Delphi would interpret that as being the same as nodefaullt.
Published Declarations 77String, floating-pomt, Int64, Variant, and class-type properties
cannot have default ‘values, and the nodefault directive has no
effect. (Delphi always uses an empty string, 2210, Unassigned, o1
‘nil as the default value for these types when reading and writing
fm files.) lt you want the effect of defining a default value for
these kinds of properties, you can play a tack in the clas’s construc
tor: set the property's value when the user drops the component on
2 form, and not wliew Delpln loads the component from the .djm
file. What makes this tricky 1s that the Competent State propery is
not set until after the constructor returns. (Read about
Componentstate in Delphi's help files.) Thus, you need to test the
‘owner's Conponentstate, as follows
constructor TStringbefaul Create (owner:
bbogin
Af (omer = ntl) or
(((esReading, esdesigning] *
Ormer.ConbcnentState) = {cadestgning])
then
SteingDroperty + ‘Dofsult value!
ends
‘This trick does not save any space mn the cfm fle, but it achieves
the goal of setting a default value for a property that does not ordi
sarily take a default value.
The primary purpose of a default value 4s to save space in a dfn file. If & prop:
leny’s valuc is the same as the default value, Delp doesn’ store that value in the
df. Ifa propery does not have a default value, Delphi always stores the value sn
the dfn. Note that inherited forms get their default values from the ancestor form,
which gets it frnm the dofaule directive. It ithe programmer's responsibilty t
initialize a propery to its default value sn the class's construetor—Delphi doesn't
do that automatically (See Hxample 35, later in this chapter, for help setting
default property values.)
‘The index directive stores the index value for an indexed property If the prop=
ceny 18 not indexed, Delphi stores the most negative integer asthe index value
‘The published property information also stores the name index, thats, the ordinal
position of the propeny sn the clase declaration. The Object Inspector can zort
properties into alphabetical order, which scrambles the declared order of a class's
properties. The name index value gives you the orignal order,
Delphi makes it casy to access « Uass's published propery information using the
‘Typingo unit, which isthe subject of the next section
FR Chapter 3— Runtime Type InformationThe TypInfo Unit
‘The Typinfo unit declares several types and functions that give you easy ate
to the published properties of an object and other snformation. The Object
Inspector relies on ths information to perform its mage. You can obtain a lit of
the published properties of 4 cass and get the name and type for each propery
Given an object relerence, you can get of set the value of any published property
“The Typetngo function retumns a pointer to a type information record, but if you
don't use the Typingo uni, you cant aces anil in dit record and must
instead weat the result as an untyped Pointer. The Typtnfo unit defines the real
type, which ss F1ypetnfo, that i, a power to a TIypetnto record. The type
information record contains type kind and the name of the type, The spe kd
ssan enumerated value that tells you what kind of type it: integer, floating pon,
string, ete
Type Data
‘Some types have additional type data, as retumed by the GetTypebata function,
‘which rerums a vlypebata pointer. You can use the type data to get the names
of an enumerated literal, the limits of an ordinal subrange, and more. Table 4-1
describes the data for each type kind,
Table 3-1. Type Kinds and Thewr Data
TihpeKind Literal | Assoctated Data
Garay No associated data,
ehchar Lionts of character subvange
tkclass Class reference, parent clas, unit where class 1s declaced,
and published properties.
Uoyunccay [No assoctated dara for dynamic arrays
tkBauneration | Ifthe rype isa subeinge of another type, the data includes
a pointer to the base type and the limits of the subrange,
otherwise, the data includes the limits of the subrange aid
2 packed list of counted strings for the names of the
feruimerated literals,
tkeloat, Floaring-pownr type: eumrency, comp, single, double, or
extended (but not Real 48),
eines Limits of integer subrange.
eemnteger Limits of integer eubsange.
tkInterface | Base interface, unit where the interface 1s declared, and the
GUD.
wasteing No ascoaated data for a long stang (Ansists:t)
tidtethod Return type, land of method, and parameter names and.
type names:
eenecord [No associated data
ekset Pointer to the enumerated type of the set elements.
tkstring Maximum length of a shor stang,
The Typinjo Tt 79
FpTable 3-1. Tybe Kinds and Their Data (continued)
Thpekind Literal | Associated Data
Exunnown No assocated data ~
tevariant No associated data,
esichar Limite of wade character subrange.
kustring [No associated data for a WideString.
[Note that the prmary purpose of type information iy support Delphi's IDE and.
for reading and writing dfn files. A secondary purpose is for initialization and
finalization of managed types. It is not a general-purpose reflection system, as you
find in Java, so information about records and arays, for example, i limita.
Published Properties
Many of the functions in the Typinfo unit make it easy for you to access the
Published propenies of an object Instead of accessing the type information
directly, you can call some functions to get or set a property value, determine
whether the property should be stored, get the property type and name, and so on.
‘To get or seta property value, you need tw know what kind of propery type you
are dealing with: ordinal, floating point, string, Variant, method, or Int64. Each
kind of type has a pair of subroutines to get and set a propery value. If the prop-
femy has methods for the reader or writer, the Typingo routines call those methenl,
just as though you were getting or setting the propeny in the usual manner.
Integer., character”, enumeration, set-, and classtype properties are ordinal. They
sire their propery values in an integer, so You must use an integer to get OF set
the propery value. When you get the propery value, cast it ro the desired type.
To set the property value, cast the value 10 an integer. Example 34 shows a
procedure that takes any ‘component as an acguineim inl teats whether tat
component publishes a propery called Font whose type is a class type. If so, the
procedure sets the component's font to Aral, 10 pt
Example 3-4: Setting an Ordinal Property
Procedure SetFontTotrial1Opt (Component: Teempanent) ;
Font: Font;
eopinfo, Phroptafo,
besin
U1 Parse fund out Sf the component has a Pont property.
Propinfo := GetPropinfo(Ccmpenent, Font);
4f Propinfo = nil then
ait:
11 Wert sve if the property bas class type.
AE PeepInts.FroptypeRlinl ~ UAelane Un
Bat)
vont. 1 a¥ont.create;
ty
Pont.tlane i= "Arial;
Font. Size := 10;
80 Chapter 3 Runtime Tipe Information‘Example 3-4: Setting an Ordinal Property (continued)
1/ Move sot che component's Font property.
Securaprep(Ccmpanent, Proplnto, Integer (Pont);
11 Set0rdProp is just 1ike Conponent..Pont. := Font except that
11 the compiler doesn't need to know about. the Font property.
11 The component's writer nopies the Tent ckject, 22 thie
1 procedure must free ite Font to avoid a menory leak
sinally.
Pont ree;
end
You can get a list of PProptngo pointers if you need to Jeam aheut all of an
‘object's properties, or you can call GetPropIngo to learn about a single property
The GetPropList function gets only piupeitis whse «ype kine! matches a set of
type kinds that you specify You can use this to leagn about events (tktfethod)
sirng-valued properties only (tkString, tkistring, tkwString), and so on
Feample 3.5 shows a procedure that takes an object ae an argument and looks up
all the ordinal-type properties, then gets the default values of those properties and
Sets the property values to the defaults. You can call this function from a
constructor 10 guarantee that the properties are prnperly initialized, thereby
avoiding 4 possible error where the propery declaration has one default value,
but the constructor has a differeut une
Example 3-5: Setting Default Property Values
// Sec the cetauit vaive tor alt publishes properties,
‘Procedure SetDofaultValues (Obj: TObject);
Fekenuneration, tintager, Herman, eset, eHachae
Prophist: PrropList
ccoune, 4: anvegers
Value! Integers
begin
1/ Count the muber of ordinal properties rhat aan have
1) degaute values
Comme 1= CotPropList (Obj, thOrdinad, afl),
// allocate mewory to store the prop info & get the zeal prop List.
\cecmen(vropuist, Count * S:260t PPropTnto));
oy
GetzropList (Obj, thordinal, Proplist);
11 Loco throu all the ordinal prepertiog
for Y += 0 to Count-i do
J) XE Who propesty has a default value, est the yaupeaty value
71 to that default,
Af vropuise(1) vetault © sopetault ten
SetordProplObj, Propulet (1), PropList(1].Default)
Sinaliy
Freeion(Proatiat) +
end;
The Tiplnfo Unt Bi‘The routines in the ypInfo unit, while not documented, are straightforward and
easy f0 use. The following list describes all the subroutines an the Typtnfo unit,
for your convenience, Conaule the Typlnfoprar source file for further detils
(provided you have at least the Professional edition of Delphi or C+ Builder)
[Note that these lunctions perform little of no error checking, It is your respons-
bility to ensure that you are calling the correct function foe the property and ite
type. Changing the value of a read-only property or getting the value of a write-
uly property, for example, results in an access violation,
GotkinumNiame function
‘function Getiamttane Typetnto: PIypelnt
Returns the name of an enumerated literal or an empty string if Value is out
of range
GotksnumProp function
function GetBrunPrep (instance: TObject; Propinfo: PPrepInto)
ering; overload:
function GettumProp (Instance: TObject; const Propane: string):
string; overload
Retuins the nae of die enumerated Iteral that isthe propemy’s v
GetBnunVale furetion
function GetSrunifalve(Typelnfo: PIypetnto; const Name: string):Integer:
Retums the ordinal value of an enumerated literal or =1 if the type has no
eral with the given name,
GetPloatProp function
function GetFioatPrep (Instance: TObject; Propingo: PPrepingo)
[Betended, overiced,
‘function GetFloatProp (Instance: Tbject; const Propane: stxing):
ibxtenaec: overload:
Gets the value of a propery with a floating-point ype,
Gettnt6-4Prop function
function Getint6¢Prop (Instance: Object) Prepinfo: PProplnto) :
Intél, overloads,
fimction GettntsdProp instance: TWbject; const Propane: string):
sntss; overioaa;
Gets the value of a property of type Tnté4 or any subrange that requires
‘more than 32 bits to represent.
GetstethodProp function
function OettetedrseyCustance: TOblect; Fropinto: ¥eropinto) :
‘Mletbod: overload:
function GetethodProp(Instance: TObject; const Prepllane: string)
‘Method; overload;
Gets the value of an event property
GeatObjectProp function
‘function GetObjectPsup(ustance: TobJecti Eropinto: veropinto;
Minclass: Telass = nil): TObject; overload;
function GetobjectProp(Instance: TUbject; const Propltne: string:
Minclass: ‘lass = nil): "Object; overload:
falue: Integer): string:
82 Chapter 3— Runtime Type InformationGets the value of a class-ype property MinClass 1s the base class that you
require for the propeny value; if the result is not of type MinClass or a
descendant, Gat Object Prop reniene nf The default isto allow an object of
any clas,
GetObjectPropCtass function
‘function GetdbjectPropClace(Inotance: Nebject; Teoplnfo: Pxoptno}
‘Clase; overlosd;
Fuictlus Getobjectrropclass instance: 1Ubject; const Propitane: string) :
‘elass; overioad:
Gets the class type from the property's type data. Instance is used only to
look up the propery information, so the fist version of this function (which
already has the propery information in the PropIn€o parameter) does not
refer to Instance,
Get rdProp junction
function GetOrdrep(instance: TObject Prapinta: #erepinfo)
‘vmgint; overload;
function GetordPrep (Instance: Thject const Propllane, =telin)
engint; overload;
Gets the value of any ordinal type propery, or any property whose value fits
mma 32 bit integer, eg, object, set, character, enumerated, oF iieger
subrange,
GetPropinfo function
furctia Qetreuytute(Rypetite: PrypeIngO; Conse ropeame: string)
PPrepinfo; overload;
function GetPrepinfo Typetnto: PIypetnfo; const Proptime: string:
‘AEinds: TypeKinds): PEropInfo; overload;
function Geteroptnto (Instance: TObject; const Propane: string;
Axindss ‘typekinds = (])+ Bprepingoy overload
fiction GetPrepInfo(aClass: Clase, const Propane: string;
‘Aeindss ‘Types ~ (1): FRewpinto; overload,
Renims the PerapTnta pointer for a published propery or nit if the clacs
does not have any such published property or ifthe named property does not
have the correct type. The frst argument can be an object reterence, a class
reference, or a class's type anformation (from the Typelngo function or
Classinfo method).
GetPropinjos procedure
Drocedure GatPrapinfos(TyreTnfa Pypetnfa: PrapList: PPeopLtse):
Gets a list ofall the PProptnfo pointers for an objec, in declaration order,
Use the class’s type data to learn how many published properties the class
has, s0 you can allocate the Propliot array
GetProplist function
function GetPropList (Nypetnto: Ptyperné
Peoplices PErcpLict) + Integer;
Gets an alphabetized list of FPropInfo pointers for the matching properties
ff an object and returns a count of the number of properties stored in
Proplist, Pass nil for the Peoplist paraucier wW get a count of the
‘number of matching properties.
‘ypeKinds: Typetinde;
Tee Tipp OaGetPropValue function
function GetPrepalue (Instance: Object; const Proptlane: string;
PreferStrinas: Boolean = True): Variant:
Gets the value of a published propery as a Variant. GetPropValue incurs
more overhead than the other Get... functions, but is easier to use. If
Proferstrings 1 True, GetPropValue will etore the property value ao a
string, if this is possible
GetsetProp function
funation Cetesttrep (inetance: TWbject; Propiafo. PRLUpEACys
Brackets: Boolean = False]: string; overloads
‘mation vecsetvrop(instance: Tonject; const Propane: string
‘Brackets: Boolean = Fale): string; overloads
Gets the value of a settype propery and returns the value as a string, The
format of the string 1 a ist of enumerated inerals, separated by commas and
spaces, You can optionally include square brackets. The format is the same as
that used in the Object Inspector.
GetStrProp function
function Gatstrbrrn(tnstanre: Thyject; Propings: PBropinéo) + stringy
‘overload;
faction detotsPrep Instance TObject; Cush Raflane: veeleg) string:
overload;
Gets the value of a stnng-type property The propeny type can be tkstring,
tkagtring, of EkHSteLng. In all cases, die prupenty value 1 automatically
converted to string,
GetTypeData function
Tunccion veciypeustaiypeinto: Plypetnto): PIypeData;
Returns 4 pointer to a type'e MypeData record, given its PIypetnge pointer.
GetVariantPrep fuunction
function GetVariantProp(Instance: Tobject; Proplnfo: PPrapinfo)
function GetVariantProp (Instance: Object; const Proptiane: string)
‘Varlant; overioaas
Gots the value of a Variant-type property
|bPublishedProp function
function TsPublishedPrep Instance: TOject; const Pregame: string):
Booleans vselosdy
fmction TsPublishedPrep|AClass: Tlase; const Proptiame: string)
Rens True ifthe class has a published property of the given name,
{sStoredProp function
function IsStoredProp (Instance: TObject; Propinfo: FPropinfo) :
Becleany overloads
function TsStoredProp (Instance: TUbject; const Propltane: string)
Dove; overioea:
Renvens the value of the stored directive, If the etored directive 19 a
method, IsStoredProp calls the method; if itis a field, the field's value is
retumed,
84 Chapter 3~ Runtime Type InformationProplsType function
function Propistype(Instance: TObject; const Propane: string;
‘rypekinds TypeFind) + Boolean) overload)
function Proptstype(AClass: Class; const Progtane: sting:
‘Typetnl: FIypening) + Boolean; overload;
Returns True if the named property exste and has the given type,
PropType function
function Propiype (Instance: TObject const Propane: string):
‘ypetind: overload;
function Propiype(aClass: TClass; const Propane: string)
Tryoenien; overiowds
enim the type kind of 2 published propery or raises an RPxopertyExror
exception ifthe class does not have a property with the given name.
SetEnumProp procedure
rocsire SctimuntroplInstance: Tobject) Propinfo: Feu Buty;
const Value: string) overload;
proceaure setinumrop{instance: Tobyect; const. Propilime: string
‘const Value: string}: overload:
Sets the value of an enumerated-type propery, gven the name of an enumer-
ated leral. If the value is not the name of an enumerated literal,
‘SetnumProp raises the EPropertyConvertError exception. If you have the
ordinal value instead ofthe literal name, call SetOx€Prop.
SetRoaiProp procedure
procedure SetFloatProp (Instance: TWhJents Praptnfa: pPrepnt:
Value: Extended) ; overload
procedure CctFlostivep( Instance: Whject, const Fropitane, sti Lins
Value: Extended); overload;
Sets the value of a property with a floating-point type.
Setint64Prop procedure
procedure SetInt64Prop(instance: Tobjact PrepIntas PPrapinfo;
‘const Value: Int6l); overload
rovodure SctInt64Mrop (Inotance: TObject; const Propiaae. string;
const Value: Intl); overload;
Sets the value of a property whose type is Int64 or a subrange that is larger
than 32 bits
SetMethodProp procedure
procedure SetMethodPrep|Instance: TWbject; Propinfo: PRropingo;
const Value. ‘Tietlnd) ; ures vad
procedure Setéethodrep instance: TObject; const Propane: string:
‘const Value: TMethod) ; overloads
Sete the valus of an event property.
SetObjectProp procedure
rocedure SetdbjectProp(Instance: TObject; Propin
Value: Object), overland
procedure SetobjectProp (Instance: TUbject; const Pregtane: string:
‘value: Tosject); overlosd;,
Prrepintor
The Typinfo Unit 83Sets the value of a classtype property, If Value is not of the correct type for
the propery, SetobjectProp silently ignores the attempt to set the property
‘SetOrdProp procedure
Drocedure SetOrdProp(tnstance: object; Prepinfo: PPropingo;
Value: Lengint); owoelosds
procedure SetordProp(Instance: Object; const Progilae: string;
Valuer tongint)? overionds
Sers the value of any oedinaltype property, including eete, obyectz, characters,
and enumerated or integer properties.
SetProp Value procedure
procodurs SctPrepValue (Instance: TObject, cust Pauyttum: vteing;
‘conet Value: Variant)
Sets the value of a propeny from a Variant. SetPropValue must be able to
convert the Variant value to the appropriate ype fur die propeny, oF else it
raises an EPropertyConvert Error exception.
SetSetProp procedure
[procedure sersecvroptunstance: IWoyact; Preprnto; PPropino;
const Value: string); overlosd:
procedure SetSetProp(Instance: Object; const Frogtiae: string;
‘const Value: strincl: overload:
Sets the value of a setype property by interpreting a string as a list of
enumerated literals. SetSetProp recognizes the format that GetSetProp
remmms 1 the format af Value i not valid, SetetProp ruses an
EPropertyConvertBrror exception,
SetstrProp procedure
[procedure Sotstetop(matance: Tobject, Prvpiatss Phau:
const Value: string); overload;
‘procedure secstrvroplinstance: ‘byect; const Propltane: ating;
‘const Value: string); overload;
Sets the value of a sttingtype property The property type can be tkstring,
Bastring, or Dastring
SatVartantProp procedure
Procedure SetvarlantProp(Instance: TUbject; Prepinfo PPrapingo,
‘const vaive: Varuant)? overload;
procedure SetVariantProp(Instance: Tbject; const Erosilane: str!
‘const Value: Variant); overload;
Sets the value of a vartane-type property.
Virtual and Dynamic Methods
‘The YMT stores a lst of pointers for virtual methods and another table n the VMT,
‘which this section refers to as the dynamic method table, lists both dynamic
‘methods and message handlers.
‘The compiler generates a small negative number for each dynamic method. This
negative number is just like a message number for a message handler. To avoid
‘86 Chapter 3 Runtime Type Informationconflicts with message handlers, the compiler does not let you compile a message
handler whose message number falls into the range of dynamic method numbers,
‘Once the compiler has done its wods, though, any distinction between dynamic
‘methods and message handlers is los. They both sit in the same table and nothing
indicates whether one entry is for a dynamic method and another 1 for a message
handler
‘The dynamic method table lists only the dynamic methods and message handlers
that a class declares; it does not include any methods inherited from ancestor
classes. The dynamic method table stars with # 2-byle wut of the number of
‘dynamic methods and message handlers, followed by a list of 2-byte method
‘numbers, followed by a list of ¢-byte method pointers. The dynamic method table
‘is organized an this fashion Gastead of having a let of records, where each record
hhas a method number and pointer) to speed up searching for a method number.
Example 3-6 shows the logical layout ot a dynamic method table, As with the
cther tables, you cannot compile ths record, because it 1s not real Pascal, just 4
descnption Of what a dynamic method table looks like,
Example 3-6: The Layout of a Dynamic Method Table
ope
Wptielareble = packed record
Come: Words
Inderes: packed exvay{1. Count) of Snallint;
‘Aédresses: packed arrayil. Count] of Pointers
end;
Dispatching a message or calling a dynamic method requires a lookup of the
method or message number in the Indexes array “Ihe table is not sorted and the
lookup is linear. Once a match 1s found. the method at the corresponding address
is mvoked. If the method number is not found, the search continues with the
smmediate base class.
when all ofthe following conditions apply:
= You are creating a large framework of hundreds of classes
— You need to declare many virwal methods in the classes near
the root of the inheritance tee.
— Those methods will rarely be overridden in derived classes.
— Those methods never need to be called when speed is
important
i ‘The only time your should even consider using dynamic methods is
‘The tradeoff herween viral andl dynamic methods is that virual method tables
include all inherited virtual methods, so they are potentially large. Dynamic
tnethod tables do noc Ist inherited methods, so they can be smaller. On the other
hand, calling a virtual method is a fast index into a table, but calling a dynamic
‘method requires a search through one or mote tables,
Virtual and Dynamic Methods 87In the VCL, dynamic methods ate used only for methods that are called in
esponse to user interactions. Thus, the slower lookup for dynamic methods will
not umpact overall performance Alen, the dynamic methods are uoually declared sn
the root classes, such as Tontrol
If you do not have a large class hierarchy, you will usually get smaller and faster
code by using virual methods instead of dynamic methods, After all, dynamic
‘methods must store the method number in addition to the method address. Unless
yyou have enough derived classes that do not override the dynamic method, the
dynamic methev!rahle will end up requinng more memory than the virtual method
table.
Initialization and Finalization
When Delph constructs an object, it automatically mitilizes sinngs, dynamic
arrays, interfaces, and Variants, When the object 1s destroyed, Delphi must decre-
ment the reference counts for strings, interfaces, dynamic arays, and free
Variants and wide stings Ta keep track of thie information, Delphi uses initial:
tation records as part of a class's RTT. In fact, every record and array that
seyuires finalization has an associated initialization record, but the compiler hides
these records. The only ones you have access to are thse assacrated with an
object's fields.
A YMT pomts to an :ntializaion table. The table contains a list of initialization
records Recavse arrays and records can be nested, cach initialization sevund
contains a pomter to another mitialization table, which can contain initialization
‘econds, and so on. An mitialzation table uses a ‘TIypeKind field to keep track of
‘Whether its initializing @ sting. 2 record, an array, ete
An initialization table begins with the type kind (1 byte), followed hy the ype
‘name as a short string, a $-byte size of the data being initialized, a 4byte count for
‘sitslization records, and then an array of zero or sive uitialization records, An
initialization record 1s just 2 pointer 10 a nested snitialization table, followed by a
“aby olfset for the field that must be initialized, Example 3-7 shows the logical
layout of the initialization table ancl record, but the declarations depict the logical
layout without being true Pascal code,
Example 3-7: The Layout ofthe Initialization Table and Record
tape
(tntctattzacion/tinalazaticn record}
‘MinitRecord = packed record
initrable: “Pinittable:
Offset: Longhord, 1/ Offset of field in abject
(Anitéalization/finalization table )
‘Tmnlttable = packed recora
(intnmsize 1) // Engure that Typekind takes wp 1 byte.
‘Typetind: Typekind
‘ypetime: packed Shortstring:
batasize: tongiord;Example 3-7: The Layout of the Initialization Table and Record (continued)
count: tengtora:
// TE Typakindstideray, Count 49 the azray size, but Inithacorde
1/ Was only one elenent; if the type kind is tkRecord, Count is the
1/ vosber of record nenbers, and InitRecords{) has a
11 eecoed for each nenber. Bor sll other types, Ceumt-0
InitRocords: array{1..Comt] of TinitRecard
‘The master TmteKecora for the class has an empry type name and zero data
size. The type kind is always tkRecord The Count 1s the number of fields that
need initialization, and the InitRecords array contains a TInitRacord for each
such mombcr. Each initialization record points to an. initialization table that
contains the type kind and type name for the associated member. This organiza
tion seems a lite strange, but you can soon grow accustomed to it
“Most types do not need intualization or linalization, but the I
lowing types do:
umazay
DataSize is the size of each array element, and the Count is the number
of elements in the array Every array clement is the same, so the
InitRecorde array contains one TinitRecora thet represents all the
array elements. The Offset in the TInitRecord has no meaningful
value.
Upyanstey
DataSize and Count are not meaningful. Delphi decreases the refer-
fence count of the array and frees the array's memory if the reference
‘count becomes zero,
tkinterface
DataSize and Count are not meaningful. Delphi calls the Release
method, which frees the mieifice ubyect if die reference coun becomes
ekistring
DataSize and Count are not mesningtul. Delphi decreases the reter-
ence count of the string and frees the string's memory if the reference
count becomes 2et0,
‘tkRecord
Datasize is the size of the record, and the Count is the number of
members that need initialization. The TnitRecords array contains a
‘TunstRecord for each member that needs initialization.
tkvariant
DataSize and Count are not meaningful. Delphi frees any memory asso-
ciated with the Variant data
erusering
DataSize and Count are not meaningful. Delphi frees the sting.
Thitialization and Finalization 89Automated Methods
‘The antomated section of a class declaration is now obsolete because i is ease 10
create a COM automation server with Delphi's type library editor, using interfaces
"Nonetheless, the compiler currently supports automated declarations for back.
‘ward compatibility A future version of the compiler might drop cupport for
automated declarations.
The Oleauto unit tells you the details of the automated method table: The table
stars with a 2-byte count, followed by a list of automation records. Fach record
has a byte dispid (dispatch identifier), a pointer to a short string method name,
“bytes of Hlags, a pointer to a list of parameters, and a code pointer. The param.
ter list starts with a Lhyte seme type, followed by a I byte count of parameters,
and ends with a list of L-byte parameter types. The parameter nantes ate not
stored, Exatuple 58 shows the declarations for the automated method table.
Example 3-8: The Layout of the Automated Metbod Table
Macutorarans = 255;
ope
‘Tintautctype = Byte;
Outemarion ontry parameter et }
PAutoParanbice = “MhitoPareeList;
‘mastotarandst — packed cert
‘Types: array{1..comt] of Iatautorvve:
end;
[ Rutanation take entry }
PhutoBhtry = “Tautomtzy;
‘utomery = packed rewid
DispID: Longtaty
Flags: Longint; ( Lower byte containg fleas )
Parans: PhutoParentist;
berana: Pointers
ena)
{ Autemation table layout }
vaucotaple = “TutoTable;
‘TautoTable = packed record
Cone: Longines
mnteieas array. comnt) of tautomntayy
ena;
90 Chapter $= Runtime Type InformationInterfaces
‘Any class can implement any number of interfaces. ‘the compaler stores a table of
interfaces as part of the class's RTTL. The VMT points to the table of interfaces,
which starts with a 4dyte count, followed by a list of interface records. Bach inter-
face record contains the GUID, a pointer to the interface’s VMT, the offset to the
anterfice’s hidden field, and a posnter to a property that implements the interface
‘with the implements directive, I the offset is zero, the interface property (called
Imploetter) must be non.ntl, and if the offset ss not 2270, InplGetter must be
Ril. The interface property can be a reference to a field, a virtual method, of a
static method, following the conventions of a property reader (which 1s described
earlier in this chapter, under “Published Properties"). When an object is
constructed, Delphi automatically checks all the interfaces, and for each interface
1 a nonzero TOLE Set, the fick! at that offset is set tw Ue waerface’s VEable (a
pointer to its VMI). Delphi defines the types for the interface table, unlike the
Other RTTT tables, inthe System unit. These types are shown in Example 3.9.
Example 3-9: Type Declarations for te Interface Table
Pintertaccintry ~ “Tintorfscotry
‘Minterfecemtry = record
viable: Pointer;
offset: Integer;
Ieploetter: Integer:
ea,
PinterfoceTable = “TinterfaceTable;
nenytount: Integers
1/ Declare tho type with the largest possible size,
1/ bot the true size of the array is BntryComt clenents.
‘Series: array[0..9999) of Tinterfacemery:
‘TobJect_amplements several methods for accessing the imerface ble. See
Chapter 5 for the detals of the Getinterface, GetInterfacemtry, and
GetInterfacerable methods.
Exploring RTTI
This chapter introduces you to a class's viral method table and runtime type
information. To better understand how Delphi stores and uses RTT, you should
explore dhe Gables on your ow, The code dat accuuipaies dus book on die
OReilly web site includes the Vmtexe program. The VatInfo unit defines a
collection of interfaces that exposes the structure of all the RTTI tables. The
‘Vmtinpl unit defines classee that implement thece interfaces, You can read the
source code for the VmtZmp1 unit or just explore the Vm program. See the
‘VntForn unit to add types that you want to explore, of to change the type
declarations,
Explonng RTA 91You can also use the meTafo interfaces in your own programs when you nced
access to the RIT tables. For example, you might write your own object persise
tence linaty whiere you need access (0 a field class table to map class names t0
class references,
‘The interfaces are self-explanatory. Because they use Delphi's automatic reference
‘counting, you don't need to worry about memory management, either. To ereate
an interface, call one of the following functions
fmetion Gettitinfo(ClassRef: TClass): TatInfo; ewerlond:
function Getimtznfo(dbiectRet: TObject): TntIngo; overload:
function Gernypetnto(RypeIntos PIypcinge)+ Typetnts,
Use the TVmtInfo interface and its related interfaces to examine and explore the
sich world of Delphi's runtime type information, For example, take a look at the
‘Bont clas, shown in Example 3-10.
‘example 3-10: Declaration of the TFont Class
‘TRont = class Moraphicsdbject)
private
Foolor: Toler
FPixelaferinch: integer;
Rictifys Thangesioes fier,
procedure GetData(var Fontata: TFantData) ;
procedure setvata(censt FontData: TFontData);
protected
Drocedure Changed; override;
‘function Getttandle: HFent
function GetHeight: Integer
function GetPiveh: TFonteiech;
function Gecstze: ineeger;
function Getstyle: Trontstyles;
function Getcharset: TPontcharae
procedure SetColor (Value: Talor)
Drocedure Setiandle Value: Hfont) ;
[procedure Setioight (Valuer Integer)
‘procedure Settiae const Value: Fontan) ;
puaceture SetPiccn value: ‘iyontPiten);
Drocedure SetSize(Value: Integer) ;
Procedure Sotstyle(Value: TFontstyles) ;
Procedure Setcharset Value: "Pont Charset)
public
Gestructor Destroy; override;
roveue hislgn(source: Teersistent); override;
roperty Fontadapter: IChangelltifier read Mlotity write Miotity:
Droperty Handle: Font read Getiiandle write Setiandle:
Property PinelsPerinch: Intayer read FPixelabertnch
write FPixelePerinch;
rehtiched
Droperty Charset: 1Fontcharset read Getcharset write Setcharset:
veoenty Color: Tolor read HUolor Write SetColor
92 Chapter 3= Runtime Type tnformationExample 3-10: Declaration of the TFont Class (continued)
property Height: Integer read Getileight write Setteight:
Dropertyy Mane: Trontiane read Cottime write Setlane;
‘property Pitch: TontPitch read GetPitch write setPitch
default fpDefaults
property Size: Integer read GetSize write Setsize stared False)
property Style: TPontstyles read Gatstyle write Setstyle;
eat
Notice that one fleld is of type ichangenotifier. The changed method Is
declared as dynamic in the base class, TGraphicsObject. TFont has no
published fields or methods, but has several published properties. Example 3-11
shows the YMT and sype information for the Font clace. You can eee that the
dynamic method table has one entry for Changed. The Size property is not
stored, but the other published properties are, The Vmitexe program can show
you the same kind af information for almost any class or rype
Example 5-11. Runstme Type Information
‘yes 40030878
Destroy: 40032820
resinstance: a00us908
Newcinatance: 4000394
Defaulttiandlers 400030K¢
Disoatch: 4000388
BeforeDestruction: 40003¢Bs
Safecal ception: 40003Cx4
arent: <0030uR4 (TUraphlestbject)
nstanceSize: 32
Clasetine: "ont"
manic Mothod Table: 4003052
‘Counts 1
40002084 (-2)
ethod Table: 00000000
‘typetnfo: 400308F4
nieteble: 40030200
‘Teaiae:
‘ypeKind: tkRecore
ataoffests 6
count: 2
al
infemable: 40030844
‘rwodlane: IChanasiot tier
‘Typekind: tkinterface
Dataotfoots 28
autotable: 00000000
type Tontchareet = 0..255; // otuByte
‘type Tolor = 2147483648. .2147483687; // otStong
Exploring RTT
BExample 3-11. Runtime Type Information (continued)
‘ape Integer = 2147483648. 2147483647; // otstong
{type Montane, // tkLotring
‘tape TontPitch = (fpOefault, ‘variable, fpeixed); // otuyte
aye Tmuswyle = (285014, retalic, fsthaerline, feSteileost)
Uupe tentstyles = set of TRontStyle; // otlByte
type Tobject = class // anit ‘syaten!
end;
‘type Tersistent = class (TUbject) // init ‘Classes!
‘pe ToaphicsObject = clase(TPersistent) // unit “Graphics*
end,
‘type Mont = class Toraphicsdbject) // untt 'Graphics*
‘published
Droperty Charset: Tontcharaet read (atatic methd 40032004)
write (static method 4003200) nodefault stored ue; // index 0
reperty Color: Toler read (field 20) write (otaLle method 409323A:)
odefault stored True; // index 1
Peopetty Height: mnteger read (static method 4003288C)
write (static method 40032894) nedefault stored True; // index 2
‘property Nene: TontHane read (static nethod 400328BC)
write (static method 40032804) noefnit ntorad Troe; // index 3
property Pitch: TFonePitch read (static method 40022¢h4)
write (static method 40032042) default 0 stored Trucs // Andex 4
property Size: Integer read (static method 4003230)
weite (elaLle method 40032c%) nocerault stored False; // index $
property Style: Trenestyles read (static method 40032060)
write (static method 40032078) nodefault stored True; // index 6
ea;
‘94 Chapier 3 Runtime Type InformationCHAPTER 4
Concurrent Programming
‘The future of programming is concurrent programming. Not 100 long ago, sequen-
tial, command-line programming gave way to graphical, event-daven programming,
and now singlethreadeel programming is yielding 1p mulithreaded! programming
Whether you are writing a web server that must handle many clients simulta-
neously of writing an end-user application such as a word processor, concurrent
programming. is for you, Perhaps the word processor checks for spelling errors
‘while the user types. Maybe it can print a file in the background while che user
continues to edit. Users expect more today from their applications, and only
‘concurrent programming can deliver the necessary power and flexibility
Delphi Pascal includes features to support concurrent programming—not as much
support as you find in languages such as Ada, but more than i most traditional
programing languages, In addition (0 the hirguage Featutes, you cat use die
Windows APL and its semaphores, threads, processes, pipes, shared memory, and
so on. This chapter describes the features that are unique to Delphi Pascal and
‘explaine how to use Delphi effectively to write concurrent programe. If you want
‘more information about the Windows API and the deuils of how Windows
handles threads, processes, semaphores, and so on, consult a book on Windows
programming, such as Inside Windows NT. second edition, by David Solomon
(Microsoft Press, 1998)
Threads and Processes
“This eection provides an overview of multithreaded programming in Windows. If
you are already familiar with threads and processes in Windows, you can skip this
section and continue with the next section, “The TThread Class
A thread 1s @ flow of contol in a program. A program can have many threads,
each with its own stack, its own copy of the processor's registers, and related
information. On a multiprocessor system, each processor can un a separatethread. On a uniprocessor system, Windows creates the illusion that theeadts are
‘running concurrently, though only one thread at atime gets to run.
‘A process is a collection of threads all running in a single address space. Every
process has at least one thread, called the main thread. Throads in the same
process can share resources such as open files and can access any valid memory
addess ai he process's address space, You can think of a process as an instance
of an application (plus any DLLs that the application loads),
‘Threads in a process can communicate easily because they can share variables
Critical sections protect threads from stepping on each others’ toes when they
access shared variables. (Read the section "Synchronizing Threads" later in te
chapter, for details about critical sections.)
You can send a Windows message to a particular thread, in which case the
receming thread must lave # message loop to handle the message. In most cases,
‘you will find it simpler to let the main thread handle all Windows messages, hut
feel free to write your own message loop for any thread that needs it.
Separate processes can communicate in a variety of ways, such as messages,
‘mutexes (short for munial exclusions), semaphores, events, memory-mapped files,
sockets, pipes, DCOM, CORBA, and so on. Most likely, you will use a combina
‘io of methods. Separate processes do not share ordinary memory, and you
‘cannot cal a function or procedure from one process to annther, although several
remote procedure call mechanisms exist, such as DCOM and CORBA. Read more
about processes and how they communivate i the section “Processes” later sn this
chapter.
Delphi has builtan support for multithreaded programming —writing applications
{and DLLs that work with multiple threads in a process, Whether you work with
threads or processes. you have the full Winlows API at your dispocal.
In a multithreaded application or library, you must be sure that the glohsal vanable
TotlultiThread 1s Tre. Most applications do this automatically by calling
ogintiroad or using the Tread class. f you wate « DLL that ugh be called
from a mulithreaded application, though, you might need to set TsttaltiThread
10 Inve manually
Scheduling and States
Windows schedules threads according to their pnorities. Higher priority threads
run before lower prionty threads. At the same parity, Windows schedules threads
0 that each thread gets a fair chance to min Windows can stop a running thread
alled preempting the thread) to give another thread a chance to run. Windows
defines several dlfferent states for threads, but they fall into one of three
categories:
Running
‘A thread 1s running when it is active on a processor. A system can have as
‘many running threads as it has processore—one thread per processor. A.
thread remains in the running state until it blocks because it must wait for
ouue uperauion (Such as the completion of /). Windows then preempts the
thread to allow another thread to run, or the thread suspends itself
‘95. Chapter J Concurrent Programming