Blaise 31UK PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 120

BLAISE

BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE
31/32
D E L P H I,
L A Z A R U S,
O X Y G E N E,
S M A R T,
A N D
P A S C AL
R E L A T E D
L A N G U A G E S
A N D R O I D,
I O S,
M A C,
W I N D O W S
&

L I N U X

TWO ISSUES IN ONE


Book Review: Coding in Delphi By Jim Duff Book:
Coding Delphi Author: Nick Hodges
Designing an API: common mistakes By Alexander Alexeev
Newest Leap developments Michael Van Canneyt
3D Printing By Bj Rao
Kinect ?! By Michael Van Canneyt
Smart Mobile Studio 2.0 By Primo Gabrijeli
Interview with David I:
plans about updating buying etc
A simple superscript text editor By David Dirkse
Interview with Gwan Tan - better office
Using GEO services in Delphi applications with TMS components
By Bruno Fierens
Correcting a bad API design: By Alexander Alexeev

maXbox

The maXbox Pure Code By Max Kleiner


Interview with Ray Konopka
Programming Bitmap Rotation By David Dirkse
Introduction to Model, View and View Model (MVVM) and the Caliburn Micro for
Delphi framework By Jeroen Pluimers
kbmFMX for XE5 (android) By Fikret Hasovic

5 / 6 2013
Publisher: Foundation for Supporting the Pascal Programming Language
in collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep)
Stichting Ondersteuning Programmeertaal Pascal

BLAISE
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE
31/32
D E L P H I,
L A Z A R U S,
O X Y G E N E,
S M A R T, A N D
P A S C AL
R E L A T E D
L A N G U A G E S
A N D R O I D,
I O S,
M A C,
W I N D O W S
&
L I N U X

CONTENTS

ISSN 1876-0589
Royal Library -Netherlands
Koninklijke Bibliotheek Den Haag

Articles
Editorial
Book Review: Coding in Delphi
By Jim Duff Book:
Coding Delphi Author: Nick Hodges
Designing an API: common mistakes
By Alexander Alexeev
Newest Leap developments
Michael Van Canneyt
3D Printing By Bj Rao
Kinect ?! By Michael Van Canneyt
Smart Mobile Studio 2.0
By Primo Gabrijeli
Interview with David I:
plans about updating buying etc
A simple superscript text editor
By David Dirkse
Interview with Gwan Tan - better office
Using GEO services in Delphi applications
with TMS components
By Bruno Fierens
Correcting a bad API design:
By Alexander Alexeev

Page 3
Page 4

Page 8
Page 21
Page 26
Page 33
Page 41
Page 48
Page 50
Page 52
Page 57
Page 65

maXbox

News and Press Releases


email only to [email protected]
Authors
A
B
C
D
F
G
H
J
L
K
M
N
O
P
S

Alexander Alexeev
Peter Bijlsma,
Michal Van Canneyt, Marco Cant,
David Dirkse, Daniele Teti
Bruno Fierens
Primo Gabrijeli,
Fikret Hasovic
Cary Jensen
Wagner R. Landgraf, Sergey Lyubeznyy
Max Kleiner
Kim Madsen, Felipe Monteiro de Cavalho
Jeremy North,
Tim Opsteeg, Inoussa Ouedraogo
Howard Page-Clark, Jeroen Pluimers
Rik Smit, Bob Swart,

Editors

The maXbox Pure Code Page


Page 77
By Max Kleiner
Interview with Ray Konopka
Page 93
Programming Bitmap Rotation
Page 98
By David Dirkse
Introduction to Model, View and View Model (MVVM)
and the Caliburn Micro for
Delphi framework
Page 102
By Jeroen Pluimers
kbmFMX for XE5 (android)
Page 113
By Fikret Hasovic

maXbox

Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit

Correctors
Howard Page-Clark, James D. Duff

Trademarks
All trademarks used are acknowledged as the property
of their respective owners.
Caveat Whilst we endeavour to ensure that what is
published in the magazine is correct, we cannot accept
responsibility for any errors or omissions. If you notice
something which may be incorrect, please contact the
Editor and we will publish a correction where relevant.
Subscriptions ( 2014 prices )
1: Printed version: subscription 60.-- Incl. VAT 6%
(including code, programs and printed magazine, 6
issues per year excluding postage).
2: Electronic - non printed subscription 45.-Incl. VAT 21% (including code, programs and
download magazine)
Subscriptions can be taken out online at

www.blaisepascal.eu
or by written order, or by sending an email to

Advertisers

[email protected]

Barnsten
BetterOffice
Components 4 Developers
ITDevCon
Lazarus the complete guide
Learnto program
maXbox
Raize Software
Smart Mobile Studio

Pag
Page
Page
Pag
Page
Page
Page
Pag
Page

20
25
120
39
19
24
77
92
40 / 46

Copyright notice
All material published in Blaise Pascal is copyright SOPP Stichting
Ondersteuning Programeertaal Pascal unless otherwise noted and may
not be copied, distributed or republished without written permission.
Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of
the PGG for download, and that articles and code will be placed on
distributable data storage media. Use of program listings by subscribers
for research and study purposes is allowed, but not for commercial
purposes. Commercial use of program listings and code is prohibited
without the written permission of the author.

Editor - in - chief
Detlef D. Overbeek, Netherlands
Tel:+31(0)30 890.66.44/Mobile +31(0)6 21.23.62.68

COMPONENTS
DEVELOPERS

Subscriptions can start at any date. All issues published


in the calendar year of the subscription will be sent as
well.

Subscriptions run 365 days.


Subscriptions will not be prolonged without notice.
Receipt of payment will be sent by email.
Subscriptions can be paid by sending the payment to:
ABN AMRO Bank Account no. 44 19 60 863
or by credit card: Paypal or TakeTwo
Name: Pro Pascal Foundation-Foundation for Supporting
the Pascal Programming Language (Stichting
Ondersteuning Programeertaal Pascal)
IBAN: NL82 ABNA 0441960863
BIC ABNANL2A VAT no.: 81 42 54 147
(Stichting Programmeertaal Pascal)

Subscription department
Edelstenenbaan 21 / 3402 XA IJsselstein,
The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile:
+ 31 (0) 6 21.23.62.68

[email protected]

Nr 5 / 6 2013 BLAISE PASCAL MAGAZINE

From The Editor

irst of all a Happy New Year to you all.


This is a season of dark days, so I especially like the
light from our Christmas tree which adds to the
warm ambience of those holiday opportunities to relaxing
with your family and friends, times that are enhanced with
little gifts and delicious meals...
2013 was supposed to have been the end of the world
and though it was not the worst of years in the political
sense, yet world crises seem not to be over, even if some of
them are lessening in severity

This year we will see 3D printing take off,


and of course Leap Motion as a
human-computer interface used increasingly.
For Leap Motion we are developing a version that will run
on ARM cpus: so we will be able to port Leap Motion
software to the Raspberry-Pi and set up some new tools.
This year we plan to offer our new group of Components
for Leap Motion on several platforms.

I wrote a chapter about the history of computing for


our Learning Pascal book which has turned out to be
a lengthy story: about 70 pages. Naturally I had to
The immense horns Mr Snowden placed on us made us
find out something about the founding father of
realize that our governments do not play by the rules.
Pascal and write a few lines about him.
Ordinary mortals can do little beyond despairing at the
In Cologne recently I met Max Kleiner and we
invasion of privacy and taking what steps we can to
discussed the origins of Pascal, and he told me
combat it. Politicians must battle this erosion of
individual's rights, and I think in the long run the battle
Professor Wirth was still alive, and so I thought it
will be won.
would be interesting to interview the author of
Simply because it has to be.
Pascal: Professor Dr. Niklaus Wirth.
I think the sheer quantity of garbage the intelligence
He will be 80 years old next month.
community is accumulating will collapse upon the
So we will visit him shortly to do an interview.
perpetrators. It will take some time to reach critical mass;
Mr. Wirth has agreed to this and you will be able to
but in the world of programming we already have a
read the outcome of our conversation in the next
solution for this: simply remove it by deletion (Garbage
issue. In his honour we will start a new project: PEP
Collection)!
PASCAL EDUCATION PROGRAM. We will launch
So many complain about deception. But who knows what
PEP for the first time on 15th February, since that is
the truth is? We all lie now and then, because we do not
his birthday. In line with this educational theme we
want to damage our relationship to someone; and I think it plan in future issues to reserve about 4 pages for
in the end humans do not always want a black-and-white
Pascal beginners, explaining the fundamentals of
difference between Yes and No, we also prefer a gray zone.
programming, and creating basic examples especially
We find this even in scientific theory (although that is denied
for those new to programming.
as well).
Young people (12 to 18 years of age) and students
String theory is a very good example of this:
it should be impossible for an entity to exist at two distinct will be offered a free Blaise magazine subscription so
they can receive information without needing to pay.
places at the same time. And when you cast your mind
back over history, and look at longer epochs, you will see
Details will be available at our website.
that there is a pendulum effect: things move back and
Many of you always prefer Delphi's help as it was in
forth. Sometimes there is a slight improvement before the
Delphi 5, 6 and 7. Here is some good news: we are
cycle starts all over again
shortly releasing The Helper Component.
The universe as a whole seems to work on this principle.
This is a component that presents help in the way we
Some claim we will find people who can write programs
think help should be organized. We plan to announce
that will protect us from any intrusion or decryption.
the first trial version in the next issue: meantime you
Dont believe them
can help us by letting us know your gripes and
wishes for an improved IDE help system.
You would do better to spend your money on the newly
developing future like the adventure of
the Leap Motion or even 3D Printing, and be creative (as
you probably have always been) in writing new programs
with new code. People outside the programming world
may not understand this need to innovate, or the many
new possibilities in our Pascal world offers.
No Operating System is excluded any more.
Pascal is not only doing good it is getting back where it
belongs: at the top of the range of available languages,
because of its enormous potential in education, for solving
problems, in facilitating Rapid Application Development,
in bringing the write-once-run-anywhere dream closer
for cross-platform programmers.
As the user group that publishes this Magazine we will
always try to help you advance to the next step.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

There are many plans for 2014:


The next Pascon conference to follow the great
success of last summer's event;
The new Leap component
Experimenting with Kinect
Many articles about Android development using
Pascal, and new opportunities for that OS
Its going to be a very busy year.
Very Exciting!

COMPONENTS
DEVELOPERS

Book Review: Coding in Delphi By Jim Duff


Developers at all levels will be able to use the
information contained within this book. It doesn't
matter if you're relatively new to Delphi or
programming or you've got many years of
experience. If there is one common trait I've found
among software developers, it is their desire to learn.
Learning a new technique or figuring out how to
apply that technique to one's unique situation is part
of the excitement of being a software developer."
Now, coming back down to the details of
what's in the book, Nick's next chapter, being the
Introduction, is yet another flag that waves your
desire to read the book. Once more, I provide some
partly quoted sentences that illustrate the book's high
value for developers.
..noticed that a lot of Delphi developers are behind.

That is, they are either using an older version of


Delphi, or they aren't using or aren't even aware of
all the features in the newer versions of Delphi that
they are using." "For instance, two powerful
language features were added in Delphi 2009:
generics and anonymous methods. Both are features
that enable the development of really cool code and
frameworks"
The content of the book contains
the following chapters:
Book: Coding Delphi
Author: Nick Hodges
There are a couple of ways to preview what a
technical software book is all about before one goes
ahead to use it. The first quick act is to look at the list
of contents and then flick through some of the
chapters to preview the wording, code samples and
pictorial views. The next step is to analyse the
knowledge and validity of the author to make sure
that the contents and sample code are going to give
you the correct, accurate and productive assistance
for your development design and coding.
As a reviewer, the above second step is an
important one in order to provide the magazine
readers with a brief and positive (or negative - not in
this case) assessment of the book. So, here we go
with a summary of the first two write ups, namely
Forward, by Allen Bauer, Embarcadero's Chief
Scientist, and Introduction by author Nick Hodges.
In Allen's Forward chapter, he provides a
write up of Nick's background beginning with "Nick
holds the honor of producing one of, if not the first
non-Borland built component. In order to learn about
Delphi component building, he built TSmiley."
Looks like that must have worked OK as Nick
became a member of Borland-Embarcadero. And
near the end of Allen's summary, he made the
following link between the positive aspects of the
book and how such value would provide a beneficial
effect for developers.
4

COMPONENTS
DEVELOPERS

Forward
Introduction
Acknowledgements
Frameworks used in Coding in Delphi
1
Exceptions and Exception Handling
2
Using Interfaces
3
Understanding Generics
4
Understanding Anonymous Methods
5
Delphi Collections
6
Enumerators in Delphi
7
IEnumerable
8
Run-time Type Information
9
Attributes
10 Using TVirtualInterface
11 Introduction to Dependency Injection
12 A Deeper Look at Dependency Injection
13 Unit Testing
14 Testing with an Isolation Framework
Appendix A: Resources
Appendix B: My Delphi Story
The list of chapters shows that the book is aimed at
experienced Delphi developers rather than being a
beginner's guide. Having already mentioned the
Forward and Introduction items above, one more
item to mention in support of the qualified author is
the final Appendix - My Delphi Story. This is the
third part that once more gives the reader the
confidence in going ahead to take in the technical
aspects of the book.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Book Review: Coding in Delphi (Continuation 1)

Now, we are ready to go, and a major influence of


this book is having this subject matter as the first
main chapter:
1. Exceptions and Exception Handling
Introduction
Structured Exception Handling
How Not to Use Exceptions
Don't Eat Exceptions
Don't Generally Trap Exceptions
Don't Go Looking For Exceptions
Don't Use the Exception Handling System as a
Generic Signaling System
How to Use Exceptions Properly
Use Exceptions to Allow Your Code to Flow Without the
Interruption of Error Handling Code
Application Writers Should Trap Exceptions
Trap only specific exceptions
Component Writers and Library Code Writers Raise
Exceptions
Raise Your Own Custom Exceptions
Let Your Users See Exception Messages
Feel Free To Provide Good Exception Messages
Provide Two Versions Of A Library Routine
Conclusion

This provides not only the 'how' together with


sample code, but the reasoning behind having it as
a fundamental method of coding.
It reminds one of the basic input-process-output of
computer processing; if these steps don't work, then
the hardware/software doesn't work properly
and/or stops working - full stop.
Part of his final comments in the Conclusion
provides the 'why': "It's quite easy to fall into the
trap of using exception handling improperly. ... Use
exceptions wisely, and you'll be able to product
robust, clean code."
2. Using Interfaces
Introduction
Decoupling
What are Interfaces?
Interfaces Everywhere
A Simple Example
Implementing an Interface
Some Further Things to Note
Interface Inheritance
Other Things to Think About
About TInterfacedObject
How to Actually Use Interfaces
Why Should You Be Using Interfaces?

If I could teach a new programmer


one thing it would be this:
Program to an interface, not an
implementation.

This sample quote that soon follows the above one,


is provided to further confirm the reasonings
provided throughout the book, in line with the
sample code and instructions.
"All through this book, I'll talk a lot about decoupling
your code and why that is really good.
But a definition here is probably a good idea.
Code can be said to be decoupled when your classes
are designed in such a way that they don't depend on
the concrete implementations of other classes.
Two classes are loosely coupled when they know as
little about each other as possible, the dependencies
between them are as thin as possible, and the
communication lines between them are as simple as
possible."
A code sample and part of the associated instructions
that provide associated reasonings are now given as
the final quoted sample in this review.
type
IName = interface
['{671FDA43-BD31-417C-9F9D-83BA5156D5AD}']
function FirstName: string;
function LastName: string;
end;

" Note that the declaration of the interface has a


Globally Unique Identifier (GUID) right after the
initial declaration. This GUID is used by the compiler
to identify this interface.
You can use an interface without the GUID,
but much of the RTL and most frameworks that take
advantage of interfaces will require a GUID be
present. (You can generate a GUID any time you want in
the IDE by typing CTRL+SHIFT+G)
The remaining chapters follow in a logical sequence,
each with code samples plus the 'how' and 'why' to
use them.

Coding in Delphi

So, finally getting into the technical documentation,


an introductory chapter Frameworks used in Coding
in Delphi provides "...a number of open source
frameworks to facilitate the coding techniques that
are discussed herein. I refer to them here at the
beginning of the book to enable you to retrieve the
code and get them set up before we begin."

This chapter has the only photo of the author, so it


just goes to show how important he believes the
following advice is.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

Book Review: Coding in Delphi (Continuation 2)


Summary
Hopefully, the selected quotations from the book
have provided the reader with positive views of the
author's ability to explain the 'how' and 'why' to
advance your coding capabilities.
Nick has also given a good background of his own
and Delphi's history, from the early days to the
present, to show how they and we readers can
advance our development capabilities as the
technical environment is accelerating in this day
and age.
The 'book' reviewed has been the pdf version and
there are several links in Appendix A such as Unit
Testing, Projects, Good Stuff etc. The following
link is also provide at the start of the book:
"Find out what other people are saying about the
book by clicking on this link to search for this
hashtag on Twitter:
https://twitter.com/search?q=#codingindelphi

Reviewed by Jim Duff


ADUG Melbourne Member
The following lines are taken out the book.
Forward
I first met Nick during a Delphi 1 pre-launch
boot-camp that was held at the brand new Borland
campus in Scotts Valley, California.
We had invited a cadre of developers, authors
and long-term luminaries to get some in-depth
training and presentations directly from the
development team and product management.
Enthusiastic and exuberant are adjectives that dont
fully characterize my first impressions of him.
He was never afraid of asking questions and
absorbed the information quickly.

Nick held the position of Product Manager for a


while then managed a portion of the Delphi R&D
team. Nick was never afraid to challenge
assumptions and actively stand up for the Delphi
developer community.
Even though Nicks position didnt require him to
write code, he didnt let that stop him.
He was always looking for ways to keep his
programming skills as sharp as possible. As new
features were developed, Nick was always right
there to give them a workout.
To this day there is a large amount of code written by
Nick that remains a key portion of the overall
regression and unit-testing process.
Nick discovered that some of the best ways to
learn about new code is to test that new code. It is not
without irony that this process requires code to be
written.
It stands to reason that Nick would end up here;
writing a book with a single focus on the code.
That is the engine that drives all the other
revolutionary features of Delphi.
Without the code, there would be no Visual in the
Visual Component Library (VCL).
In fact, Delphi has always been about getting the
developer to their code as quickly as possible. The
VCL and the newer FireMonkey component
frameworks make the use and construction of UI,
database, connection and others as simple as
possible. Its ultimate goal is to allow the developer to
focus on their task, which is to produce an
application that solves a specific problem. It is the
code that is written in between those UI, database
and connection components that make up the
application.
Developers at all levels will be able to use the
information contained within this book.
It doesnt matter if youre relatively new to Delphi or
programming or youve got many years of
experience. If there is one common trait Ive found
among software developers, it is their desire to learn.
Learning a new technique or figuring out how to
apply that technique to ones unique situation is part
of the excitement of being a software developer.
This is right up there with the thrill experienced
when some thought, idea or concept comes to life
within the computer. When a developer sees their
running application, rarely do they think about the
few moments they spent arranging some controls on
the User Interface. They feel a sense of pride about
that one difficult and thorny problem they solved
through the clever use of code. At the end of the day,
to a developer, it is really all about the code.

I cannot talk about Nick without also discussing


TSmiley. Inquisitive and curious,
Nick embraced Delphi without reservation.
To that end, Nick wasnt satisfied with what Delphi
did, but was just as keen on learning about how it did
it. To that end, Nick holds the honor of producing one
of, if not the first non-Borland built component.
In order to learn about Delphi component
building,
he built TSmiley. In this one simple component all the
core aspects of using Delphis Pascal language to
extend and enhance the VCL framework were
demonstrated.
You see, Delphi component building is all about the
code.
Allen Bauer
I had the pleasure of working closely with Nick
during his time at Borland and then Embarcadero.

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Book Review: Coding in Delphi (Continuation 3)

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

What you will find are ways to make your code much
cleaner, much more powerful, and way easier to maintain.
This book is all about the cool, new code you can write
with Delphi. It wont matter whether you are building a
VCL or an FM application. Ive titled it Coding in Delphi
because I want to make it a book that shows you simple
examples of how to use powerful features that, is about
the code. These language features are indeed advanced
features they are new relative to, say, the case statement
and thus many of you are beginners to them. By the end
of this book, you wont be.
The approach Im taking for this book is to try to
distill things down to the very basics. The examples will be
very simple but the explanations deeper. I believe that if
you can understand the basic idea in a simple example, it is
but a small leap to using those ideas in more complex code.
There is no point in giving complex examples
when you can get the main thrust using fundamental
implementations that illustrate advanced ideas.
Between the code in this book and in the samples online
(https://bitbucket.org/NickHodges/codinginde
lphi) you can learn all the precepts and then begin
applying them to your own code. In other words, nothing
fancy here, just the basics it is then up to you to use these
ideas in your own code.
This book is not done its instead a living document.
Since it is self-published on a platform that makes iteration
very easy, I plan on having frequent releases to fix typos
(which will, Im sure, sneak through despite my best efforts),
improve examples and descriptions, and keep up with
technology changes. Owners of the PDF should get
notifications of new versions automatically.
If you are reading a paper version of this book, Im sorry I
cant deliver fixes to your hard-copy perhaps
some day that will be possible.
The book will be done when you guys say it is done.
Maybe, it will never be done because Delphi keeps
growing and expanding. I guess well see.
As a result, Im totally open to feedback please feel free to
contact me at [email protected]
with suggestions corrections, and improvements.
Please join the Google Plus group for the book.
I may even add whole new chapters.
Thanks for your purchase this book was a labor of love,
so every sale is icing on the cake.
Nick Hodges
Gilbertsville, PA

https://bitbucket.org/NickHodges

Coding in Delphi

Introduction
Over the years, Ive spoken in front of a lot of Delphi
developers. The one thing that I notice is that there are a lot
more of you Delphi guys than the average Delphi guy
thinks. There are Delphi folks everywhere. Also, I have
noticed that a lot of Delphi developers are behind. That
is, they are either using an older version of Delphi, or they
arent using or arent even aware of all the features in the
newer versions of Delphi that they are using.
Something I like to do when Im in front of folks is
ask a few questions about what people are doing. Im
always saddened that the response to questions like Who
is doing unit testing? or Who is taking advantage of
Generics?
is pretty meager.
This is particularly true for the language features and the
run-time library. Its quite easy to move forward with an
older code base, utilizing the new features in the IDE and
adding new things to your app using the new high level
frameworks and components that come in the newer
versions.
For example, you might have been developing an
application since Delphi 3, moving forward through
various versions. Along the way, you may have added
some DataSnap functionality, started using the Code
Insight features in the IDE, and when you moved to XE2,
you start poking around with FireMonkey.
But its fairly easy to ignore the new language features that
come along with those new versions.
For instance, two powerful language features were added
in Delphi 2009: generics and anonymous methods.
Both are features that enable the development of really cool
code and frameworks.
But if you didnt understand or feel the need for them,
then it was pretty easy to simply not use them.
You can still do all kinds of great stuff in Delphi without
them, but with them, well, you can write some
really beautiful, testable, and amazing code.
For instance, a relatively new framework that
exists only because of these new language features is the
Spring Framework for Delphi, or Spring4D, as Ill refer to it
in this book. Spring4D is a feature rich framework that
provides a number of interesting services, including a wide
variety of collections that leverage generics, an Inversion of
Control container, encryption support, and much more.
I view Spring4Dsolid as much a part of the Delphi
RTL as SysUtils is. Using Spring4D in your code will make
many, many things easier and cleaner. But many Delphi
developers dont know this yet.
If the above is familiar, this book is for you:
The Delphi developer that hasnt quite yet made the leap
over into the cool and amazing things that you can do with
the latest versions of the Delphi language.
This book is all about introducing these new
language features and some of the intriguing things you
can do with them. It will take a look at these new language
features, and then expand into some of the open source
frameworks that have sprung up (no pun intended) as a
result. It will then show you techniques that you can use to
write SOLID, clean, testable code.
You wont find much of anything about the IDE or
the higher level frameworks here. Screen shots will be few
but code examples many. You wont find anything about
how to build better user interfaces or fancy components.

COMPONENTS
DEVELOPERS

Designing an API: common mistakes By Alexander Alexeev


starter

expert

When you want to create a public component for use by


several potential applications, you can register it as a
COM object. (Component Object Model (COM) is a binary-

interface standard for software components introduced by


Microsoft in 1993)
Because COM was designed to simplify exactly this
task, creating a publicly offered component as a COM
object is an obvious way to do it. However there are
drawbacks to development using COM.
Firstly COM technology exists only on Windows,
and secondly it has a steep learning curve - which
means that you need to learn many things before you
can begin to develop your public component.
Also if you are only developing a simple component
COM may be overkill.
Consequently a developer may avoid the COM route,
choosing to implement his component as a simple DLL.
When you create a new DLL you need to decide:
- what functions you want it to export;
- what arguments the functions will have;
- how you want to transfer data, and other issues.
Taken together, we call this the API of your DLL (it is

also termed a protocol or contract).


The API (Application Programming Interface) is a set of
rules (the contract), which you as developer of the DLL
and the user (as caller of your DLL) agree to follow in
order to understand each other and to interact
successfully.
All DLL services are provided only under this contract.

You do not have to do anything beyond distributing a .tlb


file (which can also be embedded in the .dll itself).
If the 'foreign' language can work with COM,
then it can import information from the TLB and generate
appropriate header files for itself.
A TLB file is a binary file which has been created and
edited by a TLB editor (or generated by an IDE). It is also
possible to "compile" a TLB file from a text description
- an IDL file (.idl or .ridl).
If you are smart enough, you can "borrow" this feature
from COM.
Your documentation is a textual description of the DLL
API written by the DLL developer for other developers
who will use the DLL (i.e. it is one-sided).
Of course, you can also document internal details for
yourself; but that is not the concern of this article.
So, you should provide documentation which at least
contains a formal description of the API, listing of all the
functions, methods, interfaces, and data types, along with
explanations of "how" and "why" (the so-called Reference).
Additionally, the documentation may include an
informal description of the code-development process
(a guide, a how-to, etc.). In the simplest cases, the
documentation is written directly in the header files as
comments, but more often it is provided as a separate file
(or files) in chm, html, or pdf format.

Unwritten rules
(This section is based on
http://blogs.msdn.com/b/oldnewthing/archive/
2006/03/20/555511.aspx )
There are some basic ground rules that apply to all
programming, which are so obvious that most
documentation and books do not bother explaining them
(because these rules should have been internalized by
practitioners of the art to the point where they don't need to be
expressed).
A driver planning what route to take wouldn't even
e assume you are developing a public DLL.
consider taking a shortcut through somebody's backyard or
So you will have a .dll file, you will have the
going the wrong way down a one-way street.
header files (at least *.h and *.pas), and you will
In the same way that an experienced chess player doesn't
have documentation.
even consider illegal options when deciding his next move,
The header files (or headers) form a set of source files
containing structure and function declarations used in the an experienced programmer doesn't even consider
violating the following basic rules without explicit
API for your DLL.
permission in the documentation which allows
Typically they contain no implementation.
contravening the rule:
The headers should be available in several languages.
Everything not defined is undefined.
As a rule, this means the language used to create the DLL
This may be a tautology, but it is a useful one.
(in our case - Delphi), C (as standard) and perhaps additional
Many of the rules below are just special cases
languages (such as Basic, etc.).
of this rule.
All these header files are equivalent to each other,
All parameters must be valid.
representing only translation of the API from one language
The contract for a function applies only when the caller
to another.
adheres to the conditions, and one of the conditions is
The more languages you include the better.
that the parameters are actually what they claim to be.
If you don't provide header files for a particular language,
This is a special case of the "everything not defined is
then developers using that language will be unable to use
undefined" rule.
your DLL, (unless they are able to translate your header files
o Pointers are not nil unless explicitly
from a language you provide (Delphi or C) into their language).
permitted otherwise.
This means that failing to offer headers for a particular
o Pointers actually point to what they
language is usually a big enough obstacle that developers
purport to point to.
in that language will not use your DLL, although it is not
If a function accepts a pointer to a
an absolute block to such usage. From this perspective
CRITICAL_SECTION, then you must pass a pointer
COM looks more attractive (the API description is stored in
which points to a valid CRITICAL_SECTION.
type libraries in the universal TLB format).
This article describes typical mistakes,
features and pitfalls developers encounter
as they develop a public DLL API. In
general, this article serves as a kind of
check list for you. You can compare your
code with this list and find out how good it
is, and if it contains any of the mistakes
typically found in such DLLs.

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 1)


o

Pointers must be properly aligned.


Pointer alignment is a fundamental
architectural requirement, yet something many
people overlook, having been pampered by a
processor architecture that is very forgiving of
alignment errors.
The caller has the right to use the
memory being pointed to.
This means no pointers to memory
that has been freed or to memory that the caller
does not have control over.
All buffers are as big as the declared
(or implied) size.
If you pass a pointer to a buffer and say that it
is ten bytes in length, then the buffer really
needs to be ten bytes in length (or more).
Handles refer to valid objects that
have not been destroyed.
If a function wants a window handle,
then you must pass a valid window handle.

All parameters are stable.


o You cannot change a parameter
while the function call is in progress.
o If you pass a pointer, the pointed-to memory
will not be modified by another thread for the
duration of the call.
o You cannot free the pointed-to memory either.

The correct number of parameters is passed with the


correct calling convention.
This is another special case of the "everything not
defined is undefined" rule.
o Thank goodness, modern compilers
refuse to pass the wrong number of parameters,
though you would be surprised how many
people manage to sneak the wrong number of
parameters past the compiler anyway,
usually by devious casting.
o When invoking a method on an object,
the Self parameter is the object. Again,
this is something modern compilers handle
automatically, though people using COM
from C (and yes they exist) have to pass
the Self parameter manually,
and occasionally they mess up.

Function parameter lifetime:


o The called function can use the parameters
during the execution of the function.
o The called function cannot use the parameters
once the function has returned. Of course, if the
caller and the callee have agreed on a means of
extending the lifetime,
then those agreed rules apply.

The lifetime of a parameter that is a


pointer to a COM object can be extended
bythe use of the IUnknown.AddRef
method.

Many functions are passed parameters


with the express intent that they be used
after the function returns.
It is then the caller's responsibility
to ensure that the lifetime of the parameter
is at least as long as the function needs it.
For example, if you register a callback
function, then the callback function needs
to be valid until you deregister
the callback function.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Input buffers:
o A function is permitted to read from the full extent
of the buffer provided by the caller, even if the
entire buffer is not required to determine the result.
Output buffers:
o An output buffer cannot overlap an input buffer
or another output buffer.
o A function is permitted to write to the full extent
of the buffer provided by the caller, even if not
all of the buffer is required to hold the result.
o If a function needs only part of a buffer to hold
the result of a function call, the contents of
the unused portion of the buffer are undefined.
o If a function fails and the documentation does not
specify the buffer contents on failure,
then the contents of the output buffer are
undefined.
This is a special case of the
"everything not defined is undefined" rule.
o Note that COM imposes its own rules
on output buffers. COM requires that all output
buffers be in a marshallable state even on failure.
For objects that require nontrivial marshalling
(interface pointers and BSTR/WideStrings being the
most common examples), this means that the output
pointer must be nil on failure.

(Remember, every statement here is a basic ground rule, not an


absolute inescapable fact. Assume every sentence here is prefaced
with "In the absence of indications to the contrary". If the caller
and callee have agreed on an exception to the rule, then that
exception applies.)
Error:
Providing no dedicated initialize
and finalize functions
The first mistake you can make as an API developer is not
to provide standalone functions to initialize and finalize
your DLL, but instead use the DLL_PROCESS_ATTACH
and DLL_PROCESS_DETACH notifications from the
DllMain callback-function.
DllMain is a special function in the DLL,
called automatically by the system in response to certain
events. Among those events are DLL_PROCESS_ATTACH
and DLL_PROCESS_DETACH
- these are notifications about the loading and unloading of
your DLL.
If you are using Delphi, then the initialization section
and the finalization section of Pascal units in the DLL are
executed in response to DLL_PROCESS_ATTACH
and DLL_PROCESS_DETACH, which the system sends to
the DllMain function of your DLL.
Of course, you do not see this process, it is happening
under the hood of the RTL (language support library).
You just see that when the DLL is loaded, all units are
initialized, and when it is unloaded, they are finalized.

COMPONENTS
DEVELOPERS

Designing an API: common mistakes (Continuation 2)


What is the problem here?
DllMain is no ordinary callback-function. It is invoked by a
very specific point (
http://blogs.msdn.com/b/oleglv/archive/2003
/10/24/56141.aspx ).
There are very few actions that you can take in DllMain
with guaranteed safety.
For example, loading libraries, starting threads,
thread synchronization, COM calls, even calls to other
libraries (except kernel32) performing any of these actions
inside DllMain may lead to blocking (deadlock/hang/freeze).
When you realize that developers usually do not
consider whether the code they put in the initialization
and finalization sections of their units is truly admissible
there, you also realize that relying on such behaviour
(automatic initialization of units from DllMain) is not the best
design solution.
That's why your DLL must export the two functions
like Init and Done, which will contain the actions that you
otherwise would have inserted in the initialization and
finalization sections.
Whoever loads your DLL should immediately import
and call the Init function.
Later he should also call Done just before unloading your
DLL.
Error:
Using ancient techniques for memory
management and error handling
In any API, there are two very important aspects:
how you pass data of arbitrary (dynamic) size, and how
you report any errors from API function calls.
A typical WinAPI function contains this logic:
The caller must call a function with
lpData = nil and cbSize = 0, whereupon the
function returns an ERROR_INSUFFICIENT_BUFFER
error, and cbSize will contain the amount of memory in
bytes required to store all the data.
The caller can then allocate sufficient memory and call
the function again, passing to lpData a pointer to a block
of data, and passing the size of the block to cbSize.
This approach is complex in itself (calling the function twice
instead of once), and imagine how it would work for
frequently changing data.
What happens if the data increases in size between
your two calls? Then the second call will return an
ERROR_INSUFFICIENT_BUFFER error again, and again
you will need to re-allocate more memory and repeat
the call. That is - to reliably call the function - you have to
write a loop.
Why do most of the WinAPI functions use such a
terribly complicated method? Answer: history.
When these functions were first designed there were no
modern de-facto standards; moreover the Windows
developers sacrificed convenience for the sake of microoptimizations
http://blogs.msdn.com/b/oldnewthing/
archive/2004/08/23/218837.aspx
Similarly, a typical WinAPI function reports that it
...returns True on success and False on failure.
The cause of the error can be obtained by calling
GetLastError.

10

COMPONENTS
DEVELOPERS

and some of the functions even report:


...in case of success, the function returns the size
of the data, or 0 if there is no data. In the case of error,
the function returns 0. To determine the exact cause,
you should call GetLastError,
which returns ERROR_SUCCESS for a successful call,
or an error code.
What a nightmare!
Once again, we encounter two function calls rather than
one, not to mention the complexity of the extensibility (to
use our own error codes) and the inability to receive more
context data about the error.
Accordingly, many people when designing their own API
see how it is done in the operating system and imagine
they should do the same. "Well, if that is how
Microsoft does it, then we will copy them
because (perhaps) it is correct and necessary
to do it that way."
However, they fail to realize that today's WinAPI was
created a long, long time ago.
A huge number of functions began their life in 16-bit code.
These functions were designed for requirements that
simply do not exist today.
There is no need to use such ancient and uncomfortable
approaches. Instead use a modern approach.
Here (in descending order of preference) is how you can
transfer varyingly sized data without sacrificing the
convenience of a call:

[Special case, only for strings] BSTR/WideString.

The interface with the lpData and cbSize properties.

A DLL can export a function for freeing memory


which the DLL itself allocated.

You can use system memory management


(most often: HeapAlloc/HeapFree).
Here is a list for error handling (in descending order of
preference):

COM style: HRESULT + ICreateErrorInfo.


Delphi may additionally take advantage of
the "magic" safecall call model.

The function returns HRESULT.

Functions return a sign of success/failure, the error


code is obtained from GetLastError.

Similar to the previous item, but implementing your


own error code function.
Moreover, there is no need to use the naked functions because today we have interfaces. Interfaces offer solid
advantages:

Automatic memory management


- no problems with the allocation / release.

Delphi gives you a safecall calling convention


and virtual methods
- for customizing the compiler "magic".

Simplified versioning, because an interface


can be uniquely identified by a GUID (aka IID).

Data is grouped with methods for handling this data.

Performance does not suffer (a lot).

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 3)


Error:
Using language-specific data types
or other language-specific constructs
Obviously if you create an API to be used from various
programs each written in a different language, then you
cannot use structures that exist only in your language.
For example, string (as well as AnsiString, UnicodeString,
ShortString, String[number]), array of (dynamic and open
arrays), TObject (i.e. any objects), TForm (and components),
etc.
If you use a data type (or language construct) that has no
counterpart in other languages, then code in this very
different language simply will not be able to use your API.
It would have to emulate the language constructs of your
language.
So, what can be used in an API? The following:
integer types
(Integer, Cardinal, Int64, UInt64, NativeInt, NativeUInt,
Byte, Word, etc. - with the exception of Currency);
real types
(Single and Double - except Extended, Real, Real48
and Comp);
static arrays
(array[number .. number] of) of the acceptable types;
set, enumerated and subrange-types
(with some reservations - see below;
it is preferable to replace them with integer types);
character types
(AnsiChar and WideChar, but not Char);
strings
(only in the form of WideString; strings
as PWideChar - allowed, but not recommended;
PAnsiChar is valid only for ASCII-strings;
PChar strictly prohibited;
ANSI-string is prohibited);
Boolean type
(BOOL, but not Boolean; ByteBool, WordBool and
LongBool are acceptable, but not recommended);
interfaces which use and operate
with acceptable types only;
records with fields of acceptable types;
pointers to data of acceptable types;
untyped pointers;
data types from the Winapi.Windows.pas unit
(or a similar unit for non-Windows platforms);
data types from other system headers
(they are located in the \Source\ RTL\Win of your Delphi;
replace "Win" path with OSX, iOS, etc.
- for other platforms).
Error:
Using a shared memory manager
and/or packages
Any shared memory manager
(such as ShareMem, FastShareMem, SimpleShareMem, etc.)
is a language-specific facility which does not exist in other
languages. So (as in the previous section) you should never
use any of them in your API. The same applies to run-time
packages (.bpl packages). This package concept exists only
in Delphi (and C++ Builder).

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

What is the purpose of a shared memory manager?


In a sense, a shared memory manager is a "hack".
It is a quick and dirty way to solve the problem of
exchanging dynamic data between modules.
Never use a shared memory manager at the beginning of
a new DLL project.
A shared memory manager is a means of backward
compatibility, but does not fit with new code.
If you need to exchange objects (including exceptions),
strings or other language-specific constructs you have to
use BPL-packages, not DLLs.
If you have used a DLL, then you must not use Delphispecific types and, therefore, must not use the shared
memory manager.
Hence the comment at the beginning of the earlier section
about the forbidden use of UnicodeStrings (and so on) in
DLLs.
You can easily transfer data of varying size if you follow
the guidance above; and you already know you should not
use Delphi-specific types in a DLL.
Therefore, there is no need to use a shared memory
manager at all (either alone or using run-time packages).
Error:
Failing to protect each exported function with
a try/except block
The explanation above should have made it clear that you
cannot pass any objects between modules.
An exception is also an object (in Delphi).
Adding two plus two, we realize that you cannot throw an
exception in one module and then catch it in another.
The other module has no idea how to work with an object
that was created in a different programming language.
This means, of course, that you have to think carefully how
you will report errors (as mentioned above). Here I am
talking only about the particular case of an exception.
Because an exception cannot (that is: should not) leave the
module, you must implement this rule religiously: put a
try/except block to catch all exceptions around the code of
each exported function.
Note:

The function can be exported explicitly (exports


function-name) or implicitly (e.g. as a callbackfunction or other function which returns a pointer to
the calling code).
Both options must be wrapped in a try/except block.

"Catch all exceptions," of course, does not mean that


you wrap the function in an empty try/except block.
You must not turn off all exceptions. You have to
catch them (yes, catch them all) and transform rather
than suppress them.
You must convert each exception to something
prescribed by your protocol (perhaps an error code, an
HRESULT, or whatever).
Note also that if you use the method recommended above
(interfaces with safecall) then this issue is automatically
covered for you. The safecall calling convention assures
you that every method you write will be wrapped by a
hidden try-except block through compiler "magic";
and no exception escapes your module.

COMPONENTS
DEVELOPERS

11

Designing an API: common mistakes (Continuation 4)


Error:
Using ANSI-encoding(s)
Support for Unicode appeared in the Windows API in 1996
(in Windows NT 4), and in 2000 Unicode support came to
the client OS (Windows 2000).
Although Windows 95 did contain Unicode functions,
there was no full support.
The mobile OS market was Unicode only from the start
(Windows CE, 1996) through the PocketPC,
Windows Mobile, and up to Windows Phone - all these
systems support exclusively Unicode, but not ANSI.
That is, for more than 13 years Unicode has been the
default choice in all Windows versions.
For more than 13 years the ANSI API Windows functions
have been nothing more than stubs that do nothing beyond
converting the string encoding before invoking Unicodevariants of themselves.
Support for Unicode in Delphi has been present since
Delphi 3 - as part of the COM support (that is from 1997).
Although until 2008 (Delphi 2009), the entire language
support library (RTL) and the component library (VCL)
worked in ANSI.
However, in spite of the wide open opportunity to use
Unicode when constructing their own APIs most Delphi
developers even since 1997 (over 16 years), have not
hesitated to use the "familiar types" - that is, at best a PChar
(equivalent to PAnsiChar on the systems of that time), and at
worst a string with the shared memory manager.
Of course, those who were smart used PAnsiChar,
and PWideChar (since 2000).
But WideString was hardly used - despite its undeniable
advantages: there are no problems with the exchange of
strings between modules, auto-conversion to string and
back again, built-in support for Unicode, built-in pointer
length.
Why the avoidance of WideString?
Probably because PWideChar is sufficient for easy transfer
of data inside the called function, and returning data from
called functions was required much less frequently.
To sum up:
always use Unicode in your API for strings - even if you
are using Delphi 7 and work with ANSI-strings inside:
it does not matter.
In 2013, the API must be Unicode. It is not 1995.

Gotcha:
Enumerated types
An enumerated type is a convenient way to declare casetypes (rather than using integers). What is the problem here?
Look at this code:
type
TTest = (T1, T2, T3);
var
T: TTest;

Question:
What is the size of the variable T in bytes?
This is an important question because size affects the
position of the fields in records, when passing arguments
to functions, and in much other code.
The answer is that in general you do not know the size.
It depends on the compiler and its settings. By default, it is
1 byte for Delphi. However, another compiler and/or
setting may result in 4 bytes.
Here is another question: since this type occupies 1 byte in
Delphi, it can hold up to 255 values.
But what if your enumerated type has more than 255
values?
Answer: then the variable will occupy 2 bytes.
Do you see where this is leading?
Suppose you have used 50 values in version 1 of your
DLL, so the fields of this type occupied 1 byte.
In version 2 you have extended the possible values up to
300 - and the field now occupies 2 bytes.
It would not matter if we used this type only inside our
own code.
But since you are sharing it with other code, such a change
in the size of the data will be a complete surprise to the
other code.
Overwriting (data corruption) and Access
Violations are the result.
Note:
in fact the problem arises even with far less than 300
elements. It is sufficient that the type has a 300-th element:
type
TTest = (T1, T2, T3, T4 = 300);
var
T: TTest;
begin
WriteLn(SizeOf(T)); // shows 2

Well, what about the ANSI-adapters (stubs) to Unicodefunctions: are they necessary? No. Remember why they are
You can solve this problem in two ways:
there in the WinAPI: as a means of backward compatibility
1. You can place the compiler directive {$Z4}
for legacy code which is not familiar with Unicode.
(or {$MINENUMSIZE 4}) at the beginning of each
This era ended in 2000 with the release of Windows 2000.
header file.
There is no need to create a means of
This will cause the compiler to make every enumerated
backward compatibility for something which
type 4 bytes in size, even if you do not need so much.
never existed in the first place.
No code was using your 2013 API functions with ANSI
2. You can use LongWord (or Cardinal) and a
so there is no need to add ANSI-adapters to your API.
set of numeric constants (const T1 = 0).
Note that if you are using the recommendations in the
preceding paragraphs you have already covered this issue.
By now, you should be using WideString strings (aka BSTR) to pass string data between modules;
or, in extreme cases, PWideChar.
You should not use PChar and PAnsiChar.

12

COMPONENTS
DEVELOPERS

It is possible that the second method is preferable because


it clearly answers the question:
What are the numerical values of the type's elements?

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 5)


Gotcha:
Records
The uncertainty about the size of enumerated types also
applies to records:
type
TTestRec = record
A: Byte;
B: Int64;
end;

What is the size of this record? 9? 10? 12? 16?


The amount of unused space between fields (filler bytes)
also depends on the compiler and its settings.
Overall, I would recommend using interfaces instead of
records when possible.
If you need to use a record:
either insert the directive {$A8} ({$ALIGN 8}) to the
beginning of each header file, or use the keyword packed
for the record.
The latter may be preferable - because the alignment rules
in Delphi might be different from the alignment rules in
another language (for instance consider the case of problems
similar to this bug: http://qc.embarcadero.com/wc/
qcmain.aspx? d = 75838 ).

For this purpose, a callback function is provided with a socalled user-argument: either a pointer or an integer (such as
Native(U)Int, but not (U)Int), which are not used by the API
itself and transparently passed directly to the callbackfunction.
Or (in rare cases), it can be any value that uniquely identifies
the callback function call.
For example, the system function SetTimer has idEvent,
while EnumWindows function has lpData.
These parameters are not used by the functions and are
simply passed directly to the callback-function unchanged.
That is why we can use these parameters to pass arbitrary
data.
If you do not implement user-parameters in your API, then
the calling code cannot associate a callback-function with
the data.
We will look at this issue in more detail in the next article.
Gotcha:
Mixing manual and automatic control
of an entity's lifetime
In general, you should try to use automatic control of a
lifetime.
You have less chance to screw up, because the compiler
emits code to keep track of your objects and there is less
(fallible) human work to do.
But in any case, there will always be places where you
want manual control of a lifetime.
The junction of these two control mechanisms is what can
cause problems.

Gotcha:
Sets
We can ask the same question about sets:
how many bytes do they occupy?
Look at this code:
Here, however, everything is more complicated,
type
because a set can take up to 32 bytes,
ISomething = interface
and there is no compiler directive to control the size of sets.
procedure DoSomething;
end;
Overall, the set is a syntactic convenience for dealing with
flags. So instead of sets you can use an integer type (again:
TSomething = class(TComponent, ISomething)
LongWord or Cardinal) and a set of numeric constants,
procedure DoSomething;
combining them with OR for inclusion in the "set" and
end;
checking their presence in the "set" with AND.
Error:
Failing to provide user-arguments
in callback-functions
A callback-function is a piece of executable code that is
passed as an argument to other code, which is expected to
call back (execute) the argument at some convenient time.
For example, if you want to find all the windows on your
desktop, you can use EnumWindows:
function MyEnumFunc (Wnd: HWND; lpData: LPARAM):
Bool; stdcall;
begin
/ / This is called once for each window in the system
end;
procedure TForm1.Button1Click (Sender: TObject);
begin
EnumWindows (@MyEnumFunc, 0);
end;

Since the callback function normally performs the same


task as the code that sets it, it turns out that the two pieces
of code are working with the same data.
Consequently, the data from the code setting the callback
must somehow be passed to the callback function.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

var
Comp: TSomething;
function GetSomething: ISomething;
begin
Result := Comp;
end;
begin
Comp := TSomething.Create(nil);
try
GetSomething.DoSomething;
finally
FreeAndNil(Comp);
end;
end;

As you know, TComponent does not use automatic


reference counting and you must control its lifetime
manually.
The problem in the above code is in the line
GetSomething.DoSomething.
A temporary (hidden) variable of the interface type is
created (for storing the result of the GetSomething call),
which is cleared in the last line (at end;) - after the object
has been released.

COMPONENTS
DEVELOPERS

13

Designing an API: common mistakes (Continuation 6)


Of course, this will never invoke the destructor for
TComponent (TComponent uses manual control and does not
respond to reference counting), but cleaning up is still
necessary. Clean reference counting means calling the
_Release method a method of an already deleted object.
Which will lead to an Access Violation.
Note:
Access Violations are not always raised for such errors
due to the caching behaviour of the memory manager.
Which makes such errors especially dangerous.
A similar problem can be seen in this code:
begin
Lib := LoadLibrary(...);
Win32Check(Lib <> 0);
try
Func := GetProcAddress(Lib, ...);
Intf := Func(...);
// ... some action with Intf
Intf := nil;
finally
FreeLibrary(Lib);
end;
end;

Between the LoadLibrary and FreeLibrary calls there


may be temporary variables created that hold references to
interfaces from the DLL.
Therefore, even if we have cleared all clearly visible
references before unloading DLL, hidden variables will be
released after unloading DLL and thus will call already
unloaded code (hello, another Access Violation).
Of course, we (the developers) do not have an eagle eye to
find all the places where the compiler wants to create
hidden variables, so that we can convert such hidden
variables into explicit variables and explicitly free them.
Let me remind you that the solution would be to use the
fact that all temporary hidden variables are released at the
time of exit from the procedure.
Therefore, we must clearly distinguish between code that
works with manual and automatic control:
procedure DoDangerousStuff(Comp: TComp);
begin
// ... some action with the Comp,
including the use of types with automatic control
end;
begin
Comp := TSomething.Create(nil);
try
DoDangerousStuff(Comp);
finally
FreeAndNil(Comp);
end;
end;
procedure DoDangerousStuff(Lib: HMODULE);
begin
// ... some action with Lib,
including the use of types with automatic control
end;
begin
Lib := LoadLibrary(...);
Win32Check(Lib <> 0);
try
DoDangerousStuff(Lib);
finally
FreeLibrary(Lib);
end;
end;

14

COMPONENTS
DEVELOPERS

Gotcha:
Identifying interfaces
Interfaces are different from other data types.
They have two levels of identification.
On the programming language level an interface is
identified by an identifier name (such as IUnknown,
IApplication etc.) and is no different in this aspect from
any other data type in Delphi.
Two interfaces with the same declaration but with different
type identifiers are considered to be different data types by
the compiler.
On the other hand, the interfaces may also be identified not
only at the level of programming language, but at run-time
(by the machine code) via meta-information: the
interface's GUID (IID). Two completely different
declarations, but with the same IID will be considered to be
identical by run-time machine code.
Gotcha: The immutability of interfaces
Once you have published an interface ("published"
means you publicly release a version of the DLL with this
interface definition), you cannot change it (either its IID or its
structure) because the interface is used by third party code.
Changing the interface declaration will break the other
(third party's) code.
Instead of changing the interface, you need to create a new
interface with a new GUID. You should create a new
independent interface (preferably and usually) or inherit
from your old interface (allowed).
Gotcha:
Expanding interfaces
Look at this code:
type
IColorInfo = interface
{ABC}
function GetBackgroundColor: TColorRef;
safecall;
...
end;
IGraphicImage = interface
{XYZ}
...
function GetColorInfo: IColorInfo; safecall;
end;

Suppose you want to add a new method to interface


IColorInfo:
type
IColorInfo = interface
{DEF} // <- new GUID
function GetBackgroundColor: TColorRef;
safecall;
...
procedure AdjustColor(const clrOld,
clrNew: TColorRef); safecall; // <- new method
end;
IGraphicImage = interface
{XYZ}
...
function GetColorInfo: IColorInfo; safecall;
end;

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 7)


You have changed the interface, but you also have
changed the IID, so everything should be OK, right?
Actually - no.
The IGraphicImage interface depends on the
IColorInfo interface. When you change the
IColorInfo interface, you implicitly changed the
IGraphicImage.GetColorInfo method
- because its return value has now changed to become
another: IColorInfo interface version v2.0.
Look at the following code, written with headers v2.0:
procedure AdjustGraphicColorInfo (pgi:
IGraphicImage; const clrOld, clrNew: TColorRef);
var
pci: IColorInfo;
begin
pci: = pgi.GetColorCount (pci);
pci.AdjustColor (clrOld, clrNew);
end;

If this code is run on v1.0, the call


IGraphicImage.GetColorCount returns IColorInfo
version v1.0, and this version has no
IColorInfo.AdjustColor method.
But you still call it.
Result: you skip to the end of the method table and call the
trash that lies behind it.
Quick fix - change IID for IGraphicImage,
to take account of changes in IColorInfo:
type
IGraphicImage = interface

{UVW} // <- a new GUID


...
function GetColorInfo: IColorInfo;
safecall;
end;

This code update path is very time-consuming because you


have to keep track of all references to the variable interface.
Moreover, you cannot just change the GUID
- you have to create a second interface IGraphicImage
with a new GUID and manage the two interfaces (even
though they are identical up to the return value).
When you have several of these changes and the use of a
large tree, the situation quickly gets out of control with
endless cloning of interfaces for every sneeze.
We will look at the correct solution to this problem in the
next paragraph.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Gotcha:
Function return values
Functions or methods that return an interface (as in the
previous paragraph) present a problem for the extension.
Of course, in the beginning it is a convenient solution:
you can call the function "normally" and even hook them
into chains like this:
Control.GetPicture.GetImage.GetColorInfo.GetBackgroundColor

However, this state of affairs will exist only in the very first
version of the system. As you begin to develop the system,
you will begin to create new methods and new interfaces.
In the not too distant future you'll have plenty of advanced
interfaces; and base interfaces that were originally in the
program, at the time of its birth will provide only trivially
uninteresting functions.
Overall, very often the caller will need the newer interfaces
rather than the original ones.
What does this mean? It means that almost all the code has to
call the original function to get the original interface, and then
request a new one (via Supports/QueryInterface) and only then
use the new interface.
The result is not so comfortable, and even more uncomfortable
is the fact we now have a triple calls (original/old + conversion +
desired/new).
Let us look again at the previous point:
the modification of one interface makes it necessary to make
copies of all the interfaces that use it as a return value
- even if they themselves do not change.
The best solution for both cases is that the callee code indicates
to the called function which interface it wants
- the new or the old.
This can be done, of course, by specifying the IID:
type
IGraphicImage = interface
{XYZ}
...
procedure GetColorInfo
(const AIID: TGUID; out AColorInfo);
safecall;
end;

Note that now you cannot use the result of the function, as the
result has to have a specific type (of course it does not have it we should return interfaces of different types), that's why we use
the raw data output parameter.
Then, you can write code like this:
var
Image: IGraphicImage;
ColorInfo: IColorInfoV1;
begin
...
Image.GetColorInfo(IColorInfoV1, ColorInfo);
Color := ColorInfo.GetBackgroundColor;
...
var
Image: IGraphicImage;
ColorInfo: IColorInfoV2;
begin
...
Image.GetColorInfo(IColorInfoV2, ColorInfo); //
throw a "no interface" exception, if you run on the V1
ColorInfo.AdjustColor(OldColor, NewColor);
...

COMPONENTS
DEVELOPERS

15

Designing an API: common mistakes (Continuation 8)


You do not need to work directly with the IID: Delphi will
automatically substitute the IID for the interface name.
Error:
Returning complex types via Result
A good, general rule of thumb is this:
if you need to return something more complex than an
integer type (including auto-managed types: interfaces and
WideString) then you should always use the
out-parameter instead of the result of the function.
Then you avoid bugs in Delphi like this:
http://qc.embarcadero.com/wc/qcmain.aspx?d=75838,

Error:
Interfaces without an IID
Every interface in your API must be declared with a GUID
(interface identifier - IID).
You may be tempted to skip the IID for interfaces that are
returned from functions explicitly, without request by IID.
But, as we saw above, you need to design your API in such
way that you have no functions that return an interface via
Result - because it is extremely difficult to further expand
the system. Therefore, all of your interfaces must always
have an IID.

and it seems to be a similar problem with real data type,


but I could be wrong.
I think that Delphi and MS C++ disagree over which stack
(CPU or Math CPU) should be used to return a real result
from the function, but I am not 100% sure about this,
since I failed to find a link to the bug report.

Error:
Missing interface declarations when declaring
the implementing class
As you probably know already, there are two ways to
implement interfaces in a class:
1. Automatically.
You simply declare
The problem in all these cases is that Delphi and C++ differ
TMyClass = class(the base class, the list of interfaces).
in their interpretation of the calling convention model with
Once you have declared interface support,
regard to returning complex types.
there is nothing more to do.
Delphi's documentation indicates that the following code:
2. Manually.
You override the virtual class method QueryInterface,
function Test: IInterface; stdcall;
analyse the parameters and then construct
is interpreted as:
and return the interface.
function Test: IInterface; stdcall;
while MS C++ literally follows the syntax and returns the
You would think that with the automatic method,
interface directly (EAX for x86-32).
we would surely have no problems; but look at the following
Thus, instead of declaring functions like this (for example): code (the key points are noted in comments):
function Test1: IInterface; stdcall;
function Test2: WideString; stdcall;
function Test3: TSomeRecord; stdcall;

Always use either this form (out can be replaced by var


for better performance):
procedure Test1(out Rslt: IInterface); stdcall;
procedure Test2(out Rslt: WideString); stdcall;
procedure Test3(out Rslt: TSomeRecord); stdcall;

or this one:
function Test1: IInterface; safecall;
function Test2: WideString; safecall;
function Test3: TSomeRecord; safecall;

The latter is valid for the simple reason that such code is
equivalent to:
function Test1(out Rslt: IInterface): HRESULT;
stdcall;
function Test2(out Rslt: WideString): HRESULT;
stdcall;
function Test3(out Rslt: TSomeRecord): HRESULT;
stdcall;

Please note that in our case we have removed this problem,


since we agreed to use the safecall calling convention.
However, what was said in the previous paragraph is still
in force: in terms of versioning interfaces, it is better to use
constructs like:

type
ISomeInterfaceV1 = interface
['{A80A78ED-5836-49C4-B6C2-11F531103FE7}']
procedure A;
end;
ISomeInterfaceV2 = interface(ISomeInterfaceV1)
// ISomeInterfaceV2 inherited from ISomeInterfaceV1
['{EBDD52A1-489B-4564-998E-09FCCF923F48}']
procedure B;
end;
TObj = class(TInterfacedObject, ISomeInterfaceV2)
// List ISomeInterfaceV2, but not ISomeInterfaceV1
protected
procedure A;
// necessary because object implements ISomeInterfaceV1.
Otherwise - a compilation error
procedure B;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
SI1: ISomeInterfaceV1;
SI2: ISomeInterfaceV2;
begin
SI2: = TObj.Create;
Supports(SI2, ISomeInterfaceV1, SI1);
Assert(Assigned (SI1));
// Fails, SI1 = nil (Supports call returned False)
SI1.A;
end;

It turns out that even though the object implements the


interface it does not tell "outside" that it implements it.

procedure Test1(const IID: TGUID; out Rslt);


safecall;

16

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 9)


That is, if two interfaces are connected by inheritance,
the mere inclusion of the child interface into the list of
interfaces implemented by a class does not ensure the
inclusion of an ancestor/parent interface in this list.
In other words, in order to be implemented automatically
by a class, you must ensure that this interface has appeared
at least once in the line "list interfaces" for the class (not
necessarily in this class, it can be in an ancestor, but it must
appear somewhere).
The presence of a child interface is not enough.
Note that the code:
type
ISomeInterface = interface
['{A80A78ED-5836-49C4-B6C2-11F531103FE7}']
procedure A;
end;
IAnotherInterface = interface
['{EBDD52A1-489B-4564-998E-09FCCF923F48}']
procedure B;
end;
TObj1 = class(TInterfacedObject, ISomeInterface)
protected
procedure A;
end;
TObj2 = class(TObj1, IAnotherInterface)
protected
procedure B;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
SI1: ISomeInterface;
SI2: IAnotherInterface;
begin
SI2 := TObj2.Create;
Supports(SI2, ISomeInterface, SI1);
Assert(Assigned(SI1));
SI1.A;
end;

and
type
ISomeInterfaceV1 = interface
['{A80A78ED-5836-49C4-B6C2-11F531103FE7}']
procedure A;
end;
ISomeInterfaceV2 = interface (ISomeInterfaceV1)
['{EBDD52A1-489B-4564-998E-09FCCF923F48}']
procedure B;
end;

Gotcha:
Polymorphism and implementation
of interfaces
When your object descends from a class its polymorphic
behaviour is achieved by virtual means. But when you use
interfaces, all the methods of the interface are already virtual (by
definition).
Therefore, there is no need to use virtual methods to implement
interfaces (though virtual methods may be required for other reasons
- for example, to inherit functionality).
For example:
type
ISomeInterfaceV1 = interface
['{C25F72B0-0BC9-470D-8F43-6F331473C83C}']
procedure A;
procedure B;
end;
TObj1 = class(TInterfacedObject, ISomeInterfaceV1)
protected
procedure A;
procedure B;
end;
TObj2 = class(TObj1, ISomeInterfaceV1)
protected
procedure B;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
SI: ISomeInterfaceV1;
begin
SI := TObj2.Create;
SI.A; // calls TObj1.A
SI.B; // calls TObj2.B
end;

Note that specifying ISomeInterfaceV1 for TObj2 means


that the method TObj2.B will implement ISomeInterfaceV1.B.
The key point here is - just specify the interface. Please note
that:
Method B does not have to be virtual
ISomeInterfaceV1 interface for TObj2 is assembled
"piece by piece": the method B is taken from the TObj2,
but the method A is taken from TObj1. This is a standard way
of working with interfaces and class inheritance.
However, as has been said, sometimes you may
want to use this code:

TObj1 = class (TInterfacedObject, ISomeInterfaceV1)


protected
type
procedure A;
ISomeInterfaceV1 = interface
end;
['{C25F72B0-0BC9-470D-8F43-6F331473C83C}']
procedure A;
TObj2 = class (TObj1, ISomeInterfaceV2)
procedure B;
protected
procedure B;
end;
end;
TObj1 = class(TInterfacedObject, ISomeInterfaceV1)
procedure TForm1.Button1Click (Sender: TObject);
protected
var
procedure A; virtual;
SI1: ISomeInterfaceV1;
procedure B; virtual;
SI2: ISomeInterfaceV2;
end;
begin
SI2 := TObj2.Create;
TObj2 = class (TObj1)
Supports(SI2, ISomeInterfaceV1, SI1);
protected
Assert(Assigned(SI1));
procedure B; override;
SI1.A;
end;
end;

are not a problem.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

17

Designing an API: common mistakes (Continuation 10)


procedure TForm1.Button1Click(Sender: TObject);
var
SI: ISomeInterfaceV1;
begin
SI := TObj2.Create;
SI.A; // calls TObj1.A
SI.B; // calls TObj2.B
end;

Note:
of course, you can "solve" this problem by removing const
from the parameter declaration, but you need to
understand that providing a well-formed argument is the
task of the caller, not the callee.
In general, guided by the rule "Give way to a fool",
I would recommend not to use the const modifier for the
parameters of interface types, and, of course, not to use the
constructor directly when passing arguments to such
functions.

The effect of this code is the same as in previous code


example, but the internal structure will be a bit different.
I will only note that because of this difference, it is highly
recommended to follow this rule: if your object
implements an interface, and this is its only task, then your Error:
code (and third-party code) should not use an object of this
Double freeing of an interface
class - it should use the interface only.
Destructors of classes that implement interfaces are very
fragile methods. If you try to do too much - you may be in
trouble. For example, if your destructor passes a reference
Error:
to itself to other functions, these functions may decide to
Non-obvious feature of reference counting
recall your _AddRef and _Release during their work. Look
(constructor const-parameter)
at this code:
Suppose you have a function/method with an interface
type parameter that is declared as const:
procedure DoSomething(const AArg: ISomething);

and suppose that you pass to the interface the following


argument:
Obj.DoSomething(TSomething.Create);

What will happen?


The const modifier tells the compiler that it should call
_AddRef and _Release on the interface.
On the other hand, we are creating a new object.
What is the reference count of the newly created object?
It is equal to zero.
The counter is incremented by _AddRef when the object is
used (for example, when the interface is assigned to a variable).
We have created an object with the counter set to 0,
and passed it to a method that does not change the
reference count.
As a result, the reference count never drops to 0 (simply
because it never rises from the ground), and, hence,
no destructor of the object is called. As a result,
we get a leak for this object.
The solution is to use a variable:
var
Arg: ISomething;
begin
Arg: = TSomething.Create;
Obj.DoSomething (Arg);
end;

function TMyObject._Release: Integer;


begin
Result: = InterlockedDecrement (FRefCount);
if Result = 0 then
Destroy;
end;
destructor TMyObject.Destroy;
begin
if FNeedSave then
Save;
inherited;
end;

It does not look very scary, does it?


The object just saves itself before it is destroyed.
But the Save method might look something like this:
function TMyObject.Save: HRESULT;
var
spstm: IStream;
spows: IObjectWithSite;
begin
Result := GetSaveStream(spstm);
if SUCCEEDED(hr) then
begin
Supports(spstm, IObjectWithSite, spows);
if Assigned(spows) then
spows.SetSite(Self);
Result := SaveToStream(spstm);
if Assigned(spows) then
spows.SetSite(nil);
end;
end;

By itself, it looks fine. We get a stream and save Self in it,


further establishing the context information (site) - just in
case the stream needs additional information.

The introduction of variable causes the reference count to


change, and eventually results in a call to the destructor,
because now the reference count drops to zero when the
Arg variable goes out of scope.

18

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 11)


But this simple code combined with the fact that it is
running from a destructor, gives us a recipe for disaster.
Look what happens:
1.
2.
3.

4.
5.

6.

The _Release method decrements


the reference count to zero and deletes Self.
The destructor tries to save the object.
The Save method wants to save into
the stream and sets Self as the context.
This grows the reference count from zero to one.
The SaveToStream method saves
the object to the stream.
The Save method clears the thread's context.
This reduces the reference count of our
object back to zero.
Therefore, the _Release method calls
the destructor of the object a second time.

The destruction of the object a second time leads to fullscale chaos. If you are lucky, the crash inside the recursive
destruction will be able to identify its source; but if you are
unlucky, it may cause damage to the heap, which will
remain undetected for some time, after which you'll be
scratching your head.
Therefore, as a minimum, you should insert an Assert call
into your _AddRef method, to ensure that you do not
increase the reference count from zero during the
execution of a destructor:

function TMyObject._AddRef: Integer;


begin
Assert (FRefCount >= 0);
Result := InterlockedIncrement(FRefCount);
end;
function TMyObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
procedure TMyObject.BeforeDestruction;
begin
if RefCount <> 0 then
System.Error(reInvalidPtr);
FRefCount := -1;
end;

Note:
such a check is not present in TInterfacedObject.
TInterfacedObject allows your code to run and call
the destructor twice.
This check will help you to easily catch "cases of
mysterious double calls to the destructor of the object."
But when you identify the problem, then what do you do
with it?
Here's one recipe:
http://blogs.msdn.com/b/oldnewthing/archive
/2005/09/28/474855.aspx

For our subscibers we have a special offer

The new LIB STICK version 2014


has arrived:
all 30 issues on one usb stick,
including the latest version of
Lazarus Portable
and Lazarus for Win / Lin /Mac
Price: 30 + postage 5
Only for subscribers,
otherwise you will have to become a subscriber.
Non subscribers are not eligible
If you take out a
subscription for 1 year or more
- download or printed you can have the subscription
including download and the new
LIB STICK version 2014 (8 GB)
for 50 + extra postage 5 for the stick
The NEW PRINTED version of
Learn to program using Lazarus
is now available: ISBN 978-94-90968-04-5
As Sewn Hardcover: 35 + Postage 22,50
As PDF File 35
also (included) ready for IPad and Android Pad.
for more info see page 24 and 25
The PDF book contains the new
history of computing and a download
of the latest version of Lazarus installer

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

19

http://www.barnsten.com/smashing-deal
Or call: +31 (0)23 542 22 27

20

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Programming with the leap motion By Michal Van Canneyt


on the Mac OS including Update of the code for Linux and Windows
starter

expert

Delphi 7 and later


Lazarus 1 and later

Each of these disk images contain an .pkg file with the


same name, which can be installed by ctrl-click-ing it and
selecting Open (simply double-clicking will not necessarily
work) from the menu that pops up.
This will start an installation of the package. It is best to
install the packages in the order listed here.
If the packages are installed in the default order and on
default locations, a Lazarus application will appear in the
list of applications.

MOTION

When first started, the Lazarus IDE will prompt for the
location of the fpc compiler and the sources. For a default
installation, this is /usr/local/bin/fpc and
/usr/local/share/fpcsrc, respectively.
Once installed and started, the IDE is ready for use.

Using the Leap Motion on Mac OS with Lazarus


Lazarus is a cross platform IDE supporting (among
others) Windows, Linux and Mac OS. The Leap
Motion works on all of these OS-es. While not
initially developed on the Mac, the intention was
that the lazarus components for the Leap Motion
should be usable on all platforms that the Leap
supports, and this includes Mac OS.
Introduction
The Leap Motion works on all major platforms: Windows,
Mac OS and Linux. So does Lazarus. The workings of the
Leap Motion controller on Linux and Windows were easily
verified, as the component was developed on that
platform. To check whether the component also works on
Mac OS X, Lazarus was installed on a Mac (Macbook Pro,
running OS X Lion 10.7.5) and compilation of the leap
component and one of the demo applications is tested.
Installation of Lazarus
While cross-compilation is commonplace these days, there
is nothing like native development.
So, installing Lazarus on the Mac is the firststep. This can
be easily done: From the Lazarus website, 3 disk image files
need to be downloaded:

fpc-2.6.2.intel-macosx.dmg
The Free Pascal compiler.
The IDE calls the free pascal compiler
when it needs to compile code.

Pointables
on the move

fpcsrc-2.6.2-i386-macosx.dmg
The Free Pascal Sources.
The IDE needs this to provide code insight.
lazarus-1.0.14-20131116-i386-macosx.dmg
The actual Lazarus IDE.
The version numbers may change as FPC and Lazarus
evolve.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

21

Programming with the leap motion on the Mac OS


including Update of the code for Linux and Windows (continuation 1)
Under Windows

Magnetism can be expanded


or minimized for
testing purposes
Delay for viewing individual
image changes for testing
purposes

Under Mac

22

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Programming with the leap motion on the Mac OS


including Update of the code for Linux and Windows (continuation 2)
Verifying the LazLeap package
To check whether everything works as it should 3
packages must be installed:
laz_synapse
The low-level TCP/IP socket support on which
the websocket support builds.
bauglirwebsocket
The websocket implementation needed to query
the leap motion service.
lazleap
The actual lazarus component.
These three packages can be downloaded and copied
anywhere: After opening and compiling them, the lazarus
IDE will remember where they are and will correctly
resolve all references to them.
All 3 packages compiled out-of the box, with the exception
of synapse: there the synaser unit gave an error. Since it
is not needed for TC/IP support, it can simply be removed
from the package.
Compiling the fingers and tap demo
After the packages have been compiled, the demo
programs are next. The demo programs
do not really rely on platform-specific things, and indeed,
they compile without a glitch, the running tapdemo is
shown e.g. in figure figure 1 on page 3.
The tap demo has been improved with a new magnetism
setting. The tap movement is very sensitive: while tapping,
the tip of the finger (used to select a button) moves.
The effect may be that the actual button that is under the
finger cursor on the moment the tap gesture is received,
is not the button for which the tap was intended:

The downward movement of the finger


during the tap may switch the focus to the
button below the intended.
To prevent this from happening, a kind of
magnetism is introduced: magnetism shrinks the surface
of a control, making it more difficult to be selected.
It is in fact the number of pixels that the cursor must
be inside the actual border, before the control is selected as
the new focused control.
The focus sticks to the previously selected control,
unless the finger cursor is really centered on the new
control, hence the name magnetism.
This simple trick makes the tap demo a lot more easy to
handle, as some experimenting will confirm.
Conclusion
In the case of the Leap Motion, Lazarus truly lives
up to its motto: Write once, compile anywhere.
After verifying that the leap motion controller
works on all platforms, it is time to start work on
designing some components that help to drive the
user interface with the Leap.

We will develop a complete set of components


usable for Delphi aswel Lazarus.
It will contain the following Gestures
Tapping
(Clicking)
Open Gesture
(a gesture that follows your own design andevents)
Cirkels (2D Swipe)
(Swiping - for quick rotation)
(Dragging for precise movement)
Left to right (or vice versa)
(Swiping - for quick movement with number of lines to
run predefineable)
(Swiping - for precision)
Top or down (or vice versa)
(Swiping - for quick movement with number of lines to
run predefineable)
(Swiping - for precision)
3D Swipe
(Swiping - for quick rotation)
(Dragging for precise movement)
for this one needs to have a 3D Picture
We have already added sound to the gesture.
We want to add sound reaction and (snapping
with fingers) and verbal commands (English). We
will start with this in the coming months...

You can download the latest code from your


subscription page.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

23

PUBLISHED BY

BLAISE PASCAL MAGAZINE


W W W . B L A I S E P A S C A L . E U

D E L P H I, L A Z A R U S, S M A R T M O B I L E S T U D I O
OXYGENE AND PASCAL RELATED LANGUAGES

As well as sharing with the author ... the passion of a few in the programming
community for good documentation..., it is good to see the following comment within the
Foreword section that is applicable to both today's learners and experienced developers,
now that their targets have gone beyond the single Windows Operating System : "A
maturing Lazarus has also helped slowly to convince developers for MacOS, Linux and
more recently Android and other platforms that Pascal can truly serve their needs."
In the opening chapter, the following is quoted to explain the contents and objectives of the book.
"This is a tutorial guide rather than a reference book.
When you've worked your way through the following chapters you should be able to understand how
to use Lazarus to tackle increasingly complex programming challenges, and have learned
not only how to use the Lazarus IDE and the Pascal language, but how to go about finding out what
you still need to learn." Below is the list of chapters, each one containing several topics, and
finishing off with either Review Questions or Review Exercises to keep the reader awake. A couple of
sub-topic lists are also included to show the levels of detail provided within those chapters.
1
2
3

4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20

Starting to program
Lazarus and Pascal
Types, variables, constants and assignments
a.
Pascal types, b. Ordinal types, c. The boolean type,
d.
Enumerated types, e. Type conversion, f. Typecasts,
g.
Variables, h. Initialised variables, i. Assignment: placing a value in a variable,
j.
Extended numerical assignment operators,
k.
Constants and literal values, l. A program example: simple_types,
m. Typed constants, n. Pointers, o. Review Questions
Structured types
Expressions and operators
Pascal statements
Routines: functions and procedures
Class: An elaborate type
Polymorphism
Units, GUI programs and the IDE
Display Controls
a.
TLabel, b. exploring TLabel properties, c. TStaticText,
d.
TBevel and TDividerBevel, e. TListBox, f. TStatusBar,
g.
Further options, h. Review Questions
GUI Edit controls
Lazarus GUI projects
Component Containers
Non-visual GUI Support classes
Files and Errors
Working within known limits
Algorithms and Unit tests
Debugging techniques
Further resources

LEARN TO PROGRAM
Nr Nr
5 /56/ 2013 BLAISE PASCAL MAGAZINE
USING COMPONENTS
LAZARUS
ISBN 978-94-90968-04-5
4
24
96

DEVELOPERS

NEW PRINTED BOOK:


LEARN TO PROGRAM
USING LAZARUS

PUBLISHED BY

BLAISE PASCAL MAGAZINE


W W W . B L A I S E P A S C A L . E U

D E L P H I, L A Z A R U S, S M A R T M O B I L E S T U D I O
OXYGENE AND PASCAL RELATED LANGUAGES

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

25

To 3d Print or Not to 3d print By BJ Rao


Detlef Overbeek, Editor in Chief of Blaise Pascal
Magazine, recently asked the question...
"Can Pascal somehow take part in the desktop
3D printing scene?"

As computational power diversified and


progressively became less expensive it opened up
3D printing from the lab to industry. The PC
presented the means, the technology.

Perception vs Proof

But what built the explosive and wide spread


awareness about 3D printing was not technology
alone. It was actually a simple conversation that
did. A conversation between a few who wanted to
make a particular technology more accessible. They
made the first step and that conversation moved on
to millions. It brought people together and created
an entire community. A mix of technological
development and social bonding. Democratized
development.

The quick answer? That would be a "No".


That train has left the station. Most all software
related elements in that process have been claimed
by others. Not one of them uses Pascal. Its like the
popular kids are having a party and now some of
the other kids want to join in as well. It does not
matter how much technology, ingenuity and party
spirit the other kids may bring. Its not going to
happen.
But that is too easy. No is not an answer. It has
little meaning here. It is too blunt. Moreover, it
actually does not answer the question. It avoids it.
We could think of a million reasons why something
will not work. Typically, we do. But the idea here is
to find an answer about what would work, or at
least what might work. Glass half full, not half
empty.
In addition, as people of science and technology we
don't like to think in terms of yes and no, good and
bad or black and white. Those types of answers are
too polarized. It is a too simplistic way of thinking.
It ignores all the shades of gray. Instead, we tend
to rather observe the properties and patterns of
something. Discover how these might be used to
serve some purpose or task. Creating something
new. We think about truth. If we did not then what
we create won't usually work in the real world. It's
that simple.
But there is also a kind of a problem here. Having a
scientific or technical background means that most
tend to be more inclined to search for technical
solutions as answers. That's fine. Yet, at this point,
these only serve as tactics. Actually, we should be
searching for something else. A strategy is needed.
Something that defines the situation and the
problem so that we may understand what "can" be
done and what "needs" to be done first. The
solution tactics, the technology, will follow later.

The Value of Technology


Technology itself has little value. At least not on its
own. It is still people that get things done.
Pascal, like so many, is simply a language.
Its technologies are the existing compilers,
frameworks, components and IDE's that allow you
to develop in that language. That's what makes it
accessible and applicable. Yet, the actual value and
success of a language resides in and is proportional
to a community that supports it.
Looking back 15 years ago Pascal was at an all time
high. And, for good reason. It made DOS and
Windows development extremely easy, versatile
and fast. More importantly, it was not just building
a community but communities, plural. Apart from
developing business applications it was the
standard in education. It was also the standard on
the DIY (do it yourself) front. It made things clear
and its programing-correct structure was building
good programming practices for all. Unfortunately
for reasons other than its technology it was not
setting standards. Rather, it was forced to follow
the standard settings of others.
Many of the technological benefits of Pascal exist
today. And, a lot more. The Delphi and Lazarus FPC
solutions allow developers to go farther, wider and
deeper than ever before. More than most any other
language.

Pascal is best? There is no such thing as best.


What is best depends on how something fits an
application. Yet, Pascal is making it progressively
less warranted to use something as messy and slow
So what might be the answer?
as java, something as cumbersome and tedious as
In part, Pascal might take part in the 3D printing
scene. But in order to do so, Pascal needs to create xcode and it allows easy entry for Windows
its own party. Claim a location, a platform and build developers to step into linux development.
its own entry point. Something in alignment with its But the problem is not the technology. Its is not a
lack of accessibility nor applicability. It is, typically,
merit that is also worthy of conversation and
a lack of awareness. Pascal is powerful, but people
collaboration. Something that uses technology to
allow accessibility. And, most importantly, build the need to know about it to make it truly powerful.
social bonding between people.
Democratize Development,
Centralize Awareness
How to emphasize this? Elaborate? 3D printing, for
Change has become more of a constant than an
instance, is not new. It has been around for some
exception these days. And, that brings along entry
time. A long time, in fact. Yet, the success of one
points, opportunities. Opportunities for Pascal to
thing is typically the result of the success of
enter into and create its own party.
another thing. For 3D printing it was the PC.

26

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Perception versus proof:To 3d Print or Not to print

The 3D printing scene is not immune to change


either. It is not something static.
New developments are arising all the time as these
technologies diversify and their applications widen,
deepen and demand grows.
Other platforms are also on the rise such as mobile
which may allow new solutions for 3D printing.
Mobile would seem to be an attractive an obvious
choice. Jump on the bandwagon.
A great stepping stone for Pascal and 3D printing.
In many ways that can be argued to be true.
Yet, this is a very frontal approach.
Pascal may make headlines here, even build some
awareness. But there is a lot more involved in
capturing and sustaining a conversation that builds
communities.
There are platforms other than mobile that are
finding a lot of market acceleration.
They are moving fast. More importantly,
these may offer more alignment with Pascal and
a foundation to bring to light its true merit.
This towards having Pascal not only capture but
build and sustain new communities.
Things like the Arduino, BeagleBoard and, in
particular, the Raspberry Pi.

3D Printing
a More Tangible Idea
What does 3D printing have to do with
Pascal? Well...not much really. That is
unless Pascal somehow starts finding
its way into developing solutions here.
3D printing is an exciting and
emerging manufacturing technology.
One that Pascal could take part in.
In terms of software development,
Pascal provides all the solution.
But how that might eventually happen
will depend on you. With that in mind,
the incentive here is to present a very
brief but in-depth and critical
overview about 3D printing.
Something that sheds light on how it
essentially works, what it can do as
well as what it can't do (yet).

Sure, the user base of these platforms is


significantly smaller than mobile. But this user base
may also be significantly more important to Pascal.
Why? The proportion of their developer base is
second to none! Nearly all users are also
developers to some level.
Their age range is wide starting from the junior all
the way to the senior.
Also, these devices are found progressively more in
the education system. This inherently instills
democratized development innovation.
These devices permit, for many, the first encounter
with what goes on behind the screen and with
programming. Shouldn't Pascal be one of those first
encounters? Bottom-Up. They form the root,
rather than just the branches and the leaves.
Most importantly, that which offers solution here
can impact, build and sustain awareness in the
most profound and fundamental of ways.
Pascal offers the means, the solution.
It's up to you to exercise it and build awareness.
Get a Return on Your Investment.

Misprint?
Why write another article about 3D printing?
The internet already offers an abundance of
information on the subject. Mountains, in fact.
Everyone has been talking and writing about it.
And, companies like MakerBot, Ultimaker and
several others are building a vast knowledge base
in the peer production realm. All True. But there
may be more to be said...and said again.
These days we can print out 3D objects in plastic,
metal, ceramics and even organic material. We can
print in just about any shape we can imagine.
All this suggests that we will soon be able to print
out trains, planes, automobiles and even complex
body parts. We also hear claims like; We will print
our own phones and Conventional manufacturing
techniques will soon be rendered obsolete. 3D
printing is the holy grail to manufacturing and the
distribution of products. Well...yes and no.
Talk is cheap and the web has a way of taking that
to the next level. The internet flattens things out.
Sure, truth has a way of eventually rising above it
all. Only the most robust of ideas will move up
through the ranks and prevail. Moving from
perception to proof is what brings value to
something. It, at the very least, makes it more
tangible to talk about. But that process takes time.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

27

3D Printing a More Tangible Idea


In the mean time how does one separate what is
real from what is rumor or just plain wrong?
At this point 3D printing offers the ability to print
out the "shape" of an object, in a certain material
and within certain accuracy. For many types of
products that may be enough to get the job done.
Yet, for most, there is typically a lot more involved
in the manufacturing of functional products.

Claims overshoot reality, jump out of the box,


and things can become confusing. That's fine,
its part of the process of innovation to imagine that
what we don't have or can't be done, yet.

While 3D printing technologies are rapidly


advancing and diversifying in applications, at this
point, we are pretty good at printing out only the
shape of an object and doing so within a certain
degree of accuracy. Again, there is typically a lot
The impact of 3D printing on industry is undeniable. more involved in manufacturing functional
Just as it was more than 25 years ago. But the real products.
revolution here is not only the technology.
The real revolution is more about accessibility and
Hammering Things Out
awareness. More specifically, awareness about how Use a hammer to drive a nail into a piece of wood.
we make things and how we think about making
Depending on your aim you will hit the nail with
things.
hammer without bending the nail. In most cases,
not a problem. A trivial, age-old process that most
A Revolution to Shape Ideas and Culture
anyone has done before. Now lets do the same with
Rapid prototyping, free form manufacturing,
3D metal printed products. But don't be surprised if
additive manufacturing and 3D printing. You could
the nail and/or even the hammer breaks or even
dispute the differences but they, more or less, boil
shatters on impact.
down to the same concept.
3D printers can print a metal hammer and a nail.
Engines of revolution. Labeled with words like
But creating functional products involves more than
"disruptive" and "revolution", additive
just creating the shape of an object.
manufacturing, or 3D printing technologies as they
are referred to these days, are actually not that
There are very specific technologies involved in the
new. The concept is very old. In fact, many of the
manufacturing of something even as trivial as a
patents related to the latest core technologies over nail. Nails are typically made of rolled, cold-drawn
the past 25 years have expired or will soon expire.
metal. The process involves rolling and stretching
the nail metal and aligning its metal crystals in such
What is actually new here is the recent accessibility a way that allows it to become more rigid, springy,
of these technologies to the masses. At least to
tough or otherwise less brittle. The truth of the
some level. Specifically, low cost 3D printers for
matter is, you never really hit the nail head on. But
home use and 3D print service providers. You can
due to its forging it is forgiving and springs back in
now own a 3D printer or 3D print service providers
most cases. In the worst case it bends but won't
can provide the latest and greatest technologies
break, let alone shatter.
without having to actually own the machine.
In any case, 3D printing is now more tangible to us Think about that for a moment the next time you
in our hands, minds and in society.
hear about 3D printed firearms.
Technology and its merit aside, the significance
here is the main stream "awareness" that this
brings about. And, that by itself is a revolution.
How we convert an idea into tangible and functional
form.

What this example illustrates is that while the 3D


printed shape of an object may suffice for some
applications there are many more applications
where it won't. This does not mean that 3D printing
technology is not applicable for real world products.
It simply means that the technology to induce
It typically takes about 30 years before a really new certain properties in 3D printed products is not
idea can move from concept to culture. Something
there yet. There are also many other factors
that moves into our minds and effects the way we
involved in manufacturing. What about production
do things and the ways we think about how to
cost and output?
make them.
With that said lets move on and get an insight
In other words, such "awareness" can push and
about 3D printing technologies.
even catapult the development of a new technology.
We have just witnessed it before with the PC
industry. 3D printing is now laying down another
infrastructure. One that allows these technologies
diversify and be applied to an ever wider range of
uses.
But like any revolution, what is real and what is
rumor become intermixed as things move toward a
critical mass. Belief and perception typically
precede objective observation and proof.

28

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

The Basic Idea


The idea, the core concept behind 3D printing is
actually very simple. Traditional manufacturing
techniques rely on removing material until the
desired shape and accuracy of some material has
been attained. You know, like a chisel to sculpture
wood or a lathe to cut cylindrical shapes.
3D printing or additive manufacturing takes the
opposite approach. Instead of removing material it
adds material. It builds it up until the desired object
shape and accuracy has been attained. Bottom-Up
instead of Top-Down.

The working materials for the FDM process are


widely available, inexpensive and the resulting
prints can be durable with little to no postprocessing. The concept and workings of the FDM
process also require only a simple, relatively low
cost design setup to get started. Together these
factors imply accessibility. Accessibility for all.

The process involves the deposition of molten


plastic on a flat bed using an extruder.
The extruder is typically positioned using an XY
linear motion system. In this manner layers are
Think of 3D printing as using bricks or Lego stacked built and a 3D shape is formed.
and positioned to build up an object. The bricks are
like building blocks connected together. It should be LOM
Surprisingly, not much focus is given to this
evident that the smaller the bricks, or building
technique despite its merit. Laminated Object
blocks, the more accurate the resulting object
Manufacturing involves the use of sheet material
shape will be. Its that simple.
such as paper, plastic, metal foils or fabrics.
By using a sheet material the layer thickness is
But what makes additive manufacturing so
important? There are several reasons. Probably the normalized, defined and is thereby easy to control.
Also the process allows selective coloring when,
most significant is that additive manufacturing
for instance, paper is used. The printed products
techniques are much less constrained in what they
can make in terms of object shape. Virtually any 3D are very durable and resemble wood (in the case of
paper).
object shape can be produced within the working
volume of a 3D printing system. In other words,
3D printing offers the freedom to create just about Typically a role of sheet material is unwound flat
over the working area of the printer. A laser or
any object shape you can imagine in a single
knife cuts the 2D layer profile as well as a grid of
operating session.
cuts around the object. The grid is to allow the
release of the model after printing. A layer of
Cut an apple in two. The cut surfaces are crossadhesive is applied after a layer is cut and the
sections of the apple. Think of the apple as being
process is repeated with a new sheet layer above
built up of many infinitely thin cross-sections or
the previous.
layers. If we precisely stack up these layers we
again have the object shape of the apple. The same
The LOM apparatus can be relatively simple in
approach applies to 3D printing. You are taking a
complex 3D shape and dividing it into simple layers construction and easy to scale in terms of design.
The materials used are usually non-toxic, low cost
which are much less difficult to manufacture.
and easy to handle making the LOM process an
attractive choice for the prosumer and small
Creating layers involves slicing a 3D modeled or
business.
3D scan of an object into a multitude of thin
sections. Each layer is then printed out and stacked
The only limitation is the cutting mechanism.
on the previous layer until the full 3D object shape
The laser used to cut the sheets at high speeds is
is printed. The thinner the layer the more smooth
relatively costly and can be dangerous. Using a
and accurate the object shape will be.
knife to cut the paper may be less precise and
The building blocks or base materials used to make slower. Moreover, paper is stronger and more
abrasive than one may think. A knife, at the very
a cross-section layer can be of various format.
least, would need to be made of some carbide,
The format obviously depends on the printing
technique. Formats such as sheets or beads layered widea or diamond for any long term use.
onto one and other, powders connected together
SLS/SLM
using adhesives or welding or even liquids (resins)
These techniques offer some truly spectacular 3D
which are photo-cured are employed.
print solutions. In particular, the production of
metal products. There are several variations of this
FDM
technique. What they have in common is the use of
The Fused Deposition Modeling technique (FDM)
powders as the base material format.
was probably the most responsible in forming the
entry point for the 3D printing revolution/hype in
The focus here is Selective Laser Sintering/Melting.
recent years. At least a good part of it. It's by no
means the best 3D printing technique. But it offers Powders are fused/melted together in a powder
bed to create layers, typically using a high power
accessibility to a very large audience.
laser. After each layer a new layer of powder is
brushed over and the process repeats. The powder
is brushed away from the print after it is
completed.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

29

Other powder based approaches exist. For instance,


an inkjet type dispenser dispenses an adhesive in a
metal powder bed which selectively binds a metal
powder together in order to build layers.
This green product is then sintered in order for
the adhesive to burn out and allow the metal
powder to fuse into a solid.
It should be clear that powder handling is a difficult
and messy process. These technologies can be
complex to manage and handle. Also, when working
with metal powders, elevated temperatures and
lasers there are many risks involved.
SLA
The historic Stereo Lithography process, patented in
the late 80s, formed the foundation of 3D printing
as we know it today. The technique has gone
through a long process of development and it has
some serious merit to offer.
But, traditionally, the technique also had some
serious limitations as a desktop or home use
device. Apart from the printing device itself,
probably the most apparent is the fact that it relies
on photo-cure resins to build 3D models.
The process is sound, it works. In fact, it works
very well. But the resins involved are typically toxic,
messy and costly with limited shelve life. All in all,
too cumbersome, complex and costly for many let
alone the average consumer.
But recent changes in design approaches and, in
particular, the resins involved are making a
difference. The development and diversity of photo
cure resins and suppliers are creating a better fit for
this process on our desktop.
The process relies on a photo cure resin which is
selectively and acutely cured to form layers. The
resin in a SLA process is a liquid which typically
cures under UV light. Lasers and other UV light
sources may be used. After each layer is cured
another layer is added in order to build the object.
The SLA process appears to be the next candidate
in the desktop 3D printing scene. Most systems rely
on the use of DLP projectors (Digital Light
Processing) to illuminate the resin. These are fast.
Laser scanning systems however offer other
important benefits as well.
Pascal and 3D Printing
While new suppliers of desktop 3D printers for
home use seem to appear every other week the
majority are based on the same technique (FDM).
They are variations of the same thing.
Different sizes and aspect ratio's, some may be fast
or accurate or have some combination of these
aspects. There are exceptions, but, typically, they
are all based on the same technique.

One of the reasons that so many suppliers now


exist is due the high demand for these machines.
True. But in part, the demand is the result of the
explosive awareness built by the democratized
open development itself. The conversation just gets
bigger, creating its own demand.
A marketing feedback system
like the ringing of a microphone
that is held too close
to a speaker box.
In addition, the democratized open development
has made the technology (software and hardware)
so incredibly accessible to all.
You don't need real engineering skills to create a
product and become a supplier. This is great for Joe
DIY (Do It Yourself) but also leaves a whole lot to
be desired for most others.
Turn-key? Not really. In many cases, you get what
you pay for. And, while the obsession with
low-cost can bring forward the greatest
innovation it can also deprive the development
process from making good design decisions.
All in all, becoming a supplier is as easy as creating
your own linux distro. You don't really need Linux
skills for that.
If you intend to get a 3D printer, get the facts
first. Find out who is behind it all. Cost is
important but not absolute. 3D printing is not
just software development, it's electronics
and mechanical engineering combined.
Debugging is a whole new ball-game here.
While it may seem otherwise, most 3D printing
technologies are not available to the average
consumer due to cost, complexity and safety
issues.
This does not mean that they won't one day take
part as a household appliance and be as easy to
use as a glorified coffee maker.
We are just not there yet. 3D printing is still in its
infancy. With that in mind it should be apparent
that there is room for improvement.
A lot of room. And, that means that there are
opportunities. Opportunities for Pascal to
provide solution. To appreciate this we need to
gain a better idea on the processes involved.
While 3D printing technologies may differ in their
operation and control the basic processing is more
or less the same. These include:
Pre-Process Main Process Post-Process
The Pre-Process stage relates to processing the 3D
data and preparing it for the printer to be printed in
the Main-Process.

And, that means that


there are opportunities.
Opportunities for Pascal to provide

30

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Depending on the technique, the Post-Process stage For the average user it may turn out to be an
impossible task to perform, especially these days.
relates to things like cleaning, assembling or
In fact, even when it comes to 3D modeled data
post-curing of the printed material after the
drawn in a graphics or CAD application, things can
Main-Process is completed.
take time to prepare and involve a lot of work.
Desktop 3D printing, certainly for home use, is still
In particular it is the Pre-Processing and
equally an art as it is a technology.
Main-Processing stages were the magic
happens and where Pascal can provide
The block diagram provides a birds-eye view of the
solutions. The Main-Processing stage pertains to
typical Pre-Process elements of 3D printing data
the control of the machine, the printer.
preparation. The 3D scan data process flow has also
been included to illustrate what's typically involved.
If you are new to 3D printing then you may be
inclined to think about the washer and dryer
solution. The 3D scan and 3D print solution. This is Solid Model
The 3D data used for printing must be a solid
a scenario where a 3D scanner is used to scan in
some usually trivial (typically broken) part and then model, a closed vessel. It must be leak-proof.
Think of this as modeling your house for
that part is reproduced through 3D printing.
3D printing.
A perfectly reasonable idea.
You may have modeled the front side but what
And, this line of thought is correct. It is certainly
about the back side and all in between. Modeling
where we are headed.
only one open side would not make sense to a 3D
But, in practice, and for most applications there is a printer.
lot more involved in the process. In many cases this A solid model is that which fully describes the
model in 3D space from all angles. Anything less
approach is simply too impractical, too difficult.
than that is in the strictest sense not a 3D model,
3D scanning can be (very) complex.
its not solid. It is certainly possible to force close a
In many ways it is still an art.
model, assuming that these closed sides are not of
This is certainly true when high levels of accuracy
are needed. Also, preparing the data for 3D printing interest.
can be equally, if not more, complex.
Sure, there are exceptions. But these are usually
not the rule.

MAIN PROCESS
3D PRINTER

PRE PROCESS
3D DATA
MERGE

3D SCAN
DATA

DECIMATE

SOLID
MODEL

MODEL
INTEGRITY
CHECK

SIZE,
ORIENTATE

ADD
SUPPORT
MATERIAL

SLICE
LAYERS

SMOOTH

G-CODE

EDIT

3D
PRINTER

3D
GRAPHICS

CAD

POST PROCESS
Nr 5 / 2013 BLAISE PASCAL MAGAZINE

31

There are many different 3D file data formats.


The standard for 3D printing is the STL file
format. Binary versions are popular but text (ascii)
is also available. The reasoning for ascii type
format was that this allowed 3D print operators to
manually examine the data if needed.
The STL file format is popular but it is far from
efficient. To maintain some form of readability the
file construct includes an incredible amount of
redundant information.
In particular for 3D scan data this means that
incredibly large file sizes are not uncommon.
Fortunately other file format constructs are on the
rise.
Object Orientation and Size
The accuracy of 3D printers is typically not the
same in all 3 directions. The layer thickness (Z)
may even be a constant (LOM, for instance) while
the X axis and Y axis may allow for much higher
accuracy. The minimum wall thickness of your 3D
model may also be limited.
All in all, the orientation in which you print your
model may be directly related to the desired quality
and even feasibility
of your print.
Support Material
Overhang. A 3D model may have a shape that
extends over its base foot print.
Depending on the 3D print technology,
support material for the overhang may be required.
The construct of this support material may be very
important.
In most cases it should not extend print time too
much. It should use minimal material (cost) while
allowing maximum effectiveness.
It should also allow easy and quick removal with
minimal surface damage to the 3D print.
Slicing
Creating layers means slicing up the 3D model into
2D (2.5D) cross-sections. The minimal permitted
thickness of the layers will depend on the 3D
printing machine capabilities.
The greater thickness of the layers the less the 3D
printing model will resemble that of the original 3D
data. The thinner the thickness of the layers the
more time it will take to print.

The Final Layer


3D printing has been around for some time.
Still, things are actually just getting started.
The concept is sound and proven.
And, as the awareness grows so also will these
technologies diversify. Opportunities will always
be around the corner. Its important to find entry
points. Things that you can claim and offer
solution in.
More importantly, more of us are now more aware
of what's involved in 3D printing. What it can do,
what it can't do and how it should be done. Our
collective view of 3D printing is becoming more
realistic and tangible as the hype debris settles
and our experience grows.
At this point we print using bits and pieces to
create layers but a time will come when we print
in particles and molecules to create structures.
Real or 3D printed? What's the difference?
That will be a revolution like no other seen before.
Dr. Richard Feymann
http://en.wikipedia.org/wiki/There%27s_Ple
nty_of_Room_at_the_Bottom
Molecular Imprints
http://www.molecularimprints.com/
ASML
http://www.asml.com/asml/show.do?ctx=427
Foresight Institute
http://www.foresight.org/
Dr. Eric Drexler
http://en.wikipedia.org/wiki/K._Eric_Drexl
er
3D Printing and Grey Goo
http://heywhatsthebigidea.net/3d-printingand-grey-goo/

G-Code
CNC, NC, Numerical control. CAM. 3D printers fall
back on this old but certainly not outdated machine
code called G-Code.
A standard in the manufacturing industry.
G-Code is simply lines of move to type
instructions with added control and auxiliary control
information such as speed, compensation rules etc.
This data is what is sent to the printer to control it
in the Main Process.

32

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

KINECT

KINECT

KINECT

Programming the Microsoft Kinect in Pascal By Michal Van Canneyt


starter

expert

Delphi,
Lazarus

Free Pascal ships a single unit libkinect10


which combines all definitions of the Delphi units.
The names, structures and interfaces should be identical.
Both sets of units load the library dynamically.
Loading and unloading the library must be done through
the following functions:
Function LoadNuiLibrary(Const Filename :
string = LibKinect) : Integer;
Procedure UnloadNuiLibrary;

Figure 1: The kinect for XBox 360

In February 2012 we started to do research on


the Kinect and its relatives. Now finally we have
the first results:
The Kinect is a device created by Microsoft to
enable NUI (Natural User Interface) for X-Box
and Windows. It tracks the movement of the
human body, and provides a stereoscopic image
of whatever is located in front of the camera. The
C++ API for this device can be used in Pascal
Introduction
Some years ago, Microsoft introduced the Kinect for its
game console XBox: a small camera-like device that sits on
top of the TV and registers players and their movements.
The Kinect does for complete human bodies what the
Leap Motion (introduced in an earlier article - issue Nr. 30 ,
page 47 ) does for hands: it can track the position and
movement of the human body (called skeleton tracking),
and provides a stereoscopic image (depth map) of whatever
is located in front of the camera.
In addition, the Kinect can also be used as a microphone (it
accepts voice commands) and simply as a webcam.
A picture of the device can be seen in figure 1 at the top of
this page.

When loading the library, the filename is optional and


when none is specified, the default kinect10.dll is
used.
The free Pascal version of the units uses a reference
counting mechanism, which means that
UnloadNuiLibrary must be called as much as
LoadNuiLibrary was called.
The kinect library does not need to be initialized:
once it is loaded, it is ready for use.
The library exposes a few global functions, and some
interfaces. One of these interfaces is INuiSensor,
representing the Kinect camera.
It has the following signature:
(for brevity, the arguments of the methods have been omitted)
INuiSensor = interface(IUnknown)
['{1f5e088c-a8c7-41d3-9957-209677a13e85}']
Function NuiInitialize(dwFlags : DWORD) :
HRESULT;
Procedure NuiShutdown;
Function NuiSetFrameEndEvent() : HRESULT;
Function NuiImageStreamOpen() : HRESULT;
Function NuiImageStreamSetImageFrameFlags() :
HRESULT;
Function NuiImageStreamGetImageFrameFlags() :
HRESULT;
Function NuiImageStreamGetNextFrame() : HRESULT;
Function NuiImageStreamReleaseFrame() : HRESULT;
Function NuiImageGetColorPixelCoordinates\
FromDepthPixel() : HRESULT;
Function NuiImageGetColorPixelCoordinates\
FromDepthPixelAtResolution() : HRESULT;
Function NuiImageGetColorPixelCoordinate\
FrameFromDepthPixelFrameAtResolution() :
HRESULT;
Function NuiCameraElevationSetAngle() : HRESULT;
Function NuiCameraElevationGetAngle() : HRESULT;
Function NuiSkeletonTrackingEnable() : HRESULT;
Function NuiSkeletonTrackingDisable : HRESULT;
Function NuiSkeletonSetTrackedSkeletons() :
HRESULT;
Function NuiSkeletonGetNextFrame() : HRESULT;
Function NuiTransformSmooth() : HRESULT;
Function NuiInstanceIndex : integer;
Function NuiDeviceConnectionId : PWideString;
Function NuiUniqueId : PWideString;
Function NuiAudioArrayId : PWideString;
Function NuiStatus : HRESULT;
Function NuiInitializationFlags : DWORD;

Since end 2011, the Kinect SDK is also available for


Windows PCs, and a C# (or .NET) and C++ API is
available.
The C# interface is more elaborate than the C++ interface,
but the C++ interface is usable in all programming
languages, including, as it turns out, Object Pascal.
The SDK can be downloaded for free from the MSDN
developer website, and version 1.7 was used for this article
(a new version is scheduled for Quarter 1 2014).
It contains some libraries (both for .NET and native
development), which need to be distributed with an
application that wants to connect to the Kinect.
In particular, the kinect10.dll must be distributed.
The Microsoft Kinect C++ SDK provides roughly the
same functionality as the open-source OpenNI and
OpenCV libraries, but has a much more simplified API
than the latter libraries.
end;
This article shows how to use the API for a simple
Not all of these methods will be discussed here, just the
skeleton-tracking application: discussing the complete
ones needed to track a skeleton and view the depth map.
SDK is beyond the scope of a single article.
The kinect library can be used with multile kinect
devices. The library therefore exposes some of the
Pascal Headers - The kinect API
ISensor methods as global functions:
The C++ SDK headers have been translated to Pascal.
if only a single kinect is connected to the computer, then
A version for Delphi is available on Google code:
these global functions can be used to control the kinect.
http://code.google.com/p/kinect-sdk-delphi/
Other than that no interface is used, the methods are
The Delphi units are called
the same, and the same procedures must be followed.
NuiAPI,NuiSensor, NuiImageCamera, SuiSensor Since the operating method is the same, in this article the
and NuiSkeleton.
more general approach using interfaces is used.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

33

Programming the Microsoft Kinect in Pascal (Continuation 1)


Detecting a kinect device
Once the kinect library is loaded, an interface to a kinect
device must be retrieved. This can be done using 2 global
functions:
Function NuiGetSensorCount(out Count : integer):
HRESULT;
Function NuiCreateSensorByIndex(Index : integer;
out ppNuiSensor : INuiSensor): HRESULT;

The NuiGetSensorCount function returns the number


of connected Kinect devices.
The NuiCreateSensorByIndex function then creates a
INuiSensor interface for the Index-th device.
Both functions return a HRESULT value: that means
that the result of the function can be checked using the
windows unit Failed function. The usable function result
is always returned in out parameters.
Once a device has been detected, and an interface to
the device was returned, the device must be initialized.
When the device is no longer needed, the device can be
shut down.
These operations can be performed using the following
methods of the INuiSensor interface:
Function NuiInitialize(dwFlags: DWORD): HRESULT;
Procedure NuiShutdown;

When initializing the device, the device needs to be told


what kind of processing it should do:
calculate depth image,
track player skeletons.
Since each step in processing takes CPU time,
it is important not to request processing that will not be
used anyway. This is specified in the dwFlags option to
the NuiInitialize function. The flags are an OR-ed
combination of the following constants:

That means that the calling application needs to set up


several event handles, one for each kind of data stream it
wishes to receive. These event handles must then be passed
on to the data stream initialization functions.
For the demo application, 2 streams will be examined:
the depth map and the skeleton tracking data.
Both streams are provided through memory blocks that
must be requested through some methods of the
INuiSensor interface.
The skeleton tracking stream is initialized (or stopped)
through the following functions:
Function
NuiSkeletonTrackingEnable(hNextFrameEvent:
THandle;
dwFlags : DWORD ) : HRESULT;
Function NuiSkeletonTrackingDisable : HRESULT;

The first parameter to NuiSkeletonTrackingEnable is


the handle used to report the presence of a new skeleton
frame. The second parameter determines how the tracking
data is calculated and returned:
NUI_SKELETON_TRACKING_FLAG_SUPPRESS_NO_FRAME_DATA

When set, the NuiSkeletonGetNextFrame method will


not return a E_NUI_FRAME_NO_DATA error when no data
is present, instead the call will block until data is present
or the timeout is reached.
NUI_SKELETON_TRACKING_FLAG_TITLE_SETS_TRACKED
_SKELETONS

When set, the detected players are not really tracked.


The NuiSkeletonSetTrackedSkeletons must be used
to select the players that should be fully tracked.
NUI_SKELETON_TRACKING_FLAG_ENABLE_SEATED_SUPPORT

enables seated skeleton tracking.

NUI_INITIALIZE_FLAG_USES_AUDIO
Request audio data.
NUI_INITIALIZE_FLAG_USES_COLOR
Request color data.
NUI_INITIALIZE_FLAG_USES_DEPTH
Request depth data.

This means that the 10 lower-body joints of each skeleton


are not tracked, resulting in less calculations (the default is to
track the whole body, 21 joints).

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX

The depth image stream, as well as other image streams,


are initialized through the following functions:
Function NuiImageStreamOpen(
eImageType : NUI_IMAGE_TYPE;
eResolution : NUI_IMAGE_RESOLUTION;
dwImageFrameFlags : DWORD;
dwFrameLimit
: DWORD;
hNextFrameEvent : THandle;
out phStreamHandle : THandle) : HRESULT;

Request depth data with a player index.


NUI_INITIALIZE_FLAG_USES_SKELETON
Request skeleton tracking
For the purpose of this article, only the last 2 will be used.
The difference between the
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX

When tracking a person seated in front of a computer,


this option can be used to reduce calculation time.

and
NUI_INITIALIZE_FLAG_USES_DEPTH

Function NuiImageStreamSetImageFrameFlags(
is that the former encodes a player index in the depth map:
hStream : THandle;
the depths are returned as word-sized values, and the 3 last dwImageFrameFlags : DWORD) : HRESULT;
bits of the word are used to encode a player index
The NuiImageStreamOpen function opens an image
(meaning that at most 7 players can be used)
stream. Which images the stream returns is specified
through the eImageType parameter, which can have one
Reading data from the device
of the following values:
The kinect API provides several data streams:
video, audio, depth map, skeleton data.
The API uses event handles to report the presence of data
in one of these streams.

34

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Programming the Microsoft Kinect in Pascal (Continuation 2)


NUI_IMAGE_TYPE_COLOR a color image.
NUI_IMAGE_TYPE_COLOR_INFRARED an infrared image
NUI_IMAGE_TYPE_COLOR_RAW_BAYER
a Raw Bayer color image (RGB)
NUI_IMAGE_TYPE_COLOR_RAW_YUV
a YUV color image; no conversion to RGB32.
NUI_IMAGE_TYPE_COLOR_YUV
a YUV color image, converted to RGB32
NUI_IMAGE_TYPE_DEPTH a depth image.
NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX
a depth image with player index encoded in the map.

The second parameter is a pointer to a


NUI_SKELETON_FRAME structure. On return, it points to a
record that describes the tracked skeletons.
It is described as follows
NUI_SKELETON_FRAME = record
liTimeStamp: int64;
dwFrameNumber,
dwFlags: DWORD;
vFloorClipPlane,
vNormalToGravity: Vector4;
SkeletonData : array[0..5] of NUI_SKELETON_DATA;
end;

Various streams can be opened to capture data from the


same device, but the capture of depth images must be
enabled when initializing the device.
The interesting data is the last structure, an array of 6
The resolution of the image can be specified in the
NUI_SKELETON_DATA records. The limit of 6 skeletons is
eResolution parameter, which can have one of the values hardcoded: the kinect tracks at most 6 players (A constant
exists which describes this limit: NUI_SKELETON_COUNT).
NUI_IMAGE_RESOLUTION_80x60,
The 6 elements of the array are always present, even if
NUI_IMAGE_RESOLUTION_320x240,
less skeletons have actually been detected:
NUI_IMAGE_RESOLUTION_640x480 or
Each skeleton is described by the following record:
NUI_IMAGE_RESOLUTION_1280x960.
The dwImageFrameFlags parameter can be used to
specify some flags when capturing images, it accepts the
same values as used in the
NuiImageStreamSetImageFrameFlags function.
The hNextFrameEvent parameter is the handle of the
event that must be triggered when a new frame is ready.
Finally, the phStreamHandle is the handle of the image
stream that must be used in the
NuiImageStreamGetNextFrame calls to read the image.
The NuiImageStreamSetImageFrameFlags method can be
used to modify the flags passed in the
dwImageFrameFlags parameter to
NuiImageStreamOpen:
NUI_IMAGE_STREAM_FLAG_DISTINCT_OVERFLOW
_DEPTH_VALUES is undocumented.
NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE Enable
near mode. (enable depth detection close to the camera)
NUI_IMAGE_STREAM_FLAG_SUPPRESS_NO_FRAME_DATA

NUI_SKELETON_DATA = record
eTrackingState: NUI_SKELETON_TRACKING_STATE;
dwTrackingID,
dwEnrollmentIndex,
dwUserIndex: DWORD;
Position: Vector4;
SkeletonPositions: array[0..19] of Vector4;
eSkeletonPositionTrackingState: array[0..19] of
NUI_SKELETON_POSITION_TRACKING_STATE;
dwQualityFlags: DWORD;
end;

The eTrackingState field describes whether the record


actually describes a skeleton.
It can have one of the following values:
NUI_SKELETON_NOT_TRACKED
The record does not describe a tracked skeleton.
NUI_SKELETON_POSITION_ONLY
The record describes a skeleton whose position is tracked.
NUI_SKELETON_TRACKED
record describes a fully tracked skeleton.

When set, the NuiImageStreamGetNextFrame method


will not return an E_NUI_FRAME_NO_DATA error when no
To determine the skeletons, the eTrackingState field of
data is present, instead the call will block until data is
each record in the SkeletonData array of
present or the timeout is reached.
NUI_SKELETON_FRAME
must be checked. If it contains
NUI_IMAGE_STREAM_FLAG_TOO_FAR_IS_NONZERO
NUI_SKELETON_TRACKED
, it is a usable record.
is undocumented.
For each skeleton, 20 joints are tracked.
These joints are described in the SkeletonPositions
After the image stream and skeleton stream have been set
and eSkeletonPositionTrackingState arrays.
up, frames can be read by watching the event handles.
For each of the 20 joints, a constant is defined, for example:
In the example program later on, this will be done in a
separate thread.
Interpreting skeleton frame data
When a skeleton frame is ready, it can be fetched with the
following method of INuiSensor:
Function NuiSkeletonGetNextFrame(
dwMillisecondsToWait : DWORD;
pSkeletonFrame : PNUI_SKELETON_FRAME) : HRESULT;
The first parameter is a timeout:
if no frame is ready within the specified time, the call
returns with an error condition. When an event handle is
used to signal the completion of a frame, then the call
should return at once.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

NUI_SKELETON_POSITION_HEAD,
NUI_SKELETON_POSITION_HAND_LEFT,
NUI_SKELETON_POSITION_HAND_RIGHT.
Each of these constants is an index in the
SkeletonPositions and
eSkeletonPositionTrackingState arrays.
The eSkeletonPositionTrackingState array
determines which of the SkeletonPositions
elements contains a valid position vector.
An element in the array can have one of the following
values:

COMPONENTS
DEVELOPERS

35

Programming the Microsoft Kinect in Pascal (Continuation 3)


NUI_SKELETON_POSITION_NOT_TRACKED
The array element does not contain valid data.
NUI_SKELETON_POSITION_INFERRED
The position is calculated from other data.
NUI_SKELETON_POSITION_TRACKED
The position is tracked.
The last 2 values mean that the element with the same
array index in the SkeletonPositions array, contains
a valid joint position. The skeleton tracking mechanism
may result in jittery data. The results are vectors, and the
positions will appear to have some random Brownian-like
motion. The INuiSensor interface offers the
NuiTransformSmooth function to deal with this:
Function NuiTransformSmooth(
pSkeletonFrame : PNUI_SKELETON_FRAME;
const pSmoothingParams :
PNUI_TRANSFORM_SMOOTH_PARAMETERS) : HRESULT;

This function will attempt to reduce the randomness by


applying a transformation on the received coordinates.
The transformation is controlled by the following
NUI_TRANSFORM_SMOOTH_PARAMETERS record:
NUI_TRANSFORM_SMOOTH_PARAMETERS = record
fSmoothing,
fCorrection,
fPrediction,
fJitterRadius,
fMaxDeviationRadius : single;
end;

For the depth image, the data comes in the form of an array
of word-sized values. The byte size of the array is reported
using BufferLen, the length of a single scan line can be
retrieved with the Pitch method. The actual array can be
retrieved with LockRect.
Since the array is managed by the kinect driver, it is locked
when it is retrieved. It must be unlocked using the
UnlockRect call when it is no longer needed.
The data array is described by the following record
NUI_LOCKED_RECT = record
Pitch : integer;
size : integer;
pBits : pointer;
end;
Where Pitch and Size correspond to the BufferLen and
Pitch methods of the INuiFrameTexture interface.
The pBits pointer points to the actual array.
Each element in the array is aWord value between
NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE and
NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE
if near mode is enabled.
In normal mode, the minimum and maximum values are
NUI_IMAGE_DEPTH_MINIMUM and
NUI_IMAGE_DEPTH_MAXIMUM.
When NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX
was used when creating the stream, the depth images
word values are shifted, and the last 3 bits are used to
encode a player index. (3 is the value of
NUI_IMAGE_PLAYER_INDEX_SHIFT).

The exact meaning of these parameters can be found in the


NUI API documentation on MSDN.
Interpreting depth image data
If a depth image is requested, the INuiSensors method
NuiImageStreamGetNextFrame can be used to retrieve
the actual depth image. It is declared as follows:
Function NuiImageStreamGetNextFrame(hStream:THandle;
dwMillisecondsToWait : DWORD;
pImageFrame : PNUI_IMAGE_FRAME) : HRESULT;

If the last 3 bits are nonzero, then the pixel is considered


part of a players body. The player index can be used for
example to color the corresponding pixels in a playerspecific color.

The hStream handle is an image stream handle created using the


NuiImageStreamOpen function. Similar to the
NuiSkeletonGetNextFrame function, the
dwMillisecondsToWait specifies a timeout, in case the image
is not yet ready.
On return, the location pointed to by pImageFrame will be filled
with a NUI_IMAGE_FRAME record:

The sample application:


Connects to the first found Kinect sensor.
Requests and displays skeleton frames and a depth
image stream.
Uses events to get a notification when the next frames
are ready.
Displays the depth image with a specific color for all
players, and superimposes on that, for the first
detected player, shapes representing the hands and head.
Allows to set/get the camera elevation angle.

NUI_IMAGE_FRAME = record
liTimeStamp : int64;
dwFrameNumber: DWORD;
: NUI_IMAGE_TYPE;
eImageType
eResolution : NUI_IMAGE_RESOLUTION;
pFrameTexture: INuiFrameTexture;
dwFrameFlags : DWORD;
: NUI_IMAGE_VIEW_AREA;
ViewArea
end;

Putting everything together


After the long description of the Kinect (Natural User
Interface) API, a small demonstration application will clarify
things a bit.

The program is written in Lazarus, but it should work


equally well in Delphi. It is a simple form, with 2 panels,
some controls, and 3 shapes on it. The OnCreate event
handler is used to initialize some variables and connect
The pFrameTexture field contains an INuiFrameTexture to the kinect:
interface that can be used to examine the actual frame data:
procedure TMainForm.FormCreate(Sender: TObject);
INuiFrameTexture = interface(IUnknown)
begin
['{13ea17f5-ff2e-4670-9ee5-1297a6e880d1}']
FESkeleton:=INVALID_HANDLE_VALUE;
FEDepth:=INVALID_HANDLE_VALUE;
Function BufferLen: integer;
FSDepth:=INVALID_HANDLE_VALUE;
Function Pitch: integer;
LoadNuiLibrary;
Function LockRect(Level: UINT;
TBAngle.Min:=NUI_CAMERA_ELEVATION_MINIMUM;
pLockedRect: PNUI_LOCKED_RECT;
TBAngle.Max:=NUI_CAMERA_ELEVATION_MAXIMUM;
pRect: PRECT;
if not InitKinect then
Flags: DWORD ): HRESULT;
ShowMessage('Could not initialize kinect!');
Function GetLevelDesc(Level : UINT;
For I:=1 to 6 do
out desc : NUI_SURFACE_DESC): HRESULT;
FPlayerColors[i]:=clWhite;
Function UnlockRect(Level: UINT): HRESULT;
end;
end;

36

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Programming the Microsoft Kinect in Pascal (Continuation 4)


NuiImageResolutionToSize(ImageResolution, w, h);
The variables FESkeleton and FEDepth are the events
ClientWidth
:= w;
used to receive notifications when the skeleton and depth
ClientHeight := h;
frames are ready. The FSDepth variable will contain a
FBDepth:= TBitmap.Create;
handle for the depth image stream.
FBDepth.Width := w;
The range of the cameras elevation angle is determined by
FBDepth.Height:= h;
the NUI_CAMERA_ELEVATION_MINIMUM and
Result:=true;
if Not Failed
NUI_CAMERA_ELEVATION_MAXIMUM constants
(FKinect.NuiCameraElevationGetAngle(@A))
(-27 and 27, respectively), these values are used to initialize a
then TBAngle.Position:= A;
track bar control which can be used to set the angle of the
end;
Kinects camera. Lastly, an array of colors is initialized to
The last statements retrieve the elevation angle of the
show the players on the depth map.
kinects camera, and initialize a trackbar (TBAngle) with the
After loading the Kinect library, the InitKinect function
current position of the camera.
is called to actually initialize everything:
The TEventDispatcherThread is a thread descendant (in
function TMainForm.InitKinect (EnableNear :
the EventDispatcherThread unit) which simply loops
Boolean = False): boolean;
var w,h : DWord; C,i : integer;
and waits for kinect events. When a kinect event is detected,
NS : INuiSensor; E : Int64;
a windows WM_USER message is sent to the main form.
begin
Result:=false;
FKinect := nil;
if Failed(NuiGetSensorCount(C)) then exit;
I:=0;
While (FKinect=Nil) and (i<C) do
begin
if Not Failed(NuiCreateSensorByIndex(i,NS))
then
if (NS.NuiStatus=S_OK) then FKinect:= NS;
Inc(I);
end;
if not Assigned(FKinect) then exit;
if Failed(FKinect.NuiInitialize(NUIOptions))
then
begin
FKinect:=Nil;
Exit;
end;

This code is pretty straightforward, it requests the number


of kinect devices, and connects to the first available one.
If none was detected, it exits. The first available sensor is
then initialized, the NUIOptions constant is defined as:
Const
NUIOptions =
NUI_INITIALIZE_FLAG_USES_SKELETON or
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX;

After the kinect was initialized, an event handle is created,


and used to enable skeleton tracking:
FESkeleton:= CreateEvent(nil,True,False,nil);
FKinect.NuiSkeletonTrackingEnable(
FESkeleton,SkeletonOptions);

SkeletonOptions is a constant requesting seated support


and near range. The next thing to do is request a depth
image, again using an event handle to get notifications:
FEDepth:= CreateEvent(nil,true,false,nil);
if Failed(FKinect.NuiImageStreamOpen(
ImageOptions, ImageResolution,0,2,
FEDepth, FSDepth)) then
Exit;
if EnableNear then
if
Failed(FKinect.NuiImageStreamSetImageFrameFlags
(FSDepth,ImageStreamOptions)) Exit;

If all went well, a thread can be set up to check for events


on the FESkeleton and FEDEpth
FTEvents:=
TEventDispatcherThread.CreateDispatcher(Handle,
FESkeleton,
FEDEpth);

Lastly, the size of the depth image is used to create a


bitmap (which will be used to draw the depth image) and set
the width and height of the form: FEDEpth.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

This is one way of handling the events, another way would


be to use the OnIdle event of the application, or a timer, to
check for new events. Instead of sending a message,
it is also possible to use the Synchronize or Queue methods
to let the main thread respond to the arrival of new data.
The threads Execute method looks very simple:
procedure TEventDispatcherThread.Execute;
begin
if (FHWnd=INVALID_HANDLE_VALUE) or
(FESkeleton=INVALID_HANDLE_VALUE)
then exit;
While not terminated do begin
if (WaitForSingleObject(FESkeleton,50)=
WAIT_OBJECT_0)
then
begin
SendMessage(FHWnd,WM_USER,MsgSkeleton,0);
ResetEvent(FESkeleton);
end;
if (WaitForSingleObject(FEDepth,50)=
WAIT_OBJECT_0)
then
begin
SendMessage(FHWnd,WM_USER,MsgDepth,0);
ResetEvent(FEDepth);
end;
end;
end;

If an event is received on either of the 2 handles,


a WM_USER message is sent to the main form, which will
then take appropriate action. The message parameters are
defined as constants. Note that the event is reset after the
message is sent.
Reacting on the messages is done by implementing a
message handler method in the form for the WM_USER
message:
procedure TMainForm.eventDispatcher(var msg:
TMessage);
begin
if (msg.WParam=msgSkeleton)
then OnNewSkeletonFrame
else if (msg.WParam=msgDepth)
then OnNewDepthFrame;
DoTick(msg.WParam=msgDepth);
end;

The message handler method simply examines the message


parameter and calls the appropriate method to deal with
the message. After that it executes a tick, which collects and
displays some statistics.
The actual work is done in OnNewSkeletonFrame and
OnNewDepthFrame. The first one is responsible for
drawing the head and hand joints in the skeleton frame.
It examines the received data and positions 3 shapes on the
form:

COMPONENTS
DEVELOPERS

37

Programming the Microsoft Kinect in Pascal (Continuation 5)


procedure TMainForm.OnNewSkeletonFrame;
var i : integer; fr : NUI_SKELETON_FRAME;
PSD : PNUI_SKELETON_DATA;
tsp : NUI_TRANSFORM_SMOOTH_PARAMETERS;
begin
FillChar(fr,sizeof(NUI_SKELETON_FRAME),0);
if Failed(
FKinect.NuiSkeletonGetNextFrame(0,@fr))
then Exit;
PSD:= Nil;
I := 0;
While (PSD=Nil)and (i<NUI_SKELETON_COUNT) do
begin
if (fr.SkeletonData[i].eTrackingState =
NUI_SKELETON_TRACKED) then
PSD:= @fr.SkeletonData[i];
Inc(I);
end;
if Not Assigned(PSD) then Exit;

This code fetches the next NUI_SKELETON_FRAME


structure from the kinect, and initializes
a pointer to the first skeleton (PSD). The following step is
smoothing out the received
coordinates:
With tsp do begin
fCorrection := 0.3;
fJitterRadius:= 1.0;
fMaxDeviationRadius:= 0.5;
fPrediction := 0.4;
fSmoothing
:= 0.7;
end;
if Failed(FKinect.NuiTransformSmooth(@fr,@tsp))
then Exit;

procedure TMainForm.OnNewDepthFrame;
Const
DS
= NUI_IMAGE_PLAYER_INDEX_SHIFT;
MINS = NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE shr DS;
MAXS = NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE shr DS;
var
IFDepth : NUI_IMAGE_FRAME;
FT : INuiFrameTexture; lck : NUI_LOCKED_RECT;
depth : pword; CD : Word; p : byte;
x, y : integer; w, h, GS : cardinal;
C : TCanvas;
begin
if (FSDepth=INVALID_HANDLE_VALUE) then Exit;
if Failed(
FKinect.NuiImageStreamGetNextFrame(
FSDepth,0,@IFDepth)) then Exit;
NuiImageResolutionToSize(
IFDepth.eResolution, w, h);
The above code retrieves the image from the kinect and calculates
a width and heigh with it. The next step is to retrieve the image
data from the INuiFrameTexture interface:
try
FT:=IFDepth.pFrameTexture;
if not assigned(FT) then Exit;
if Failed(FT.LockRect(0,@lck,nil,0))
then Exit;
try
if lck.Pitch<>(2*w) then Exit;
depth:=lck.pBits;

The following steps transfer are a loop over the depth


data, transferring it as a grayscale to the bitmap.
The depths that have a player index in them are
transferred to the bitmap using the players color.
And finally, the 3 shapes are positioned
If there is no player index, the depth value is
ShowJoint(SHead,PSD,NUI_SKELETON_POSITION_HEAD);
transformed to a grayscale value ranging from 0 to
ShowJoint(SLeft,PSD,NUI_SKELETON_POSITION_HAND_LEFT);
ShowJoint(SRight,PSD,NUI_SKELETON_POSITION_HAND_RIGHT); 255. Note that the bitmap canvas is locked for better
end;
performance:
The ShowJoint will check if the requested joint position (the
C:=FBDepth.Canvas;
third parameter) was tracked, and if so, position the shape so
C.Lock;
it is centered on this position.
for y:=0 to h-1 do
To position a shape on the depth bitmap, the skeleton
for x:=0 to w-1 do
coordinates must be transformed to an X,Y position on the
begin
depth image. This can be done with the aid of the
CD:=Depth^;
P:=(CD and NUI_IMAGE_PLAYER_INDEX_MASK);
NuiTransformSkeletonToDepthImage function:
Procedure NuiTransformSkeletonToDepthImage(
vPoint : TVector4;
out fDepthX : single;
out fDepthY : single;
eResolution : NUI_IMAGE_RESOLUTION);
It receives a position vector, and a resolution. It returns an X,Y
coordinate which is a coordinate on a depth bitmap
corresponding to the given resulution.
All this is used in the ShowJoint function:
Procedure TMainForm.ShowJoint(S : TShape; PSD :
PNUI_SKELETON_DATA; HI : Integer);
Var
x, y : single;
begin
S.Visible:=
PSD^.eSkeletonPositionTrackingState[HI]=
NUI_SKELETON_POSITION_TRACKED;
if S.Visible then
begin
NuiTransformSkeletonToDepthImage(
PSD^.SkeletonPositions[HI],x,y,
NUI_IMAGE_RESOLUTION_640x480);
S.Left:=Round(x)-(S.Width div 2);
S.Top:=Round(y)-(S.Height div 2);
end;
end;
Finally, the depth image must be rendered: for this, the depth
image needs to be interpreted and transferred to a bitmap, and
then the bitmap is drawn on a panel:

38

COMPONENTS
DEVELOPERS

if (P<>0) then
C.Pixels[X,Y]:=FPlayerColors[p]
else if (CD>=NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE)
and
(CD<=NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE) then
begin
GS:=Round(((CD shr ds) - MINS) / MAXS * 255);
GS:=GS and $FF;
GS:=GS or (GS shl 8) or (GS shl 16);
C.Pixels[X,Y]:=GS;
end
else
C.Pixels[X,Y]:=clBLack;
Inc(depth);
end;
C.Unlock;

Lastly, the retrieved depth image data is released,


and the bitmap is drawn on the panel:
finally
FT.UnlockRect(0);
end;
finally FKinect.NuiImageStreamReleaseFrame(
SDepth,@IFDepth);
end;
PImage.Canvas.Draw(0,0,FBDepth);
For X:=0 to PImage.ControlCount-1 do
if PImage.Controls[x] is TShape then
PImage.Controls[x].Repaint;
end;

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Programming the Microsoft Kinect in Pascal (Continuation 6)


Finally, to make sure the shapes representing the head and
hands are properly shown, they are repainted.
The form contains some logic to display a tick and average
frames per second count, and to set the colors of the
players. This logic is not relevant to the operation of the
kinect.
However, the routine to set the cameras elevation angle
needs some explanation: Setting the elevation angle of the
camera takes some time: it is a mechanical operation,
involving a small motor inside the kinect. Setting a new
position while the previous position was not yet
established, will result in an error. It is therefor important
not to send commands too often or too much. The
following code attempts to do that:

Note that the trackbar position is reversed; it is positioned


vertically, with the minimum value (-27) at the top, and the
maximum value (27) at the bottom of the trackbar.
Everything put together, the running program results in a
figure like figure below.

Conclusion
The kinect is a device which is one way of
implementing a Natural User Interface: use the
human body to control the computer. While it is
originally aimed at gaming, there may be
specialized uses for this device outside the
gaming industry. For a more fine-grained control
of the computer, the resolution of the Kinects
procedure TMainForm.TBAngleChange(Sender: TObject); skeleton detection is not fine enough:
it cannot detect individual fingers of the hand.
Var A : Longint;
begin
This gap may be better filled by the Leap Motion
If TBAngle.Position=FLastPosition then Exit;
device.
If Not Failed
Both devices are available to Object Pascal
(FKinect.NuiCameraElevationGetAngle(@A))
programmers, and there are certainly Object
then begin
Pascal game programmers that will consider the
if (A<>-TBAngle.Position) then
A:=-TBAngle.Position;
ability to use the kinect a nice addition to their
begin
if Failed
(FKinect.NuiCameraElevationSetAngle(A))
then ShowMessage(Format(SErrSetAngle,[A]));
FLastPosition:=-A;
end;
end
else
ShowMessage(SErrGetAngle);
end;

Figure 2: The demo program in action

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

39

The license is now


free for students!
Smart Mobile Studio now offers a free educational licence for students in
school or university or who are members of an after-school coding club.

30

Nr 4 / 2013 BLAISE PASCAL MAGAZINE


www.SmartMobileStudio.com

Smart Mobile Studio 2.0 By Primo Gabrijeli


starter

expert

SMART

A year and a half after the original release of


Smart Mobile Studio 1.0 I'm proud to present the
first major upgrade Smart Mobile Studio 2.0. A
lot of things have happened since the original
release. The team is now larger, counting five
developers. Lennart, the original father of the
Pascal for the Internet concept, has accepted
new challenges, and I have stepped in as a
programming manager for the Smart team. This
article is therefore not an objective outsider view,
but a bunch of notes written by a proud
programmer

Smart Mobile Studio 2.0 is available as a public beta at


smartmobilestudio.com.
Pascal for Everything
I like to describe Smart Mobile Studio 2 as a development
environment that allows you to run Pascal everywhere,
meaning that you can run Pascal programs on phones
(either from the browser, from the home screen or from
PhoneGap/Cordova-packaged applications), on desktops (in a
browser), on servers (with the help of Node.js) and even on
microprocessors (with the Espruino initiative).
Smart Mobile Studio 2 (SmartMS for short) supports six
different project types.

Canvas game project is a project type best used for writing


games. It provides a game with a HTML5 canvas,
a mechanism to call your application with a choosen
framerate, and RTL support for CSS3 sprites and bitmapbased fonts.
Console project creates an application which mimics a
console-type program. This project type is suitable for
testing ides, debugging portions of a larger framework and
for unit testing.
Espruino (Microcontroller) creates an application that will
run on an Espruino firmware (www.espruino.com).
Espruino is a JavaScript interpreter for microcontrollers
which can run as a part of the microcontroller firmware. In
short, this allows you to connect a development board to a
Windows computer (either with a serial cable or with a
Bluetooth module), write a Pascal program, press F9 and it
will execute on the microprocessor. For more information
about microcontroller programming with SmartMS, view
Figure 1: Overview of the IDE, in the next pages an
the following article:
overview - enlarged - with the new Delphi-like double-click http://smartmobilestudio.com/2013/11/17/
that opens the code in your Editor. Never thought of
micro-controller-programming/.
how important this is...

Figure 2: Create an new project

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

41

Smart Mobile Studio 2.0 (Continuation 1)


Several Editions
With version 2 we have decided to make Smart Mobile
Studio more accessible for everyone and we have split it
into multiple editions.
On the entry level there is a Basic edition. It supports all
project types except the Visual components project and
doesn't contain the visual designer (as it is only used in the
visual project type). The price is merely $42, on par with the
original Turbo Pascal software.
Figure 4: the Espruino board
Node.js project creates a server-side application designed
to run as a part of the Node.js platform (nodejs.org). You
can run such applications on Windows, OS X and Linux
computers. The Visual components project creates a formand component-based application, just as you would do it
in Delphi or Lazarus.
A visual project can contain multiple forms and the user
can navigate between them. Such projects can run in any
modern browser. You can also use modal forms which do
not use full display (parts of old form are visible below).The
WebWorker project (thread) is a special light-weight project
type which creates a web worker, a thread-like browser
entity that allows multithreaded computing in a browser.

The middle ground holds the Professional edition.


It contains everything from the Basic edition plus the visual
project type and the visual designer.
The price is a bit higher, $149.
If you want to work with the databases we are also
offering the Enterprise edition which adds database
connectors to the Professional level. You can easily connect
to RemObjects services or to DataSnap servers and
SmartMS will create an appropriate Pascal connector for
you automatically. (To use with the DataSnap server you also
have to have a Delphi/RAD Studio Enterprise installed on the
same computer.) This edition costs $399.

Figure 8: Turning
Figure 5: SmartMS-design

44

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Smart Mobile Studio 2.0 (Continuation 2)


All editions come with a year worth of upgrades.
You can, of course, use them without upgrading after the
year has passed.
We are also offering an Educational license which is a
free license (functionally equivalent to Enterprise) for
educational facilities (schools, universities, clubs ).
New visual designer
In the SmartMS version 1, visual designer was a bit rough
around the edges. It was functioning OK, but it was really
not working as smooth as users wanted. This too has
passed and with the version 2 users will be getting a new
designer with better look and, besides all, better event
support. Finally you'll be able to create events by doubleclicking on a button or an event in the Object Inspector.
You will also notice that some components are better
looking. The new designer allows us to display a better
representation of components. This functionality is not yet
finished and will be continually improved in 2.0 betas and
in later (2.1) releases. User-components can be organized
in packages and added via the new Packages menu.
Smart comes with an example package containing the
TeeChart graphing component.
Compiler changes
Besides stability and code generation improvements,
compiler now supports external variables and call
variables. This simplifies interfacing with existing
JavaScript libraries (such as used for Node.js integration).
HTML and CSS templates are now scriptable and run
through a DWScript preprocessor before they are used.
CSS is compressed during the compilation so that
minimum space is used.
We have added new ways to reference an external
resource. In addition to the old command {$R
'resource.js'} which copied the resource file into the
deployment folder, the compiler now supports the
{$R 'file:resource.js'} syntax.
You can also specify an external file, which is not copied to
the deployment folder, with a syntax
{$R 'http://server/path/script.js'}.
Additionally, you can specify resource MIME type:
{$R 'text/javascript:
http://server/path/script.js'}.

There is also a command-line compiler, smsc, which is freely


redistributable and which you can deploy on your servers or
bundle with your solutions.
As the main IDE, command-line compiler runs only
on Windows.
When compiling inside the SmartMS IDE, you can use prebuild and post-build scripts to execute external actions
(http://smartmobilestudio.com/2013/11/25/newbuild-system/).
For improved debugging experience, the IDE supports source
map debugging with Chrome.
That allows you to debug your programs in Chrome while
looking at the Pascal source, not at the JavaScript.
(Sadly, this functionality is broken in the 2.0 beta.
We'll fix it for the next beta.)
IDE improvements
Besides the new visual designer and package manager,
there's a bunch of smaller changes that make the IDE easier
for use. For example, you can Ctrl+Click on a symbol
to jump on its definition. You can move lines around with
Alt+Shift+Up/Down keys.
The Project-related state (open tabs, current line in the editor,
bookmarks) is stored in a <projectname>.dsk file and is
restored when a project is opend.
We have changed the project file format.
The new project format is incompatible with Smart 1 and uses
a new extension .sproj.
Forms are stored in xml-based .sfm files and can be edited
outside the SmartSM IDE. We have also changed the
preferences file format which is now XML-based
(preferences.xml). The snippets system was also
redesigned, which now stores one snippet per folder and
allows for a simple upgrade and backup.
The Project management is now more flexible.
You no longer have to choose the project path when creating a
new project. In fact, you can create and compile a project
without saving the source! (Resulting HTML/CSS/JS program is
still save in a TEMP folder but is deleted when SmartMS exits.)
Externally modified files are now displayed in a manner
similar to the excellent DDevExtensions Delphi extension (big
thanks to Andreas Hausladen for showing us the way and allowing
us to steal his idea). You can compare changes with in-editor
state in external (configurable) tool or in a built-in diff viewer.

Figure 6: Relod changed files

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

45

Smart Mobile Studio 2.0 (Continuation 3)


Examples
I'd like to show you few examples of original and compiled
code so you can see how well the compiler works. For
example, let's take a short method from the included Game
of life demo.
procedure TGameOfLifeEngine.SetArea(X,Y:Integer);
var Index: Integer;
begin
FNumCellsX := X;
FNumCellsY := Y;
FArea.SetLength(FNumCellsY + 2);
// include two wrap-around lines
for Index := 0 to FArea.Length - 1 do
FArea[Index].SetLength(FNumCellsX + 2);
// include two wrap-around columns
end;

This would get compiled to a following JavaScript


fragment:
/// procedure TGameOfLifeEngine.SetArea(X: Integer; Y: Integer)
/// [line: 91, column: 29, file: GameOfLifeEngine]
,SetArea:function(Self, X$5, Y$5) {
var Index$1 = 0;
Self.FNumCellsX = X$5;
Self.FNumCellsY = Y$5;
$ArraySetLenC(Self.FArea,(Self.FNumCellsY+2),
function(){return[]});
var $temp27;
for(Index$1 = 0,$temp27 = Self.FArea.length;
Index$1<$temp27;Index$1++) {
$ArraySetLen($DIdxR(Self.FArea,Index$1,"
in TGameOfLifeEngine.SetArea [line: 99,
column: 17, file: GameOfLifeEngine]"),
(Self.FNumCellsX+2),false);

Figure 7: the project options

As you can see, this is fairly long-worded JavaScript code


which does some run-time checking (as the Range checking
compiler options was enabled).
If we, however, uncheck the Range checking and enable
Code packing and Code obfuscation options, we'll get
something much more interesting, a very compact
JavaScript code.

And for the end I'd like to show you how to write a Node.js
application (the code also comes from the Mileage clientserver demo).

,rl:function(S,E1,yco){var IB=0;S.ny=E1;
S.kG=yco;$ArraySetLenC(S.Vk,(S.kG+2),
function(){return[]});var $tR;
for(IB=0,$tR=S.Vk.length;IB<$tR;IB++)
{$ArraySetLen(S.Vk[IB],(S.ny+2),false)}}

//start http server


http.createServer(
procedure(request: JServerRequest; response:
JServerResponse)
begin
if request.url.StartsWith('/Mileage/Read')
then
FetchData(response)
else
if equest.url.StartsWith('/Mileage/Save')
then StoreData(request, response)
else response.end('MileageServer v0.1')
end)
.listen(80, '');

Next example (from the Mileage client-server demo)


shows how to generate HTTP GET request and process the
response. It also shows few features of the enhanced Pascal
language used in SmartMS lambda expressions
(anonymous functions) and inline variable declaration.
procedure TMainForm.InitializeForm;
begin
var http := TW3HttpRequest.Create;
http.OnDataReady := lambda (Sender)
for var line in
Sender.ResponseText.Split(#13) do begin
if line = '' then continue;
var data := line.Split(#9);
var item :=
TListTemplate(lbData.Items[lbData.Add]);
item.Date := data[0];
item.Distance := data[1];
item.Volume := data[2];
end;
UpdateChart;
end;
http.Get('http://localhost/Mileage/Read');
end;

46

procedure TServer.Run;
begin
//load http module
var http := NodeJS.http.http;

console_.log([
'Server running at http://127.0.0.1:80/']);
end;

Credits
At the end I'd like to give the credit where it's due.
Smart Mobile Studio wouldn't be there without
Lennart Aasenden, the original author, Jrn Einar
Angeltveit, our CEO, Eric Grange, author of the
Pascal-to-JavaScript compiler, Christian Budde,
who wrote lots of version 2 code including
complete v2 designer, and Andr Mussche, who
wrote RemObjects and DataSnap connectors.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

www.SmartMobileStudio.com

Interview with David I.


As developers remember, Microsoft had releases that did
not follow a calendar year. Sometimes there were several
years between versions of Windows. Apple has followed a
(mostly) annual cycle of releases for OS X and iOS.
Google releases new Android platforms when they are
ready. Microsoft has responded promising annual cycles
and new releases of Windows SDKs within an annual cycle.
We also look at the devices, Smartphones and Tablets
for example, that are released with much more frequency.
Add other devices and gadgets in the home automation,
health/medical, wearable and other categories that are all
programmable and have developer APIs and REST services
and you can see that we need to continue to innovate to stay
on top of all the platforms and devices that developers, their
users and their businesses care about.
The amount of R&D activity inside Embarcadero has
ramped up enormously to meet the opportunities and
deliver capabilities to our customers.
We are spending much more than a lot of other developer
tools companies and delivering more native optimized
compiler, component and library functionality than ever
before.

David seems to have a new job....

Editor:
First to make it easy
Could you explain the plans about updating buying etc of
Delphi and how you want to proceed in the near future?
David I.
We are investing a lot of time and money to expand the
reach of platforms we are supporting for Delphi and
C++Builder.
This is not just a Windows world anymore.
Over the past few years, mobile and device platforms have
expanded the types of applications developers need to
build beyond just the desktop.
At the same time the requirements for applications have
grown to include the Internet, web services, REST services
and cloud computing.
All of these new platforms and architectures have allowed
us to help expand the reach of developers.
Along with these new innovations, we are delivering
enhancements to our products, IDEs, components, libraries
and tools.

For our customers, we offer many ways for them to keep up


with us. Developers can choose what versions/releases they
want to upgrade to.
Developers can also sign up for Software Assurance,
an annual program, so they can receive all of the
technologies when they are available.
Delphi and C++Builder developers have all the choices on
how to proceed and to stay up to date on the latest platform,
device and technology advances. Simply put, we are doing
more for developers today than we have ever done in all the
years since Turbo Pascal version 1.0.
When we released our most recent XE5 release, we were
also testing iOS7 and next generation Android.
We released our iOS7 update in a very timely update soon
after iOS7 appeared. We are committed to verifying that our
products work with the next generation Windows, OS X,
iOS and Android platforms.
Where we need to we will also update our products to take
advantage of new APIs, innovations and opportunities.
Separate from platform support, our R&D teams are also
innovating in IDEs, multi-device designers, tool chain
enhancements, compiler technologies, components and in
the runtime libraries.

All of this is being done to help our existing customers


move forward fast and also reach out to new developers
who want to build apps fast, deploy on multiple devices and
Now we are supporting 4 major platforms - Windows, keep up with new innovations. Will the upgrade or new
Delphi be 6 months from now on? If so there is the danger
OS X, iOS and Android. Each of these platforms
people will wait for quite some updates to engage again?
(and the devices that support them) continues to move ahead
We have our public roadmap that list some of the work we
on their own pace of innovation and release schedules.
are doing with general time frames - for example
This makes it harder to synchronize with all of them on a
C++Builder iOS and Android support this Winter.
specific schedule. We must also work to support the
has managed to create a Beta
for parts
lazarus.
ThereVersion
are so many moving
in the IDEs, compilers,
release cycles of our R&D efforts and also the platforms
RTL and
device/platform
support
thatbe
it is not
easy to
themselves.
If you buy the full source codeFM,(for
Delphi)
you
will
able
predict when we will release updates, new versions and
It was easier in the past, when we were a Windows
to install the first even
working
version.
new products.
only development product, to follow the cycles of
Windows releases.
We have allready tested it.

FastReport

48

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

For example, we had originally planned to have Delphi for


iOS support in XE3, but we needed more time to complete
the ARM compiler and mobile language enhancements,
so these were released when ready in April of this year.
Our first Android support was ready in the summer,
so it fit our "traditional" fall release. Some developers will
remember the early years of Turbo Pascal and Borland
Pascal where we released versions sometimes in
Springtime, sometimes when a new release of DOS or
Windows appeared, and even Turbo Pascal for the Mac
that was released separately on its own schedule.
Some of our customers have waited for releases that
had capabilities they are interested in or need.
Other developers keep up with every release.
Some developers are still using very old Windows only
versions of Delphi (3, 5, 6, 7, 2007, etc).
If Microsoft dropped 32-bit from Windows, I would expect
that most Delphi developers would move forward to use
the 64-bit Delphi compiler.
We are very proud that our products are so good, that
they can be used for years and support multiple versions of
the platforms including Windows, OSX, iOS and Android
versions.
Since we don't time out the releases that developers
purchase, it is their choice when to move forward.
We continue to move forward and new and existing
developers are coming with us. At the same time, we will
continue to help developers understand how to use the
current and latest capabilities for development.
Our DocWiki online documentation system and our
community/partner ecosystem still have all of the
information going back multiple versions, so developers
can get the information they need to be successful
regardless of what version they are using.
We will keep innovating; continue to support the latest
platforms and development technologies across all of the
platforms: Windows, OS X, iOS and Android. If someone
is doing Windows only development on an older Windows
version, they can keep using the latest versions products.

David I
I see no dangers anywhere. We are providing capabilities
for all developers whether they are mature, youngsters or
newbies. We are the only native code, optimizing compiler,
rapid application prototyping developer tools company on
the planet that offers one code base,
multi-device targeting that is on all of the major platforms
Windows, OS X, iOS and Android.
We give you a wide range of RTL functions and
components to improve your ability to build apps for the
platforms and at the same time we allow you to go right
down to the operating system and the hardware if you
need to.
We have been doing these things since the days of DOS
(Turbo Vision) and Windows (Object Windows, VCL and now
FM). We know how to abstract the OS and hardware
without keeping developers out of those spaces.
Everything, the APIs, devices, sensors, operating system
and other hardware features are available for your
programs.
We have millions of seasoned professionals and new
generations of young developers using our products
around the world..
This year, South Africa has chosen Delphi as the
standard programming language and product for their
High Schools computer classes. This means that a new
generation of South African students will learn object
programming, component based development, event
programming and rapid application prototyping in high
school and will be well prepared for college and industry.
Editor
Will you go there?
David Intersimone :
I have been to South Africa one time before and am
planning on going again sometime early next year.
I look forward to my return visit to South Africa.
Thanks David!

We also provide customers, using our latest releases,


with access to past releases of our products.
For new developers and existing customers who want to
move forward, we are here for them. And for those who
eventually need something new - we are continuing to
innovate in advance of their needs.
We are doing more engineering, have invested more in
new technologies and platforms, and acquired additional
capabilities to advance and broaden the depth and breadth
of our product offerings. Embarcadero is spending more
and has more R&D centers around the world than ever in
our history to meet the needs of new customers, existing
customers and future developers.
Editor:
Is there no danger that youngsters or newbies will just go
for the new possibilities especially in the so called new
markets like the far (or closeby) east?

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

49

A simple superscript text editor By David Dirkse


starter

expert

Delphi 7

This article describes a text editor suitable for


formulas with exponents.
The formula is displayed in a paintbox named
formulabox. So, editing including cursor
movement has to be programmed.
The formula must be translated to "plain" text for
further input to procedures for the painting of
graphics or calculations.
Also a procedure takes care of the translation
from plain text to superscript text.

Figure 1: This is a picture of the project:

The top box is an editbox, holding plain text.


The lower box is the paintbox with the superscript text.
Bitbuttons super and plain take care of the translation and
display of texts. While typing text, the first concern is the
destination of the characters: edit- or paintbox.
Therefore is
var

plaintext: boolean;

// true if plaintext editor

Edit- and paintbox are components of Form1.


A form has a property keyPreview of type boolean.
If true keyboard events and characters are , before being
delivered to the active component on the form, presented
in two procedures:
(after procedure to ...end the code is supplied by myself)
1.
procedure TForm1.FormKeyDown(Sender: TObject;
var Key: Word;
Shift: TShiftState);
begin
if plaintext = false then
begin
if (textlength = 0) then exit;
clearcursor;
case key of
VK_LEFT
: kbLeft;
VK_RIGHT : kbRight;
VK_UP
: kbUp;
VK_DOWN
: kbDown;
VK_BACK
: kbBack;
VK_DELETE : kbDelete;
VK_HOME
: kbHome;
VK_END
: kbEnd;
end;//case
key := 0;
showformula;
paintcursor;
end;//if
end;

50

COMPONENTS
DEVELOPERS

Key is a code (word) depending on the key that was


pressed. This procedure is used for the cursor movements
and also for backspace, delete, home and end.
If no further action may take place, the procedure should
supply the code key := 0;
Neglecting this may result in unwanted changes of any
active component on the form. VK stands for "virtual key"
.
VK_LEFT is the code generated when pressing the left
arrow key. kbLeft, kbRight....are small procedures handling
other codes. Showformula paints the formula in the
paintbox.

2.
procedure TForm1.FormKeyPress(Sender: TObject;
var Key: Char);
const fmask = ['a'..'z','A'..'Z','0'..'9','+',
'-','*','/','^','=',';',' ','.',',','(',')'];
var mask : set of char;
begin
mask := fmask;
if plaintext then mask := mask + [#08];
//add backspace character
if not (key in mask) then key := #0
else
if plaintext = false then
begin
addchar(key);
key := #0;
end;
end;

In this case key is the ascii code of the typed character.


If no further action may take place after this procedure then
make key := #0;
key is filtered to eliminate unused characters like #,$,%,&.
Problem is, that an editbox uses the backspace (#08) ascii
code for editing. In the case of plain text therefore, the
backspace code has to be added to the filter.
procedure addchar() inserts the character in the
superscript tekst.
With plain text only the filter does it's work : the character
is automatically send to the editbox which has it's own
editing procedures.
Property activecontrol of a form indicates the active
control (the component that has focus). This control receives
the events from mouse and keyboard.
form1.activecontrol := nil takes care that no
component is active. if assigned(activecontrol)
shows if there is any control active.
In this little project, plaintext = true when activecontrol =
edit1 and plaintext = false when the forms' activecontrol is
nil. Actions for setting and clearing plain text vs.
superscript text editing are in

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

A simple superscript text editor (Continuation 1)


procedure setplaintext;
begin
with form1 do activecontrol := edit1;
clearcursor; //verwijder cursor uit paintbox
plaintext := true;
end;
procedure setsupertext;
begin
form1.activecontrol := nil;
clearcursor; //must be painted later
plaintext := false;
end;

To paint superscript text in the paintbox,


in design time it's properties are set to
width
height
font
font
font

660
30
"courier new",
size 12
style bold

procedure kbLeft;
begin
if cursX > 1 then
begin
dec(cursX);
if cursX - bias < 1 then dec(bias);
setmodeF;
end;
end;

If the cursor exits the box at the left side, the bias must be
adjusted to keep the cursor inside.
setmodeF sets variabele upmode true or false, which takes
care of the vertical cursor position. In this case, the right
character position (F = forward) is copied.
procedure kbBack handles the backspace:

procedure kbBack;
var i : byte;
which yields a fixed character width of 11 pixels.
begin
The formulabox may display 60 characters at the time.
if cursX > 1 then
However, a formula may be much longer,(up to 250
begin
characters) , in which case part of the formula is outside the
for i := cursX-1 to textlength-1 do
box. The number of characters left outside the box is
formtext[i] :=
var bias : byte;
formtext[i+1];
Also the position of the cursor has to be remembered
formtext[textlength].ch := #0;
var cursX : byte;
dec(textlength);
Accuracy is important:
dec(cursX);
if cursX - bias < 1 then dec(bias);
cursX has the index of the character where it is placed
setmodeB;
before in the text. Far left of the paintbox is position 1.
end;
end;

Translation of superscript- to plain text means inserting ^(


when the vertical cursor position changes from low to hi
and inserting ) when to position changes to low again.
Bits 0 and 1 of variabele m encode the current position (bit
0) and previous position (bit 1), where low = 0 and hi = 1.
m = 1 decodes as the transition from low to hi, m = 2
translates for hi to low.
A case statement investigates m.
The value of the bias has to be added for the final cursor
position. Then, the cursor may be in the lower or upper
position which is remembered by
var upmode : boolean = false;
For each character in de string, the position (up, down)
must be recorded.
The text has a special dataformat to facilitate this:
const charwidth = 11;
maxchardisplay = 60; //characters displayed in
paintbox
//max length of text
maxchar = 250;
type TFormText = record
ch : char; //character code
up : boolean; //position
end;
var formtext : array[1..maxchar] of TFormText;
textlength : byte = 0; //number of characters in text

Procedure showformula paints the complete line of


text. For each character
procedure paintchar(n) is called to paint character n
of the text. Please look at the source code for details.
The specific procedures for cursor control are small but
tricky because accurate counting is needed.
Below is procedure kbLeft to advance the cursor 1 place to
the left.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Opposite, when translating plain text to superscript text,


the text must be scanned for ^(
to detect a low to hi transition. Then the number of
parenthesis must be counted : +1 for ( and -1 for ).
Occurrence of a ) together with a zero count detects a hi
to low transition.
A mousedown event in the paintbox calls
Formulabox.mousedown(...) which delivers the (x,y)
coordinates. These are used to calculate the cursorpositie
in de superscript text:
var hh : byte;
begin
hh := bias + ((x+4) div charwidth) + 1;
if hh > textlength + 1
then hh := textlength + 1;
cursX := hh;
if cursX <= textlength
then upmode := formtext[cursX].up
else upmode := false;

Please refer to the source code for more details.


This concludes the description of this small editor project.

COMPONENTS
DEVELOPERS

51

Interview with Gwan Tan - better office


Gwan Tan is the owner of better office.
We met a long time ago and had several times
very interesting discussions especially after
meetings of first time Delphi presentations.
Because we want to present very interesting
people we meet and their companies using Delphi
and Pascal we want to know their opinion about
the market, developments and critical annotations.

So I did my final thesis with that faculty working on my


own project for a Chinese consumer word processor
although I speak no Chinese.
Editor
Was there any follow up for this Chinese consumer?
Gwan
Unfortunately not.
At the end of my project 1989-1990 China had the 'incident'
at the Tiananmen Square.
That caused China to close down for a number of years.
As the targets were the consumers in China any further
investment was considered too risky. So the project 'died'
after demonstrating the first prototype.
Editor
So international situations CAN change the personal
history. Now you have your own company?

Editor
Gwan
Could you give some details about you and your company? Well, I started my first company with two friends just after
my study in 1989/1990.
Gwan
That company was called Co-Assist and we did a lot of
Well, I'm 54 years old. I started programming at university work with Turbo Pascal but later mostly with Paradox
(TUE) in 1977. There we learned languages like ALGOL60 (Borland's database program at the time).
and APL and programming on punch cards for a
In 1997 I left Co-Assist and founded Sibylle IT.
Burroughs mainframe. Later in the study (electrical
A number of years ago I also started a cooperation with a
engineering) I learned Pascal especially Turbo Pascal.
German company called better office.
Turbo Pascal was actually the first program I bought
That cooperation led to founding the company better
legally because of the amount of handbooks coming with
office benelux and is a cooperation between better
it. Copying them at the time was almost as expensive as
office (Germany), Sibylle IT and PSO (Jeroen
buying the program.
Pluimers'company).
Editor
What does TUE mean ?
Gwan
Technical University Eindhoven (at that time it was the THE
Technische Hogeschool EIndhoven).

Editor
How did this cooperation start?
It is unusual that people have interest beyond the border
other countries.

Gwan
Well it started actually at a BorCon (Borland Conference) in
Editor
the USA. I met the owners/directors of better office and on
The technical part of it, was that of any influence?
personal level we had a click.
At the time I had no direct interest in doing any work in
Gwan
Germany. But as I got an invitation for a Christmas Party, I
Electrical engineering is only taught at technical
decided to go to Oldenburg (city in the north of Germany near
universities.
Bremen).
At technical Universities you can achieve the engineers title
So the contact was continued on German soil. With these
(Ir.) At general universities you can become a Master of
first contacts I met other German developers (and
Science, (M Sc) Docterandus in Dutch (Drs.)
publishers) and I was invited to the first German Delphi
But electrical engineering is mostly an applied science.
Conference (EKON).
The more theoretical study is physics.
During the years I got more and more involved with
I always liked the applied sciences more than theory.
German developers and slowly got some clients there too.
In the beginning I wanted to specialize in data
As the German market was getting more and more
communications. But during a training period in the USA
interesting and there seemed to be good opportunities for
I got to my hands on the first Apple Macintosh and found
Dutch developers I sought a more structured approach to
programming a much more interesting area,
the German market leading to better office benelux.
especially with user interfaces and databases.
Back at university I continued in that area,
Editor
also as at the time there was no department of Information
So now I understood you are mainly working in Germany.
Technology.
What special fields of interest?
That area was divided between the mathematical
department and the electro-technical department (faculty of
Digital Systems).

52

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Interview met Gwan Tan - better office (Continuation 1)


Gwan
In the past years our main projects are in Germany
although we still have an old client with sites in the
Netherlands, Ireland, France, Germany and Thailand.
In Germany we specialize on ERP support programs.
We have a production planning system running in
cooperation with the clients SAP system.
We also have a system running with another client that
handles the sales departments, warehouse and distribution
of the clients main German factory.
Editor
so you're mainly doing business with larger contractors?
Is there any specializing like mobile or is that a new field?
Gwan
We work most of all directly for a client.
As our programs are often connected with the backbone
systems of the client we have to cooperate with their other
contractors.
In the future one can foresee much more use of tablets,
handhelds and smartphones even within the factory.
The information people need during a production is not
confined to a working place and therefore to a single
computer.
The people need to have their info with them like they
have their wireless company phones.
Within a warehouse it a already more obvious and the
sales departments especially those sales persons in the
field need to be connected with the main company system.
So we are looking the area of mobile computing and are
therefore very interested in the (future) extensions of
Delphi and Pascal in general.
At present our clients are not ready for mobile computing.
They are in the field of IT mostly conservative,
needing proven technology and do not want to be
pioneers.
So we have some time to develop applications that will get
their interest growing.
Editor
So you -up to now- are actually VCL users?
Gwan
Yes, in our German projects we are using VCL mostly.
Especially Jeroen is investigating/playing with the newer
extensions.
Editor
I got the impression that you are doing websites as well?
What languages do you use for this? Intraweb maybe or
php or...
Gwan
Websites are not our main area. It is mostly our German
colleagues who work in that area.
They use mostly php as far as I know. I have to admit
that I'm not right person to ask about that area.

Gwan
What do you mean with combine with? We have done
projects were we have Pascal clients with Java middleware
etc. Is that what you want to know?
Editor
Yes. And the databases?
Gwan
As for databases we mostly use InterBase or Firebird if we
have the freedom of choice. Often we have to connect and
develop with MySQL, SQL Server or Oracle. Basically we
know InterBase and Firebird best but we are extending our
knowledge and experience in MySQL and SQL Server as
often clients expect us to work with these databases.
Editor
Do you have a project that you are especially proud of?
Gwan
Depends on what to be proud of. Technically I'm very
proud of the first production planning system we
developed for a customer in Mainz.
On a more general level I'm very proud of 'having
survived' so far in the ERP-project just across the border.
That project was partly challenged for technical reasons
but even more for organizational reasons.
At the start the requirements seemed to be quite good
defined but as the project went on it became clearer the
requirements were not as complete as expected.
So much more interaction with the users was necessary to
find out what had to be developed.
In the meantime especially top management put a lot of
pressure on getting the program(s) up and running.
Especially Edwin van der Kraan, my main developer in
this project, had a very hard time.
But now after 2.5 years the system is up and running and
stable. So I'm proud of the project and especially proud of
Edwin.
Editor
What are fields you would especially point at for reasons
of learning and avoiding eventual upcoming problems?
How do you respond to pressure without getting squeezed?
Gwan
Pressure is at almost all projects present. But sometimes
the pressure is very high. To keep the pressure bearable
one has to keep the targets of the project well defined and
plan sufficient moments where especially management can
see the progress made and the direction of the project.
Depending on the part of the project and the pressure of
management one has to plan those moments more often.
But do not forget to explain to management that such
moments cost time and therefore will slow down the
project. These milestones should be useful for checking the
progress and direction of the project.

Editor
Editor
I think planning and explaining the plans are the most
If you work with Pascal what other languages you have to difficult and therefore very often underestimated subjects
combine with?
for a project. Do you have any special advice or helpful
ideas for developers?

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

53

Interview met Gwan Tan - better office (Continuation 2)


Gwan
The main issue here is management.
They are to be considered like normal users.
When you design a screen flow you image to be that user.
What do I want/need to see when I have to do something.
Management is the same like users they (should) know
what they need and like users they do not understand the
technical issues behind the programs.
Therefore one has to prepare the meetings with
management not just by making nice looking planning
sheets but also by showing them as much as possible what
has been developed and want the ideas are for the next
step(s).
Management is often very insecure about software
projects. They need to have the feeling that they are still in
control.
A large part of my job is 'holding their hand' during the
project. Giving them the confidence that the project will
turn out to be a success.
Editor
Do you create a visual overview of what steps could be
taken or what the flow will be? Do you use video for this
or a conferencing room?
Gwan
Depending on the milestone and the audience I will create
power point presentations but with a lot of switching to
the prototype.
Even management knows that power point presentations
do not necessarily cover reality. I like to use a conference
room.
I want to be able to scan the audience for facial expressions
and body language in general. Also in contrast to video
conferencing I believe in the face-to-face communication.
With video there is always a kind of wall in between, a
distance. Still, sometimes a video conference is more
practical. But I would only do that for smaller milestones.
I still would want to have the main persons responsible for
the project with me in the same room.
Editor
So this brings up another subject:
nowadays Embarcadero is doing a lot of video
conferencing.
I must say it seemed interesting. But the details you just
mentioned are not covered by that.
I find these video examples etc. very tiring and at a
distance.
I think there should be more as we had in the past
conferences you could personally attend to.
Gwan
I can see the organizational and financial reasons for video
conferencing etc. but like you I like 'real' conferences much
better.
I still go to such events like the Delphi Tage in Leipzig.
Saturday and the EKON in Cologne in November.
I really miss the old BorCons.
The chance to meet other developers from all over the
world and discuss anything while having a beer in the bar
or in the old days having a cigarette outside of the
conference building.
But I do not expect Embarcadero to organize anything like
that again.

54

COMPONENTS
DEVELOPERS

Editor
We organized that last time in Leiden - Netherlands and
that was a great success - because of meeting each other.
Some reasons: we had 14 speakers and all the speakers as
well the developers were very "touchable". Attendees
thought this was great - so we will do that over again at
our next conferences.
And now for something quite different: What is your
opinion about FireMonkey? Do you ever use it in your
projects?
Gwan
I believe FireMonkey might be the way to give Delphi a
future. I have no idea if it will be a success. I believe in the
Delphi IDE and I love Pascal but as Pascal is almost not
used at schools and universities one has to attract the
future developers and IT-managers.
For that it has to be 'sexy' again. I will have a closer look at
it as soon as I can find more time.
I also hope to get more info from the upcoming
conferences. Online fora are great but listening to the
attendants of (Delphi-) conferences gives a better or at least
other impression.
If the impression is positive I will have a more detailed
look in what way I can use FireMonkey, what kind of
projects, what kind of applications and especially in what
kind of situations.
Clients might be asking about it in the future but I expect
that in the beginning we will have to propose using it to
potential clients.
They often do not know it.
Editor
We have plans for creating a Pascal learning program, at
least for the Netherlands.
Gwan
A Pascal learning program sounds great. The main issue
will be how to get it to the attention of teachers and even
better students? For those who are already interested in
Pascal and want to learn it they will be very happy to find
such a program as Pascal/Delphi-books are getting rare.
But if you can get it to the schools and universities a major
step in getting future Pascal developers is made.
Editor
What do you actually really miss in Delphi?
What would you you like to be changed?
Gwan
I do not miss that much in Delphi. I'm still using Delphi XE
for the current main project.
Missing functionality is mostly covered by extensions like
GExperts and component packs like Developer Express.
My main problem is the documentation of programs.
Most developers do not like to document and they barely
describe what is in each of the modules.
I would like a way for developers to describe their
reasoning for certain parts of the program.
Next to the questions where did he do what.
I often have the question WHY?

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Interview met Gwan Tan - better office (Continuation 3)


Editor
What's your special quality as company (in Delphi?)
towards companies?
Gwan
As mentioned earlier we develop applications with
databases, mostly Delphi and InterBase but sometimes
with Visual Studio and other databases like MySQL, SQL
Server and Firebird. Although we have developed a
number of ERP-related applications we also have done a lot
of work for social housing corporations.
We developed modules connecting with the main
administration systems of those corporations.
We also created several applications that handle a lot of
statistical/market-oriented information and make that
information available to the corporation.
I prefer not to give names of customers although with
social housing corporations it would be possible.
Ok, there are many corporations we worked for.
Often it is easier to mention the cities they are at as that is
mostly their working area.
The main cities are: Maastricht, Nijmegen, Eindhoven,
Nieuwegein, Almelo, Enschede, Ede, Dantumadeel,
Woudenberg and Raamsdonks Veer. I probably have
forgotten several corporations but this should give an idea

Gwan
I have seen a demo of the Leap Motion in Leipzig at the
Delphi Tage. I was quite impressed by its possibilities. It
does need some work to get a decent version out but I can
see some great opportunities with it.
I will have to think some more about itt as I have to find
possible uses for my customers. It might be interesting to
develop some managerial/marketing applications with it for
instance to manipulate the views of graphs etc.
Editor
What's your idea of 3d printing? We want to make it
available for Delphi and are working hard to get that done...

Gwan
I find 3D printing very interesting. I even thought about
getting me one just to try it. Except from finding the time to
'play' with it I did not have an immediate idea for something
to print that I could use. I will first have to spend some time
to think about what I would like to print with it before
searching for the best 3D-printer to do the job. I am certain
that 3D-printing will become more and more important in
the future.
The price has gone down a lot and the capabilities of those
'cheap' printers are improving a lot. To be able to develop
applications with Delphi for it would be great as I expect
Editor
customers to ask for those kinds of applications in the near
How do you feel about the upcoming updates of versions in future. So if you have something for Delphi applications
Delphi every 6 mays?
with 3D-printers let me know. I would be very interested in
The next XE6 will probably be released in April...
trying it.
Gwan
I can understand Embarcadero needs to get a better cash
flow in order to keep investments in development possible
but I do not believe the market will accept updates to be
released that often. Especially if they charge the normal
cost. Developers (and their bosses) will not go for each
update and will only update if one really needs to update.
Also if updates are released that often most updates will be
actually just patches. They should be free anyway and
made available as soon as possible. So if Embarcadero is
really going for a release schedule of once every 6 months
and charges for each update their standard prices I expect
part of the market to turn away from the product.
How large that part will be is hard to predict but as the
Delphi market is already small any part will hurt.
Editor
Did you ever consider the other Pascal? FPC- combined
with Lazarus? Have you ever tried that?

Editor
Sure! In this issue we have started our first articles about
this. There is even a possibility of creating the so called 4th
dimension: Interactive layer printing...Like printing a layer
that is able to react towards light or temperature. One could
even print layers that enable special things like creating an
interactive object with printed chips on it...
Gwan
The future capabilities of 3D-printing are going to be
enormous. Already some creative minds have found new
ways to use 3D-printing. I wonder what will be done with it
in the future. So your articles will give everyone the chance
to become part of that future.
I do think that that is one of the main reasons for magazines,
conferences and fora etc. to be reading and discussing the
future not just what can be done today but also what might
be possible in 5 or 10 years....

Gwan
I have not tried Lazarus yet but especially if Embarcadero
is moving to a 6 months update schedule Lazarus will be a
possible alternative. I'm sure I will have a look at Lazarus
in the next 3-6 months if only to see what it can and how
much work it would be to switch from Delphi to Lazarus.
For now I would not switch to Lazarus but it depends very
much on Embarcadero and their path for the future.
Editor
I have two final questions:
We have been busy creating the new extra userinterface,
not for everything useful but can be the Leap Motion...
Have you heard about that or seen something?

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

55

productivity software building blocks

Using GEO services in Delphi applications


with TMS components
Mapping

Basis of geo services


The fundament on which all geo services are built is the
determination of a position on our planet. For this, in
almost all cases, the system of longitude and latitude is
used. The latitude is a value between -90 and +90 that
defines the angle of the position between south-pole and
north-pole. The longitude is a value between -180 and +180
that defines the position along the equator. These values
are typically expressed in decimals or with degrees,
minutes and seconds. For ease of calculation, most services
use the decimal notation.

The 3 major services that provide digital maps are Google


Maps, Microsoft Bing and Open Street Maps. Digital maps
are provided via a HTTP service and can be displayed via a
browser. These mapping services are targeted mainly at
browser usage, so if we want to take advantage of these
maps from a Delphi application, the main two challenges
are calling a Javascript API from the Delphi application
that is executed in the browser and handle Javascript
events that are being triggered from the map and that
ideally are exposed as class events to a Delphi application.
For a Windows application, to call Javascript functions
from a Delphi application for a map displayed in a
browser, the HTMLWindow.execScript() function can be
used. To handle Javascript events from the Windows
TWebBrowser at Delphi application level, it is required to
implement the IDocHostUIHandler interface. Fortunately,
this is somewhat easier from an IntraWeb application as
the map is displayed typically on the same page where the
IntraWeb application page is rendered. For a FireMonkey
mobile application, a technique similar to the Windows
desktop application is necessary but in this case with the
browser that runs on the mobile device. Fortunately, TMS
software offers components for desktop, web and mobile
application with the same interface, so this makes using the
maps easy on any of these platforms.

If there is a classification to make for what geo services are


being used, we'd divide these in following categories:

To get started with using Google maps, drop the TMS


TWebGMaps component on the form and add the code:

Introduction
In the past few years, a vast array of services
related to a position on our planet earth became
available. With a wide range of components, TMS
software offers seamless access to these services
from Delphi VCL desktop applications, IntraWeb
web applications and FireMonkey mobile
applications running on iOS or Android. As such,
it's mostly your imagination that is the limitation
of what you can do these days with geo services
in your applications.

- Mapping: techniques for displaying & manipulating


maps and visualize information on maps
- Geocoding / reverse geocoding:
techniques for converting an address to a longitude and
latitude and vice versa
- Geolocation: techniques to obtain the longitude
and latitude of a computing device
- Geo POI services: services that provide information /
data about points of interest at a specific position
- Routes: calculate the routes between two
or more positions
In this article, we have examples for using each of these
different services from Delphi applications.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

webgmaps1.MapOptions.DefaultLatitude := 51.2;
webgmaps1.MapOptions.DefaultLongitude := 4.37;
webgmaps1.Launch;

This code snippet initializes the map for position 51.2 /


4.37 that is Antwerp, Belgium. Next we can add a marker
at this location. Markers are exposed via a markers
collection. By default a marker with a hint is added with
this code:
webgmaps1.Markers.Add(51.2,4.37,'Antwerp')
Next task is to draw a polyline on this map. To illustrate
this, code will be added to draw a triangle between 3 cities:
Antwerp, Gent, Brussels.

57

productivity software building blocks

Using GEO services in Delphi applications


with TMS components (Continuation 1)
To draw a polygon, we basically create a path, i.e. a
collection of coordinates between which the polygon is
drawn. The path is of the type TPath and this collection
contains TPathItem that holds the longitude and latitude
of each point. When this path collection is created, it is
added to the PolyLines collection that is available at
TWebGMaps level. This results in code:
var
pt: TPath;
pti : TPathItem;
begin
pt := TPath.Create(webgmaps1);
pti := pt.Add;
pti.Latitude := 51.2;
pti.Longitude := 4.37;
pti := pt.Add;
pti.Latitude := 51.05;
pti.Longitude := 3.7;
pti := pt.Add;
pti.Latitude := 50.85;
pti.Longitude := 4.35;
pti := pt.Add;
pti.Latitude := 51.2;
pti.Longitude := 4.37;
webgmaps1.Polylines.Add(false,false,false,nil,pt,clred,255,2,true,100);
pt.Free;
end;

58

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

productivity software building blocks

Using GEO services in Delphi applications


with TMS components (Continuation 2)
begin
Geocoding / reverse geocoding
// set the address
Geocoding is the process of converting an
WebGMapsGeocoding1.Address :=
address to a longitude and latitude coordinate.
'5617 Scotts Valley Dr #200, Scotts Valley, CA';
Reverse geocoding means obtaining an address
// launch geocoding
WebGMapsGeocoding1.LaunchGeocoding;
starting from a longitude and latitude
// pan map to location retrieved
coordinate. This is typically performed by a
WebGMaps1.MapPanTo(WebGMapsGeocoding1.ResultLatitude,
company that has all the required mapping data
WebGMapsGeocoding1.ResultLongitude);
to perform this function. Some of the services
// add a marker
that provide this are: Google Maps, Microsoft
WebGMaps1 .Markers .Add (WebGMapsGeocoding1 .ResultLatitude ,
WebGMapsGeocoding1 .ResultLongitude ,'Embarcadero' );
Bing, OpenAddresses, Yahoo PlaceFinder and
end;
several smaller services. When looking at the
performance, reliability and quality of the
To have a streetview on the location retrieved , following code can be used :
service, Google easily comes out as best.
Therefore, we created two components that
// set coordinates of location to see with street view
WebGMaps1 .StreetViewOptions .DefaultLatitude :=
make using the Google geocoding and reverse
WebGMapsGeocoding1 .ResultLatitude ;
geocoding service very easy. This is
WebGMaps1 .StreetViewOptions .DefaultLongitude :=
TWebGMapsGeocoding and
WebGMapsGeocoding1 .ResultLongitude
TWebGMapsReverseGeocoding. To use these
// let the map switch to streetview
components is as simple as specifying the
webgmaps1 .StreetViewOptions .Visible := true ;
address and retrieving the result longitude and
latitude and vice versa.
To demonstrate geocoding, we'll perform a
lookup of the geolocation of the Embarcadero
office and have it displayed on a map and
switch to streetview.
With a TWebGMapsGeoCoding component
and TWebGMaps component on the form,
following code obtains the longitude & latitude
of the Embarcadero office in Scotts Valley, adds
a marker on the map and pans the map to it:

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

59

productivity software building blocks

Using GEO services in Delphi applications


with TMS components (Continuation 3)

Geolocation
Geolocation is the name used for all kinds of techniques
that provide information about the location of a device.
These days, most mobile devices have a GPS built-in and
this can return the longitude and latitude of the device
immediately. From Delphi XE4, the non-visual component
TLocationSensor is provided that allows you to get this
information. Using TLocationSensor is easy.
Set LocationSensor.Active = true and via the
event OnLocationChanged, the position is returned.
The accuracy of determining this location is around 10
metres typically. When no GPS is available, we must resort
to different techniques. These techniques can be:
ISP IP address based: many ISPs have a database of
what IP address range is being used in what area. Services
exist that gather this information that can be used to
retrieve location information based on an IP address.
Cell phone based: when a mobile device is connected
to a cell phone access point, the position of the cell phone
access point is known and thus also the area the signal of
this cell phone access point covers. There are also services
that collect this information and make it accessible.
OpenCellID is an example.
See: http://www.opencellid.org/cell/map

60

Wi-Fi service based: Similar as with cell phone based


geolocation, Wi-Fi based geolocation could be an option
when a device is connected via a Wi-Fi access point and
the location of the Wi-Fi access point is known.
An example of a service that collects information on the
position of Wi-Fi access points is
http://www.skyhookwireless.com
For mobile devices, typically a fallback mechanism is
used. First, there is a check if a GPS exists.
When not, it can try to see if it can find a position based
on the IP address, the connected cell phone access point or
Wi-Fi access point. This is the mechanism that is built-in
these days in any HTML5 compliant browser. This allows
web applications to determine where the device is located
that connects to it. This is known in the HTML5 standard
as HTML5 Geolocation API.
To make it easy for desktop applications to determine as
good as possible the location of a machine, TMS software
offers a component TAdvIPLocation that uses the
FreeGEOIP service. To make it easy for IntraWeb web
applications, we have a component
TTIWIPhoneGeolocation that uses the HTML5
Geolocation API to determine the location.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

productivity software building blocks

Using GEO services in Delphi applications


with TMS components (Continuation 4)
Sample code:
if AdvIPLocation1.GetIPLocation then
begin
webgmaps1.MapOptions.DefaultLatitude :=
AdvIPLocation1.IPInfo.Latitude;
webgmaps1.MapOptions.DefaultLongitude :=
AdvIPLocation1.IPInfo.Longitude;
webgmaps1.Launch;
memo1.Lines.Add(AdvIPLocation1.IPInfo.ZIPCode + ' ' +
AdvIPLocation1.IPInfo.City);
memo1.Lines.Add(AdvIPLocation1.IPInfo.CountryName);
end;

This code snippet uses the non-visual component


TAdvIPLocation to obtain the location based on the IP
address of the machine and shows this location on a map
and adds the location city name, ZIP code and country in a
memo.
GEO POI services
Geo POI services is the name of services that provide point
of interest information at a specific location. This includes
things as railway stations, museums, restaurants, sports
infrastructure etc... Typically, a service can provide a list of
points of interest that matches a requested category and a
specific location. It can then offer information such as
address, description, recommendations, opening-hours of
the points of interest. Many services exist that offer this
kind of information but the main suppliers with the biggest
amount of information are FourSquare, Google Place, Bing
Spatial Data Services, Factual...
As FourSquare is one of the leading services, TMS software
has a component TAdvFourSquare that makes using this
service very easy. Typically, all we need to do is specify a
location, i.e. longitude & latitude, specify category of
points of interest we're interested in and possibly also a
radius. The service then returns a list of points of interests
of which we can query a description, photo, etc..
To illustrate this, we'll use the component
TAdvFourSquare, available in the TMS Cloud Pack. To
start using this component, it is necessary to first obtain a
(free) FourSquare application key and secret. You can
register for this at https://developer.foursquare.com
First we obtain the different categories and subcategories
of points of interests that FourSquare has and fill a listbox
with this:

Next, when we click on a category in the listbox, we


perform a query of the top 10 points of interests nearby the
computer location (obtained with TAdvIPLocation) and fill
the listbox with this info:
procedure TForm1.ListBox1Click(Sender: TObject);
var
id: string;
i: integer;
la,lo:double;
begin
id := listbox1.Items[listbox1.ItemIndex];
id := copy(id,pos('/',id)+1, 255);
listbox2.Items.Clear;
AdvIPLocation1.GetIPLocation;
orgla := AdvIPLocation1.IPInfo.Latitude;
orglo := AdvIPLocation1.IPInfo.Longitude;

// This fills the AdvFourSquare Venues


collection with points of interest:
AdvFourSquare1.GetNearbyVenues(
orgla,orglo,'','',id);
// Add the summary line to a listbox
for i := 0 to advfoursquare1.Venues.Count - 1
do
begin
listbox2.Items.Add(
AdvFourSquare1.Venues[i].Summary);
end;
end;

var
i,j: integer;
id: string;

begin
AdvFourSquare1.App.Key := FourSquare_AppKey;
AdvFourSquare1.App.Secret := FourSquare_AppSecret;
AdvFourSquare1.GetCategories;
for i := 0 to advfoursquare1.Categories.Count - 1 do
begin
listbox1.Items.Add(AdvFourSquare1.Categories[i].Summary);
for j := 0 to AdvFourSquare1.Categories[i].SubCategories.Count - 1 do
begin
listbox1.Items.Add(AdvFourSquare1.Categories[i].SubCategories[j].Summary +
'/'+AdvFourSquare1.Categories[i].SubCategories[j].ID);
end;
end;
end;

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

61

productivity software building blocks

Using GEO services in Delphi applications


with TMS components (Continuation 5)
Other than the summary information, FourSquare makes a
lot more information available. This includes the longitude
& latitude of the point of interest, the address, phone
number, website URL when available, opening hours when
available etc... All this information is made easily accessible
via the TFourSquareVenue class in the Venues collection.

Routes
A final important part in useful geo information based
services we can consume from Delphi applications, is
getting routing or directions information to travel from a
location A to a location B. Again, the three major suppliers
of these services are Google with the Google Directions
API, Microsoft with Bing Routes API and the Openroute
service (http://www.openrouteservice.org/). Such service
typically works in following way: we make a request
based on two locations, either specified as two sets of
longitude/latitude of two sets of addresses, the start
address and end address. The service then returns one
route or a set of routes that can be used to travel from start
point to end point. Note that some services also support
waypoints, i.e. points between the start point and end
point the route must go along. A route is typically
returned as a series of textual descriptions of the route to
follow. Each part of the route that is described is called a
leg. A set of routes can be returned when alternative routes
exist. Along the textual description, typically also polygon
data is returned and this polygon data can be used to
visualize the route on a map.

In this example, we use the TWebGMaps component as well


as the TWebGMapsDirectionList. TWebGMapsDirectionList
is a component especially designed to visualize HTML
formatted directions information returned by Google.
Getting directions is as simple as calling
WebGMaps.GetDirections with start and end address. Here
we obtain directions information from San Francisco center
to the Embarcadero offices in Scotts Valley

var
from_address, to_address: string;
begin
from_address := 'San Francisco';
to_address :=
'5617 Scotts Valley Dr #200, Scotts Valley, CA';
webgmaps1.GetDirections(from_address,to_address);
// fill the list component WebGMapsDirectionList with route description
webgmaps1.FillDirectionList(WebGMapsDirectionList1.Items);
// render the route on the map
webgmaps1.RenderDirections(from_address,to_address);
end;

62

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

productivity software building blocks

Using GEO services in Delphi applications


with TMS components (Continuation 6)

Many more options are available, such as specifying


waypoints, route types, language, travel mode
(walking/bike/car) etc... that can be explored with the
TMS TWebGMaps component.
Summary
It is amazing what amount of rich (and in many
cases free) geo information and geo services are
available to us Delphi programmers these days.
With this information, we can add useful
functionality to Delphi Windows based
applications, IntraWeb web based applications
and FireMonkey mobile applications for iOS and
Android devices. At TMS software, we offer a
wide range of components that allow you to
consume these services right-away. This allows
you to avoid studying the various APIs yourself
and be productive immediately to integrate
these in your applications. The several products
covered in this article that make use of these
services are:
TMS WebGMaps:
http://www.tmssoftware.com/site/webgmaps.asp
TMS WebOSMaps:

About the author


Bruno Fierens
Studied civil electronic engineering at university
of Ghent, Belgium (1987-1992) and started a
career as R&D digital hardware engineer.
Besides the fascination for electronics,
Bruno Fierens set the first steps in
programming with Turbo Pascal v3.0
and used all Borland Pascal & Delphi versions
since that time.
In 1996, he founded TMS software for the
activity of application and component
development with Delphi.
TMS software became Borland Technology
Partner in 1998, developed Delphi Informant
award-winning grid & scheduling components
and now has an international team of software
developers working on a large portfolio of
components.
Bruno Fierens is from 2012 Embarcadero MVP
and frequent speaker at Delphi conferences
world-wide.
He does and oversees VCL, IntraWeb,
.NET and FireMonkey component development.

http://www.tmssoftware.com/site/webosmaps.asp

TMS Cloud Pack:


http://www.tmssoftware.com/site/cloudpack.asp
TMS Cloud Pack for FireMonkey:
http://www.tmssoftware.com/site/tmsfmxpack.asp
TMS WebGMaps for FireMonkey:
http://www.tmssoftware.com/
site/tmsfmxwebgmaps.asp
TMS WebOSMaps for FireMonkey:
http://www.tmssoftware.com/
site/tmsfmxwebosmaps.asp
TMS IntraWeb WebGMaps:
http://www.tmssoftware.com/site/webgmaps.asp

TMS IntraWeb WebOSMaps:


http://www.tmssoftware.com/site/webosmaps.asp
TMS IntraWeb iPhone controls pack:
http://www.tmssoftware.com/
site/tmsiwiphone.asp

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

63

productivity software building blocks

Using GEO services in Delphi applications


with TMS components (Continuation 5)

64

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Correcting a bad API design: By Alexander Alexeev


missing user-arguments for callback functions
This article is a supplement to the other article,
"Designing an API: typical mistakes." on page 8 of
this issue. In this article we will discuss how you
can fix one of those mistakes listed in the
previous article, namely: the lack of userarguments for the callback-functions.
Introduction to the callback-functions
In computer programming, a callback is a piece of
executable code that is passed as an argument to other
code, which is expected to call back (execute) the argument
at some convenient time.
For example, if you want to set the timer by using the
Windows API, you can call the SetTimer function, passing
a pointer to the function to it, which will be the callbackfunction. The system will call your function whenever the
timer fires:
procedure MyTimerHandler(Wnd: HWND; uMsg: UINT;
idEvent: UINT_PTR; dwTime: DWORD); stdcall;
begin

// Will be called after 100 ms.


end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetTimer(Handle, 1, 100, @MyTimerHandler);
end;

type
PEnumArgs = ^ TEnumArgs;
TEnumArgs = record
ClassName: String;
Windows: TStrings;
end;
function FindWindowsOfClass(Wnd: HWND;
lpData: LPARAM): Bool; stdcall;
var
Args: PEnumArgs;
WndClassName, WndText: String;
begin
Args := Pointer(lpData);
SetLength(WndClassName,
Length(Args.ClassName) + 2);
SetLength(WndClassName, GetClassName(Wnd,
PChar(WndClassName), Length(WndClassName)));
if WndClassName = Args.ClassName then
begin
SetLength(WndText,
GetWindowTextLength(Wnd) + 1);
SetLength(WndText, GetWindowText(Wnd,
PChar(WndText), Length(WndText)));
Args.Windows.Add(Format ('%8x:%s', [Wnd,
WndText]));
end;
Result := True;
end;

procedure TForm1.Button1Click(Sender: TObject);


var
Here's another example: if you want to find all the windows
Args: TEnumArgs;
on your desktop, you can use the EnumWindows function: begin
// In the Edit, you can enter values ??such as:
function MyEnumFunc(Wnd: HWND; lpData: LPARAM):
// 'TForm1', 'IME', 'MSTaskListWClass', 'Shell_TrayWnd',
Bool; stdcall;
//'TTOTAL_CMD', 'Chrome_WidgetWin_1'
begin
// This is called once for each window in the system
Args.ClassName := Edit1.Text;
end;
Args.Windows := Memo1.Lines;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumWindows(@MyEnumFunc, 0);
end;

Since the callback function normally performs the same


task as the code that sets it, it turns out that the two pieces
of code has to work with the same data. Consequently, the
data from the code setting callback must be somehow
passed to the callback function. For this purpose, the
callback functions provide so-called user-argument: either
a pointer or an integer (such as Native(U)Int, but not
(U)Int), which are not used by the API and transparently
passed to the callback-function. Or (in rare cases), it can be
any value that uniquely identifies the function call.

Memo1.Lines.BeginUpdate;
try
Memo1.Lines.Clear;
EnumWindows(@FindWindowsOfClass,LPARAM(@Args));
finally
Memo1.Lines.EndUpdate;
end;
end;

Note: the equivalent of user-parameters are the Tag property


and Data property, although their use is not always
ideologically correct (correct: a derived child class).

For example, in SetTimer function has idEvent, and


EnumWindows function has lpData. We can use these
parameters to pass arbitrary data. Here, for example, how
to find all the windows of a given class:

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

65

Correcting a bad API design (Continuation 1)


Static callback-method I
nstead of callback-function
As the modern applications are usually built as a set of
classes - it would be nice to isolate the callback function: to
make it not global, but a member of the class. This is easily
done as follows, using a static class methods:

Bad API design


If not too experienced developer did the design of the API,
he may not think about the need for a user-arguments in
callback-functions (as well as many other things - see
previous article). As a result, you may have the following
code on hand:

type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Edit1: TEdit;

type
TCallback = function (FoundData: TData): BOOL;
cdecl;
function RegisterCallback (Callback: TCallback):
Integer; cdecl;

// Adapter
strict private
class function
InternalEnumWindowsCallback(Wnd: HWND; lpData:
LPARAM): Bool; stdcall; static;

As you can see, there are no user-parameter, and the only


argument for callback-function represents the actual data
for the function.

// Hi-level interface
protected
function EnumWindowsCallback(const AWnd:
HWND): Boolean; virtual;
function EnumWindows: Boolean;
end;

Using global variables


Of course, because the developer API - is not you, then you
cannot change the prototype of the callback function. What
to do? Since we cannot send data through a local option,
the only option is a global parameter:

// ...

var
GMemo: TStrings;

function TForm1.EnumWindows: Boolean;


begin
Result: = WinAPI.Windows.EnumWindows(
@InternalEnumWindowsCallback,
LPARAM(Pointer(Self)));
end;
class function
TForm1.InternalEnumWindowsCallback(Wnd: HWND;
lpData: LPARAM): Bool;
var
Form: TForm1;
begin
Form: = TForm1(lpData);
Result := Form.EnumWindowsCallback(Wnd);
end;
function TForm1.EnumWindowsCallback(
const AWnd: HWND): Boolean;
var
WndClassName, WndText: String;
begin
// Your code - it can work with the members of the class.
// For example:
SetLength(WndClassName,
Length(Edit1.Text) + 2);
SetLength(WndClassName, GetClassName(
AWnd, PChar(WndClassName),
Length(WndClassName)));

function MyCallback(FoundData: TData): BOOL;


cdecl;
begin
GMemo.Lines.Add(DataToString(FoundData));
end;
function TForm1.Button1Click(Sender: TObject);
begin
GMemo := Memo1;
RegisterCallback(MyCallback);
end;

Of course, we can make callback-function to be a member


of the class, as we did above (with help of static
keyword); but it should be understood that in this case it
will be all the same global function and variable, but a little
disguised. Accordingly, we cannot access properties and
methods of the class within callback.
This solution may be considered as "satisfactory" because it
works, but uses global variables - that is. Sometimes this
may be acceptable, but often we need to call the callbackfunction multi-threaded (which, however, can be solved
through threadvar) or even just a couple of different calls
within a single thread. In this case, it seems that there is no
reliable way to identify function's data?

if WndClassName = Edit1.Text then


begin
SetLength(WndText,
GetWindowTextLength(AWnd) + 1);
SetLength(WndText, GetWindowText(
AWnd, PChar(WndText), Length(WndText)));
Memo1.Lines.Add(Format('%8x:%s', [AWnd,
WndText]));
end;
Result := True;
end;

66

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Correcting a bad API design (Continuation 2)


The correct solution: dynamic code
However, an experienced programmer can offer
guaranteed working version, "adding" user-parameter to
the existing "bad" API.
This is not an impossible task as it may seem, but the
solution is not trivial.
The essence of the idea is that the user-parameter can be
replaced by the actual function callback that will be unique
to each use. Thus, the call will not be identified by the
parameter, but rather to which callback function is called.
Statement of the problem
Of course, in a pre-compiled file there cannot be an
arbitrary number of functions for arbitrary userarguments. That is why this solution requires generating
code on the fly (in run-time).
Fortunately, it's not too difficult task because you can use
the services of the Delphi compiler to generate the
template. What's more - you may not even know
assembler. But you need to have some idea of the memory
architecture in Windows.
So, suppose we have the following:
type
TData = Integer; // just as an example, it could be
// anything: a pointer, record, etc.
TCallback =
function(FoundData: TData): BOOL; cdecl;
TRegisterCallbackFunc = function(Callback:
TCallback): Integer; cdecl;
TUnregisterCallbackFunc =
procedure(Callback: TCallback); cdecl;
var
RegisterCallback: TRegisterCallbackFunc;
// imported from a DLL
UnregisterCallback: TUnregisterCallbackFunc;
// imported from a DLL

This is our "bad" API. Since the API is usually located in a


separate DLL, then I made an example of a functionvariable, rather than a normal function.
So our challenge: to add support of the user-parameter to
the API.
Note: not always "bad" API is the result of developer mistake.
Sometimes this kind of design is dictated by technical
constraints.
For example, the function SetWindowsHookEx can set
hooks both locally (in the current thread) and globally (for all
processes in the system).
You simply cannot have a user-parameter with the design:
because the user-parameter during installation of the hook
will not make sense at the time of the callback call - as the
hook is called from another process.
However, if you want to install a local trap in the same
process, the user-argument would be quite useful.
For example, if you want to keep track of window
messages for some of your windows.
In this case, the hook will be limited only to your main
thread, but you may have many windows, so threadvar
cannot help you.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Creating a master template


First step: write the following dummy code:
type
TRealCallbackFunc = function(FoundData: TData;
Data: Pointer): BOOL; cdecl;
function InternalCallback(FoundData: TData):
BOOL; cdecl;
var
RealCallback: TRealCallbackFunc;
begin
RealCallback: = Pointer ($ 12345678);
Result: = RealCallback (
FoundData, Pointer ($ 87654321));
end;
procedure TForm1.Button1Click (Sender: TObject);
begin
InternalCallback (0);
end;

Here: InternalCallback - this is a callback-function, the


prototype of which is fully consistent with API.
TRealCallbackFunc is a modified callback-function
with desired prototype, which is different from the API
only by the presence of an additional parameter: it is our
user-parameter. Although the prototype of RealCallback
can be arbitrary, but for ease of debugging, it is desirable
that he would be as similar to InternalCallback as possible.
However, you can also put a user-parameter to be the first
parameter and change the call model from
cdecl/stdcall to register - it will allow you to skip
intermediate adapter in the future, if you ever want to
make this callback-function a member of the class. In this
case, the Self will be transferred in user-parameter - thus
the signature of callback-function binary matches the
signature of the conventional method of the class.
We should just call RealCallback via fixed pointer,
passing a fixed pointer as the user-parameter. The values of
$12345678 and $87654321 are chosen for the simple reason
that it will be easy to spot them in the native code. You can
use any other "magic" values. Surely, in the case of 64-bit
code you need to use 16 bytes magic values instead of 8
byte, for example: $1234567890ABCDEF and
$FEDCBA0987654321.
The RealCallback function is not called directly, but
indirectly - through a RealCallback variable. I will explain
below why it is done.
So, add a direct call to InternalCallback in your code:
procedure TForm1.Button1Click(Sender: TObject);
begin
InternalCallback(0);

// here: TData = Integer (in this example),


// you can pass any value
end;
Warning: do not run this code! This code will result in an
exception being thrown (Access Violation), because it calls
"something" via "trash" address. Instead, set a breakpoint
on the call of InternalCallback in Button1Click. Run
the project, click the button, stop on a breakpoint, go to the
function InternalCallback (via F7 / Step into, but do not use F8
/ Step over) and open CPU-debugger (Ctrl + Alt + C or View /
Debug Windows / CPU View). You will see the following
code:

COMPONENTS
DEVELOPERS

67

Correcting a bad API design (Continuation 3)

You can see that all of the code consists of four lines: begin
(aka - the prologue), assigning the function to variable,
the call, end (aka - the epilogue). Each line is labeled with
comment (in bold font), which shows the location in the
module and the code line itself. Lines below the comment
is a machine (native) code that was compiled from that line.
Do not worry, you absolutely do not need to
understand what it says! All you need is to determine the
beginning and end of the function. It is easy to do: start of
the function is at the very top and marked by a blue line.
The end of the function is clearly in line Unit1.pas.43: end;
- i.e. you need to take another 6 lines after it - up to a line
Unit1.pas.46: begin - line which starts some other function.
Note: If you do know assembler, then you know that the
function ends in a line with a ret command (return control),
and the next line is just a garbage filler.
Select the code with your mouse and copy it somewhere
via the clipboard. Again, you do not need to run this code!
If you do not know assembly language, then all you need
to know:
The first column is the address of the instruction.
These addresses belong to your exe (after all it is the exe code
that we have called from Button1Click) and we are not
interested in these. The second column: hex-code of
machine code. That is, this is the machine code in a pure
form, which will be executed by the processor (CPU)
and this is where we are concerned.
The third column - is the assembly code corresponding to
machine code. The great thing about this listing - we do
not need the assembly code, we need only native code.
Now I'll explain why... Now look: this function has only
two values of the variables:

68

COMPONENTS
DEVELOPERS

Address of the called function


(RealCallback): $12345678
The value of user-parameter: $87654321

All other code is static and it does not depend on anything,


i.e. it will be exactly the same in all cases: for any userarguments for any callback-functions.
This means that if we want to generate the functions
themselves, such as InternalCallback, we can just
copy all the code and simply substitute two numbers: the
address of the function and user-argument.
Address of the function and user-parameter is easy to spot
in machine code, since we used the magic value of
$12345678 and $87654321.
Note:
this is why I used the design with indirect function call
instead of direct call: because in this design the call is
defined as "call the function at that address."
There is clearly "this address" present - which means that it
can be easily changed. If the call was direct, the machine
code were said to "call a function that lies in N bytes before
this instruction". Altering such address would be much
more difficult.
That is all that relates to parsing the template.
Please note that we did not use any special knowledge,
apart from the ability to use a debugger and some common
sense.
Then proceed to the coding. First, you need to copy our
template into a byte array for later use in the program code.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Correcting a bad API design (Continuation 4)


To do this, I first wrote out the entire machine code
(second column):
55
8BEC
83C4F8
C745F878563412
// 12345678
6821436587
// 87654321
8B4508
50
FF55F8
83C408
8945FC
8B45FC
59
59
5D
C3
Remember, you do not need to write these codes manually
- select text within CPU-debugger with the mouse and use
Ctrl + C - this will copy the selected lines to the clipboard.
Then, go to the code editor and paste the text from the
clipboard, and then remove the four comment lines,
as well as the first and last columns, leaving only native
code.
Note that x86 is little-endian architecture - which means
that the numbers "are written in reverse."
Of course, you do not have to change the byte order.

This weird code says that a CallbackTemplate pointer (and


any string - is a pointer, for a static array you'd have to take a
pointer explicitly via @) should be interpreted not as a string,
but as a function of the type TCallback.
So this function, therefore, should be called. Here is a
longer version of the same code:
procedure TForm1.Button1Click (Sender: TObject);
var
Template: AnsiString;
Ptr: Pointer;
CB: TCallback;
begin
Template := CallbackTemplate;
Ptr := Pointer(Template);
CB := TCallback(Ptr);
CB(0);
end;

Set a breakpoint to the function call (in any form - long or


short), run the program, click, stop up on the breakpoint.
Do not press F7!
As we have no source code for the function encoded in
CallbackTemplate, the debugger will try to execute the
whole function in a single pass - which will lead to Access
Violation, as both of our pointer ($12345678 and
$87654321) point to trash. Instead, call the CPU debugger
(Ctrl + Alt + C or View / Debug Windows / CPU View), and
hit the F7 key few times - until you will be transferred to
the familiar code:

After that, I removed the line breaks, combining all the


machine code in a long stream of bytes:
558BEC83C4F8C745F87856341268214365878B450850FF55F8 005B5C70 55 push ebp
005B5C71 8BEC mov ebp, esp
83C4088945FC8B45FC59595DC3
005B5C73 83C4F8 add esp, - $ 08
005B5C76 C745F878563412 mov [ebp-$ 08], $
After that I put in the #$ every two characters and
12345678
designed this constant as a string:
005B5C7D push $ 6821436587 87654321
005B5C82 8B4508 mov eax, [ebp + $ 08]
const
005B5C85 50 push eax
CallbackTemplate: RawByteString =
005B5C86 FF55F8 call dword ptr [ebp-$ 08]
#$55#$8B#$EC#$83#$C4#$F8#$C7#$45#$F8#$78#$56#$34# 005B5C89 83C408 add esp, $ 08
$12#$68#$21 +
005B5C8C 8945FC mov [ebp-$ 04], eax
005B5C8F 8B45FC mov eax, [ebp-$ 04]
#$43#$65#$87#$8B#$45#$08#$50#$FF#$55#$F8#$83#$C4#
005B5C92 59 pop ecx
$08#$89#$45 +
005B5C93 59 pop ecx
#$FC#$8B#$45#$FC#$59#$59#$5D#$C3;
005B5C94 5D pop ebp
005B5C95 C3 ret
Why is the string?
Well, it's easier and shorter than the declaring array of
Make sure that this piece is exactly the same as the source
bytes: no need to insert spaces, commas, specify the
code from the screenshot above. If there is a match, then
dimension of the array.
you've done everything correctly, the template is ready. If
One-byte string, no encoding (RawByteString - it is
not - correct and be more careful in the future.
AnsiString in older versions of Delphi) - so this string is
The code to call RealCallback and InternalCallback can be
actually an array of bytes.
removed. We have our hands on the machine code - which
Now it would be a good idea to check that we have not
is what we needed.
made a mistake.
Change the button click handler as follows:
Note:
of course, the machine code is specific to the target CPU.
procedure TForm1.Button1Click (Sender: TObject);
Machine code for x86-32 will not run on x86-64 and ARM
begin
(and vice versa). That is why, if you are writing crossTCallback(Pointer(CallbackTemplate))(0);
platform code, you will need to repeat the operation for
end;
each target platform and create a constant for each
platform. For example, for x86-64, we get:

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

69

Correcting a bad API design (Continuation 5)

and CallbackTemplate will be:


const
CallbackTemplate: RawByteString =

{$IFDEF CPUX86}
#$55#$8B#$EC#$83#$C4#$F8#$C7#$45#$F8#$78#$56#$34#$12#$68#$21#$43#$65 +
#$87#$8B#$45#$08#$50#$FF#$55#$F8#$83#$C4#$08#$89#$45#$FC#$8B#$45#$FC + #$59#$59#$5D#$C3;

{$ENDIF}
{$IFDEF CPUX64}
#$55#$48#$83#$EC#$30#$48#$8B#$EC#$89#$4D#$40#$48#$B8#$EF#$CD#$AB#$90 +
#$78#$56#$34#$12#$48#$89#$45#$20#$8B#$4D#$40#$48#$BA#$21#$43#$65#$87 +
#$BA#$DC#$FE#$00#$FF#$55#$20#$89#$45#$2C#$8B#$45#$2C#$48#$8D#$65#$30 + #$5D#$C3;

{$ENDIF}
Note:
you can do it even for ARM (iOS and Android), but for that you need to run your code on a real device, not in the
emulator (which uses x86). Unfortunately, I have no iOS or Android devices, so I cannot give you an example with native
ARM code.
Dynamic code generation
The next step - we need to create a real callback function (with real addresses) from the CallbackTemplate template.
Actually, it is very simple - just replace the addresses in the template and the code is ready. There is only a small caveat:
any executable code must be placed in a memory page that has the attribute of the EXECUTE in the x86 architecture. If we
simply allocate memory (GetMem/AllocMem or just use a string array, or some other buffer), it will be the "data": it will
have access to read, write, but not execute. Therefore, an attempt to call this code will result in Access Violation.
Note: "read" and "execute" were equivalent in early generations of x86 processors. Therefore, although technically put
equality between them was never correct, some lazy developers took advantage of this feature of early x86
implementations and pass control to the code in the data segment. Again: it was never correct, but it worked on older
processors. Now, this code will crash. See also http://en.wikipedia.org/wiki/Data_Execution_Prevention.
Let us remember how the memory manager works: it splits the memory page into blocks of memory that the program
"allocates" from memory. This means that we cannot just take the memory via Delphi functions and change page
attributes: this memory will be located in the same memory page along with some other data, and by changing access to
the page we would change the access to some other data. For this reason, we need to allocate memory from the system
directly, without intermediaries helpers.
In summary, here is the appropriate code:

70

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Correcting a bad API design (Continuation 6)


unit FixCallbacks;
interface

// Function dynamically creates code from a template


// The template should use values of // $12345678/$1234567890ABCDEF and $87654321/$FEDCBA0987654321
// as stubs for callback function and user argument respectively. Result of this function can be passed as callback into "bad" API
function AllocTemplate(const ATemplate: RawByteString; const ACallback, AUserParam: Pointer): Pointer;

// Function releases dynamic code allocated via AllocTemplate


// You should pass result from AllocTemplate as ATemplate argument into DisposeTemplate
procedure DisposeTemplate(const ATemplate: Pointer);
implementation
uses Windows;
{$IFDEF CPU386}
{$DEFINE CPU32}
{$ENDIF}
{$IFDEF CPUX64}
{$DEFINE CPU64}
{$ENDIF}
{$IFDEF CPUARM}
{$DEFINE CPU32}
{$ENDIF}
function AllocTemplate(const ATemplate: RawByteString; const ACallback, AUserParam: Pointer): Pointer;
procedure StrReplace(var ATemplate: RawByteString; const ASource, ADest: NativeUInt);
var X: Integer;
begin
for X := 1 to Length(ATemplate) - SizeOf(ASource) do
if PNativeUInt(@ATemplate[X])^ = ASource then
begin
PNativeUInt(@ATemplate[X])^ := ADest;
Break;
end;
end;
var OrgPtr: Pointer; OrgSize: Cardinal; Ptr: PPointer; ActualTemplate: RawByteString; Dummy: Cardinal;
begin
Result := nil; ActualTemplate := ATemplate;
OrgSize := Length(ATemplate);
OrgPtr := VirtualAlloc(nil, OrgSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
if not Assigned(OrgPtr) then Exit;
try
Ptr := OrgPtr;
StrReplace(ActualTemplate,
{$IFDEF CPU32}$12345678{$ENDIF}{$IFDEF CPU64}$1234567890ABCDEF{$ENDIF},
NativeUInt(ACallback));
StrReplace(ActualTemplate,
{$IFDEF CPU32}$87654321{$ENDIF}{$IFDEF CPU64}$FEDCBA0987654321{$ENDIF},
NativeUInt(AUserParam));
Move(Pointer(ActualTemplate)^, Ptr^, Length(ActualTemplate));
if not VirtualProtect(OrgPtr, OrgSize, PAGE_EXECUTE_READ, @Dummy) then Exit;
if not FlushInstructionCache(GetCurrentProcess, OrgPtr, OrgSize) then Exit;
Result := Ptr; OrgPtr := nil;
finally
if Assigned(OrgPtr) then VirtualFree(OrgPtr, 0, MEM_RELEASE);
end;
end;
procedure DisposeTemplate(const ATemplate: Pointer);
begin
if ATemplate = nil then Exit;
VirtualFree(ATemplate, 0, MEM_RELEASE);
end;
end.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

71

Correcting a bad API design (Continuation 7)


This module offers two universal functions for dynamic code generation of callback-functions. The logic of the code
should be clear from the preliminary description of the code idea.
With these functions, we can alter the interface module for the "bad" API as follows:
// ...
interface
// ...
type
TData

= Integer; // remains the same

// New type as TCallback replacement, with added lpUser


TRealCallback
= function(FoundData: TData; const lpUser: Pointer): BOOL; cdecl;
// New register functions
function RegisterCallback(const Callback: TRealCallback;
const lpUser: Pointer; out Cookie: Pointer): Integer;
procedure UnregisterCallback(const Cookie: Pointer);
implementation
uses
FixCallbacks; // "magic" functions
// Old declarations are hidden in implementation
type
TCallback
= function(FoundData: TData): BOOL; cdecl;
TRegisterCallbackFunc
= function(Callback: TCallback): Integer; cdecl;
TUnregisterCallbackFunc = procedure(Callback: TCallback); cdecl;
var
InternalRegisterCallback: TRegisterCallbackFunc;
InternalUnregisterCallback: TUnregisterCallbackFunc;
function RegisterCallback(const Callback: TRealCallback;
const lpUser: Pointer; out Cookie: Pointer): Integer;
var
UniqueCallback: TCallback;
begin
Cookie := AllocTemplate(CallbackTemplate, @Callback, lpUser);
UniqueCallback := TCallback(Cookie);
Result := InternalRegisterCallback(UniqueCallback);
end;
procedure UnregisterCallback(const Cookie: Pointer);
begin
InternalUnregisterCallback(TCallback(Cookie));
DisposeTemplate(Cookie);
end;
// ...

And then we can write code like this:


function MyCallback(FoundData: TData; const lpUser: Pointer): BOOL; cdecl;
var
Self: TForm1;
begin
Self := TForm1(lpUser);
Self.Memo1.Lines.Add(IntToStr(FoundData)); // <- NEW! Access to form's fields
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// FCallbackHandle is a private form's field
RegisterCallback(MyCallback, Pointer(Self), FCallbackHandle);
end;
procedure TForm1.Button2Click (Sender: TObject);
begin
UnregisterCallback(FCallbackHandle);
FCallbackHandle := nil;
end;

72

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Correcting a bad API design (Continuation 8)


Voila! Da Magics!
A practical example
Please note that the AllocTemplate and DisposeTemplate
functions are universal and do not depend on your code.
To illustrate the versatility of this - let's write code to
monitor window messages using SetWindowsHookEx.
We will use the WH_GETMESSAGE hooks. To do this, we
need to create a template first. Write this code:
type
TGetMsgProc = function(Self: Pointer; code:
Integer; _wParam: WPARAM; _lParam: LPARAM):
LRESULT;
function InternalGetMsgProc(code: Integer;
_wParam: WPARAM; _lParam: LPARAM): LRESULT;
stdcall;
var
GetMsgProc: TGetMsgProc;
begin
GetMsgProc := Pointer({$IFDEF
CPUX86}$12345678{$ENDIF}{$IFDEF
CPUX64}$1234567890ABCDEF{$ENDIF});
Result := GetMsgProc(Pointer({$IFDEF
CPUX86}$87654321{$ENDIF}{$IFDEF
CPUX64}$FEDCBA0987654321{$ENDIF}), code,
_wParam, _lParam);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
InternalGetMsgProc(0, 0, 0);
end;

We get such an assembly listing for this code:


Win32
Unit1.pas.34: begin
005B5C64 55
push ebp
005B5C65 8BEC
mov ebp,esp
005B5C67 83C4F8
add esp,-$08
Unit1.pas.35: GetMsgProc := Pointer({$IFDEF
CPUX86}$12345678{$ENDIF}{$IFDEF
CPUX64}$1234567890ABCDEF{$ENDIF});
005B5C6A C745F878563412
mov [ebp-$08],$12345678
Unit1.pas.36: Result :=
GetMsgProc(Pointer({$IFDEF
CPUX86}$87654321{$ENDIF}{$IFDEF
CPUX64}$FEDCBA0987654321{$ENDIF}), code, _wParam,
_lParam);
005B5C71 8B4510
mov eax,[ebp+$10]
005B5C74 50
push eax
005B5C75 8B4D0C
mov ecx,[ebp+$0c]
005B5C78 8B5508
mov edx,[ebp+$08]
005B5C7B B821436587
mov eax,$87654321
005B5C80 FF55F8
call dword ptr [ebp$08]
005B5C83 8945FC
mov [ebp-$04],eax
Unit1.pas.37: end;
005B5C86 8B45FC
mov eax,[ebp-$04]
005B5C89 59
pop ecx
005B5C8A 59
pop ecx
005B5C8B 5D
pop ebp
005B5C8C C20C00
ret $000c

Win64
Unit1.pas.34:
begin
00000000006990B0 55
push rbp
00000000006990B1 4883EC30
sub rsp,$30
00000000006990B5 488BEC
mov rbp,rsp
00000000006990B8 894D40
mov
[rbp+$40],ecx
00000000006990BB 48895548
mov
[rbp+$48],rdx
00000000006990BF 4C894550
mov
[rbp+$50],r8
Unit1.pas.35: GetMsgProc := Pointer({$IFDEF
CPUX86}$12345678{$ENDIF}{$IFDEF
CPUX64}$1234567890ABCDEF{$ENDIF});
00000000006990C3 48B8EFCDAB9078563412 mov
rax,$1234567890abcdef
00000000006990CD 48894520
mov
[rbp+$20],rax
Unit1.pas.36: Result :=
GetMsgProc(Pointer({$IFDEF
CPUX86}$87654321{$ENDIF}{$IFDEF
CPUX64}$FEDCBA0987654321{$ENDIF}), code, _wParam,
_lParam);
00000000006990D1 48B92143658709BADCFE mov
rcx,$fedcba0987654321
00000000006990DB 8B5540
mov
edx,[rbp+$40]
00000000006990DE 4C8B4548
mov
r8,[rbp+$48]
00000000006990E2 4C8B4D50
mov
r9,[rbp+$50]
00000000006990E6 FF5520
call qword ptr
[rbp+$20]
00000000006990E9 48894528
mov
[rbp+$28],rax
Unit1.pas.37: end;
00000000006990ED 488B4528
mov
rax,[rbp+$28]
00000000006990F1 488D6530
lea
rsp,[rbp+$30]
00000000006990F5 5D
pop rbp
00000000006990F6 C3
ret

Cut the machine code and make it into a constant:


const
GetMsgProcTemplate: RawByteString =

{$IFDEF CPUX86}
#$55#$8B#$EC#$83#$C4#$F8#$C7#$45#$F8#$78#$56#$34#
$12#$8B#$45#$10#$50#$8B#$4D +
#$0C#$8B#$55#$08#$B8#$21#$43#$65#$87#$FF#$55#$F8#
$89#$45#$FC#$8B#$45#$FC#$59 +
#$59#$5D#$C2#$0C#$00;

{$ENDIF}
{$IFDEF CPUX64}
#$55#$48#$83#$EC#$30#$48#$8B#$EC#$89#$4D#$40#$48#
$89#$55#$48#$4C#$89#$45#$50 +
#$48#$B8#$EF#$CD#$AB#$90#$78#$56#$34#$12#$48#$89#
$45#$20#$48#$B9#$21#$43#$65 +
#$87#$09#$BA#$DC#$FE#$8B#$55#$40#$4C#$8B#$45#$48#
$4C#$8B#$4D#$50#$FF#$55#$20 +
#$48#$89#$45#$28#$48#$8B#$45#$28#$48#$8D#$65#$30#
$5D#$C3;

{$ENDIF}

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

73

Correcting a bad API design (Continuation 9)


Everything is ready to go, you can call the code now:
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FHook: Pointer;
FHookHandle: THandle;
function GetMsgProc(code: Integer; _wParam: WPARAM; _lParam: LPARAM): LRESULT;
procedure InstallHook;
procedure UninstallHook;
end;
var
Form1: TForm1;
implementation
uses
FixCallbacks;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
InstallHook;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UninstallHook;
end;
function TForm1.GetMsgProc(code: Integer; _wParam: WPARAM; _lParam: LPARAM): LRESULT;
// Determinates if this window message is designated to our form or any of its sub-windows
function FindParent(AWnd: HWND; const ATarget, AException: HWND): Boolean;
begin
Result := False;
while AWnd <> 0 do
begin
if AWnd = AException then
Break;
if AWnd = ATarget then
begin
Result := True;
Break;
end;
AWnd := GetParent(AWnd);
end;
end;
var
Msg: PMsg;
begin
Msg := PMsg(_lParam);
// Is this a message for our form or its components (excluding the Memo)?
if (code = HC_ACTION) and FindParent(Msg.hwnd, Handle, Memo1.Handle) then
begin
// If yes then log the window message
Memo1.Lines.Add(Format('Wnd: %d, Message: %d, wParam: %d, lParam: %d', [Msg.HWnd, Msg.message,
Msg.wParam, Msg.lParam]));
end;
Result := CallNextHookEx(FHookHandle, code, _wParam, _lParam);
end;

74

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Correcting a bad API design (Continuation 10)


procedure TForm1.InstallHook;
const
GetMsgProcTemplate: RawByteString =

{$IFDEF CPUX86}
#$55#$8B#$EC#$83#$C4#$F8#$C7#$45#$F8#$78#$56#$34#$12#$8B#$45#$10#$50#$8B#$4D +
#$0C#$8B#$55#$08#$B8#$21#$43#$65#$87#$FF#$55#$F8#$89#$45#$FC#$8B#$45#$FC#$59 +
#$59#$5D#$C2#$0C#$00;

{$ENDIF}
{$IFDEF CPUX64}
#$55#$48#$83#$EC#$30#$48#$8B#$EC#$89#$4D#$40#$48#$89#$55#$48#$4C#$89#$45#$50 +
#$48#$B8#$EF#$CD#$AB#$90#$78#$56#$34#$12#$48#$89#$45#$20#$48#$B9#$21#$43#$65 +
#$87#$09#$BA#$DC#$FE#$8B#$55#$40#$4C#$8B#$45#$48#$4C#$8B#$4D#$50#$FF#$55#$20 +
#$48#$89#$45#$28#$48#$8B#$45#$28#$48#$8D#$65#$30#$5D#$C3;

{$ENDIF}
type
TGetMsgProc = function(code: Integer; _wParam: WPARAM; _lParam: LPARAM): LRESULT of object;
var
T: TGetMsgProc;
M: TMethod;
begin
UninstallHook;
T := GetMsgProc;
M := TMethod(T);
FHook := AllocTemplate(GetMsgProcTemplate, M.Code, M.Data);
Win32Check(Assigned(FHook));
FHookHandle := SetWindowsHookEx(WH_GETMESSAGE, FHook, HInstance, GetCurrentThreadId);
Win32Check(FHookHandle <> 0);
end;
procedure TForm1.UninstallHook;
begin
if FHookHandle <> 0 then
begin
UnhookWindowsHookEx(FHookHandle);
FHookHandle := 0;
end;
if Assigned(FHook) then
begin
DisposeTemplate(FHook);
FHook := nil;
end;
end;
end.

This example sets a hook on the window messages. Every


message that targets our form or any component on it,
excluding Memo, will be logged in the Memo.
In this example, we pass the user-argument as the first
parameter, and use the register calling convention instead
of stdcall. And so we can use the form method as a
callback. There is no need to introduce a static class method
adapter. But be careful: the compiler cannot check you
here. If you make a mistake in the method's signature, you
crash your application.
That's all I wanted to say today.
IMPORTANT NOTE
The approach described in this article is a hack.
This means that it is a crutch, a workaround,
a way to get the code to work somehow.
It is no excuse for writing "bad" API. If you have control
over the callback-function prototypes - change them!
Introduce the support for user-arguments.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

About the Author:


Alexander Alexeev ( )
Alexander started working with Delphi while still
at university. Following graduation as a Master of
Mathematics, he took several jobs in the Delphi
world. Today he works for the EurekaLab s.a.s.
company, where he develops and supports
EurekaLog for Delphi/C++ Builder. Alex lives in
the Russian Federation and maintains a personal
blog, where he writes about Delphi.
Some of his articles are available in English at
eurekalog.blogspot.com. His favourite topics are
debugging-related.
He is one of our best authors making you
understand how Programming works...

COMPONENTS
DEVELOPERS

75

maXbox
PURE CODE FOR
OBJECT SCRIPTING

Features

mobile programming, no installation, administration or configuration needed


easy to deploy with text code or byte code scripting
big library of 430 examples and exercices
debug, recompile and decompile function
worldwide 28 tutorials with maxbox starter series, arduino and Blix
also starts from stick or share on linux systems
softpedia, d3000 and heise award EKON best
COM, serial, html, xml, http, rest and ftp functions (indy sockets)
more than 15150 functions in V3.9.9
contains maXcom, maXbase, maXtex, maXnet and maXbook
Units of JEDI,CLX,VCL,PCRE,SysTools,Indy,TurboPower in Delphi VirtualMachine
Units Explorer, WebServer, Browser and CryptoBox on Board

http://sourceforge.net/projects/maxbox

Pure Code By Max Kleiner


Coding with maXbox

maXbox

Now let's take a look at the code of this project. Our first line
is
01 program Motion_HTTPServer_Arduino41_RGB_LED;

We have to name the game, means the program's name is


above.

maXbox or Hex in the Box


maXbox is a free scripter tool with an inbuilt
Delphi engine of Pascal Script in in one exe
(including several DLLs and helper files)! It is
designed for teaching, develop, test and
analyzing apps and algorithms and runs under
Win and Linux (CLX) to set Delphi in a box
without installation or administration. The tool is
based on an educational program with examples
and exercises (from biorhythm, form builder to
how encryption works). Units are precompiled
and objects invokable! With a 26 part tutorial for
coders or say it Pure Code for the Pascal
Community (PC^2).
Get the Code
So let's get a real example, the box has a lot of it.
The tool is split up into the toolbar across the top,
the editor or code part in the centre and the output
window at the bottom.
Change that in the menu /view at our own style.
We will start with an extract of a http-server demo, just to
show you several objects and a form in maXbox.

In maXbox you will start the web server example as a


script, so the web server IS the script that starts the
Indy objects, configuration from ini-file and a browser
too; on board is also an add-on or more:
Options/Add_ons/Easy_Browser/.
Before this starter code will work you will need to
download maXbox from a website. It can be downloaded from
http://sourceforge.net/projects/maxbox
(you'll find the download to maxbox3.zip on the top of the page).
Once the download has finished, unzip the file, making
sure that you preserve the folder structure as it is. If you
double-click maxbox3.exe the box opens a default demo
program.
Test it with F9 / F2 or press Compile and you should
hear a sound. So far so good now we'll open the examples:
305_webserver_arduino3ibz_rgb_led.txt

This example requires two instant objects


(you remember, all objects are converted and precompiled)
from the classes: TIdCustomHTTPServer and
TComPort so the second one is to establish a
connection with the COM Ports to Arduino (see below).
TComPort by Dejan Crnila
(http://sourceforge.net/projects/comport/)
are Delphi/C++ Builder serial communications
components.
It is generally easy to use for basic serial communications,
alternative to the TurboPower ASYNCPro.
It includes 5 components:
TComPort,
TComDataPacket,
TComComboBox,
TComRadioGroup and
TComLed.
With these tools you can build serial communication apps
easier and faster than ever.
First we start with the web server and second we explain the
COM port.
After creating the object in line 125 we use first methods to
configure our server calling Port and IP. The object makes a
bind connection with the Active method by passing a web
server configuration.
125 HTTPServer:= TIdCustomHTTPServer.Create(self);
So the object HTTPServer has some methods and
properties like Active you can find in the
TIdCustomHTTPServer.pas unit or IdHTTPServer library.
A library is a collection of code or classes, which you can
include in your program or in maXbox already done. By
storing your commonly used code in a library, you can
reuse more code.
Let's get back to our HTTP Create in line 125. In line 131
and 132 you see a Port and IP address configuration of a
const in line 13, instead of IP you can also set a host name
as parameter.
126
127
128
129
130
131
132
133
134
135
136

with HTTPServer do begin


if Active then Free;
if not Active then begin
bindings.Clear;
bindings.Add;
bindings.items[0].Port:= APORT; // 8080
bindings.items[0].IP:= IPADDR; // 192.168.1.53'
Active:= true;
onCommandGet:= @HTTPServerGet;
PrintF('Listening HTTP on %s:%d.'
,[Bindings[0].IP, Bindings[0].Port]);
end;

If you can't find the two files try also the zip-file loaded
from:
http://www.softwareschule.ch/examples/305_w
ebserver_arduino3ibz_rgb_led.txt

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

77

Coding with maXbox (Continuation 1)

maXbox

Figure 1: Code design with Hex in the Box

Although you can find plenty to complain about in this


code, its really not that bad. Its compact and simple and
easy to understand. However, within this code it is easy to
see the power of scripting because it's agile and high
available but you can't hide the functionality.
If a maXbox script or app is programmed with the default
host standard, it is always started relative to the path
where the maXbox3.exe as the host itself is:
playMP3(ExePath+'examples\maxbox.mp3');

This problem might not be identified in the testing process,


since the average user installs to the default drive of the
archive and directory and testing might not include the
option of changing the installation directory.
By the way you find all info concerning a script or app in
menu /Program/Information/

So for example you want to play a song or refer to other


external resources in a script, your external file will be
found relative to ExePath():
E:\Program Files\maxbox\maxbox3\
examples\maxbox.mp3'
ExePath is a useful function where you always get the path
of maXbox.
If someone tries to start (install) the script or the app to or
from a different drive for space or organizational reasons,
it may fail to (install) or to run the script after installation.
(You don't have to install maXbox or a script anyway, just
unzip or copy the file.)
A solution might be an absolute path:
myMemo.lines.saveToFile('D:\data\examples\mymemo
_tester.txt');
Figure 2: menu /Program/Information/

78

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

maXbox

Coding with maXbox (Continuation 2)


Another solution to prevent hard coded literals is a
constant or the call of a user dialog. An indirect reference,
such as a variable inside the program called 'FileName',
could be expanded by accessing a "select browse for file"
dialog window, and the program code would not have to
be changed whwn the file is moved.
procedure GetMediaData(self: TObject);
begin
if PromptForFileName(selectFile,
'Media files (*.mp3)|*.mp3|*.mpg)|*.mpg', '',
'Select your mX3 media file Directory',
'D:\kleiner2005\download', False)
then begin

// Display this full file/path value


However it is advisable for programmers and developers
not to fix the installation path of a program or hard code
some resources, since the default installation path is
different in different natural languages, and different
computers may be configured differently. It is a common
assumption that all computers running Win have the
primary hard disk labelled as drive C:\,
but this is not the case.
As you will see the configuration of maXbox is possible
with a boot loader script and a simple ini-file too.
Extensions are possible with the Open Tools API and

a small CLI (Command Line Interface).


Tutorials (for downloading to the harddisk)
You can read or download tutorials 1 till 26 from the
program: /Help/Tutorials/:
from the Internet:
http://sourceforge.net/apps/
maXbox
mediawiki/maxbox/
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial
Tutorial

00 Function-Coding (Blix the Programmer)


01 Procedural-Coding
02 OO-Programming
03 Modular Coding
04 UML Use Case Coding
05 Internet Coding
06 Network Coding
07 Game Graphics Coding
08 Operating System Coding
09 Database Coding
10 Statistic Coding
11 Forms Coding
12 SQL DB Coding
13 Crypto Coding
14 Parallel Coding
15 Serial RS232 Coding
16 Event Driven Coding
17 Web Server Coding
18 Arduino System Coding
18_3 Arduino RGB LED Coding
19 WinCOM /Arduino Coding
20 Regular Expressions RegEx
21 Android Coding (coming 2014)
22 Services Coding
23 Real Time Systems
24 Clean Code
25 maXbox Configuration
26 Socket Programming with TCP
27 XML & TreeView (coming 2014)
28 Closures (coming 2014)
29 UML Scripting (coming 2014)
30 Web of Things (coming 2014)

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

We go on with the boot loader and his functionality.


maXbox and the script loader system has default folders
which organize files logically on the hard disk.
You can also copy all 6 folders and root files to a folder on
your USB-stick that stores the same content in your
maXbox installation folder and it will start from the stick!
23.05.2013
23.05.2013
09.05.2013
09.05.2012
12.05.2013
17.03.2013
29.03.2013
11.12.2007
11.11.2012
09.04.2013
28.11.2010
27.10.2005
07.11.2010
07.02.2013
21.10.2011
02.01.2009
11.05.2013
11.05.2013
12.05.2013
03.12.2012
12.05.2013
12.05.2013
21.04.2012
14.11.2005
10.12.2012
12.05.2013
11.10.2010

<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
97'370
254'464
580'096
5'426
3'866
103'424
138
10'544
59'060
71'807
11'887'616
5'133'220
994
12'503
42'773
2'309'571
9'533
383'488
17'202
36'854
135'168

docs
examples
exercices
crypt
source
web
bds_delphi.dci
dbxint30.dll
dmath.dll
firstdemo3.txt
firstdemo3.uc
income.dll
maildef.ini
maxbootscript_.txt
maxbox.mp3
maxbox.png
maxbox3.exe
maxbox3clx
maxboxdef.ini
maxboxerrorlog.txt
maxboxnews.htm
maxbox_functions_all.pdf
maxdefine.inc
midas.dll
pas_includebox.inc
readmefirst_maxbox3.txt
TIFFRead.dll

When you start the box a boot script is loaded.


Within the boot script, you can perform common source
control tasks, such as file check in, check out, and of course
change IDE settings and synchronization of your current
version. This is a script where you can put all the global
settings and styles of the IDE of maXbox, for example:
with maxForm1 do begin
caption:= caption +'Boot Loader Script
maxbootscript.txt';
color:= clteal;
IntfNavigator1Click(self);
tbtnCompile.caption:= 'Compile!';
tbtnUsecase.caption:= 'UML UC';
maxform1.ShellStyle1Click(self);
memo2.font.size:= 16;
Info1Click(self);
end;
Writeln('BOOTSCRIPT ' +BOOTSCRIPT+ ' loaded')

When you want to see a complete copy of that file, look at:
07.02.2013

10'544 maxbootscript_.txt

When you delete the underscore in the filename to


maxbootscript.txt the system performs next time when you
load maXbox and presents you with a different view. This
boot script results in the picture below for example. The
trick of renaming the file has a simple explanation. The inifile default to load the boot script is YES so it can be easier
to rename the file instead of change the ini-file to set to YES,
cause of missing permissions, testing or so:
BOOTSCRIPT=Y
Maybe you want to change the colour or the caption of a
button or a frame; you can do this by accessing the Open
Tools API of the object maxForm1.

COMPONENTS
DEVELOPERS

79

maXbox

Coding with maXbox (Continuation 3)

maXbox configuration
<<extended>>

Start maXbox
Load Boot
Script

<<extended>>

bds_delphi.dci
(templates)

<<include>>

Actor HEX
in the BOX

Parse Ini
File

maxbootscript.txt

maxboxdef.ini

PRE PROCESSING

Compile Script
maxboxdef.ini
include file

<<include>>

Run App

Byte Code
Figure 3: Configuration Step by Step

80

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Coding with maXbox (Continuation 4)


In this section we deal with multiple instances of maXbox
and its creation. You can create multiple instances of the
same app to execute multitask code, just type <F4>. For
example, you can launch a new instance within a script of
the box in response to some user action, allowing each
script to perform the expected response.
ExecuteShell(ExePath+'maxbox3.exe','"'
+ExePath+'examples\'+script+'"');
S_ShellExecute(ExePath+'maxbox3.exe',
ExePath+'examples\'+script,secmdopen);

There's no good way to launch one application (maXbox)


with multiple scripts in it.
Maybe with OLE Automation in that sense you open office
programs (word, excel) or other external shell objects.
CreateOleObject creates a single uninitialized object of
the class specified by the ClassName parameter.
ClassName specifies the string representation of the
Class ID (CLSID).
CreateOleObject is used to create an object of a
specified type when the CLSID is known and when the
object is on a local or in-proc server.
Only the objects that are not part of an aggregate are
created using CreateOleObject.
Try the example of OLE Objects
318_excel_export3.TXT and the tutorial 19.
An external script or a least a second one could also be a
test case to compare the behaviour.
Each test case and test project is reusable and rerun able,
and can be automated through the use of shell scripts or
console commands.

maXbox

An advantage of using S_ShellExecute or


ShellExecute3 is the no wait condition.

In this article we show 4 steps to build a configuration:


1. First building block is the use of a
boot script maxbootscript.txt.
2. Second we jump to the template file bds_delphi.dci
3. Third the Ini-file maxboxdef.ini environment
and code settings.
4. Forth we set an include file in our script, like
pas_includebox.inc to call external functions of
a unit, it's very powerful, but tough to built.
Ok we have finished the boot script what about a template
file. The code template file bds_delphi.dci
stands for code completion templates.
In the Code Editor, type an object, class, structure or a
pattern name followed by <Ctrl J> to display the object.
But it's not a full code completion where you get after the
dot (.) a list of types, properties, methods, and events, if
you are using the Delphi or C# languages.
It's more a template copy of your building blocks.
In the menu /Debug/Code Completion List you get
the defaults, here is an extract:
[cases | case statement |
Borland.EditOptions.Pascal]
case | of
: ;
: ;
end;
[try | try except |
Borland.EditOptions.Pascal]
try |
except
end;

Figure 4:

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

81

Coding with maXbox (Continuation 5)

maXbox

Figure 5: mX bootscript GUI Loaded

I prefer\; myform<Ctrl J> which represents or copies a


form builder in your editor.

Useless to say you can add your own templates


to the file. Many of the Code Editor features are also
available when editing HTML and CSS files. Code
Completion (CTRL+J) and syntax highlighting are
available for HTML, XML and CSS files.

Most of the Open Tools API declarations reside also


in that file bds_delphi.dci.(at the bottom)
Note: Call the methods with maxForm1., e.g.
:maxForm1.ShellStyle1Click(self);

A standard approach to break a running loop in a


script or configuration is the well known KeyPress or
IsKeyPressed function you can use and check:
procedure LoopTest;
begin
Randomize;
REPEAT
Writeln(intToStr(Random(256*256)));
UNTIL isKeyPressed; //on memo2 output
if isKeypressed then writeln(Key has been
pressed!');
end;

82

COMPONENTS
DEVELOPERS

As you know the memo2 is the output window as the


shell, so the keypress is related to memo2; by the way
memo1 is the editor itself!
Another function KeyPressed(VK: Integer): Boolean;
returns True, if key VK has been pressed.
Let's jump to the Ini-file. Many applications use ini
files to store configuration information. Using ini files has
the advantage that they can be used in cross-platform
applications and they are easy to read and edit.
The ini file format is still popular; many configuration
files (such as Desktop or Persistence settings file) are in
this format.
This format is especially useful in cross-platform
applications, where you can't always count on a system
Registry for storing configuration information.
I never was a friend of the Registry so you can also start
maXbox from a stick.
In maXbox code, TIniFile is the game of advantage.
When you instantiate the TIniFile or TMemIniFile object,
you pass the name of the ini file as a parameter to the
constructor. If the file does not exist, it is automatically
created.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Coding with maXbox (Continuation 6)

maXbox

You are then free to read values using the various read methods, such as
ReadString, ReadDate, ReadInteger, or ReadBool.
This is how we can read the ini file of maXbox: maxboxdef.ini
procedure getMaxBoxIniShort;
begin
with TIniFile.Create(ExePath+'maxboxdef.ini') do
try
except_conf := ReadString ('Form','EXCEPTIONLOG','');
execute_conf:= ReadString ('Form','EXECUTESHELL','');
boot_conf
:= ReadString ('Form','BOOTSCRIPT','');
ip_port
:= ReadInteger('Web','IPPORT',0);
finally
writeln('inifile sysdata1: '+except_conf+':'+execute_conf);
writeln('inifile sysdata2: '+boot_conf+':'+intToStr(ip_port));
Free;
end;
end;

This process is handled directly, through an object so each time it changes


timestamp of the file also and not on demand.

In other words TIniFile works directly with the ini file on disk while
TMemIniFile buffers all changes in memory and does not write them to
disk until you call the UpdateFile method.
Alternatively, if you want to read an entire section of the ini file, you can use the
ReadSection method. Similarly, you can write values using methods such as
WriteBool, WriteInteger, WriteDate, or WriteString.
Each of the Read routines takes three parameters. The first parameter (Form in
our example) identifies the section of the ini file. The second parameter identifies
the value you want to read, and the third is a default value in case the section or
value doesn't exist in the ini file.
The Ini File
As you already know the object we now step through the meaning of the ini file.
On subsequent execution of maXbox, the ini values are read in when the form is
created and written back out in the OnClose and other in between events.

In maXbox you can also start with read only mode (Options/Save before
Compile), so nothing will be write on the disk.

//*** Definitions for maXbox mX3 ***


[FORM]
LAST_FILE=E:\maxbox\maxbox3\examples\140_drive_typedemo.txt //10 files
FONTSIZE=14
EXTENSION=txt
SCREENX=1386
SCREENY=1077
MEMHEIGHT=350
PRINTFONT=Courier New
LINENUMBERS=Y
EXCEPTIONLOG=Y
//save log files menu Debug/Show Last Exceptions
EXECUTESHELL=Y
//prevents execution of ExecuteShell()/ExecuteCommand()
BOOTSCRIPT=Y
//enabling load a boot script
MACRO=Y
//put macros in your source header file
NAVIGATOR=Y
//set the nav listbox at the right side of editor
MEMORYREPORT=Y
//shows memory report on closing maXbox
[WEB]
IPPORT=8080
//internal webserver ../Options/Add Ons/WebServer2
IPHOST=192.168.1.53
ROOTCERT='filepathY'
//for use of HTTPS and certificates
SCERT='filepathY'
RSAKEY='filepathY'
VERSIONCHECK=Y
//checks over web the version

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

83

Coding with maXbox (Continuation 7)

maXbox

Now let's take a look at the code of the memory report in the project file:
Application.CreateForm(TMaxForm1, MaxForm1);
if maxform1.STATMemoryReport = true then ReportMemoryLeaksOnShutdown:= true;
We name it, means the ini-file sets the STATMemoryReport true or false.
This example requires two objects from the classes: TMaxForm1 and TMemoryManager of mX4 so the second one
is from the well known VCL Lib. This re includes a new memory manager that significantly improves start-up time,
runtime speed, and hyper threading performance.
If the ini-file doesn't exist, renamed or damaged, maXbox produces a new one with the default values. Test it by copy the
maXbox3.exe in an empty directory; just says that a template file is missing but it starts and will run!
If you only want to install a new maXbox with file or directory names, be sure the ini-file will not be overwritten by
unpacking the zip (so let's make a copy before).
Maybe you just know that by starting the maXbox it checks on the internet the last version if the ini-file allows this
VERSIONCHECK=Y.

Figure 6: Another ini file setting runs

84

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

maXbox

Coding with maXbox (Continuation 8)


Let's do the last step with an include file. Under Include Files, list the files you
want to include in a script. A script can include a script from a file or from
another unit. To include script from a file, use the following code statement:
305_indy_elizahttpserver.TXT
{$I ..\maxbox3\examples\305_eliza_engine.INC}

If no valid file is found, the following message will be displayed:

>>> Fault : Unable to find file


'..\maxbox3\examples\305_eliza_engined.INC' used from
'E:\maxbox\maxbox3\maxbootscript_.txt'.
If you need to specify additional compiler options, you can invoke the compiler from the
command line with the Command Line Interface (CLI).

As you know, there's a simple test to run the CLI out of the box with a
ShellExecute() or a similar RunFile() Command.
ShellExecute3(ExePath+'maxbox3.exe',ExePath+'examples\'
+ascript,secmdopen);
ShellExecute3(ExePath+'maxbox3.exe',
ExePath+'examples\003_pas_motion.txt',secmdopen);
A simple CLI is more relevant today than ever for scripting, and modern shell
implementations such as maXbox or PowerShell have a lot to bring to the table.

The game of Life (see Examples)

Figure 7: Every teach and game programmer's portfolio should


include two code standards, John Conway's Game of Life and Fractals.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

85

maXbox

Coding with maXbox (Continuation 9)

Figure 7: More of Script Configuration


At pre last is to say you can use DLL's too. Selecting this type of application sets up your project as a DLL dependency,
with the exported methods expected by the Library, e.g.:
43: procedure SetErrCode(ErrCode: Integer);
external '[email protected]'; { Sets error code }
function
10:

MyMessageBeep(para: integer): byte;


external '[email protected] stdcall';

Test: How can we show the run dialog?

procedure TForm1_FormCreateShowRunDialog;
var ShellApplication: Variant;
begin
ShellApplication:= CreateOleObject('Shell.Application');
ShellApplication.FileRun;
end;

Check your system environment with GetEnvironmentString:


SaveString(ExePath+'\Examples\envinfo.txt',GetEnvironmentString);
OpenFile(ExePath+'\Examples\envinfo.txt');

86

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

maXbox

Coding with maXbox (Continuation 10)

The Macro
You can set the macros like #host: in your header or
elsewhere in a line, but not two or more on the same line
when it expands with content:
Let's have a look at the demo 369_macro_demo.txt
{********************************************
* Project

: Macro Demo

* App Name: #file:369_macro_demo.txt


* Purpose
* Date

: Demonstrates the functions of macros in header

: 21/09/2010

14:56

- #date:01.06.2013 16:38:20

* #path E:\maxbox\maxbox3\examples\
* #file 369_macro_demo.txt
* #perf-50:0:4.484
* History

: translate/implement to maXbox June 2013, #name@max

: system demo for mX3, enhanced with macros, #locs:149

*********************************************

All macros are marked with red. One of my favour is #locs means
lines of code and you get always the certainty if something has
changed by the numbers of line.
So the editor has a programmatic macro system which allows the pre
compiler to be extended by user code I would say user tags.
Below an internal extract from the help file All Functions List
maxbox_functions_all.pdf:
//---------------------------------------------------------------------------//**************mX4 Macro Tags **************************
//----------------------------------------------------------------------10183:
#name, i#date, i#host, i#path, i#file, i#head, i#sign, i#teach

10181:
10182:
10184:
10185:
10186:
10187:
10188:
10189:
10190:
10191:
10192:
10193:
10194:
10195:

SearchAndCopy(memo1.lines, '#name', getUserNameWin, 11);


SearchAndCopy(memo1.lines, '#date', datetimetoStr(now), 11);
SearchAndCopy(memo1.lines, '#host', getComputernameWin, 11);
SearchAndCopy(memo1.lines, '#path', fpath, 11);
SearchAndCopy(memo1.lines, '#file', fname, 11);
SearchAndCopy(memo1.lines, '#locs', intToStr(getCodeEnd), 11);
SearchAndCopy(memo1.lines, '#perf', perftime, 11);
SearchAndCopy(memo1.lines, '#head',Format('%s: %s: %s %s ',
[getUserNameWin, getComputernameWin, datetimetoStr(now), Act_Filename]),11);
SearchAndCopy(memo1.lines, '#sign',Format('%s: %s: %s ',
[getUserNameWin, getComputernameWin, datetimetoStr(now)]), 11);
10196: SearchAndCopy(memo1.lines, '#tech',Format('perf: %s threads: %d %s %s',
[perftime, numprocessthreads, getIPAddress(getComputerNameWin), timetoStr(time)]), 11);

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

87

maXbox

Coding with maXbox (Continuation 11)

maXbox Macro Context


<<extended>>

Start maXbox
Actor HEX
in the BOX

<<extended>>

<<include>>

bds_delphi.dci
(templates)

Load Boot
Script
Parse Ini
File

Macro
Macro

Macro Actor

Macro
maxbootscript.txt
Macros
#name, get_UserNameWin, 11)
#date, datetimetoStr(now), 11)
#host, get_ComputernameWin, 11)
#path, fpath, internal func 11)
#file, fname,internal func 11)
#locs, intToStr(getCodeEnd), 11)
#perf,perftime, internal func 11)
#head, Format(%S: %S: %S: %S:)

maxboxdef.ini

Compile Script
Some DLL or Lib

PRE PROCESSING include file

<<include>>

Run App
Byte Code

88

Some macros produce simple combinations of one liner tags but at least they replace
the content by reference in contrary to templates which just copy a content by value.

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

maXbox

Coding with maXbox (Continuation 12)


Build your own IDE
At last we go back to the magic boot script which will be the key to modify the IDE
especially with the inbuilt SynEdit API (since V3.9.8.9). What does it mean? It means
you can change or rebuild your IDE not just by fixed options or settings but also in a
programmatic way in your boot script without compilation! Imagine you want to set a
vertical red line on the gutter to the left:
//memo1.Gutter.BorderColor:= clred;
//memo1.Gutter.ShowLineNumbers:= true;

//---> reflection to box!


//---> reflection to box!

You simply put the line above on the boot script and make sure the ini file has it set to Yes.
BOOTSCRIPT=Y //enabling load a boot script
In combination with the Open Tools API you can tweak the GUI with new or change
buttons, events and behaviour for example:
if extractFileName(maxform1.appname) = '370_synedit.txt'
then
begin
Options:= +[eoShowSpecialChars];
ActiveLineColor:= clyellow;
maxform1.tbtnUseCase.caption:= 'SynScriptUC';
maxform1.ShellStyle1Click(self)
end
else ActiveLineColor:= clgreen;

Be aware of the internal representation of SynEdit TSynMemo at maXbox editor is


always memo1. and the console output as you know memo2., so don't name an object
var memo1 otherwise your script will show or jump to unexpected content.
More secure is the namespace with maxform1
maxform1.memo1.font.size:= 14; instead of memo1.font.size:= 14;

with CL.AddClassN(CL.FindClass('TForm'),'TMaxForm1') do begin


10230: ('memo2', 'TMemo', iptrw);
10231: ('memo1', 'TSynMemo', iptrw);
maxform1.memo1.Options:= +[eoShowSpecialChars];
maxform1.memo1. ActiveLineColor:= clyellow;

More examples at 370_synedit.txt and all the other changeable properties or


methods you find at the bottom of the help file <All Functions List>
maxbox_functions_all.pdf
10197: //-------------------------------------------------------------------------10198: //**************mX4 Public Tools API *******************
10199: //---------------------------------------------------------------//-------------------------------------------------------------------------10702: file : unit uPSI_fMain.pas; OTAP Open Tools API Catalog
10703: // Those functions concern the editor and pre-processor, all of the IDE
10704: Example: Call it with maxform1.Info1Click(self)
10705: Note: Call all Methods with maxForm1., e.g.:
10706: maxForm1.ShellStyle1Click(self);
You can also enhance the API with functions like the example above GetEnvironmentString:
function getEnvironmentString2: string;
var list: TStringList; i: Integer;
begin
list:= TStringList.Create;
try GetEnvironmentVars(list, False);
for i:= 0 to list.Count-1 do
result:= result + list[i]+#13#10;
finally list.Free;
end;
end;

The Open Tools API is a collection of classes and functions of SynEdit and VCL components for
extending and enhancing the design and your editor environment. Unlike other development
tools, you use maXbox (Delphi) to extend maXbox. You don't need to learn a new scripting
language because PascalScript works for you.
The Open Tools API puts you in control; reshape maXbox to match your needs.
I mean an elegant and efficient application has some script features.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

89

Coding with maXbox (Continuation 13)

90

COMPONENTS
DEVELOPERS

maXbox

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Coding with maXbox (Continuation 14)


Feedback: [email protected]
Links of maXbox and DelphiWebStart:
http://www.softwareschule.ch/maxbox.htm
http://sourceforge.net/projects/maxbox
http://sourceforge.net/apps/mediawiki/maxbox/
http://sourceforge.net/projects/delphiwebstart

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

maXbox

About the Author:


Max Kleiner
The professional environment of Max Kleiner is in the
range OOP, UML and system design, including as a
trainer, developer and publisher. His focus is on IT
security, navigation and simulators that need the power
of the Delphi compiler. As a teacher in a Fach Hochschule
(University of Applied Sciences) and on behalf of a firm
also microcontroller and the Web of Things have been
added. The book "Patterns konkret" (published in 2003)
is still relevant with the Clean Code initiative.

COMPONENTS
DEVELOPERS

91

Ray Konopka
Interview with the famous writer of Developing Custom Delphi Components

When I first met Ray I was impressed


because of his age. He seems so
young, knowing that he is one of the
very early adoptees of Delphi.
Through the time that I spent with
him I learned his secret: he is
enormously precise, always on the
job, concentrated and very
disciplined. Given his enormous
knowledge he surely is a man to learn
from...

Editor:
I would like to ask you about how you got started with Pascal?
You were one of the very early adapters I suppose?

Ray Konopka:
My very first experience with Pascal was Berkley Software
Distribution Pascal atI llinois Benedictine College (now
called Benedictine University) in 1986 in what I refer to as
my first "real" programming course.
I had a programming course in high school that used
AppleSoft Basic on Apple IIe computers, but there was a
real elegance to Pascal that I really liked. Soon after, I
started working with Turbo Pascal 3.
Editor:
How did you start, you met people from Borland by that time?
Ray Konopka:
Actually, meeting people from Borland came much later.
During my time at University, I continued to use Turbo
Pascal and really fell in love with the language and the
product. I even bought a copy for myself so I could work
with it at home. During my 3rd and 4th years at university
I had the opportunity to work at Argonne National
Laborator in the Physics department.
I used Turbo Pascal to write software that
communicated with various pieces of scientific equipment
to capture data for various experiments. It was during this
time that I switched from TurboPascal 3 to TurboPascal 4.
It was my first real world experience programming
outside of the classroom....
I graduated from University in 1989, where I majored
in Computer Science and Mathematics, with a minor in
Physics. The following year I attended Northwestern
University on a fellowship and 9 months later complete
my Masters degree in Computer Science.
I continued to use Turbo Pascal, and some other languages
throughout this time. After graduating from
Northwestern, I went to work for AT&T Bell Labs.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

As you can imagine, most of my work there was in C.


However, I continued to use Turbo Pascal at home and got
into object-oriented programming with Turbo Pascal 5.
For the next few years, I bounced around a few jobs
trying to find a good fit for me. During this time, I also
started reading a magazine called PC Techniques.
I really liked this magazine because it covered a lot of
topics and it had articles on Turbo Pascal, which were
pretty hard to find.
At this time, most my Pascal work was isolated to my
own time and not involved with my job. I decided to write
an article for the magazine and submitted it. My first
published article was on creating a World Coordinate
System in Turbo Pascal. That was in 1993...

I submitted a couple more articles and then Jeff


Duntemann, publisher of PC Techniques, contacted me to
see if I was interested in writing a column for the
magazine. I thought this was really cool, but not as cool as
the name that Jeff came up with for the column.
The column was called Blazing Pascal.
How cool is that!
It was through the magazine and column that I first got
involved in what was to become Delphi. I was very
fortunate to work with very early versions
of the product. I was even able to write articles about
Delphi in 1994, about 6 months before Delphi even
shipped.
My first column that covered Delphi was published in
September 1994. Shortly after that, Borland setup a Delphi
95 preview day at their Scotts Valley campus.
I was not going to miss that...
It was at that preview event that I first met many of
the people behind Delphi. It was a great experience for me,
especially when I had a chance to meet Anders Hejlsberg.
Imagine my surprise when after I introduced myself to
Anders, he responds, "Oh, yes, I know. I've been reading
your articles." It was an experience I'll never forget.

93

Interview with Ray Konopka (continuation 1)


Editor:
Jeff Duntermaan was also an author for Delphi books?
Ray Konopka:
Yes, he was. In fact, he wrote the Object-Oriented
Programming Guide that shipped with Turbo Pascal.
I had talked with and emailed Jeff for quite a while before
we had a chance to meet. We finally met at the Borland
Conference in San Diego in 1995.
Editor:
You also wrote a very famous book. Could you tell something
about that? Will you try to do so again?
I sometimes regert that it isnt available anymore...
Ray Konopka:
Very early on in my experience with Delphi,
I saw the potential that the VCL provided.
It was Jeff that suggested I write a book about Delphi,
even before Delphi shipped, and I said we should do it on
how to build custom components.
It was also at this time, that I decided to start working for
myself as a Delphi consultant.
I started Raize Software Solutions (now called Raize
Software) on February 7, 1995, a week before Delphi 1
officially launched. I consulted during the day and worked
on the book at nights.
One of my primary goals for the book was to come up with
real-world practical examples. (I really do not like contrived
examples with nondescript names like Foo and Bar.)...
The complete title of the book was called Developing
Custom Delphi Components.
Editor:
I have the first and the second edition...

I've been working with it since its inclusion into Delphi,


and it is very flexible and has tremendous potential.
But it is also very different from the VCL.
When FMX was first released for desktop
development, it was very hard to justify using it unless you
really needed the enhanced display functionality it offered,
or if you really needed to go to Mac OSX.
Editor:
So whenn do you start writing articles about this?
There is a tremendous need for this...
Ray Konopka:
I know, I know
I've been presenting sessions on FM for quite some time,
so some material is out there, but those are not the same as
an article. One thing that I've been working on is a
redesign of the Raize Software website.
My plan is to have a formal blog and other resources
where I can provide some of this information to
developers.
I believe this approach will be easier for me to manage
given my time constraints and also provided developers
with additional resources for Delphi development and also
provide developers...
Editor:
Maybe you could help explaining what firemomkey really is
and what it means.
Ray Konopka:
Actually, that is something that I focus on in the first part
of my Creating Custom FM Controls sessions. A developer
has to have an understanding of what the framework
provides and how it works before one can even think of
extending it.

Ray Konopka:
FireMonkey is very interesting.
Excellent. The first edition
Editor:
I've been working with it since its inclusion
was published in 1995 and
Maybe even explain what a
into Delphi, and it is very flexible and has
the second edition titled
framework itself means.
tremendous potential.
A lot of people have vague
Developing Custom Delphi 3
But it is also very different from the VCL.
ideas about that...
Components came out in
1997. You can still find a
Ray Konopka:
copy or two on eBay every once and a while. It is also
I understand. Although I do like to write things that are
possible to get a PDF Edition of the second book. What is
more applied rather than simply provide a high level
really cool is that all of the material covered in the book
explanation.
still applies to developing Delphi components today.
But, I do understand your point.
Certainly there are more capabilities and features in the
VCL and FMX, but the principles covered in the book still
apply. Of course, this begs the question of writing another Editor:
I am always trying to attract new people towards Pascal. Do you
book.
have any ideas about that?
Editor:
Maybe you could do something about Firemonkey?
Ray Konopka:
The biggest factor in writing another book is time. Back
when I wrote the first two books, Raize Software was just
starting out, we didn't even have a product out when the
first book was written. Now, things are just so much
busier. What I have tried to do in place of writing a new
book is to cover the new topics in presentations and
articles. FireMonkey is very interesting.

94

Ray Konopka:
The biggest hurdle is the incorrect perception that Pascal is
an old language. Unfortunately, that is a hard hurdle to
get over because even if you look at 3 of the most popular
implementations of Pascal:
Delphi, Lazarus, FreePascal, only one is easily connected to
Pascal. So someone may have heard about Delphi, but may
not know its relationship to Pascal.
However, to make real in-roads, developers have to
be able to see that using a particular tool will allow them to
become successful.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Interview with Ray Konopka (continuation 2)


Ray Konopka:
What I mean by this is that the days of using a single
language are long gone.
Developers will select the tool that allows them to deliver a
particular solution. Just look at how popular Objective C
has become.
It's an odd little language, and I much prefer Delphi and
Pascal, but if you wanted to target iPhones and iPads,
you needed to learn Objective C.
Now, that Delphi XE4 and XE5 are out with support for
mobile development, that may change. If developers see
value in a tool, they will learn the language.
Editor:
We need - easy to do exaples.
I think espacially for younger people.
Ray Konopka:
Certainly, but again, they can't be contrived.
On the other hand, you cannot make them too complicated.
This is the problem that Apple has with their
documentation. There are lots of examples, but they are
overly complex and it is very difficult to extract the one
piece of information you need.
The other problem you have, and this applies to the FM
Framework, is that the examples need to change as the
framework changes.
Think about the Generics presentation that I presented at
Be-Delphi this year.
I could have come up with contrived names like TTest or
TFoo but that gives no context for the attendee.
On the other hand, I did not want to create an overly
complex example that overloads the attendee with nonrelevant information.
Finding that balance is key to a good presentation or
article.And no more DB Grids on the side of a 3D rotating
cube.
Editor:
I loved your presentation,
because of its simpliticity that you showd.
I told you about our Help Component project.
We will very soon ship the first free examples - so people can
become known to it - so they can try out and give their opinion.
I was asked to do this for Firemonky at first.
We actually already started on the VCL.
What would you suggest to start with?

Editor:
That is a real problem. I would love to hear an answer from our
readers. I was thinking of starters and doing the VCL.
Because we are starting this teaching project for schools:
PEP (Pascal Ecducation Program).
The problem with FM is, that its still evolving...
So you opt for the VCL?
Ray Konopka:
If you already have the VCL ones, done, then yes,
I would go with that and get some feedback.
Editor:
Problem is there are about 550 to be done...
Back to youre explainations: you had the chance to do buisnes
with the Walt Disney Company. How did that start?
And what is your job there?
Advisor or Developer or Creator?
Ray Konopka:
I started consulting with Walt Disney Parks and Resorts
back in 2002.
Initially, I was brought in for Delphi training, but shortly
after that, I became involved in the development of the
FASTPASS system, which is used in all of the Disney Parks
world-wide.
My work on FASTPASS led to other development
projects, such as the Free On Your Birthday promotion
that ran at Disneyland and Walt Disney World in 2009,
and the Give a Day, Get a Disney Day promotion
that ran in 2010. The success of these projects led to even
more work, but as a consultant there were restrictions on
just how much I could take on.
So, in October of 2011, I switched from consultant to
part-time cast member, which is what employees of The
Walt Disney Company are called.
And yes, I even have a Disneyland name badge.
To be precise I am a Sr Application Developer
and I continue to work on the architecture and development
of the FASTPASS system as well as several other projects,
including many new mobile projects that are
used by Operations Cast Members in the parks.
Editor:
About your component book: you think its still a good book for
starters in the field?
I was wondering if the book might still be worth to be reprinted?

Ray Konopka:
The first few chapters in the book are certainly useful for
any Delphi developer, but the book as a whole does focus on
a specific kind of development, namely reusable component
building.
As a result, its a book that is great to have in your
toolbox, but not necessarily the best choice for a developer
just trying to learn Delphi. Marco Cantus written several
editions of his Mastering Delphi book that would be better
Editor:
suited for that. However, one of the points that I make early
We already started with the first 50 components.
in the book is that there is a lot of value in gaining a better
understanding of how Delphi and the VCL is organized and
Ray Konopka:
You also need to consider which version of FM you want to architected. So while a developer may not have a specific
need to develop their own custom component,
support. Are you going to suppor them all?
understanding what all is involved will make the reader a
Or, just the most recent?
more effective Delphi developer.
That will dictate which version of Delphi is required,
Ray Konopka:
I suppose that would depend on what type of apps the
people testing it out are building.
The VCL is certainly a safe bet as there is still a lot of
development being done with the VCL.
But if your target audience is doing more mobile
development, then FM might make sense.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

95

Interview with Ray Konopka (continuation 3)


Editor:
How did your own company start?
Raize Components came about as a direct result from
feedback I received from the first book. Shortly after it was
published, I started to receive quite a few emails from
developers asking if it was okay for them to use the
components that I presented in the books in their own
applications. You will recall that I specifically wanted to
create real-world practical examples in the book. From the
responses I got, I met my goal.
With this initial demand in place, I used the book's
component exapmles as a foundation, extended those
components to provide even more functionality and added
24 new components it. This became Raize Components for
Delphi version 1.0.
The product has grown a lot since that initial release.
There are now more than 125 controls in the
library and it is loaded with features and capabilities.
CodeSite also has its roots in some material that was
presented in the book, and was an internal tool for quite a
while before it became a product. CodeSite was developed
to help debug Raize Components.
Back in the early days of Delphi, custom controls were
added to the IDE by rebuilding the component librarypackages were not introduced until Delphi 3.
As a result, if your custom control had an issue or
crash, it would take all of Delphi down and so it was
critical to have a system in place to log information about
how a custom control is working (especially at design-time).
This is how CodeSite started.
But it wasn't until I showed CodeSite to Mark Miller
(then of Eagle Software, now with Developer Express) and he
used to to track down issues he was having with his
CodeRush product that it became clear that CCodeSite
clearly had value as a product of its own.
CodeSite too has evolved since its original version.
It has grown from just a debugging tool to a full featured
application logging system. And starting with RAD Studio
XE, CodeSite Express has been included "in the box".
CodeSite Express is a fully functional version of CodeSite
that provides basic logging functionality, while the
CodeSite Studio edition provides more powerful features
and services. A list of the differences between the two
editions can be found the colum alongside and
http://www.raize.com/DevTools/CodeSite/
Editions.asp
Editor
Yes I know this is really a great tool.
I know developers that would want re-event it if they didnt
have this tool.
Thank you so much Ray for your explanations and help
With respect to Raize Software, there is a lot of
information on our website http://www.raize.com.
As noted earlier, I started the company
back in 1995 with the specific intent of providing
tools and services to the Delphi community.

Free Tools
http://www.raize.com/DevTools/FreeTools.asp
BDS/Delphi Icons
Raize BDS Icons is an icon library that contains a
set of icons for the various file types used by the
BDS and earlier versions of Delphi. The icons in this
new library are more consistent in their design and
show a direct association with the BDS/Delphi.
IDE Palette Menu
Are you tired of scrolling through the pages of the
Borland Developer Studio, Delphi, or C++Builder
component palettes looking for the right page?
If so, then download and try out the Raize Palette
Menu expert, which when installed adds a new
menu to the selected IDE that provides instant
access to any page on the component palette.
Raize Font
The Raize Font is a clean, crisp, fixed-pitched sans
serif screen font that is much easier to read than
the fixed pitched fonts that come with Windows.
Ideally suited for programming, scripting, html
writing, etc., the Raize Font can be used in any IDE
or text editor.

CodeSite is available in two editions: CodeSite Express


and CodeSite Studio. The Express edition includes core
logging functionality but does not include the full range of
functionality included in CodeSite Studio.

CodeSite Studio includes these additional features


and capabilities:
Using TraceMethod to record both an EnterMethod
and ExitMethod message with a single statement
Recording time durations using a built-in highprecision timer
Remote Destinations (i.e. transporting CodeSite
messages to a remote machine)
Have logging classes directly connect to a remote
CodeSite Dispatcher
Special event in logging classes to hook into logging
process (VCL: OnSendMsg; .NET: Sending)
Sending Color, Point, Size, Rectangle structures
Sending Bitmaps, Icons, Images, Screen Shots
Sending Collections
Sending Controls, Parents, and Window Handles
(WinForms)
Sending Custom Data and the ICodeSiteCustomData
interface
Sending Text Files, Files, and Streams
SendIf methods
Sending System Info, Memory Status,
and Stack Trace
Sending Xml Data and Xml Files
ExitMethodCollapse method
Event Log Methods: LogError, LogEvent, LogWarning
Writing values to the CodeSite Scratch Pad
.NET Configuration File Support
CodeSite Express is currently included in
Embarcadero's RAD Studio XE, XE2, XE3, and XE4.

You can find a nice overview of each of the


developer tools that we offer on the following page:
http://www.raize.com/DevTools/Products.asp

96

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Attention to detail is our passion

The CodeSite Logging System gives developers deeper insight into how their code is executing, which
enables them to locate problems more quickly and ensure their application is running correctly.
CodeSite's logging classes let developers capture all kinds of information while their code executes and
then send that information to a live display or to a log file. Furthermore, both styles of logging, live
logging and file logging, can be performed locally or remotely.
A key element to CodeSite's effectiveness is that unlike message boxes and inspecting variables on
breakpoints, CodeSite messages are not transient. The resulting log of messages provides valuable
information for locating problem areas in your code.

Raize Components is a user interface design system for Borland Delphi and Borland C++Builder. At its
center is a collection of more than 125 general-purpose native VCL controls. Built on a foundation of
technology first created more than eight years ago, these high-quality components give developers
unsurpassed power and flexibility without sacrificing ease-of-use.
In addition to the core set of controls, Raize Components includes more than 100 component designers
focused on simplifying user interface development. Now more than ever, developers use Raize
Components to build sophisticated user interfaces in less time with less effort.
Raize Components comes with complete source code for all components, packages, and design editors at
no additional charge. Documentation is provided through an extensive context-sensitive online help
system. Raize Components also features One-Step Installation, Automatic Help Integration, and Dynamic
Component Registration.

DropMaster is a set of 4 native VCL controls for use in Delphi and C++Builder. While the VCL components
included with Delphi and C++Builder permit drag and drop between windows in the same application,
DropMaster allows developers to add support for drag and drop between applications. The drag and drop
can be between the developer's new application and existing applications such as the Microsoft Office
suite, a web browser, etc., or between two custom-written applications.
DropMaster also comes with a collection of more than 40 example applications, which demonstrate the
features of the DropMaster components in real-world situations. They also represent the results of
extensive research into the drag and drop behavior of many popular commercial applications.

Inspex is an advanced set of native VCL grid controls specifically designed for inspecting objects and
other data types in your programs. From the light-weight TIxItemListEditor for editing lists of name-value
pairs to the advanced TIxObjectInspector for inspecting all published properties of objects and
components, there is an inspector control in the Inspex collection that will meet your needs.

ScratchPad is a general-purpose text editor with features typically found in programming editors. For
instance, you can edit multiple files at the same time in a sleek tabbed interface. ScratchPad also
supports syntax highlighting a variety of file types including, AutoCorrect, Keyboard Templates, and
Bookmarks.

http://www.raize.com/DevTools/Products.asp
Nr 5 / 2013 BLAISE PASCAL MAGAZINE

97

Programming Bitmap Rotation By David Dirkse


Editor: David wrote this article especilaly
for us because we needed it for our Leap
Motion Development. It was so much
interesting that we decided to publish the
basic Delphi code and handling...
This article describes a Delphi project for
bitmap rotation. It does it in three different
qualities: Simple, Good and Excellent
Result for the Bitmap.
There are 3 units:
- unit1: exerciser to test the rotation procedures
- rotation_unit : procedures for bitmap rotation
- clock_unit : time measurement procedures
Exerciser
The form has buttons for loading and saving bitmaps.
Also 3 modes of rotation are selectable

Rotation takes place between a source and a destination


bitmap. In coarse mode, the source bitmap is scanned pixel
by pixel and projected on the destination bitmap.
Therefore, not every pixel of the destination bitmap may be
covered.
In medium mode, the pixels of the destination bitmap are
scanned and their value is loaded from the source bitmap.
This insures that all pixels of the destination bitmap are
covered.
In fine mode, the scanning is the same as in medium mode,
but each pixel is divided in 9 equal parts.
Parts may cover different pixels in the source map, the
proportions are summed for the final color of the
destination pixel.
Programming
The programmer has to create both the source and the
destination bitmap. Before a rotation may take place, the
rotation_unit has to be informed about the names of the
bitmaps. This is done by a call to

- coarse: fast but less accurate


- medium: somewhat slower but destination bitmap is fully
procedure setmaps(sourcemap,destination map)
covered
- fine: slow, but with soft edges (under construction)
Setmaps sets the pixelformat of both bitmaps to 32 bit.
Also, the dimensions of the destination bitmap are adjusted
Bitmaps are displayed in paintbox1.
to accomodate all possible rotations of the source map.
Moving the mousepointer over the paintbox with
leftmousebutton pressed,
Hereafter, images may be drawn in the source map and
causes the picture to rotate in the selected mode.
procedure coarserotate(deg : word)
Below is an example of medium mode rotation:
procedure mediumrotate(deg: word)
procedure finerotate(deg: word)

may be called.
deg is the rotation angle in degrees from 0..360
Rotation is clockwise, so, for a left rotation of 90 degrees,
270 degrees must be specified.
Do not forget to call the setmaps procedure after loading an
image from a file into the source map. This insures the
proper 32 bit format and dimensions of the destination
map.
Rotation theory (medium mode)
Picture below shows the coordinate system relative to the
bitmaps:

Figure 1 / 2: Little info

98

Figure 3: Rotation theorie

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Programming Bitmap Rotation (Continuation 1)


The source bitmap is painted in red.
Black pixels of the destination map are scanned, the color is
obtained from to corresponding red pixel of the source
map.
Rotation calculation
Now we describe how the position on the source map is
calculated given position (x,y) of the destination map.
(x,y) is regarded as the vector addition of (x,0) and (0,y), so
the x and the y vectors.
These vectors are rotated separately, then the resulting
vectors are added to obtain the rotated (x,y) position.

Figure 4: Basic position

The destination map always is a square.


Also, width and height are odd.
The maps are divided into 4 quadrants
1 : right bottom
2 : left bottom
3 : left top
4 : right top
Pixel scanning of the destination map takes place
horizontally from the center outward.
For quadrants 1 and 4, a row is scanned left to right
starting at a certain y position.
For quadrants 2 and 3, a row is scanned right to left
starting at a y position.
So, for a certain rotated position (x,y) in the coordinate
system, the coordinates of the original position have to be
calculated.
Then these positions are used to calculate the
corresponding pixels in the source and destination maps.
Let's observe a rotation of 30 degrees (clockwise) using the
above bitmaps

Figure 6: Rotation calculation

in the above figure, (x',y') is the corresponding source


map position which provides the color for (x,y) on the
destination map.
Rotation of the horizontal vector results in OD.
Rotation of the vertical vector results in OC.
OD is the addition of (horizontal) vector OB and
(vertical) vector BD.
OC is the addition of (horizontal) vector OA and
(vertical) vector AC.
Addition of the horizontal vectors make x', addition of
the vertical vectors make y'.
In the source code:
(x,y) are the coordinates on the destination map
xtx = OB
xty = BD
ytx = OA
yty = AC
xty means : x to y, the contribution of the rotated x
vector to the final y vector.
Now, for quadrant 1 :
tx = xtx + ytx
ty = - xty + yty

Figure 5: Rotation of 30 degrees

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

For the other quadrants, the signs of xtx, ytx, xty, yty
may change.

COMPONENTS
DEVELOPERS

99

Programming Bitmap Rotation (Continuation 2)


Addressing the pixels in the bitmaps
Of course, Delphi property pixels[x,y] may be used to
change pixelvalues.
However, this way is incredibly slow.
Also, the scanline[y] property, which returns the memory
pointer of the first pixel in row y, is very slow.
Better is to calculate the pointer to a certain pixel.
Because the bitmaps are in 32 bit format we first define
type PDW = ^dword;
PDW is a pointer to a 32 bit unsigned variable.
Address calculations are done with variables in
dword ( = cardinal) format.
First, the pointer to pixel [0,0] must be obtained.
For this, scanline[0] is used by the setmaps procedure.
scanline[1] returns the pointer to pixel [0,1].
scanline[1] - scanline[0] gives the pointer difference
between two rows.
(note: row 1 pointer is smaller then the row 0 pointer).
In the rotation procedure

Also an adjustment must be made to position the center of


a bitmap over (0,0), the coordinate system origin.
scx is the center x pixel of the source map
scy is the center y pixel of the source map
dcxy is the x and y center of the destination map
Now, pixel (x,y) of the source map is addressed by
trunctx := trunc(tx); //tx is floating point format
truncty := trunc(ty); //...
ttx := scx + trunctx; //add center
tty := scy + truncty;
PS := PSBM - tty*Slinestep + (ttx shl 2); //pointer to
source pixel
pix := PDW(PS)^;
pix receives the color value of the source map pixel.
For quadrant 1, the pointer to the destination map is
Ybase1 := PDBM - (dcxy + y)*Dlinestep;
PD := Ybase1 + ((dcxy+x) shl 2);
PDW(PD)^ := pix;
For more details, I refer to the source code.

PSBM = scanline[0] for the source map


PDBM = scanline[0] for the destination map
Slinestep = scanline[0] - scanline[1] for the source map
Dlinestep = scanline[0] - scanline[1] for the destination map

Figure 7: Raw (coarse) - see the pixelerrors causing a pattern of bad information - or no information
Quick and dirty: see the measurement buttons

100

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Programming Bitmap Rotation (Continuation 3)

Figure 8: Medium - lesser pixelerrors better result but the calculation takes a lot more time

Figure 9: Fine - see the smooth picture no information loss - but the calculation takes ten times as much

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

101

Introduction to MVVM and the


Caliburn Micro for Delphi framework By Jeroen Pluimers
Delph XE5

Introduction to MVVM
The Model View ViewModel (MVVM) is an architectural
pattern used in software engineering that originated from
Microsoft as a specialization of the Presentation Model design
pattern introduced by Martin Fowler
MVVM in Delphi using the
Caliburn Micro for Delphi framework.
A bit of background on
Model, View and View Model
MVVM is one of the ways to help split parts of your
application into three layers. Doing so makes it easier to
"organize the mess", allows you to unit test not only the
business logic but also the interaction logic in an early
stage, and makes it easier to switch user interfaces when
aiming for cross-platform versions of your applications.
The really cool thing is that you do not have to start
with MVVM from scratch. You can apply the ideas from
MVVM into existing applications as well and evolve them
into a fully fledged MVVM application.
It means MVVM gives you a lot of freedom on which I
will write more at a later stage. Lets first focus on the
introduction though.
My encounter with MVVM was in the .NET and later in
the Delphi world with a self written framework similar to
what Malcolm Grooves presented at CodeRage 7.
Back then I wasn't too impressed with what I achieved.
Even though I was used to naming conventions, most of
the things were still manual labour. Too much tedious
manual labour.
Later I bumped into Caliburn for .NET, but at that
time I was working with applications that hadn't been
maintained since around 2005 in a way that was hard to
move to MVVM. Last year, I was really glad to see DSharp
was having an MVVM presentation model, and even more
happy that it was being revamped into something very
much alike Caliburn Micro, which was the successor of
Caliburn.
Pieces of the puzzle started falling into place when I
recognized how easily you could integrate it with existing
applications.
Before explaining more about MVVM, lets take a step
back and look at the 3rd object.
The 3rd object

(3rd object images thanks to Paul Stovell's 3rd object article)


All too often, applications - Delphi or otherwise are "structured" like these:

It all comes down to Separation of Concerns:


Cutting business logic away from your UI
Swapping your UI (VCL, FireMonkey, Mobile, Web, )
Making it easier to test user interaction without a View
layer
By pushing tests from a View to automated tests, you move
them from the time consuming and labour intensive
acceptance test or end-to-end test phases into the automated
unit test phase. This way it becomes much easier and
cheaper to test the user interaction logic from the View
Model.
(The image was made thanks to Jonas Bandi)
BUSINESS
FACING

expert

TECHNOLOGY
FACING

starter

ACCEPTANCE
TESTS

UNIT TESTS

INTEGRATION
TESTS

END TO END
TESTS

GRANUARITY / SCOPE

Binding the 3rd object


The question is: how do you bind View, Model and the 3rd
object? That highly depends on what kind (or even
flavour) of 3rd object architecture you use: MVC, MVP,
MVVM, et cetera. A few of them are here:
MODEL

VIEW

PRESENTER

MODEL

VIEW

VIEW MODEL

MODEL

VIEW

VIEW = MODEL

or, if you are lucky, like:

VIEW MODEL

MODEL

VIEW

Adding a 3rd object makes sense to make things


less monolithic and more maintainable:

MODEL

VIEW

VIEW MODEL

MODEL

VIEW

MODEL

VIEW

THIRD OBJECT

5
102

COMPONENTS
DEVELOPERS

VIEW MODEL

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Steps in MindScape AppView Step - By - Step (Introduction 1)


Caliburn sticks to the last one: the View binds to the View Model,
and the View Model to the Model. Updates come
from the Model to the View Model and from the View Model to
the View. There is no binding between the View and Model at all.
This setup allows you to change any of the 3 layers and be very
flexible.
The traditional 3rd layer in Delphi is to separate user interface into
forms, business logic into data modules and
your data into some form of storage. This has worked for 15 years,
but still needs a lot of plumbing and it is hard
to change the UI as the binding is usually tied to the VCL
platform.

with these class


and sequence
diagrams:

MVVM: the View Model


When starting with MVVM, the term View Model wasn't making
much sense to me at first. I was missing the steering layer from the
controller or presenter in MVC and MVP. And I'm not the only one,
as you can see from this MVVM Wikipedia excerpt:
The term "View model" is a major cause of confusion in
understanding the pattern when compared to the more
widely implemented MVC or MVP patterns. The role of the
controller or presenter of the other patterns has been
substituted with the framework binder (e.g., XAML) and
view model as mediator and/or converter of the model to
the binder.

In MVVM the steering role is taken over by the framework,


and the View Model focuses a lot more on (testable!) logic
than the controller in MVC or the presenter in MVP.
MVVM favours these concepts:

Decoupling

Composition over inheritance

Test driven development

Patterns, conventions, ...

MVVM origins: XP, Agile and the


Presentation Model
The origins from MVVM go far back to the Agile
software mantra that nowadays everybody advocates
but actually evolved in 2001 with the Agile Manifesto
that was based on the eXtreme Programming (aka XP)
from the mid 90s of last century.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

103

Steps in MindScape AppView Step - By - Step (Introduction 2)


Back in 2004, Martin Fowler published his famous Presentation
Model article presenting a simple UI: This is very much like the
MVVM, with the exception that in MVVM the framework will take
care of all the binding. (Martin Fowler
kindly gave permission to use his pictures
and the below book cover and portrait).
Refactoring
Back in 1999 - in the middle of the XP era Martin Fowler wrote a great book titled
Refactoring: Improving the Design of
Existing Code. It influenced the way we
write software until today even in Delphi. It is so current that in
2012 it appeared in a special Kindle edition. The book has a
catalog of simple recipes that describe how to improve certain bad
coding patterns into better code. Patterns like Extract Method or
Encapsulate Field are essential to how we develop code and give
us a set of conventions on how to do just that.
MVVM is all about patterns and conventions, so lets look at
another important book, but now at the start of the Agile era.

Why this MVVM article?


MVVM combines refactoring, patterns, conventions and much
more into a way of developing software that is maintanable and
testable in a way that makes the best use of modern language
features without the need of the developer to master each and all
of those features in great detail.
There are two reasons I started advocating using MVVM with
Delphi. The primary one is that there are exciting times ahead for
Delphi developers. With added functionality like generics,
attributes and a truckload of RTTI, new (and hopefully old!)
platforms, lots of new possibilities - including MVVM - are
already there, or on the horizon.
Another reason is that these open source projects can use your
help. Simple help like just using it and giving feedback can
tremendously move them forward. So here is my favourite
shortlist of projects you could contribute to:
Spring4D
DSharp (including the Caliburn feature branch on which
this article is based)
DUnit
DUnitX and DelphiMocks
DelphiSpec
The Delphi JEDI projects (including JCL and JVCL)
GExperts
Note that the vast majority of those projects use a DVCS for
version control like Git or Mercurial, so that is another learning
opportunity for many Delphi developers. Caliburn Micro for
.NET was our origin. Back in 2010, Rob Eisenberg gave a very
influential speech titled Build Your Own MVVM Framework,
which led to wide adoption of Caliburn Micro in the .NET world.
The Delphi implementation is mimicked after it, and now even a
JavaScript library is: Durandal (also the name of a sword). His
speech has since then been published on Channel 9 and well
worth viewing as lots of the Delphi implementation works in a
similar way.

Patterns
In 2004, the - instantly famous - Gang of Four (abbreviated to
GoF) published the book Design Patterns: Elements of Reusable
Object-Oriented Software. The book is another catalog, this time
on patterns, with a common vocabulary about recipes for
developing new code. The Gang of Four consists of Erich Gamma,
Richard Helm, Ralph Johnson and John Vlissides.
Martin Fowler on the GoF book in relation to the 3rd object:
In my view the Gang of Four is the best book ever written on
object-oriented design - possibly of any style of design. This book
has been enormously influential.
The 3rd object is just a (relatively) new way of using patterns.

104

COMPONENTS
DEVELOPERS

Caliburn Micro for Delphi is part of DSharp


Currently, Caliburn Micro for Delphi is in alpha stage. It is hosted
at the DSharp repository from Stefan Glienke.
Stefan has done most of the core DSharp work and a lot of
Spring4D work (both DSharp and Caliburn depend on Spring4D).
Most of the Caliburn specific work has been done by Marko
Vonina.
Internally, all these frameworks heavily depend on these Delphi
features:
interfaces - attributes - generics - RTTI
They expose many classes and interfaces (attributes are based on
classes). Applications based on Caliburn will use interfaces and
attributes a lot.
Make sure you read the Coding in Delphi book by Nick Hodges if
you feel unfamiliar with them. Actually: just read the book
anyway. I learned a lot while reading it!

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Steps in MindScape AppView Step - By - Step (Continuation 1)


The goal is to write an application using MVVM
that:
is based on patterns and conventions by using RTTI
- is driven by the View Model (not the View)
- has the user interaction logic in the Model View
- can have the View Model
- be unit tested in an early stage
- has the data storage in a Model
- can have the Model
- be unit tested in an early stage as well runs in Delphi
XE or higher builds warning and hint free.
During the steps you will see some failures that will be
fixed in subsequent steps. That is deliberate: it is nice
having working demos but in practice you will bump into
things, so it is good to see the things you might bump
into upfront and how easy it is to solve them.
The projects are based on the Mindscape HQ
MVVM demos that use the Caliburn Micro for .NET
framework. Though Delphi is different than .NET,
the ideas in the framework are kept the same as much
as possible. The demos will teach you where things differ
most.
So the Delphi application will eventually look similar to
this Caliburn Micro for .NET based WPF application:
it can add, double and increment, all from a View Model
automagically updating the View. In fact the Delphi
application will also add a Model that is persistent in an
INI file!

VCL project
Name the form AppView, the form unit AppViewForm and clean
up the uses list, private and public sections:
unit AppViewForm;
interface
uses Forms;
type
TAppView = class(TForm)
end;
var
AppView: TAppView;
implementation

{$R *.dfm}
end.
Name the application MindScape_AppViewVCL:
program MindScape_AppViewVCL_Step00;
uses Forms,
AppViewForm in 'AppViewForm.pas' {AppView};

{$R *.res}
begin
ReportMemoryLeaksOnShutdown := True;
Application.Initialize();
Application.MainFormOnTaskbar := True;
Application.CreateForm(TAppView, AppView);
Application.Run();
end.
DUnit testing project
This is based on the standard DUnit project template with
a twist: it reports memory leaks, and a bit more cleaned
up code when switching between console and GUI
applications.
program MindScape_AppViewTests_Step00;

{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
Forms, TestFramework, GUITestRunner,
TextTestRunner;

{$R *.res}
begin
ReportMemoryLeaksOnShutdown := True;
A common thing is that Caliburn Micro for Delphi depends
if IsConsole then
heavily on RTTI (Run Time Type Information)
with TextTestRunner.RunRegisteredTests do
to make the conventions and patterns work for you.
Free()
In the steps, I will put () parenthesis to parameterless methods to
else
begin
set them apart from properties or fields.
Application.Initialize();
Most of the times that is optional, but at times it is required,
and for me it makes it easier to see what the code is aimed at:
GUITestRunner.RunRegisteredTests();
method calls often have side effects, but most of the time accessing
end;
fields and properties should not.
end.
Some of the steps might sound overly detailed, and the total
Step 01: adding the interfaces and View Model
might scare you at first. The scaring is not on purpose,
Add two units to the projects:
but the fine grained steps are: for me it is important to show you
AppInterfaces with the interface definitions and AppViewModel
what it is involved, and what you might bump into. In practice
that contains the View Model.
these steps will take only a short amount of time.
For the project names, you can optionally include a _Step## prefix Later we will will add another unit with a Model as well.
After that make the modifications to the units and project file as
where ## has the step number.
shown in these sections.
I've done that in the examples below as that makes it easier for
you to find back the individual steps in the
DSharp code repository.
Lets get on with the steps...
Step 00: an empty VCL and DUnit application
Create a new project group that contains an empty VCL project
with one empty form, and a DUnit test project
that has no tests yet.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

The AppInterfaces unit


For now, there will be one interface in it that defines the Model
View. Note that you need a GUID for this
interface, as that will enable the compiler to generate RTTI
(RunTimeTypeInformation)

COMPONENTS
DEVELOPERS

105

Steps in MindScape AppView Step - By - Step (Continuation 2)


unit AppInterfaces;
interface
type
IAppViewModel = interface
['{38C5B8ED-8269-463D-847D-09F6A7B99584}']
end;
implementation
end.

The AppViewModel unit


The AppViewModel unit contains the View Model for our
application. It will drive the View.
For now the View Model will be empty:
it's just the scaffolding for the rest of the steps.
Since the TAppViewModel depends on the
IAppViewModel interface, the AppViewModel unit uses
the AppInterfaces unit.
TAppViewModel descends from TScreen which is a
base class that can be bound to a container that can
be visible (usually a TForm or TFrame).
The final step is in the initialization section of the unit:
it refers to the ClassName of the TAppViewModel.
This effectively instructs the compiler and linker to include
the class and the RTTI in the executable.
This allows Caliburn to use the RTTI and bind the View
Model and View together.
unit AppViewModel;
interface
uses
AppInterfaces,
DSharp.PresentationModel;
type
TAppViewModel = class(TScreen, IAppViewModel)
end;
implementation
initialization
TAppViewModel.ClassName;
end.

No modifications for the form.


The really cool thing is that the form does not require any
modification. It does not need to implement the
IAppViewModel interface (that is done by the
TAppViewModel class).
The Caliburn framework will take care of binding the
View Model and View for you. The project file also needs a
few modifications: Insert these at the top of the uses list:
{$IFDEF CodeSite}
DSharp.Logging.CodeSite,
{$ENDIF CodeSite}
DSharp.PresentationModel.VCLApplication,
This enables the CodeSite logging tool that is included with
most recent Delphi versions.
DSharp has support of other logging destinations like
SmartInspect, Console, or OutputDebugString as well.
It will also extend the TApplication using class helpers so
the Caliburn framework can initialize the repository of
View Models and Views, and then start the application.
Finally replace
Application.CreateForm(TAppView, AppView);
Application.Run();

The first lines will initialize the logging.


The last line will use the TAppViewModel to start the
application with. Later we will switch to the
IAppViewModel for that.
This is the main program you are after:
program MindScape_AppViewVCL_Step01;
uses
{$IFDEF CodeSite}
DSharp.Logging.CodeSite,
{$ENDIF CodeSite}
DSharp.PresentationModel.VCLApplication,
Forms,
AppInterfaces in 'AppInterfaces.pas',
AppViewForm in 'AppViewForm.pas' {AppView},
AppViewModel in 'AppViewModel.pas';
{$R *.res}
begin
ReportMemoryLeaksOnShutdown := True;
Application.Initialize();
Application.MainFormOnTaskbar := True;
{$IFDEF DEBUG}
Application.WithDebugLogger();
{$ENDIF DEBUG}
{$IFDEF CodeSite}
Application.WithLogger<TCodeSiteLog>;
{$ENDIF CodeSite}
Application.Start<TAppViewModel>();
end.

Add unit AppViewModelTestCase to the test project


Like the View and View Model, the initial
TAppViewModelTestCase is also empty.
The next steps will add tests each time functionality is
added to the View Model.
For the test case to compile, also add the AppViewModel
unit to the DUnit test project.
unit AppViewModelTestCase;
interface
uses
TestFramework,
AppInterfaces,
AppViewModel;
type
TAppViewModelTestCase = class(TTestCase)
strict private
FAppViewModel: IAppViewModel;
strict protected
property AppViewModel: IAppViewModel
read FAppViewModel;
public
procedure SetUp; override;
procedure TearDown; override;
end;
implementation
procedure TAppViewModelTestCase.SetUp;
begin
FAppViewModel := TAppViewModel.Create();
end;
procedure TAppViewModelTestCase.TearDown;
begin
FAppViewModel := nil;
end;
initialization
RegisterTest(TAppViewModelTestCase.Suite);
end.

with
{$IFDEF DEBUG}
Application.WithDebugLogger();
{$ENDIF DEBUG}
{$IFDEF CodeSite}
Application.WithLogger<TCodeSiteLog>;
{$ENDIF CodeSite}
Application.Start<TAppViewModel>();

106

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Steps in MindScape AppView Step - By - Step (Continuation 3)


Step 2:
making Caliburn recognize your form
as a valid View

When you try running the unit test


from Step 01, it works. But the
VCL app shows a tiny form with
caption ChildForm and content
View not found for TAppViewModel.
So lets start to explain why you get
the View not found for
TAppViewModel error in the first
place. Caliburn needs to be able to
find which View belongs to a
View Model. In our case, it needs to find a View for the
TAppViewModel. We know it is TAppView (it is one of
the Caliburn conventions), but Caliburn doesn't know about
TAppView as it needs the RTTI for it.
So, like we did with TAppViewModel, we need to
make sure that the RTTI for TAppView is registered as
well. So we need to do the TAppView.ClassName trick.
But that is not all: we also need to make sure that
Caliburn can add some extra functionality.
For that, add the unit DSharp.Bindings.VCLControls
to the uses list. It contains (among a lot of other things) a
new TForm implementation that descends from
Forms.TForm (this is called an interceptor class, something
presented for instance by Delphi.about.com in 2009).
The TForm interceptor adds extra behaviour to
Forms.TForm like notifications.
In fact DSharp.Bindings.VCLControls contains
interceptor classes for these Delphi units:
ComCtrls
CommCtrl
Controls
ExtCtrls
Forms
Grids
StdCtrls

So the AppViewForm unit now becomes this:


unit AppViewForm;
interface
uses
DSharp.Bindings.VCLControls;
type
TAppView = class(TForm)
end;
implementation

It almost is, and indeed it is the first step. And you get
another error that too is part of the learning experience:
Exception EResolveException
No component was registered for the service type:
IAppViewModel

(Sometimes you will get a series of other exceptions, we will


investigate that to see where it needs fixing).
The cause of this exception is that Caliburn tried to find the
class implementing IAppViewModel but
couldn't get to TAppViewModel.
RTTI is not the only part in the foundation of Caliburn:
there is also a composition framework inside
DSharp that - not surprisingly - is mimicked after a .NET
framework as well. You need to decorate the
IAppViewModel with the InheritedExport or
InheritedExportAttribute attribute which works
virtually identical to the InheritedExportAttribute
in .NET.
When booting your application, Caliburn composes a
graph of dependent objects (Models, View Models, Views)
that build your application.
The InheritedExport attribute is in the
DSharp.PresentationModel unit so that's why your
IAppViewModel declaration should look like this:
unit AppInterfaces;
interface
uses
DSharp.PresentationModel;
type
[InheritedExport]
IAppViewModel = interface
['{38C5B8ED-8269-463D-847D-09F6A7B99584}']
end;
implementation
end.

If you now run the application, it looks nice again

{$R *.dfm}
initialization
TAppView.ClassName;
end.

Step 3:
drive the application using
the View Model Interface
The app now is already View Model driven by the
TAppViewModel class.
Wouldn't it be cool if it were driven by the
IAppViewModel interface?
You'd think it is as simple as modifying the project and
replace
Application.Start<TAppViewModel>();
with
Application.Start<IAppViewModel>();

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

107

Steps in MindScape AppView Step - By - Step (Continuation 4)


Wow, an empty form in just 3 steps!
The cool things:
it is View Model driven
(the next steps will show more of that)
Caliburn automagically did the binding - hence the
Caption is now TAppViewModel, not AppView
no global variables, Application.FormCreate
and stuff like that any more: everything is dynamic
So lets move on!
Step 4:
have the View Model drive the View's Caption
Add an Increments... caption
Lets have the View Model steer a better Caption on the
View, like Increments...:

It's a simple two-step process


add a resourcestring to the AppInterfaces unit:
resourcestring
IAppViewModel_DisplayName = 'Increments...';

then add a public constructor to the TAppViewModel like


this:

108

COMPONENTS
DEVELOPERS

unit AppViewModel;
interface
uses AppInterfaces, DSharp.PresentationModel;
type
TAppViewModel = class(TScreen, IAppViewModel)
public
constructor Create(); override;
end;
implementation
constructor TAppViewModel.Create();
begin
inherited Create();
DisplayName := IAppViewModel_DisplayName;
end;
initialization
TAppViewModel.ClassName;
end.

Caliburn will automatically call this constructor when


composing the object graph. Try it and enjoy how easy this
step was, and how little design-time effort it took.
test for the Increments... caption
Add a published method Test_DisplayName to the test
case: published procedure Test_DisplayName();
then add the
DSharp.PresentationModel unit to the DUnit
test project and implementation uses
clause:
uses DSharp.PresentationModel;
and implement the Test_DisplayName method.
procedure AppViewModelTestCase.Test_DisplayName();
var
LHaveDisplayName: IHaveDisplayName;
begin
LHaveDisplayName := AppViewModel as
IHaveDisplayName;
CheckEquals(IAppViewModel_DisplayName,
LHaveDisplayName.DisplayName);
end;

The IHaveDisplayName interface is one of the many interfaces


in Caliburn. This one exposes the DisplayName property.
TScreen implements that interface so it indicates it supports
having and supporting the DisplayName property. Now run
and see if the first unit test on the View Model succeeds. Do
you get the same result
as left?

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Steps in MindScape AppView Step - By - Step (Continuation 5)


Step 05:
adding a Count property to the View Model
and control to the View
One of the Caliburn conventions is that if you have a
control in the View with the same name as a property in
the View Model, then they will automatically be bound
together.
So lets start with adding a TEdit control called Count
on the View, and notice that Delphi automatically
extends the uses list for you:
uses
DSharp.Bindings.VCLControls,
Classes,
Controls,
StdCtrls;
type
TAppView = class(TForm)
Count: TEdit;
end;

Now add the Count property to the View Model:


TAppViewModel = class(TScreen, IAppViewModel)
strict private
FCount: Integer;
strict protected
function GetCount(): Integer; virtual;
procedure SetCount(const Value: Integer);
virtual;
public
constructor Create(); override;
property Count: Integer read GetCount
write SetCount;
end;

Step 06:
fixing the binding.
The previous step told about the importance of the
interceptor classes in the DSharp.Bindings.VCLControls
unit. And that is exactly the reason why at run-time you got
Count into the caption of the TEdit:
it wasn't bound to the integer value of Count in the View
Model, as the interceptor classes could not do their
work. The reason is that Delphi does not see them as they
are obscured by the units that expose the actual control.
The lesson is easy:
always make sure that units like
DSharp.Bindings.VCLControls that have
interceptor classes are always the last in
the uses list.
So the solution is very simple, modify the uses list from
uses
DSharp.Bindings.VCLControls, Classes,
Controls, StdCtrls;
into
uses
Classes, Controls, StdCtrls,
DSharp.Bindings.VCLControls;

Now run and enjoy the results of this step that was very
easy to perform, but had a high impact.

and have it backed by get and set methods


function TAppViewModel.GetCount(): Integer;
begin
Result := FCount;
end;
procedure TAppViewModel.SetCount(const Value:
Integer);
begin
if Count <> Value then
begin
FCount := Value;
NotifyOfPropertyChange('Count');
end;
end

The DSharp.Bindings.VCLControls have class


interceptors adding notification to most controls that ship
with Delphi, the View Model must also notifications.
The above SetCount implementation shows this for
properties; we will see later that this also can hold for
methods
Now lets run the application and see if design-time
gets bound on run-time:

Step 07:
add buttons to increment or decrement
the count
Here you will see that the Caliburn convention of naming
controls not only holds for properties in the View Model,
but also for methods.
Lets start with the View Model: add two public methods
here named DecrementCount and
IncrementCount
procedure TAppViewModel.DecrementCount;
begin
Count := Count - 1;
end;
procedure TAppViewModel.IncrementCount;
begin
Count := Count + 1;
end;
Now add two buttons with the same name in the View:
type
TAppView = class(TForm)
Count: TEdit;
IncrementCount: TButton;
DecrementCount: TButton;
end;

You see that it doesn't, and that's what the next step
will fix.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

109

Steps in MindScape AppView Step - By - Step (Continuation 5)


Now run and see how the design-time gets translated:

Caliburn binds public methods and properties in


the View Model to controls with the same name in
the View. Methods are bound to the action of a
control. Properties are bound to the content of
the control.

This was almost too easy!.


Lets add some logic to limit the values to which Count can
be incremented or decremented in the next step.

Update the SetCount method so that Caliburn gets a


notification that CanDecrementCount and
CanIncrementCount change when Count changes:
procedure TAppViewModel.SetCount(const Value:
Integer);
begin
if Count <> Value then
begin
FCount := Value;
NotifyOfPropertyChange('Count');
NotifyOfPropertyChange('CanDecrementCount');
NotifyOfPropertyChange('CanIncrementCount');
end;
end;

The last step is very important: if you forget it the buttons


will not get disabled when the value of Count
gets at the edge of the allowed range.
Without it, you can get the run-time behaviour on the left,
but you want the run-time behaviour on the right:

Step 8:
limiting the range of Count
between -10 and +10
Modifying the View Model and View
The easiest way of limiting the range is by using constants, so add
these to the interface of the AppInterfaces unit:
const
MinimumCount = -10;
MaximumCount = +10;

Now add these public properties to the View Model:


property CanDecrementCount: Boolean
read GetCanDecrementCount;
property CanIncrementCount: Boolean
read GetCanIncrementCount;
and have them backed by get methods:
function TAppViewModel.GetCanDecrementCount():
Boolean;
begin
Result := Count > MinimumCount;
end;

Adding unit tests


I like having unit tests around the boundary cases, and to
have a certain symmetry. So these are the published test
methods added:
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure

Test_DecrementCount_MaximumCount();
Test_DecrementCount_MaximumCount_Minus1();
Test_DecrementCount_MaximumCount_Plus1();
Test_DecrementCount_MinimumCount();
Test_DecrementCount_MinimumCount_Minus1();
Test_DecrementCount_MinimumCount_Plus1();
Test_DisplayName();
Test_IncrementCount_MaximumCount();
Test_IncrementCount_MaximumCount_Minus1();
Test_IncrementCount_MaximumCount_Plus1();
Test_IncrementCount_MinimumCount();
Test_IncrementCount_MinimumCount_Minus1();
Test_IncrementCount_MinimumCount_Plus1();

function TAppViewModel.GetCanIncrementCount():
Boolean;
begin
Result := Count < MaximumCount;
end

with implementations like these:

It is always a good idea to make the View Model robust so


that it can withstand unwanted calls. So update
the DecrementCount and IncrementCount so they
throw an exception when they cannot perform their
respective action:

procedure TAppViewModelTestCase.
Test_DecrementCount_MaximumCount();
begin
AppViewModel.Count := MaximumCount;
AppViewModel.DecrementCount();
end;

procedure TAppViewModel.DecrementCount;
begin
if not CanDecrementCount then
raise EInvalidOperation.Create(
'not CanDecrementCount');
Count := Count - 1;
end;

procedure TAppViewModelTestCase.
Test_DecrementCount_MaximumCount_Minus1();
begin
AppViewModel.Count := MaximumCount-1;
AppViewModel.DecrementCount();
end;

procedure TAppViewModel.IncrementCount;
begin
if not CanIncrementCount then
raise EInvalidOperation.Create(
'not CanIncrementCount');
Count := Count + 1;
end;

110

COMPONENTS
DEVELOPERS

procedure TAppViewModelTestCase.
Test_DecrementCount_MaximumCount_Plus1();
begin
AppViewModel.Count := MaximumCount+1;
AppViewModel.DecrementCount();
end;

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Steps in MindScape AppView Step - By - Step (Continuation 6)


Since the unit tests use the IAppViewModel interface to
access the View Model, we need to modify it to
expose the Count, GetCount, SetCount,
DecrementCount and IncrementCount members:
type
[InheritedExport]
IAppViewModel = interface
['{38C5B8ED-8269-463D-847D-09F6A7B99584}']
procedure DecrementCount();
function GetCount(): Integer;
procedure IncrementCount();
procedure SetCount(const Value: Integer);
property Count: Integer read GetCount
write SetCount;
end;

Even after the interface change, you will get some test errors
when running the unit test, but that is fine: the next step will
fix those.
The important thing to remember here is:
by using MVVM you can test your View Model
independent of your UI in an early stage.

Step 9:
ensuring the unit test results make sense
Of the failing methods, these fail in a sort of expected way:
procedure Test_DecrementCount_MinimumCount();
procedure Test_IncrementCount_MaximumCount();

but these should have been caught when


assigning the Count property:
procedure Test_DecrementCount_MinimumCount_Minus1();
procedure Test_IncrementCount_MaximumCount_Plus1();

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

111

Steps in MindScape AppView Step - By - Step (Continuation 7)


So the unit tests reveal that the View Model isn't protecting
Count from being assigned out of range values.
Lets fix that first, then take another look at the unit tests.
Since Caliburn is part of DSharp which depends on
Spring4D which has a Guard.CheckRange method,
fixing Count is as easy as including this line at the start of
the SetCount method:
Guard.CheckRange((Value >= MinimumCount)

and
(Value <= MaximumCount), 'Value');
BTW: Don't forget to add the unit Spring to the
implementation uses list of the AppViewModel unit.
Now setting Count to an out-of-range value causes an
EArgumentOutOfRangeException to be raised.
That brings us to the unit tests: we need certain tests to
expect certain kinds of exceptions. DUnit can
do just that using the little known ExpectedException
property, which means that some of the tests need
to be modified.
First add the SysUtils and Classes units to the
implementation uses list of the AppViewModelTestCase
unit. Add the line
ExpectedException :=
EArgumentOutOfRangeException;

at the start of these methods:


procedure
procedure
procedure
procedure

About the Author


Jeroen Pluimers
Makes things work.
Specialist in .NET, Win32, x64,
SQL,
Visual Studio and Delphi.
Knows how to strike a balance between
old and brand new technology to make
things work.
DOS, mobile, big systems, you name it.
Married to a cancer survivor.
As curator responsible for his brother
who has an IQ < 50.
30+ year member of world class
marching band Adest Musica.
Trained and performed the 2013 half
marathon in New York.
Twitter: jpluimers
Blog: wiert.me
LinkedIn: jwpluimers

Continuation:
Future steps for the example will be in the next Blaise
issue. They will cover binding TAction with
TActionManager, using the Caliburn logging, adding
unit tests for the new actions, adding increment-byvalue with tests, adding a Model to the View Model,
and creating a FireMonkey UI with TActionList next to
the VCL UI.

Test_DecrementCount_MaximumCount_Plus1();
Test_DecrementCount_MinimumCount_Minus1();
Test_IncrementCount_MaximumCount_Plus1();
Test_IncrementCount_MinimumCount_Minus1();

Then add the line


ExpectedException := EInvalidOperation;

at the start of these methods:


procedure Test_DecrementCount_MinimumCount();
procedure Test_IncrementCount_MaximumCount();

Now the unit tests run fine!

112

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

kbmFMX for XE5 By Fikret Hasovic


starter

expert

Delphi XE5

Performance on mobile platforms was the initial reason for creation


of the kbmFMX controls, which are currently only readonly.
kbmFMX components are part of kbmMemTable v. 7.40 Standard
beta installation. They include data aware grid, memo, image
component.
kbmFMX is designed for XE5 only, but supports all targets,
including mobile platforms.
Great news is that kbmMW supports mobile platforms now!
I'll explain here how to create an android application with embedded kbmMW
application server, so you can fully utilize all kbmMW power!
We start from scratch with creating FireMonkey Mobile Application :

COMPONENTS
DEVELOPERS

I'll create Tabbed with Navigation Application:

After clicking OK button, Delphi XE5 will ask for a directory


where your application source code should be generated:

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

113

COMPONENTS
kbmFMX for XE5 (Continuation 1)

DEVELOPERS

After selecting the folder, the Delphi designer will open, and here is the main window with the mobile application unit created:

Notice here that the default layout is for


Google Nexus 4 phone.
Since I have aSamsung Galaxy Tab 2 7,
I will change layout to Google Nexus 7,
using device ComboBox:

So, the new form in design mode will look like this:

114

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
kbmFMX for XE5 (Continuation 2)

DEVELOPERS

So, here you notice that Delphi has created


a sceleton application with gesture support,
and with tabbed interface,
located at the bottom of the form,
as Android standard:

Also, Delphi XE5 will create the following code to handle gestures and device keys:
procedure TTabbedwithNavigationForm.FormKeyUp(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
begin
if Key = vkHardwareBack then
begin
if (TabControl1.ActiveTab = TabItem1) and (TabControl2.ActiveTab = TabItem6) then
begin
ChangeTabAction2.Tab := TabItem5;
ChangeTabAction2.ExecuteTarget(Self);
ChangeTabAction2.Tab := TabItem6;
Key := 0;
end;
end;
end;
procedure TTabbedwithNavigationForm.TabControl1Gesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
{$IFDEF ANDROID}
case EventInfo.GestureID of
sgiLeft:
begin
if TabControl1.ActiveTab <> TabControl1.Tabs[TabControl1.TabCount-1] then
TabControl1.ActiveTab := TabControl1.Tabs[TabControl1.TabIndex+1];
Handled := True;
end;
sgiRight:
begin
if TabControl1.ActiveTab <> TabControl1.Tabs[0] then
TabControl1.ActiveTab := TabControl1.Tabs[TabControl1.TabIndex-1];
Handled := True;
end;
end;
{$ENDIF}
end;

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

115

COMPONENTS
kbmFMX for XE5 (Continuation 3)

DEVELOPERS

I want this app to be able to use camera and/or pick a


picture from the gallery, so I'll create an ActionList for
Media actions:

I will use the SQLite database to store my photo album together with some notes or cooments about them.
Here is SQL to create table in SQLite database:
CREATE TABLE "main"."Images" ("ImageID" INTEGER PRIMARY KEY AUTOINCREMENT
NOT NULL UNIQUE , "Image" BLOB, "Comment" VARCHAR)
For the purpose of creating an SQLite database, you can use the excellent SQLite Manager,
which is actually an add-on for Firefox, but you can use any other tool you prefer:

Now, to use SQLite db in your app, you add the kbmMWSQLiteConnectionPool component:

116

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
kbmFMX for XE5 (Continuation 4)

DEVELOPERS

On
http://www.components4programmers.com/produ
cts/kbmmw/download/sampleprojects.htm
you can find the LocalServer downloadable sample,
explaining how to use kbmMW based server embedded
in client application.
I have used the same schema here, only used SQLite
specific connectivity instead of BDE in sample.
So, after adding required components on main form, this is
how it looks like now:

Here you can specify the


Database, but you should
specify it dinamically because
Android requires that each
sqlite database is used together
with the application which
must reside in
/data/data/application_namespace/files/,
so we do something like following, using OnSetupDBConnection
Event of kbmMWSQLiteConnectionPool:
procedure TTabbedwithNavigationForm.kbmMWSQLiteConnectionPool1SetupDBConnection(
Connection: TkbmMWSQLiteConnection; Database: Pointer);
begin

{$IF DEFINED(ANDROID)}
kbmMWSQLiteConnectionPool1.Database := \TPath.Combine(TPath.GetDocumentsPath, 'myimages.sqlite');
{$ENDIF}
end;
you need to add System.iOUtils to the uses list.
In this instance, the Database path on the device will be
/data/data/com.fikrethasovic.AndroidFMXApp/ OK, back to kbmMW, you create standard QueryService,
and use SQLite specific components: Beside other
files/myimages.sqlite
components, added are two buttons, which are connected to
If your device is rooted, you can phisically access it,
the alMedia ActionList, which is responsible for the
but as default it is hidden from user,
unless you root our device, which will void your warranty. camera and gallery manipulation.
But, we need to use the Image captured by the Camera or
Notice, to use the Tpath class,
picked from the Gallery (or CameraRoll), so we will use
OnDidFinishTaking Event of standard media actions:
procedure
TTabbedwithNavigationForm.TakePhotoFromLibraryAct
ion1DidFinishTaking(
Image: TBitmap);
begin
if qClientSide.State <> dsEdit then
qClientSide.Insert;
(qClientSide.FieldByName('Image') as
TBlobField).Assign(Image);
end;

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

117

COMPONENTS
kbmFMX for XE5 (Continuation 5)

DEVELOPERS

You can use the same code for both Media actions here.
Here you notice kbmFMXDBGrid, kbmFMXDBImage and kbmFMXDBMemo components which are connected to
qClientSide kbmMWQuery responsible for fetching data from SQLite database.
I have removed some tabs I don't need, and left two only, so my interface looks like this:

Before we try to run it, don't forget to add following code to OnCreate method of main form:
kbmMWServer1.RegisterService(TkbmMWInventoryService,false);
kbmMWServer1.RegisterServiceByName('KBMMW_QUERY',TTestQuery,false);
kbmMWServer1.Active := True;
You can go without Inventory service, but you might want to test it here, by creating some test procedure, just use standard kbmMW
code... Since kbmFMX components are ReadOnly, I have used a standard Memo component to enter note/comment for pictures, so I
needed some code like this:
procedure TTabbedwithNavigationForm.qClientSideAfterScroll(DataSet: TDataSet);
begin
Memo1.Text := DataSet.FieldByName('Comment').AsString;
end;
procedure TTabbedwithNavigationForm.qClientSideBeforePost(DataSet: TDataSet);
begin
qClientSide.FieldByName('Comment').AsString := Memo1.Text;
end;
Application source code and compiled aplication dpk will be available for download from BlaisePascal, so let me try to run app and
show few screenshots here... Don't forget to add needed files for deployment, using Deployment tool:

Now, to use SQLite db in your app, you add kbmMWSQLiteConnectionPool component:

118

COMPONENTS
DEVELOPERS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
kbmFMX for XE5 (Continuation 6)

Delphi will add the launcher related files, but you need to
add some files, in this case SQLite database, or to add code
to create it from your application.
I have added db file here, and RemotePath should be
assets\internal\ so the client application will be able
to connect to.
OK, here are a few screenshots from my Samsung Galaxy
Tab 2 7 P3110:

DEVELOPERS

Here is one more:

Since I have a few android devices here, I have tested it on


different android versions, and it works on 4.0.3, 4.2.2,
4.3.1 and 4.4.2, stock LG and custom ROM's (SlimBean,
SlimKat).
Please note that you can just recompile (you might need to
change a couple of lines of code) this application for iPhone,
but since I don't own that kind of device, I haven't tested it.
It is a very exciting time for us folks!
I have added several images to database which is distributed Use full the kbmMW and kbmMemTable potential
together with kbmFMX components to create stunning
together with application, so this is the first screen, and
android business applications.
following is when you change tab to single photo:

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS
DEVELOPERS

119

kbmMW v4.40 available!


kbmMemTable v. 7.40 available!
Supports Delphi/C++Builder/RAD Studio 2009 to XE5 (32bit, 64bit and OSX where applicable).
kbmMW for XE5 includes full support for Android and IOS (client and server).

kbmMW is the premier high performance, high


functionality multi tier product for serious system
development.
-

Native high performance 100% developer


defined application server with support for
loadbalancing and failover
Native high performance JSON and XML
(DOM and SAX) for easy integration with
external systems
Native support for RTTI assisted object
marshalling to and from XML/JSON
High speed, unified database access
(35+ supported database APIs) with connection
pooling, metadata and data caching on all tiers
Multi head access to the application server,
via AJAX, native binary, Publish/Subscribe, SOAP,
XML, RTMP from web browsers, embedded
devices, linked application servers, PCs, mobile
devices, Java systems and many more clients

Supports Delphi/C++Builder/RAD Studio 2009 to


XE5 (32bit, 64bit and OSX where applicable).
kbmMW for XE5 includes full support for Android
and IOS (client and server).
kbmMemTable is the fastest and most feature rich
in memory table for Embarcadero products.
-

Easily supports large datasets


Easy data streaming support
Optional to use native SQL engine
Supports nested transactions and undo
Native and fast build in
aggregation/grouping features
Advanced indexing features
for extreme performance

Warning!

kbmMemTable and kbmMW


are highly addictive!
Once used, and you are hooked for life!

COMPONENTS
DEVELOPERS

ESB, SOA,MoM, EAI TOOLS FOR INTELLIGENT SOLUTIONS.


kbmMW IS THE PREMIERE N-TIER PRODUCT
FOR DELPHI / C++BUILDER BDS DEVELOPMENT
FRAMEWORK FOR WIN 32
/ 64, .NET AND LINUX
WITH CLIENTS RESIDING ON WIN32 / 64, .NET, LINUX, UNIX MAINFRAMES, MINIS, EMBEDDED DEVICES, SMART PHONES AND
TABLETS.

You might also like