You are on page 1of 120

BLAISE

BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE
D E L P H I,
A N D P A S C AL
A N D R O I D,
31/32
L A Z A R U S,

I O S,
O X Y G E N E,
R E L A T E D
M A C,
S M A R T,
L A N G U A G E S
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ž Gabrijelčič
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

The maXbox Pure Code By Max Kleiner


maXbox
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
D E L P H I,
P A S C AL
I O S, M A C,
31/32
L A Z A R U S,
R E L A T E D
O X Y G E N E, S M A R T, A N D
L A N G U A G E S
W I N D O W S & L I N U X
A N D R O I D,

CONTENTS ISSN 1876-0589


Royal Library -Netherlands
Koninklijke Bibliotheek Den Haag
Articles
Editorial Page 3 Editor - in - chief
Book Review: Coding in Delphi Page 4 Detlef D. Overbeek, Netherlands
By Jim Duff Book: Tel:+31(0)30 890.66.44/Mobile +31(0)6 21.23.62.68
Coding Delphi Author: Nick Hodges News and Press Releases
Designing an API: common mistakes Page 8 email only to editor@blaisepascal.eu
By Alexander Alexeev Authors
Newest Leap developments Page 21 A Alexander Alexeev
Michael Van Canneyt B Peter Bijlsma,
3D Printing By Bj Rao Page 26 C Michaël Van Canneyt, Marco Cantù,
Kinect ?! By Michael Van Canneyt Page 33 D David Dirkse, Daniele Teti
Smart Mobile Studio 2.0 Page 41 F Bruno Fierens
By Primož Gabrijelčič G Primož Gabrijelčič,
Interview with David I: Page 48 H Fikret Hasovic
plans about updating buying etc J Cary Jensen
A simple superscript text editor Page 50 L Wagner R. Landgraf, Sergey Lyubeznyy
By David Dirkse K Max Kleiner
Interview with Gwan Tan - better office Page 52 M Kim Madsen, Felipe Monteiro de Cavalho
Using GEO services in Delphi applications N Jeremy North,
with TMS components Page 57 O Tim Opsteeg, Inoussa Ouedraogo
By Bruno Fierens P Howard Page-Clark, Jeroen Pluimers
Correcting a bad API design: Page 65 S Rik Smit, Bob Swart,
By Alexander Alexeev

maXbox Editors
Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit
The maXbox Pure Code Page Page 77
By Max Kleiner Correctors
Interview with Ray Konopka Page 93 Howard Page-Clark, James D. Duff
Programming Bitmap Rotation Page 98 Trademarks
By David Dirkse All trademarks used are acknowledged as the property
Introduction to Model, View and View Model (MVVM) of their respective owners.
and the Caliburn Micro for Caveat Whilst we endeavour to ensure that what is
Delphi framework Page 102 published in the magazine is correct, we cannot accept
By Jeroen Pluimers responsibility for any errors or omissions. If you notice
kbmFMX for XE5 (android) Page 113 something which may be incorrect, please contact the
By Fikret Hasovic 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

maXbox download magazine)


Subscriptions can be taken out online at
www.blaisepascal.eu
or by written order, or by sending an email to
Advertisers office@blaisepascal.eu
Barnsten Pag 20 Subscriptions can start at any date. All issues published
BetterOffice Page 25 in the calendar year of the subscription will be sent as
Components 4 Developers Page 120 well.
ITDevCon Pag 39 Subscriptions run 365 days.
Lazarus the complete guide Page 19 Subscriptions will not be prolonged without notice.
Learnto program Page 24 Receipt of payment will be sent by email.
maXbox Page 77 Subscriptions can be paid by sending the payment to:
Raize Software Pag 92 ABN AMRO Bank Account no. 44 19 60 863
Smart Mobile Studio Page 40 / 46 or by credit card: Paypal or TakeTwo
Name: Pro Pascal Foundation-Foundation for Supporting
the Pascal Programming Language (Stichting
Copyright notice Ondersteuning Programeertaal Pascal)
All material published in Blaise Pascal is copyright © SOPP Stichting IBAN: NL82 ABNA 0441960863
Ondersteuning Programeertaal Pascal unless otherwise noted and may BIC ABNANL2A VAT no.: 81 42 54 147
not be copied, distributed or republished without written permission.
(Stichting Programmeertaal Pascal)
Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of Subscription department
the PGG for download, and that articles and code will be placed on Edelstenenbaan 21 / 3402 XA IJsselstein,
distributable data storage media. Use of program listings by subscribers The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile:
for research and study purposes is allowed, but not for commercial + 31 (0) 6 21.23.62.68
purposes. Commercial use of program listings and code is prohibited office@blaisepascal.eu
without the written permission of the author.

2 COMPONENTS
DEVELOPERS 4 Nr 5 / 6 2013 BLAISE PASCAL MAGAZINE
From The Editor

F 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
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
with your family and friends, times that are enhanced with on ARM cpus: so we will be able to port Leap Motion
little gifts and delicious meals... software to the Raspberry-Pi and set up some new tools.
This year we plan to offer our new group of Components
2013 was supposed to have been the end of the world – for Leap Motion on several platforms.
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 I wrote a chapter about the history of computing for
them are lessening in severity… 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
realize that our governments do not play by the rules.
find out something about the founding father of
Ordinary mortals can do little beyond despairing at the Pascal and write a few lines about him.
invasion of privacy and taking what steps we can to In Cologne recently I met Max Kleiner and we
combat it. Politicians must battle this erosion of discussed the origins of Pascal, and he told me
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;
but in the world of programming we already have a
Mr. Wirth has agreed to this and you will be able to
solution for this: simply remove it by deletion (Garbage read the outcome of our conversation in the next
Collection)! issue. In his honour we will start a new project: PEP –
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
as well).
for those new to programming.
String theory is a very good example of this: Young people (12 to 18 years of age) and students
it should be impossible for an entity to exist at two distinct will be offered a free Blaise magazine subscription so
places at the same time. And when you cast your mind they can receive information without needing to pay.
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
that will protect us from any intrusion or decryption.
think help should be organized. We plan to announce
Don’t believe them… the first trial version in the next issue: meantime you
can help us by letting us know your gripes and
You would do better to spend your money on the newly wishes for an improved IDE help system.
developing future like the adventure of
the Leap Motion or even 3D Printing, and be creative (as There are many plans for 2014:
you probably have always been) in writing new programs The next Pascon conference to follow the great
with new code. People outside the programming world success of last summer's event;
may not understand this need to innovate, or the many
The new Leap component –
new possibilities in our Pascal world offers.
Experimenting with Kinect
No Operating System is excluded any more. Many articles about Android development using
Pascal is not only doing good it is getting back where it Pascal, and new opportunities for that OS…
belongs: at the top of the range of available languages,
because of its enormous potential in education, for solving It’s going to be a very busy year.
problems, in facilitating Rapid Application Development, Very Exciting!
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 COMPONENTS


DEVELOPERS 4 3
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 Forward


Author: Nick Hodges Introduction
Acknowledgements
There are a couple of ways to preview what a Frameworks used in Coding in Delphi
technical software book is all about before one goes
ahead to use it. The first quick act is to look at the list 1 Exceptions and Exception Handling
of contents and then flick through some of the 2 Using Interfaces
chapters to preview the wording, code samples and 3 Understanding Generics
pictorial views. The next step is to analyse the 4 Understanding Anonymous Methods
knowledge and validity of the author to make sure 5 Delphi Collections
that the contents and sample code are going to give 6 Enumerators in Delphi
you the correct, accurate and productive assistance 7 IEnumerable
for your development design and coding. 8 Run-time Type Information
As a reviewer, the above second step is an 9 Attributes
important one in order to provide the magazine 10 Using TVirtualInterface
readers with a brief and positive (or negative - not in 11 Introduction to Dependency Injection
this case) assessment of the book. So, here we go 12 A Deeper Look at Dependency Injection
with a summary of the first two write ups, namely 13 Unit Testing
14 Testing with an Isolation Framework
Forward, by Allen Bauer, Embarcadero's Chief
Appendix A: Resources
Scientist, and Introduction by author Nick Hodges.
Appendix B: My Delphi Story
In Allen's Forward chapter, he provides a
write up of Nick's background beginning with "Nick
The list of chapters shows that the book is aimed at
holds the honor of producing one of, if not the first
experienced Delphi developers rather than being a
non-Borland built component. In order to learn about
beginner's guide. Having already mentioned the
Delphi component building, he built TSmiley."
Forward and Introduction items above, one more
Looks like that must have worked OK as Nick
item to mention in support of the qualified author is
became a member of Borland-Embarcadero. And
the final Appendix - My Delphi Story. This is the
near the end of Allen's summary, he made the
third part that once more gives the reader the
following link between the positive aspects of the
confidence in going ahead to take in the technical
book and how such value would provide a beneficial
aspects of the book.
effect for developers.
4 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Book Review: Coding in Delphi (Continuation 1)
So, finally getting into the technical documentation,
If I could teach a new programmer
an introductory chapter Frameworks used in Coding
one thing it would be this:
in Delphi provides "...a number of open source Program to an interface, not an
frameworks to facilitate the coding techniques that implementation.
are discussed herein. I refer to them here at the
beginning of the book to enable you to retrieve the
This sample quote that soon follows the above one,
code and get them set up before we begin."
is provided to further confirm the reasonings
provided throughout the book, in line with the
Now, we are ready to go, and a major influence of
sample code and instructions.
this book is having this subject matter as the first
main chapter:
"All through this book, I'll talk a lot about decoupling
1. Exceptions and Exception Handling
Introduction
your code and why that is really good.
Structured Exception Handling But a definition here is probably a good idea.
How Not to Use Exceptions Code can be said to be decoupled when your classes
Don't Eat Exceptions are designed in such a way that they don't depend on
Don't Generally Trap Exceptions
Don't Go Looking For Exceptions the concrete implementations of other classes.
Don't Use the Exception Handling System as a Two classes are loosely coupled when they know as
Generic Signaling System little about each other as possible, the dependencies
How to Use Exceptions Properly
between them are as thin as possible, and the
Use Exceptions to Allow Your Code to Flow Without the
Interruption of Error Handling Code communication lines between them are as simple as
Application Writers Should Trap Exceptions possible."
Trap only specific exceptions A code sample and part of the associated instructions
Component Writers and Library Code Writers Raise
Exceptions
that provide associated reasonings are now given as
Raise Your Own Custom Exceptions the final quoted sample in this review.
Let Your Users See Exception Messages
Feel Free To Provide Good Exception Messages type
Provide Two Versions Of A Library Routine IName = interface
Conclusion ['{671FDA43-BD31-417C-9F9D-83BA5156D5AD}']
function FirstName: string;
This provides not only the 'how' together with function LastName: string;
sample code, but the reasoning behind having it as end;
a fundamental method of coding.
It reminds one of the basic input-process-output of " Note that the declaration of the interface has a
computer processing; if these steps don't work, then Globally Unique Identifier (GUID) right after the
the hardware/software doesn't work properly initial declaration. This GUID is used by the compiler
and/or stops working - full stop. to identify this interface.
Part of his final comments in the Conclusion You can use an interface without the GUID,
provides the 'why': "It's quite easy to fall into the but much of the RTL and most frameworks that take
trap of using exception handling improperly. ... Use advantage of interfaces will require a GUID be
exceptions wisely, and you'll be able to product present. (You can generate a GUID any time you want in
robust, clean code." the IDE by typing CTRL+SHIFT+G)”
2. Using Interfaces
Introduction
The remaining chapters follow in a logical sequence,
Decoupling each with code samples plus the 'how' and 'why' to
What are Interfaces? use them.
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?
Coding in Delphi

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 4 5
Book Review: Coding in Delphi (Continuation 2)
Summary Nick held the position of Product Manager for a
Hopefully, the selected quotations from the book while then managed a portion of the Delphi R&D
have provided the reader with positive views of the team. Nick was never afraid to challenge
author's ability to explain the 'how' and 'why' to assumptions and actively stand up for the Delphi
advance your coding capabilities. developer community.
Nick has also given a good background of his own Even though Nick’s position didn’t require him to
and Delphi's history, from the early days to the write code, he didn’t let that stop him.
present, to show how they and we readers can He was always looking for ways to keep his
advance our development capabilities as the programming skills as sharp as possible. As new
technical environment is accelerating in this day features were developed, Nick was always right
and age. there to give them a workout.
To this day there is a large amount of code written by
The 'book' reviewed has been the pdf version and
Nick that remains a key portion of the overall
there are several links in Appendix A such as Unit
regression and unit-testing process.
Testing, Projects, Good Stuff etc. The following
Nick discovered that some of the best ways to
link is also provide at the start of the book:
learn about new code is to test that new code. It is not
"Find out what other people are saying about the without irony that this process requires code to be
book by clicking on this link to search for this written.
hashtag on Twitter: It stands to reason that Nick would end up here;
https://twitter.com/search?q=#codingindelphi writing a book with a single focus on the code.
That is the engine that drives all the other
Reviewed by Jim Duff revolutionary features of Delphi.
ADUG Melbourne Member Without the code, there would be no “Visual” in the
Visual Component Library (VCL).
The following lines are taken out the book. In fact, Delphi has always been about getting the
developer to their code as quickly as possible. The
Forward VCL and the newer FireMonkey component
I first met Nick during a Delphi 1 pre-launch “ frameworks make the use and construction of UI,
boot-camp” that was held at the brand new Borland database, connection and others as simple as
campus in Scotts Valley, California. possible. Its ultimate goal is to allow the developer to
We had invited a cadre of developers, authors focus on their task, which is to produce an
and long-term luminaries to get some in-depth application that solves a specific problem. It is the
training and presentations directly from the code that is written in between those UI, database
development team and product management. and connection components that make up the
Enthusiastic and exuberant are adjectives that don’t application.
fully characterize my first impressions of him. Developers at all levels will be able to use the
He was never afraid of asking questions and information contained within this book.
absorbed the information quickly. It doesn’t matter if you’re relatively new to Delphi or
programming or you’ve got many years of
I cannot talk about Nick without also discussing experience. If there is one common trait I’ve found
TSmiley. Inquisitive and curious, among software developers, it is their desire to learn.
Nick embraced Delphi without reservation. Learning a new technique or figuring out how to
To that end, Nick wasn’t satisfied with what Delphi apply that technique to one’s unique situation is part
did, but was just as keen on learning about how it did of the excitement of being a software developer.
it. To that end, Nick holds the honor of producing one This is right up there with the thrill experienced
of, if not the first non-Borland built component. when some thought, idea or concept comes to life
In order to learn about Delphi component within the computer. When a developer sees their
building, running application, rarely do they think about the
he built TSmiley. In this one simple component all the few moments they spent arranging some controls on
core aspects of using Delphi’s Pascal language to the User Interface. They feel a sense of pride about
extend and enhance the VCL framework were that one difficult and thorny problem they solved
demonstrated. through the clever use of code. At the end of the day,
You see, Delphi component building is all about the to a developer, it is really all about the code.
code.
I had the pleasure of working closely with Nick Allen Bauer
during his time at Borland and then Embarcadero.

6 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Book Review: Coding in Delphi (Continuation 3)
Introduction
Over the years, I’ve spoken in front of a lot of Delphi What you will find are ways to make your code much
developers. The one thing that I notice is that there are a lot cleaner, much more powerful, and way easier to maintain.
more of you Delphi guys than the average Delphi guy This book is all about the cool, new code you can write
thinks. There are Delphi folks everywhere. Also, I have with Delphi. It won’t matter whether you are building a
noticed that a lot of Delphi developers are “behind”. That VCL or an FM application. I’ve titled it “Coding in Delphi”
is, they are either using an older version of Delphi, or they because I want to make it a book that shows you simple
aren’t using or aren’t even aware of all the features in the examples of how to use powerful features – that, is about
newer versions of Delphi that they are using. the code. These language features are indeed advanced
Something I like to do when I’m in front of folks is features – they are new relative to, say, the case statement
ask a few questions about what people are doing. I’m – and thus many of you are beginners to them. By the end
always saddened that the response to questions like “Who of this book, you won’t be.
is doing unit testing?” or “Who is taking advantage of The approach I’m taking for this book is to try to
Generics?” distill things down to the very basics. The examples will be
is pretty meager. very simple but the explanations deeper. I believe that if
This is particularly true for the language features and the you can understand the basic idea in a simple example, it is
run-time library. It’s quite easy to move forward with an but a small leap to using those ideas in more complex code.
older code base, utilizing the new features in the IDE and There is no point in giving complex examples
adding new things to your app using the new high level when you can get the main thrust using fundamental
frameworks and components that come in the newer implementations that illustrate advanced ideas.
versions. Between the code in this book and in the samples online
For example, you might have been developing an (https://bitbucket.org/NickHodges/codinginde
application since Delphi 3, moving forward through lphi²) you can learn all the precepts and then begin
various versions. Along the way, you may have added applying them to your own code. In other words, nothing
some DataSnap functionality, started using the Code fancy here, just the basics – it is then up to you to use these
Insight features in the IDE, and when you moved to XE2, ideas in your own code.
you start poking around with FireMonkey. This book is not done – it’s instead a living document.
But it’s fairly easy to ignore the new language features that Since it is self-published on a platform that makes iteration
come along with those new versions. very easy, I plan on having frequent releases to fix typos
For instance, two powerful language features were added (which will, I’m sure, sneak through despite my best efforts),
in Delphi 2009: generics and anonymous methods. improve examples and descriptions, and keep up with
Both are features that enable the development of really cool technology changes. Owners of the PDF should get
code and frameworks. notifications of new versions automatically.
But if you didn’t understand or feel the need for them,
then it was pretty easy to simply not use them.
If you are reading a paper version of this book, I’m sorry I
You can still do all kinds of great stuff in Delphi without
can’t deliver fixes to your hard-copy – perhaps
them, but with them, well, you can write some
some day that will be possible.
really beautiful, testable, and amazing code.
The book will be done when you guys say it is done.
For instance, a relatively new framework that
Maybe, it will never be done because Delphi keeps
exists only because of these new language features is the
growing and expanding. I guess we’ll see.
Spring Framework for Delphi, or Spring4D, as I’ll refer to it
As a result, I’m totally open to feedback – please feel free to
in this book. Spring4D is a feature rich framework that
contact me at nickhodges@gmail.com
provides a number of interesting services, including a wide
with suggestions corrections, and improvements.
variety of collections that leverage generics, an Inversion of
Please join the Google Plus group for the book.³
Control container, encryption support, and much more.
I may even add whole new chapters.
I view Spring4Dsolid as much a part of the Delphi
Thanks for your purchase – this book was a labor of love,
RTL as SysUtils is. Using Spring4D in your code will make
so every sale is icing on the cake.
many, many things easier and cleaner. But many Delphi
developers don’t know this yet. Nick Hodges
If the above is familiar, this book is for you: Gilbertsville, PA
The Delphi developer that hasn’t quite yet made the leap
over into the cool and amazing things that you can do with ²https://bitbucket.org/NickHodges
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
Coding in Delphi

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 won’t find much of anything about the IDE or
the higher level frameworks here. Screen shots will be few
but code examples many. You won’t find anything about
how to build better user interfaces or fancy components.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 7
Designing an API: common mistakes By Alexander Alexeev
starter expert You do not have to do anything beyond distributing a .tlb
file (which can also be embedded in the .dll itself).
When you want to create a public component for use by If the 'foreign' language can work with COM,
several potential applications, you can register it as a
then it can import information from the TLB and generate
COM object. (Component Object Model (COM) is a binary-
appropriate header files for itself.
interface standard for software components introduced by
A TLB file is a binary file which has been created and
Microsoft in 1993)
edited by a TLB editor (or generated by an IDE). It is also
Because COM was designed to simplify exactly this
task, creating a publicly offered component as a COM
possible to "compile" a TLB file from a text description
object is an obvious way to do it. However there are - an IDL file (.idl or .ridl).
drawbacks to development using COM. If you are smart enough, you can "borrow" this feature
Firstly COM technology exists only on Windows, from COM.
and secondly it has a steep learning curve - which
means that you need to learn many things before you Your documentation is a textual description of the DLL
can begin to develop your public component.
API written by the DLL developer for other developers
Also if you are only developing a simple component
COM may be overkill. who will use the DLL (i.e. it is one-sided).
Consequently a developer may avoid the COM route, Of course, you can also document internal details for
choosing to implement his component as a simple DLL. yourself; but that is not the concern of this article.
When you create a new DLL you need to decide: So, you should provide documentation which at least
- what functions you want it to export; contains a formal description of the API, listing of all the
- what arguments the functions will have; functions, methods, interfaces, and data types, along with
- how you want to transfer data, and other issues.
explanations of "how" and "why" (the so-called Reference).
Taken together, we call this the API of your DLL (it is
Additionally, the documentation may include an
also termed a protocol or contract).
informal description of the code-development process
The API (Application Programming Interface) is a set of
(a guide, a how-to, etc.). In the simplest cases, the
rules (the contract), which you as developer of the DLL
documentation is written directly in the header files as
and the user (as caller of your DLL) agree to follow in
comments, but more often it is provided as a separate file
order to understand each other and to interact
successfully.
(or files) in chm, html, or pdf format.
All DLL services are provided only under this contract.
Unwritten rules
This article describes typical mistakes, (This section is based on
features and pitfalls developers encounter http://blogs.msdn.com/b/oldnewthing/archive/
as they develop a public DLL API. In 2006/03/20/555511.aspx )
general, this article serves as a kind of There are some basic ground rules that apply to all
check list for you. You can compare your programming, which are so obvious that most
code with this list and find out how good it documentation and books do not bother explaining them
is, and if it contains any of the mistakes (because these rules should have been internalized by
typically found in such DLLs. 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

W
e assume you are developing a public DLL.
So you will have a .dll file, you will have the consider taking a shortcut through somebody's backyard or
header files (at least *.h and *.pas), and you will going the wrong way down a one-way street.
have documentation. In the same way that an experienced chess player doesn't
The header files (or headers) form a set of source files even consider illegal options when deciding his next move,
containing structure and function declarations used in the an experienced programmer doesn't even consider
API for your DLL. violating the following basic rules without explicit
Typically they contain no implementation. permission in the documentation which allows
The headers should be available in several languages. contravening the rule:
As a rule, this means the language used to create the DLL • Everything not defined is undefined.
(in our case - Delphi), C (as standard) and perhaps additional This may be a tautology, but it is a useful one.
languages (such as Basic, etc.). Many of the rules below are just special cases
All these header files are equivalent to each other, of this rule.
• 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).
8 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes (Continuation 1)
o Pointers must be properly aligned. • Input buffers:
Pointer alignment is a fundamental o A function is permitted to read from the full extent
architectural requirement, yet something many of the buffer provided by the caller, even if the
people overlook, having been pampered by a entire buffer is not required to determine the result.
processor architecture that is very forgiving of • Output buffers:
alignment errors.
o An output buffer cannot overlap an input buffer
o The caller has the right to use the
or another output buffer.
memory being pointed to.
This means no pointers to memory o A function is permitted to write to the full extent
that has been freed or to memory that the caller of the buffer provided by the caller, even if not
does not have control over. all of the buffer is required to hold the result.
o All buffers are as big as the declared o If a function needs only part of a buffer to hold
(or implied) size. the result of a function call, the contents of
If you pass a pointer to a buffer and say that it the unused portion of the buffer are undefined.
is ten bytes in length, then the buffer really o If a function fails and the documentation does not
needs to be ten bytes in length (or more). specify the buffer contents on failure,
o Handles refer to valid objects that then the contents of the output buffer are
have not been destroyed. undefined.
If a function wants a window handle, This is a special case of the
then you must pass a valid window handle. "everything not defined is undefined" rule.
o Note that COM imposes its own rules
• All parameters are stable.
on output buffers. COM requires that all output
o You cannot change a parameter
buffers be in a marshallable state even on failure.
while the function call is in progress.
o If you pass a pointer, the pointed-to memory For objects that require nontrivial marshalling
will not be modified by another thread for the (interface pointers and BSTR/WideStrings being the
duration of the call. most common examples), this means that the output
o You cannot free the pointed-to memory either. pointer must be nil on failure.

• The correct number of parameters is passed with the (Remember, every statement here is a basic ground rule, not an
correct calling convention. absolute inescapable fact. Assume every sentence here is prefaced
This is another special case of the "everything not with "In the absence of indications to the contrary". If the caller
defined is undefined" rule. and callee have agreed on an exception to the rule, then that
o Thank goodness, modern compilers exception applies.)
refuse to pass the wrong number of parameters,
though you would be surprised how many Error:
people manage to sneak the wrong number of Providing no dedicated initialize
parameters past the compiler anyway, and finalize functions
usually by devious casting. The first mistake you can make as an API developer is not
o When invoking a method on an object, to provide standalone functions to initialize and finalize
the Self parameter is the object. Again, your DLL, but instead use the DLL_PROCESS_ATTACH
this is something modern compilers handle and DLL_PROCESS_DETACH notifications from the
automatically, though people using COM DllMain callback-function.
from C (and yes they exist) have to pass
DllMain is a special function in the DLL,
the Self parameter manually,
called automatically by the system in response to certain
and occasionally they mess up.
events. Among those events are DLL_PROCESS_ATTACH
·• Function parameter lifetime: and DLL_PROCESS_DETACH
o The called function can use the parameters - these are notifications about the loading and unloading of
during the execution of the function. your DLL.
o The called function cannot use the parameters If you are using Delphi, then the initialization section
once the function has returned. Of course, if the and the finalization section of Pascal units in the DLL are
caller and the callee have agreed on a means of executed in response to DLL_PROCESS_ATTACH
extending the lifetime, and DLL_PROCESS_DETACH, which the system sends to
then those agreed rules apply. the DllMain function of your DLL.
§ The lifetime of a parameter that is a Of course, you do not see this process, it is happening
pointer to a COM object can be extended
under the hood of the RTL (language support library).
bythe use of the IUnknown.AddRef
You just see that when the DLL is loaded, all units are
method.
§ Many functions are passed parameters initialized, and when it is unloaded, they are finalized.
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 COMPONENTS


DEVELOPERS 4 9
Designing an API: common mistakes (Continuation 2)
What is the problem here? and some of the functions even report:
DllMain is no ordinary callback-function. It is invoked by a ...in case of success, the function returns the size
very specific point ( of the data, or 0 if there is no data. In the case of error,
http://blogs.msdn.com/b/oleglv/archive/2003 the function returns 0. To determine the exact cause,
/10/24/56141.aspx ). you should call GetLastError,
There are very few actions that you can take in DllMain which returns ERROR_SUCCESS for a successful call,
with guaranteed safety. or an error code.
For example, loading libraries, starting threads,
thread synchronization, COM calls, even calls to other What a nightmare!
libraries (except kernel32) – performing any of these actions Once again, we encounter two function calls rather than
inside DllMain may lead to blocking (deadlock/hang/freeze). one, not to mention the complexity of the extensibility (to
When you realize that developers usually do not use our own error codes) and the inability to receive more
consider whether the code they put in the initialization context data about the error.
and finalization sections of their units is truly admissible
there, you also realize that relying on such behaviour Accordingly, many people when designing their own API
(automatic initialization of units from DllMain) is not the best see how it is done in the operating system and imagine
design solution. they should do the same. "Well, if that is how
That's why your DLL must export the two functions Microsoft does it, then we will copy them
like Init and Done, which will contain the actions that you because (perhaps) it is correct and necessary
otherwise would have inserted in the initialization and to do it that way."
finalization sections. However, they fail to realize that today's WinAPI was
Whoever loads your DLL should immediately import created a long, long time ago.
and call the Init function. A huge number of functions began their life in 16-bit code.
Later he should also call Done just before unloading your These functions were designed for requirements that
DLL. simply do not exist today.
Error: There is no need to use such ancient and uncomfortable
Using ancient techniques for memory approaches. Instead use a modern approach.
management and error handling
In any API, there are two very important aspects: Here (in descending order of preference) is how you can
how you pass data of arbitrary (dynamic) size, and how transfer varyingly sized data without sacrificing the
you report any errors from API function calls. convenience of a call:
A typical WinAPI function contains this logic: • [Special case, only for strings] BSTR/WideString.
The caller must call a function with • The interface with the lpData and cbSize properties.
lpData = nil and cbSize = 0, whereupon the • A DLL can export a function for freeing memory
function returns an ERROR_INSUFFICIENT_BUFFER which the DLL itself allocated.
error, and cbSize will contain the amount of memory in • You can use system memory management
bytes required to store all the data. (most often: HeapAlloc/HeapFree).
The caller can then allocate sufficient memory and call
the function again, passing to lpData a pointer to a block Here is a list for error handling (in descending order of
of data, and passing the size of the block to cbSize. preference):
This approach is complex in itself (calling the function twice • COM style: HRESULT + ICreateErrorInfo.
instead of once), and imagine how it would work for Delphi may additionally take advantage of
frequently changing data. the "magic" safecall call model.
What happens if the data increases in size between • The function returns HRESULT.
your two calls? Then the second call will return an • Functions return a sign of success/failure, the error
ERROR_INSUFFICIENT_BUFFER error again, and again code is obtained from GetLastError.
you will need to re-allocate more memory and repeat • Similar to the previous item, but implementing your
the call. That is - to reliably call the function - you have to own error code function.
write a loop.
Why do most of the WinAPI functions use such a Moreover, there is no need to use the naked functions -
terribly complicated method? Answer: history. because today we have interfaces. Interfaces offer solid
When these functions were first designed there were no advantages:
modern de-facto standards; moreover the Windows • Automatic memory management
developers sacrificed convenience for the sake of micro- - no problems with the allocation / release.
optimizations • Delphi gives you a safecall calling convention
http://blogs.msdn.com/b/oldnewthing/ and virtual methods
archive/2004/08/23/218837.aspx - for customizing the compiler "magic".
• Simplified versioning, because an interface
Similarly, a typical WinAPI function reports that it can be uniquely identified by a GUID (aka IID).
...returns True on success and False on failure. • Data is grouped with methods for handling this data.
The cause of the error can be obtained by calling • Performance does not suffer (a lot).
GetLastError.

10 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes (Continuation 3)
Error: What is the purpose of a shared memory manager?
Using language-specific data types In a sense, a shared memory manager is a "hack".
or other language-specific constructs It is a quick and dirty way to solve the problem of
Obviously if you create an API to be used from various exchanging dynamic data between modules.
programs each written in a different language, then you Never use a shared memory manager at the beginning of
cannot use structures that exist only in your language. a new DLL project.
For example, string (as well as AnsiString, UnicodeString, A shared memory manager is a means of backward
ShortString, String[number]), array of (dynamic and open compatibility, but does not fit with new code.
arrays), TObject (i.e. any objects), TForm (and components), If you need to exchange objects (including exceptions),
etc. strings or other language-specific constructs you have to
use BPL-packages, not DLLs.
If you use a data type (or language construct) that has no If you have used a DLL, then you must not use Delphi-
counterpart in other languages, then code in this very specific types and, therefore, must not use the shared
different language simply will not be able to use your API. memory manager.
It would have to emulate the language constructs of your Hence the comment at the beginning of the earlier section
language. about the forbidden use of UnicodeStrings (and so on) in
DLLs.
So, what can be used in an API? The following:
• integer types You can easily transfer data of varying size if you follow
(Integer, Cardinal, Int64, UInt64, NativeInt, NativeUInt, the guidance above; and you already know you should not
Byte, Word, etc. - with the exception of Currency); use Delphi-specific types in a DLL.
• real types Therefore, there is no need to use a shared memory
(Single and Double - except Extended, Real, Real48 manager at all (either alone or using run-time packages).
and Comp);
• static arrays Error:
(array[number .. number] of) of the acceptable types; Failing to protect each exported function with
• set, enumerated and subrange-types a try/except block
(with some reservations - see below; The explanation above should have made it clear that you
it is preferable to replace them with integer types); cannot pass any objects between modules.
• character types An exception is also an object (in Delphi).
(AnsiChar and WideChar, but not Char); Adding two plus two, we realize that you cannot throw an
• strings exception in one module and then catch it in another.
(only in the form of WideString; strings The other module has no idea how to work with an object
as PWideChar - allowed, but not recommended;
that was created in a different programming language.
PAnsiChar is valid only for ASCII-strings;
PChar strictly prohibited;
This means, of course, that you have to think carefully how
ANSI-string is prohibited);
you will report errors (as mentioned above). Here I am
• Boolean type
talking only about the particular case of an exception.
(BOOL, but not Boolean; ByteBool, WordBool and
Because an exception cannot (that is: should not) leave the
LongBool are acceptable, but not recommended);
module, you must implement this rule religiously: put a
• interfaces which use and operate
try/except block to catch all exceptions around the code of
with acceptable types only;
• records with fields of acceptable types; each exported function.
• pointers to data of acceptable types;
• untyped pointers; Note:
• data types from the Winapi.Windows.pas unit • The function can be exported explicitly (“exports
(or a similar unit for non-Windows platforms); function-name”) or implicitly (e.g. as a callback-
• data types from other system headers function or other function which returns a pointer to
(they are located in the \Source\ RTL\Win of your Delphi; the calling code).
replace "Win" path with OSX, iOS, etc. Both options must be wrapped in a try/except block.
- for other platforms). • "Catch all exceptions," of course, does not mean that
you wrap the function in an empty try/except block.
Error: You must not turn off all exceptions. You have to
Using a shared memory manager catch them (yes, catch them all) and transform rather
and/or packages than suppress them.
Any shared memory manager You must convert each exception to something
(such as ShareMem, FastShareMem, SimpleShareMem, etc.) prescribed by your protocol (perhaps an error code, an
is a language-specific facility which does not exist in other HRESULT, or whatever).
languages. So (as in the previous section) you should never
use any of them in your API. The same applies to run-time Note also that if you use the method recommended above
packages (.bpl packages). This package concept exists only (interfaces with safecall) then this issue is automatically
in Delphi (and C++ Builder). 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.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 11
Designing an API: common mistakes (Continuation 4)
Error: Gotcha:
Using ANSI-encoding(s) Enumerated types
Support for Unicode appeared in the Windows API in 1996 An enumerated type is a convenient way to declare case-
(in Windows NT 4), and in 2000 Unicode support came to types (rather than using integers). What is the problem here?
the client OS (Windows 2000). Look at this code:
Although Windows 95 did contain Unicode functions,
there was no full support. type
The mobile OS market was Unicode only from the start TTest = (T1, T2, T3);
var
(Windows CE, 1996) through the PocketPC,
T: TTest;
Windows Mobile, and up to Windows Phone - all these
systems support exclusively Unicode, but not ANSI. Question:
That is, for more than 13 years Unicode has been the What is the size of the variable T in bytes?
default choice in all Windows versions. This is an important question because size affects the
For more than 13 years the ANSI API Windows functions position of the fields in records, when passing arguments
have been nothing more than stubs that do nothing beyond to functions, and in much other code.
converting the string encoding before invoking Unicode- The answer is that in general you do not know the size.
variants of themselves. It depends on the compiler and its settings. By default, it is
1 byte for Delphi. However, another compiler and/or
Support for Unicode in Delphi has been present since setting may result in 4 bytes.
Delphi 3 - as part of the COM support (that is from 1997).
Although until 2008 (Delphi 2009), the entire language Here is another question: since this type occupies 1 byte in
support library (RTL) and the component library (VCL)
Delphi, it can hold up to 255 values.
worked in ANSI.
But what if your enumerated type has more than 255
values?
However, in spite of the wide open opportunity to use
Answer: then the variable will occupy 2 bytes.
Unicode when constructing their own APIs most Delphi
developers even since 1997 (over 16 years), have not
Do you see where this is leading?
hesitated to use the "familiar types" - that is, at best a PChar
Suppose you have used 50 values in version 1 of your
(equivalent to PAnsiChar on the systems of that time), and at
DLL, so the fields of this type occupied 1 byte.
worst – a string with the shared memory manager.
In version 2 you have extended the possible values up to
Of course, those who were smart used PAnsiChar,
300 - and the field now occupies 2 bytes.
and PWideChar (since 2000).
It would not matter if we used this type only inside our
But WideString was hardly used - despite its undeniable
own code.
advantages: there are no problems with the exchange of
But since you are sharing it with other code, such a change
strings between modules, auto-conversion to string and
in the size of the data will be a complete surprise to the
back again, built-in support for Unicode, built-in pointer
other code.
length.
Overwriting (data corruption) and Access
Why the avoidance of WideString?
Violations are the result.
Probably because PWideChar is sufficient for easy transfer
of data inside the called function, and returning data from
Note:
called functions was required much less frequently.
in fact the problem arises even with far less than 300
elements. It is sufficient that the type has a 300-th element:
To sum up:
always use Unicode in your API for strings - even if you type
are using Delphi 7 and work with ANSI-strings inside: TTest = (T1, T2, T3, T4 = 300);
it does not matter. var
In 2013, the API must be Unicode. It is not 1995. T: TTest;
begin
WriteLn(SizeOf(T)); // shows 2
Well, what about the ANSI-adapters (stubs) to Unicode-
functions: 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
backward compatibility for something which This will cause the compiler to make every enumerated
never existed in the first place. type 4 bytes in size, even if you do not need so much.
No code was using your 2013 API functions with ANSI –
so there is no need to add ANSI-adapters to your API. 2. You can use LongWord (or Cardinal) and a
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. It is possible that the second method is preferable because
By now, you should be using WideString strings (aka - it clearly answers the question:
BSTR) to pass string data between modules; What are the numerical values of the type's elements?
or, in extreme cases, PWideChar.
You should not use PChar and PAnsiChar.
12 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes (Continuation 5)
Gotcha: For this purpose, a callback function is provided with a so-
Records called user-argument: either a pointer or an integer (such as
The uncertainty about the size of enumerated types also Native(U)Int, but not (U)Int), which are not used by the API
applies to records: itself and transparently passed directly to the callback-
function.
type
Or (in rare cases), it can be any value that uniquely identifies
TTestRec = record
A: Byte;
the callback function call.
B: Int64; For example, the system function SetTimer has idEvent,
end; while EnumWindows function has lpData.
These parameters are not used by the functions and are
What is the size of this record? 9? 10? 12? 16? simply passed directly to the callback-function unchanged.
The amount of unused space between fields (filler bytes) That is why we can use these parameters to pass arbitrary
also depends on the compiler and its settings. data.
If you do not implement user-parameters in your API, then
Overall, I would recommend using interfaces instead of the calling code cannot associate a callback-function with
records when possible. the data.
If you need to use a record: We will look at this issue in more detail in the next article.
either insert the directive {$A8} ({$ALIGN 8}) to the
beginning of each header file, or use the keyword packed Gotcha:
for the record. Mixing manual and automatic control
The latter may be preferable - because the alignment rules of an entity's lifetime
In general, you should try to use automatic control of a
in Delphi might be different from the alignment rules in
lifetime.
another language (for instance consider the case of problems
similar to this bug: http://qc.embarcadero.com/wc/ You have less chance to screw up, because the compiler
emits code to keep track of your objects and there is less
qcmain.aspx? d = 75838 ).
(fallible) human work to do.
But in any case, there will always be places where you
want manual control of a lifetime.
Gotcha:
The junction of these two control mechanisms is what can
Sets
cause problems.
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;
Overall, the set is a syntactic convenience for dealing with end;
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.
var
Error: Comp: TSomething;
Failing to provide user-arguments
function GetSomething: ISomething;
in callback-functions begin
A callback-function is a piece of executable code that is Result := Comp;
passed as an argument to other code, which is expected to end;
call back (execute) the argument at some convenient time.
For example, if you want to find all the windows on your begin
Comp := TSomething.Create(nil);
desktop, you can use EnumWindows: try
GetSomething.DoSomething;
function MyEnumFunc (Wnd: HWND; lpData: LPARAM): finally
Bool; stdcall; FreeAndNil(Comp);
begin end;
/ / This is called once for each window in the system end;
end;

procedure TForm1.Button1Click (Sender: TObject);


As you know, TComponent does not use automatic
begin reference counting and you must control its lifetime
EnumWindows (@MyEnumFunc, 0); manually.
end; The problem in the above code is in the line
GetSomething.DoSomething.
Since the callback function normally performs the same A temporary (hidden) variable of the interface type is
task as the code that sets it, it turns out that the two pieces created (for storing the result of the GetSomething call),
of code are working with the same data. which is cleared in the last line (at “end;”) - after the object
Consequently, the data from the code setting the callback has been released.
must somehow be passed to the callback function.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 13
Designing an API: common mistakes (Continuation 6)
Of course, this will never invoke the destructor for Gotcha:
TComponent (TComponent uses manual control and does not Identifying interfaces
respond to reference counting), but cleaning up is still Interfaces are different from other data types.
necessary. Clean reference counting means calling the They have two levels of identification.
_Release method – a method of an already deleted object. On the programming language level an interface is
Which will lead to an Access Violation. identified by an identifier name (such as IUnknown,
IApplication etc.) and is no different in this aspect from
Note: any other data type in Delphi.
Access Violations are not always raised for such errors – Two interfaces with the same declaration but with different
due to the “caching” behaviour of the memory manager. type identifiers are considered to be different data types by
Which makes such errors especially dangerous. the compiler.
A similar problem can be seen in this code:
On the other hand, the interfaces may also be identified not
begin only at the level of programming language, but at run-time
Lib := LoadLibrary(...); (by the machine code) – via meta-information: the
Win32Check(Lib <> 0);
try interface's GUID (IID). Two completely different
Func := GetProcAddress(Lib, ...); declarations, but with the same IID will be considered to be
Intf := Func(...); identical by run-time machine code.
// ... some action with Intf
Intf := nil;
finally Gotcha: The immutability of interfaces
FreeLibrary(Lib); Once you have published an interface ("published"
end; means you publicly release a version of the DLL with this
end;
interface definition), you cannot change it (either its IID or its
Between the LoadLibrary and FreeLibrary calls there structure) because the interface is used by third party code.
may be temporary variables created that hold references to Changing the interface declaration will break the other
interfaces from the DLL. (third party's) code.
Therefore, even if we have cleared all clearly visible
references before unloading DLL, hidden variables will be Instead of changing the interface, you need to create a new
released after unloading DLL and thus will call already interface with a new GUID. You should create a new
unloaded code (hello, another Access Violation). independent interface (preferably and usually) or inherit
from your old interface (allowed).
Of course, we (the developers) do not have an eagle eye to
find all the places where the compiler wants to create Gotcha:
hidden variables, so that we can convert such hidden Expanding interfaces
variables into explicit variables and explicitly free them. Look at this code:
Let me remind you that the solution would be to use the
fact that all temporary hidden variables are released at the type
time of exit from the procedure. IColorInfo = interface
Therefore, we must clearly distinguish between code that {ABC}
works with manual and automatic control: function GetBackgroundColor: TColorRef;
safecall;
...
procedure DoDangerousStuff(Comp: TComp);
end;
begin
// ... some action with the Comp,
IGraphicImage = interface
including the use of types with automatic control
end; {XYZ}
...
begin function GetColorInfo: IColorInfo; safecall;
Comp := TSomething.Create(nil); end;
try
DoDangerousStuff(Comp); Suppose you want to add a new method to interface
finally
FreeAndNil(Comp); IColorInfo:
end;
end; type
IColorInfo = interface
{DEF} // <- new GUID
procedure DoDangerousStuff(Lib: HMODULE); function GetBackgroundColor: TColorRef;
begin
safecall;
// ... some action with Lib,
...
including the use of types with automatic control
procedure AdjustColor(const clrOld,
end;
clrNew: TColorRef); safecall; // <- new method
begin end;
Lib := LoadLibrary(...);
Win32Check(Lib <> 0); IGraphicImage = interface
try {XYZ}
DoDangerousStuff(Lib); ...
finally function GetColorInfo: IColorInfo; safecall;
FreeLibrary(Lib); end;
end;
end;

14 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes (Continuation 7)
You have changed the interface, but you also have Gotcha:
changed the IID, so everything should be OK, right? Function return values
Functions or methods that return an interface (as in the
Actually - no. previous paragraph) present a problem for the extension.
Of course, in the beginning it is a convenient solution:
The IGraphicImage interface depends on the you can call the function "normally" and even hook them
IColorInfo interface. When you change the into chains like this:
IColorInfo interface, you implicitly changed the
Control.GetPicture.GetImage.GetColorInfo.GetBackgroundColor
IGraphicImage.GetColorInfo method
- because its return value has now changed to become However, this state of affairs will exist only in the very first
another: IColorInfo interface version v2.0. version of the system. As you begin to develop the system,
you will begin to create new methods and new interfaces.
Look at the following code, written with headers v2.0: In the not too distant future you'll have plenty of advanced
interfaces; and base interfaces that were originally in the
procedure AdjustGraphicColorInfo (pgi:
IGraphicImage; const clrOld, clrNew: TColorRef); program, at the time of its birth will provide only trivially
var uninteresting functions.
pci: IColorInfo;
begin Overall, very often the caller will need the newer interfaces
pci: = pgi.GetColorCount (pci);
rather than the original ones.
pci.AdjustColor (clrOld, clrNew);
end; What does this mean? It means that almost all the code has to
call the original function to get the original interface, and then
If this code is run on v1.0, the call request a new one (via Supports/QueryInterface) and only then
IGraphicImage.GetColorCount returns IColorInfo use the new interface.
version v1.0, and this version has no The result is not so comfortable, and even more uncomfortable
IColorInfo.AdjustColor method. is the fact we now have a triple calls (original/old + conversion +
desired/new).
But you still call it.
Result: you skip to the end of the method table and call the
Let us look again at the previous point:
trash that lies behind it.
the modification of one interface makes it necessary to make
copies of all the interfaces that use it as a return value
Quick fix - change IID for IGraphicImage,
- even if they themselves do not change.
to take account of changes in IColorInfo:

type The best solution for both cases is that the callee code indicates
IGraphicImage = interface to the called function which interface it wants
{UVW} // <- a new GUID - the new or the old.
... This can be done, of course, by specifying the IID:
function GetColorInfo: IColorInfo;
type
safecall;
IGraphicImage = interface
end; {XYZ}
...
This code update path is very time-consuming because you procedure GetColorInfo
have to keep track of all references to the variable interface. (const AIID: TGUID; out AColorInfo);
safecall;
end;
Moreover, you cannot just change the GUID
- you have to create a second interface IGraphicImage Note that now you cannot use the result of the function, as the
with a new GUID and manage the two interfaces (even result has to have a specific type (of course it does not have it -
though they are identical up to the return value). we should return interfaces of different types), that's why we use
the raw data output parameter.
When you have several of these changes and the use of a
large tree, the situation quickly gets out of control with Then, you can write code like this:
endless cloning of interfaces for every sneeze.
var
Image: IGraphicImage;
We will look at the correct solution to this problem in the
ColorInfo: IColorInfoV1;
next paragraph. 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);
...

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 15
Designing an API: common mistakes (Continuation 8)
You do not need to work directly with the IID: Delphi will Error:
automatically substitute the IID for the interface name. Interfaces without an IID
Every interface in your API must be declared with a GUID
Error: (interface identifier - IID).
Returning complex types via Result You may be tempted to skip the IID for interfaces that are
A good, general rule of thumb is this: returned from functions explicitly, without request by IID.
if you need to return something more complex than an But, as we saw above, you need to design your API in such
integer type (including auto-managed types: interfaces and way that you have no functions that return an interface via
WideString) – then you should always use the Result - because it is extremely difficult to further expand
out-parameter instead of the result of the function. the system. Therefore, all of your interfaces must always
Then you avoid bugs in Delphi like this: have an IID.
http://qc.embarcadero.com/wc/qcmain.aspx?d=75838,
and it seems to be a similar problem with real data type,
but I could be wrong. Error:
I think that Delphi and MS C++ disagree over which stack Missing interface declarations when declaring
(CPU or Math CPU) should be used to return a real result the implementing class
from the function, but I am not 100% sure about this, As you probably know already, there are two ways to
since I failed to find a link to the bug report. implement interfaces in a class:
1. Automatically.
The problem in all these cases is that Delphi and C++ differ You simply declare
in their interpretation of the calling convention model with TMyClass = class(the base class, the list of interfaces).
regard to returning complex types. Once you have declared interface support,
Delphi's documentation indicates that the following code: there is nothing more to do.
2. Manually.
function Test: IInterface; stdcall; You override the virtual class method QueryInterface,
is interpreted as: analyse the parameters and then construct
function Test: IInterface; stdcall; and return the interface.
while MS C++ literally follows the syntax and returns the
interface directly (EAX for x86-32). You would think that with the automatic method,
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; type


function Test2: WideString; stdcall; ISomeInterfaceV1 = interface
function Test3: TSomeRecord; stdcall; ['{A80A78ED-5836-49C4-B6C2-11F531103FE7}']
procedure A;
end;
Always use either this form (“out” can be replaced by “var”
for better performance): ISomeInterfaceV2 = interface(ISomeInterfaceV1)
// ISomeInterfaceV2 inherited from ISomeInterfaceV1
procedure Test1(out Rslt: IInterface); stdcall; ['{EBDD52A1-489B-4564-998E-09FCCF923F48}']
procedure Test2(out Rslt: WideString); stdcall; procedure B;
procedure Test3(out Rslt: TSomeRecord); stdcall; end;

TObj = class(TInterfacedObject, ISomeInterfaceV2)


or this one:
// List ISomeInterfaceV2, but not ISomeInterfaceV1
protected
function Test1: IInterface; safecall; procedure A;
function Test2: WideString; safecall; // necessary because object implements ISomeInterfaceV1.
function Test3: TSomeRecord; safecall;
Otherwise - a compilation error
procedure B;
The latter is valid for the simple reason that such code is end;
equivalent to:
procedure TForm1.Button1Click(Sender: TObject);
function Test1(out Rslt: IInterface): HRESULT; var
stdcall; SI1: ISomeInterfaceV1;
function Test2(out Rslt: WideString): HRESULT; SI2: ISomeInterfaceV2;
stdcall; begin
function Test3(out Rslt: TSomeRecord): HRESULT; SI2: = TObj.Create;
stdcall; Supports(SI2, ISomeInterfaceV1, SI1);
Assert(Assigned (SI1));
// Fails, SI1 = nil (Supports call returned False)
Please note that in our case we have removed this problem, SI1.A;
since we agreed to use the safecall calling convention. end;

However, what was said in the previous paragraph is still It turns out that even though the object implements the
in force: in terms of versioning interfaces, it is better to use interface it does not tell "outside" that it implements it.
constructs like:
procedure Test1(const IID: TGUID; out Rslt);
safecall;

16 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes (Continuation 9)
That is, if two interfaces are connected by inheritance, Gotcha:
the mere inclusion of the child interface into the list of Polymorphism and implementation
interfaces implemented by a class does not ensure the of interfaces
inclusion of an ancestor/parent interface in this list. When your object descends from a class its polymorphic
In other words, in order to be implemented automatically behaviour is achieved by virtual means. But when you use
by a class, you must ensure that this interface has appeared interfaces, all the methods of the interface are already virtual (by
at least once in the line "list interfaces" for the class (not definition).
necessarily in this class, it can be in an ancestor, but it must Therefore, there is no need to use virtual methods to implement
appear somewhere). interfaces (though virtual methods may be required for other reasons
The presence of a child interface is not enough. - for example, to inherit functionality).
Note that the code:
For example:
type type
ISomeInterface = interface ISomeInterfaceV1 = interface
['{A80A78ED-5836-49C4-B6C2-11F531103FE7}'] ['{C25F72B0-0BC9-470D-8F43-6F331473C83C}']
procedure A; procedure A;
end; procedure B;
end;
IAnotherInterface = interface
['{EBDD52A1-489B-4564-998E-09FCCF923F48}'] TObj1 = class(TInterfacedObject, ISomeInterfaceV1)
procedure B; protected
end; procedure A;
procedure B;
TObj1 = class(TInterfacedObject, ISomeInterface) end;
protected
procedure A;
TObj2 = class(TObj1, ISomeInterfaceV1)
end;
protected
TObj2 = class(TObj1, IAnotherInterface) procedure B;
protected end;
procedure B;
end; procedure TForm1.Button1Click(Sender: TObject);
var
procedure TForm1.Button1Click(Sender: TObject); SI: ISomeInterfaceV1;
var begin
SI1: ISomeInterface; SI := TObj2.Create;
SI2: IAnotherInterface; SI.A; // calls TObj1.A
begin SI.B; // calls TObj2.B
SI2 := TObj2.Create;
end;
Supports(SI2, ISomeInterface, SI1);
Assert(Assigned(SI1));
SI1.A; Note that specifying ISomeInterfaceV1 for TObj2 means
end;
that the method TObj2.B will implement ISomeInterfaceV1.B.
The key point here is - just specify the interface. Please note
and
that:
type • Method B does not have to be virtual
ISomeInterfaceV1 = interface • ISomeInterfaceV1 interface for TObj2 is assembled
['{A80A78ED-5836-49C4-B6C2-11F531103FE7}']
procedure A; "piece by piece": the method B is taken from the TObj2,
end; but the method A is taken from TObj1. This is a standard way
of working with interfaces and class inheritance.
ISomeInterfaceV2 = interface (ISomeInterfaceV1)
['{EBDD52A1-489B-4564-998E-09FCCF923F48}']
procedure B; However, as has been said, sometimes you may
end; want to use this code:
TObj1 = class (TInterfacedObject, ISomeInterfaceV1)
protected
procedure A; type
end; ISomeInterfaceV1 = interface
['{C25F72B0-0BC9-470D-8F43-6F331473C83C}']
TObj2 = class (TObj1, ISomeInterfaceV2) procedure A;
protected procedure B;
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 4 17
Designing an API: common mistakes (Continuation 10)
procedure TForm1.Button1Click(Sender: TObject); Note:
var of course, you can "solve" this problem by removing const
SI: ISomeInterfaceV1;
begin from the parameter declaration, but you need to
SI := TObj2.Create; understand that providing a well-formed argument is the
SI.A; // calls TObj1.A task of the caller, not the callee.
SI.B; // calls TObj2.B
end; In general, guided by the rule "Give way to a fool",
I would recommend not to use the const modifier for the
The effect of this code is the same as in previous code parameters of interface types, and, of course, not to use the
example, but the internal structure will be a bit different. constructor directly when passing arguments to such
I will only note that because of this difference, it is highly functions.
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
Error: trouble. For example, if your destructor passes a reference
Non-obvious feature of reference counting to itself to other functions, these functions may decide to
(constructor const-parameter) recall your _AddRef and _Release during their work. Look
Suppose you have a function/method with an interface at this code:
type parameter that is declared as const:
function TMyObject._Release: Integer;
begin
procedure DoSomething(const AArg: ISomething); Result: = InterlockedDecrement (FRefCount);
if Result = 0 then
and suppose that you pass to the interface the following Destroy;
argument: end;

destructor TMyObject.Destroy;
Obj.DoSomething(TSomething.Create); begin
if FNeedSave then
What will happen? Save;
The const modifier tells the compiler that it should call inherited;
end;
_AddRef and _Release on the interface.
It does not look very scary, does it?
On the other hand, we are creating a new object. The object just saves itself before it is destroyed.
What is the reference count of the newly created object?
It is equal to zero. But the Save method might look something like this:
The counter is incremented by _AddRef when the object is
used (for example, when the interface is assigned to a variable). function TMyObject.Save: HRESULT;
var
We have created an object with the counter set to 0, spstm: IStream;
spows: IObjectWithSite;
and passed it to a method that does not change the begin
reference count. Result := GetSaveStream(spstm);
As a result, the reference count never drops to 0 (simply if SUCCEEDED(hr) then
because it never rises from the ground), and, hence, begin
Supports(spstm, IObjectWithSite, spows);
no destructor of the object is called. As a result, if Assigned(spows) then
we get a leak for this object. spows.SetSite(Self);
Result := SaveToStream(spstm);
The solution is to use a variable: if Assigned(spows) then
spows.SetSite(nil);
end;
var
end;
Arg: ISomething;
begin
Arg: = TSomething.Create; By itself, it looks fine. We get a stream and save Self in it,
Obj.DoSomething (Arg); further establishing the context information (site) - just in
end; 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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes (Continuation 11)
But this simple code combined with the fact that it is function TMyObject._AddRef: Integer;
running from a destructor, gives us a recipe for disaster. begin
Assert (FRefCount >= 0);
Look what happens: Result := InterlockedIncrement(FRefCount);
end;
1. The _Release method decrements
the reference count to zero and deletes Self. function TMyObject._Release: Integer;
begin
2. The destructor tries to save the object. Result := InterlockedDecrement(FRefCount);
3. The Save method wants to save into if Result = 0 then
the stream and sets Self as the context. Destroy;
This grows the reference count from zero to one. end;
4. The SaveToStream method saves procedure TMyObject.BeforeDestruction;
the object to the stream. begin
5. The Save method clears the thread's context. if RefCount <> 0 then
This reduces the reference count of our System.Error(reInvalidPtr);
FRefCount := -1;
object back to zero.
end;
6. Therefore, the _Release method calls
the destructor of the object a second time. Note:
such a check is not present in TInterfacedObject.
The destruction of the object a second time leads to full- TInterfacedObject allows your code to run and call
scale chaos. If you are lucky, the crash inside the recursive the destructor twice.
destruction will be able to identify its source; but if you are This check will help you to easily catch "cases of
unlucky, it may cause damage to the heap, which will mysterious double calls to the destructor of the object."
remain undetected for some time, after which you'll be But when you identify the problem, then what do you do
scratching your head. with it?
Here's one recipe:
Therefore, as a minimum, you should insert an Assert call http://blogs.msdn.com/b/oldnewthing/archive
into your _AddRef method, to ensure that you do not /2005/09/28/474855.aspx
increase the reference count from zero during the
execution of a destructor:

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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Programming with the leap motion By Michaël 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.

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.

MOTION
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 Pointables
when it needs to compile code. 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 4 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 We will develop a complete set of components
shown e.g. in figure figure 1 on page 3. usable for Delphi aswel Lazarus.

The tap demo has been improved with a new ’magnetism’ It will contain the following Gestures
setting. The tap movement is very sensitive: while tapping, Tapping
the tip of the finger (used to select a button) moves. • (Clicking)
The effect may be that the actual button that is under the Open Gesture
finger cursor on the moment the tap gesture is received, • (a gesture that follows your own design andevents)
is not the button for which the tap was intended: Cirkels (2D Swipe)
• (Swiping - for quick rotation)
• (Dragging for precise movement)
Left to right (or vice versa)
The downward movement of the finger • (Swiping - for quick movement with number of lines to
during the tap may switch the focus to the run predefineable)
button below the intended. • (Swiping - for precision)
To prevent this from happening, a kind of Top or down (or vice versa)
magnetism is introduced: magnetism ’shrinks’ the surface • (Swiping - for quick movement with number of lines to
of a control, making it more difficult to be selected. run predefineable)
It is in fact the number of pixels that the cursor must • (Swiping - for precision)
be inside the actual border, before the control is selected as 3D Swipe
the new focused control. • (Swiping - for quick rotation)
The focus sticks to the previously selected control, • (Dragging for precise movement)
unless the finger cursor is really centered on the new for this one needs to have a 3D Picture
control, hence the name ’magnetism’.
We have already added sound to the gesture.
This simple trick makes the tap demo a lot more easy to
We want to add sound reaction and (snapping
handle, as some experimenting will confirm. with fingers) and verbal commands (English). We
will start with this in the coming months...
Conclusion
In the case of the Leap Motion, Lazarus truly lives
up to it’s 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.

You can download the latest code from your


subscription page.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 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 Starting to program
2 Lazarus and Pascal
3 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
4 Structured types
5 Expressions and operators
6 Pascal statements
7 Routines: functions and procedures
8 Class: An elaborate type
9 Polymorphism
10 Units, GUI programs and the IDE
11 Display Controls
a. TLabel, b. exploring TLabel properties, c. TStaticText,
d. TBevel and TDividerBevel, e. TListBox, f. TStatusBar,
g. Further options, h. Review Questions
12 GUI Edit controls
13 Lazarus GUI projects
14 Component Containers
15 Non-visual GUI Support classes
16 Files and Errors
17 Working within known limits
18 Algorithms and Unit tests
19 Debugging techniques
20 Further resources

LEARN TO PROGRAM
USING COMPONENTS
24
96
LAZARUS 4 Nr Nr
5 /56/ 2013 BLAISE PASCAL MAGAZINE
ISBN 978-94-90968-04-5
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 4 25
To 3d Print or Not to 3d print By BJ Rao

Detlef Overbeek, Editor in Chief of Blaise Pascal As computational power diversified and
Magazine, recently asked the question... progressively became less expensive it opened up
"Can Pascal somehow take part in the desktop 3D printing from the lab to industry. The PC
3D printing scene?" presented the means, the technology.

Perception vs Proof But what built the explosive and wide spread
awareness about 3D printing was not technology
The quick answer? That would be a "No". alone. It was actually a simple conversation that
did. A conversation between a few who wanted to
That train has left the station. Most all software make a particular technology more accessible. They
related elements in that process have been claimed made the first step and that conversation moved on
by others. Not one of them uses Pascal. Its like the to millions. It brought people together and created
popular kids are having a party and now some of an entire community. A mix of technological
the other kids want to join in as well. It does not development and social bonding. Democratized
matter how much technology, ingenuity and party development.
spirit the other kids may bring. Its not going to
happen. The Value of Technology
Technology itself has little value. At least not on its
But that is too easy. “No” is not an answer. It has own. It is still people that get things done.
little meaning here. It is too blunt. Moreover, it Pascal, like so many, is simply a language.
actually does not answer the question. It avoids it. Its technologies are the existing compilers,
We could think of a million reasons why something frameworks, components and IDE's that allow you
will not work. Typically, we do. But the idea here is to develop in that language. That's what makes it
to find an answer about what would work, or at accessible and applicable. Yet, the actual value and
least what might work. Glass half full, not half success of a language resides in and is proportional
empty. to a community that supports it.

In addition, as people of science and technology we Looking back 15 years ago Pascal was at an all time
don't like to think in terms of yes and no, good and high. And, for good reason. It made DOS and
bad or black and white. Those types of answers are Windows development extremely easy, versatile
too polarized. It is a too simplistic way of thinking. and fast. More importantly, it was not just building
It ignores all the shades of gray. Instead, we tend a community but communities, plural. Apart from
to rather observe the properties and patterns of developing business applications it was the
something. Discover how these might be used to standard in education. It was also the standard on
serve some purpose or task. Creating something the DIY (do it yourself) front. It made things clear
new. We think about truth. If we did not then what and its programing-correct structure was building
we create won't usually work in the real world. It's good programming practices for all. Unfortunately
that simple. for reasons other than its technology it was not
setting standards. Rather, it was forced to follow
But there is also a kind of a problem here. Having a the standard settings of others.
scientific or technical background means that most
tend to be more inclined to search for technical Many of the technological benefits of Pascal exist
solutions as answers. That's fine. Yet, at this point, today. And, a lot more. The Delphi and Lazarus FPC
these only serve as tactics. Actually, we should be solutions allow developers to go farther, wider and
searching for something else. A strategy is needed. deeper than ever before. More than most any other
Something that defines the situation and the language.
problem so that we may understand what "can" be
done and what "needs" to be done first. The Pascal is best? There is no such thing as best.
solution tactics, the technology, will follow later. What is best depends on how something fits an
application. Yet, Pascal is making it progressively
So what might be the answer? less warranted to use something as messy and slow
In part, Pascal might take part in the 3D printing as java, something as cumbersome and tedious as
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
merit that is also worthy of conversation and lack of accessibility nor applicability. It is, typically,
collaboration. Something that uses technology to a lack of awareness. Pascal is powerful, but people
allow accessibility. And, most importantly, build the need to know about it to make it truly powerful.
social bonding between people.
Democratize Development,
How to emphasize this? Elaborate? 3D printing, for Centralize Awareness
instance, is not new. It has been around for some “Change” has become more of a constant than an
time. A long time, in fact. Yet, the success of one exception these days. And, that brings along entry
thing is typically the result of the success of points, opportunities. Opportunities for Pascal to
another thing. For 3D printing it was the PC. enter into and create its own party.
26 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Perception versus proof:To 3d Print or Not to print 3D Printing
The 3D printing scene is not immune to change a More Tangible Idea
either. It is not something static.
New developments are arising all the time as these What does 3D printing have to do with
technologies diversify and their applications widen, Pascal? Well...not much really. That is
deepen and demand grows. unless Pascal somehow starts finding
Other platforms are also on the rise such as mobile its way into developing solutions here.
which may allow new solutions for 3D printing. 3D printing is an exciting and
emerging manufacturing technology.
Mobile would seem to be an attractive an obvious
One that Pascal could take part in.
choice. Jump on the bandwagon.
A great stepping stone for Pascal and 3D printing.
In terms of software development,
In many ways that can be argued to be true. Pascal provides all the solution.
Yet, this is a very frontal approach. But how that might eventually happen
Pascal may make headlines here, even build some will depend on you. With that in mind,
awareness. But there is a lot more involved in the incentive here is to present a very
capturing and sustaining a conversation that builds brief but in-depth and critical
communities. overview about 3D printing.
Something that sheds light on how it
There are platforms other than mobile that are
essentially works, what it can do as
finding a lot of market acceleration.
They are moving fast. More importantly,
well as what it can't do (yet).
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.

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 Misprint?
the way to the senior. Why write another article about 3D printing?
Also, these devices are found progressively more in The internet already offers an abundance of
the education system. This inherently instills information on the subject. Mountains, in fact.
democratized development innovation. Everyone has been talking and writing about it.
These devices permit, for many, the first encounter And, companies like MakerBot, Ultimaker and
with what goes on behind the screen and with several others are building a vast knowledge base
programming. Shouldn't Pascal be one of those first in the peer production realm. All True. But there
encounters? Bottom-Up. They form the root, may be more to be said...and said again.
rather than just the branches and the leaves.
These days we can print out 3D objects in plastic,
Most importantly, that which offers solution here metal, ceramics and even organic material. We can
can impact, build and sustain awareness in the print in just about any shape we can imagine.
most profound and fundamental of ways.
Pascal offers the means, the solution. All this suggests that we will soon be able to print
It's up to you to exercise it and build awareness. out trains, planes, automobiles and even complex
body parts. We also hear claims like; “We will print
Get a Return on Your Investment. 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 Claims overshoot reality, jump out of the box,
real from what is rumor or just plain wrong? and things can become confusing. That's fine,
its part of the process of innovation to imagine that
At this point 3D printing offers the ability to print what we don't have or can't be done, yet.
out the "shape" of an object, in a certain material
and within certain accuracy. For many types of While 3D printing technologies are rapidly
products that may be enough to get the job done. advancing and diversifying in applications, at this
Yet, for most, there is typically a lot more involved point, we are pretty good at printing out only the
in the manufacturing of functional products. 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 What this example illustrates is that while the 3D
here is the main stream "awareness" that this printed shape of an object may suffice for some
brings about. And, that by itself is a revolution. applications there are many more applications
How we convert an idea into tangible and functional where it won't. This does not mean that 3D printing
form. 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 The working materials for the FDM process are
actually very simple. Traditional manufacturing widely available, inexpensive and the resulting
techniques rely on removing material until the prints can be durable with little to no post-
desired shape and accuracy of some material has processing. The concept and workings of the FDM
been attained. You know, like a chisel to sculpture process also require only a simple, relatively low
wood or a lathe to cut cylindrical shapes. cost design setup to get started. Together these
3D printing or additive manufacturing takes the factors imply accessibility. Accessibility for all.
opposite approach. Instead of removing material it
The process involves the deposition of molten
adds material. It builds it up until the desired object
shape and accuracy has been attained. Bottom-Up plastic on a flat bed using an extruder.
instead of Top-Down. 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
evident that the smaller the bricks, or building Surprisingly, not much focus is given to this
blocks, the more accurate the resulting object technique despite its merit. Laminated Object
shape will be. Its that simple. Manufacturing involves the use of sheet material
such as paper, plastic, metal foils or fabrics.
But what makes additive manufacturing so By using a sheet material the layer thickness is
important? There are several reasons. Probably the normalized, defined and is thereby easy to control.
most significant is that additive manufacturing Also the process allows selective coloring when,
techniques are much less constrained in what they for instance, paper is used. The printed products
can make in terms of object shape. Virtually any 3D are very durable and resemble wood (in the case of
object shape can be produced within the working paper).
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
any object shape you can imagine in a single over the working area of the printer. A laser or
operating session. knife cuts the 2D layer profile as well as a grid of
cuts around the object. The grid is to allow the
Cut an apple in two. The cut surfaces are cross- release of the model after printing. A layer of
sections of the apple. Think of the apple as being adhesive is applied after a layer is cut and the
built up of many infinitely thin cross-sections or process is repeated with a new sheet layer above
layers. If we precisely stack up these layers we the previous.
again have the object shape of the apple. The same
approach applies to 3D printing. You are taking a The LOM apparatus can be relatively simple in
complex 3D shape and dividing it into simple layers construction and easy to scale in terms of design.
which are much less difficult to manufacture. The materials used are usually non-toxic, low cost
and easy to handle making the LOM process an
Creating layers involves slicing a 3D modeled or attractive choice for the prosumer and small
3D scan of an object into a multitude of thin business.
sections. Each layer is then printed out and stacked
on the previous layer until the full 3D object shape The only limitation is the cutting mechanism.
is printed. The thinner the layer the more smooth The laser used to cut the sheets at high speeds is
and accurate the object shape will be. relatively costly and can be dangerous. Using a
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
a cross-section layer can be of various format. abrasive than one may think. A knife, at the very
The format obviously depends on the printing least, would need to be made of some carbide,
technique. Formats such as sheets or beads layered widea or diamond for any long term use.
onto one and other, powders connected together
using adhesives or welding or even liquids (resins) SLS/SLM
which are photo-cured are employed. These techniques offer some truly spectacular 3D
print solutions. In particular, the production of
FDM metal products. There are several variations of this
The Fused Deposition Modeling technique (FDM) technique. What they have in common is the use of
was probably the most responsible in forming the powders as the base material format.
entry point for the 3D printing revolution/hype in
recent years. At least a good part of it. It's by no The focus here is Selective Laser Sintering/Melting.
means the best 3D printing technique. But it offers Powders are fused/melted together in a powder
accessibility to a very large audience. bed to create layers, typically using a high power
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, One of the reasons that so many suppliers now
an inkjet type dispenser dispenses an adhesive in a exist is due the high demand for these machines.
metal powder bed which selectively binds a metal True. But in part, the demand is the result of the
powder together in order to build layers. explosive awareness built by the democratized
This “green” product is then sintered in order for open development itself. The conversation just gets
the adhesive to burn out and allow the metal bigger, creating its own demand.
powder to fuse into a solid. A marketing feedback system
like the ringing of a microphone
It should be clear that powder handling is a difficult that is held too close
and messy process. These technologies can be to a speaker box.
complex to manage and handle. Also, when working In addition, the democratized open development
with metal powders, elevated temperatures and has made the technology (software and hardware)
lasers there are many risks involved. so incredibly accessible to all.
You don't need real engineering skills to create a
SLA product and become a supplier. This is great for Joe
The historic Stereo Lithography process, patented in DIY (Do It Yourself) but also leaves a whole lot to
the late 80s, formed the foundation of 3D printing be desired for most others.
as we know it today. The technique has gone Turn-key? Not really. In many cases, you get what
through a long process of development and it has you pay for. And, while the obsession with
some serious merit to offer. “low-cost” can bring forward the greatest
innovation it can also deprive the development
But, traditionally, the technique also had some process from making good design decisions.
serious limitations as a desktop or home use All in all, becoming a supplier is as easy as creating
device. Apart from the printing device itself, your own linux distro. You don't really need Linux
probably the most apparent is the fact that it relies skills for that.
on photo-cure resins to build 3D models.
If you intend to get a 3D printer, get the facts
The process is sound, it works. In fact, it works first. Find out who is behind it all. Cost is
very well. But the resins involved are typically toxic, important but not absolute. 3D printing is not
messy and costly with limited shelve life. All in all, just software development, it's electronics
too cumbersome, complex and costly for many let and mechanical engineering combined.
alone the average consumer. Debugging is a whole new ball-game here.

But recent changes in design approaches and, in While it may seem otherwise, most 3D printing
particular, the resins involved are making a technologies are not available to the average
difference. The development and diversity of photo consumer due to cost, complexity and safety
cure resins and suppliers are creating a better fit for issues.
this process on our desktop. This does not mean that they won't one day take
part as a household appliance and be as easy to
The process relies on a photo cure resin which is use as a glorified coffee maker.
selectively and acutely cured to form layers. The We are just not there yet. 3D printing is still in its
resin in a SLA process is a liquid which typically infancy. With that in mind it should be apparent
cures under UV light. Lasers and other UV light that there is room for improvement.
sources may be used. After each layer is cured A lot of room. And, that means that there are
another layer is added in order to build the object. opportunities. Opportunities for Pascal to
provide solution. To appreciate this we need to
The SLA process appears to be the next candidate gain a better idea on the processes involved.
in the desktop 3D printing scene. Most systems rely
on the use of DLP projectors (Digital Light While 3D printing technologies may differ in their
Processing) to illuminate the resin. These are fast. operation and control the basic processing is more
Laser scanning systems however offer other or less the same. These include:
important benefits as well.
Pre-Process – Main Process – Post-Process
Pascal and 3D Printing The Pre-Process stage relates to processing the 3D
While new suppliers of desktop 3D printers for data and preparing it for the printer to be printed in
home use seem to appear every other week the the Main-Process.
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.
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
relates to things like cleaning, assembling or impossible task to perform, especially these days.
post-curing of the printed material after the In fact, even when it comes to 3D modeled data
Main-Process is completed. drawn in a graphics or CAD application, things can
take time to prepare and involve a lot of work.
In particular it is the Pre-Processing and Desktop 3D printing, certainly for home use, is still
Main-Processing stages were the magic equally an art as it is a technology.
happens and where Pascal can provide
solutions. The Main-Processing stage pertains to The block diagram provides a birds-eye view of the
the control of the machine, the printer. typical Pre-Process elements of 3D printing data
preparation. The 3D scan data process flow has also
If you are new to 3D printing then you may be been included to illustrate what's typically involved.
inclined to think about the “washer and dryer”
solution. The 3D scan and 3D print solution. This is Solid Model
a scenario where a 3D scanner is used to scan in The 3D data used for printing must be a solid
some usually trivial (typically broken) part and then model, a closed vessel. It must be leak-proof.
that part is reproduced through 3D printing. Think of this as modeling your house for
A perfectly reasonable idea. 3D printing.
And, this line of thought is correct. It is certainly You may have modeled the front side but what
where we are headed. about the back side and all in between. Modeling
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
approach is simply too impractical, too difficult. model in 3D space from all angles. Anything less
3D scanning can be (very) complex. than that is in the strictest sense not a 3D model,
In many ways it is still an art. it’s not solid. It is certainly possible to force close a
This is certainly true when high levels of accuracy model, assuming that these closed sides are not of
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.

PRE PROCESS MAIN PROCESS


3D DATA 3D PRINTER

MERGE

ADD
3D SCAN SOLID MODEL SIZE, SLICE
DECIMATE INTEGRITY ORIENTATE SUPPORT
DATA MODEL CHECK MATERIAL LAYERS

SMOOTH G-CODE

3D
EDIT
PRINTER

3D
GRAPHICS

CAD

POST PROCESS

Nr 5 / 2013 BLAISE PASCAL MAGAZINE 31


There are many different 3D file data formats. The Final Layer
The standard for 3D printing is the STL file 3D printing has been around for some time.
format. Binary versions are popular but text (ascii) Still, things are actually just getting started.
is also available. The reasoning for ascii type The concept is sound and proven.
format was that this allowed 3D print operators to And, as the awareness grows so also will these
manually examine the data if needed. technologies diversify. Opportunities will always
The STL file format is popular but it is far from be around the corner. Its important to find entry
efficient. To maintain some form of readability the points. Things that you can claim and offer
file construct includes an incredible amount of solution in.
redundant information.
In particular for 3D scan data this means that More importantly, more of us are now more aware
incredibly large file sizes are not uncommon. of what's involved in 3D printing. What it can do,
Fortunately other file format constructs are on the what it can't do and how it should be done. Our
rise. collective view of 3D printing is becoming more
realistic and tangible as the hype debris settles
Object Orientation and Size and our experience grows.
The accuracy of 3D printers is typically not the
same in all 3 directions. The layer thickness (Z) At this point we print using bits and pieces to
may even be a constant (LOM, for instance) while create layers but a time will come when we print
the X axis and Y axis may allow for much higher in particles and molecules to create structures.
accuracy. The minimum wall thickness of your 3D Real or 3D printed? What's the difference?
model may also be limited.
All in all, the orientation in which you print your That will be a revolution like no other seen before.
model may be directly related to the desired quality
and even feasibility Dr. Richard Feymann
of your print. http://en.wikipedia.org/wiki/There%27s_Ple
nty_of_Room_at_the_Bottom
Support Material
Overhang. A 3D model may have a shape that Molecular Imprints
extends over its base foot print. http://www.molecularimprints.com/
Depending on the 3D print technology,
support material for the overhang may be required. ASML
http://www.asml.com/asml/show.do?ctx=427
The construct of this support material may be very
important.
In most cases it should not extend print time too Foresight Institute
http://www.foresight.org/
much. It should use minimal material (cost) while
allowing maximum effectiveness.
Dr. Eric Drexler
It should also allow easy and quick removal with http://en.wikipedia.org/wiki/K._Eric_Drexl
minimal surface damage to the 3D print. er

Slicing 3D Printing and Grey Goo


Creating layers means slicing up the 3D model into http://heywhatsthebigidea.net/3d-printing-
2D (2.5D) cross-sections. The minimal permitted and-grey-goo/
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.

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
Programming the Microsoft Kinect in Pascal By Michaël Van Canneyt
KINECT

Free Pascal ships a single unit libkinect10


starter expert Delphi, which combines all definitions of the Delphi units.
Lazarus
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
When loading the library, the filename is optional and
In February 2012 we started to do research on when none is specified, the default kinect10.dll is
the Kinect and its relatives. Now finally we have used.
the first results: The free Pascal version of the units uses a reference
The Kinect is a device created by Microsoft to counting mechanism, which means that
enable NUI (Natural User Interface) for X-Box UnloadNuiLibrary must be called as much as
and Windows. It tracks the movement of the LoadNuiLibrary was called.
human body, and provides a stereoscopic image The kinect library does not need to be initialized:
of whatever is located in front of the camera. The once it is loaded, it is ready for use.
C++ API for this device can be used in Pascal The library exposes a few global functions, and some
interfaces. One of these interfaces is INuiSensor,
Introduction representing the Kinect camera.
Some years ago, Microsoft introduced the Kinect for its It has the following signature:
game console XBox: a small camera-like device that sits on (for brevity, the arguments of the methods have been omitted)
top of the TV and registers players and their movements. INuiSensor = interface(IUnknown)
The Kinect does for complete human bodies what the ['{1f5e088c-a8c7-41d3-9957-209677a13e85}']
Leap Motion (introduced in an earlier article - issue Nr. 30 , Function NuiInitialize(dwFlags : DWORD) :
HRESULT;
page 47 ) does for hands: it can track the position and Procedure NuiShutdown;
movement of the human body (called skeleton tracking), Function NuiSetFrameEndEvent() : HRESULT;
and provides a stereoscopic image (depth map) of whatever Function NuiImageStreamOpen() : HRESULT;
is located in front of the camera. Function NuiImageStreamSetImageFrameFlags() :
HRESULT;
In addition, the Kinect can also be used as a microphone (it
Function NuiImageStreamGetImageFrameFlags() :
accepts voice commands) and simply as a webcam. HRESULT;
A picture of the device can be seen in figure 1 at the top of Function NuiImageStreamGetNextFrame() : HRESULT;
this page. Function NuiImageStreamReleaseFrame() : HRESULT;
Function NuiImageGetColorPixelCoordinates\
FromDepthPixel() : HRESULT;
Since end 2011, the Kinect SDK is also available for Function NuiImageGetColorPixelCoordinates\
Windows PCs, and a C# (or .NET) and C++ API is FromDepthPixelAtResolution() : HRESULT;
available. Function NuiImageGetColorPixelCoordinate\
The C# interface is more elaborate than the C++ interface, FrameFromDepthPixelFrameAtResolution() :
HRESULT;
but the C++ interface is usable in all programming Function NuiCameraElevationSetAngle() : HRESULT;
languages, including, as it turns out, Object Pascal. Function NuiCameraElevationGetAngle() : HRESULT;
The SDK can be downloaded for free from the MSDN Function NuiSkeletonTrackingEnable() : HRESULT;
developer website, and version 1.7 was used for this article Function NuiSkeletonTrackingDisable : HRESULT;
Function NuiSkeletonSetTrackedSkeletons() :
(a new version is scheduled for Quarter 1 2014).
HRESULT;
It contains some libraries (both for .NET and native Function NuiSkeletonGetNextFrame() : HRESULT;
development), which need to be distributed with an Function NuiTransformSmooth() : HRESULT;
application that wants to connect to the Kinect. Function NuiInstanceIndex : integer;
In particular, the kinect10.dll must be distributed. Function NuiDeviceConnectionId : PWideString;
Function NuiUniqueId : PWideString;
The Microsoft Kinect C++ SDK provides roughly the Function NuiAudioArrayId : PWideString;
same functionality as the open-source OpenNI and Function NuiStatus : HRESULT;
OpenCV libraries, but has a much more simplified API Function NuiInitializationFlags : DWORD;
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 4 33
Programming the Microsoft Kinect in Pascal (Continuation 1)
Detecting a kinect device That means that the calling application needs to set up
Once the kinect library is loaded, an interface to a kinect several event handles, one for each kind of data stream it
device must be retrieved. This can be done using 2 global wishes to receive. These event handles must then be passed
functions: on to the data stream initialization functions.
For the demo application, 2 streams will be examined:
Function NuiGetSensorCount(out Count : integer): the depth map and the skeleton tracking data.
HRESULT; Both streams are provided through memory blocks that
Function NuiCreateSensorByIndex(Index : integer; must be requested through some methods of the
out ppNuiSensor : INuiSensor): HRESULT; INuiSensor interface.
The skeleton tracking stream is initialized (or stopped)
The NuiGetSensorCount function returns the number through the following functions:
of connected Kinect devices.
The NuiCreateSensorByIndex function then creates a Function
NuiSkeletonTrackingEnable(hNextFrameEvent:
INuiSensor interface for the Index-th device. THandle;
Both functions return a HRESULT value: that means dwFlags : DWORD ) : HRESULT;
that the result of the function can be checked using the Function NuiSkeletonTrackingDisable : HRESULT;
windows’ unit Failed function. The usable function result
is always returned in out parameters. The first parameter to NuiSkeletonTrackingEnable is
Once a device has been detected, and an interface to the handle used to report the presence of a new skeleton
the device was returned, the device must be initialized. frame. The second parameter determines how the tracking
When the device is no longer needed, the device can be data is calculated and returned:
shut down.
NUI_SKELETON_TRACKING_FLAG_SUPPRESS_NO_FRAME_DATA
These operations can be performed using the following
methods of the INuiSensor interface: When set, the NuiSkeletonGetNextFrame method will
Function NuiInitialize(dwFlags: DWORD): HRESULT; not return a E_NUI_FRAME_NO_DATA error when no data
Procedure NuiShutdown; is present, instead the call will block until data is present
or the timeout is reached.
When initializing the device, the device needs to be told
NUI_SKELETON_TRACKING_FLAG_TITLE_SETS_TRACKED
what kind of processing it should do: _SKELETONS
• calculate depth image,
• track player skeletons. When set, the detected players are not really tracked.
Since each step in processing takes CPU time, The NuiSkeletonSetTrackedSkeletons must be used
it is important not to request processing that will not be to select the players that should be fully tracked.
used anyway. This is specified in the dwFlags option to
NUI_SKELETON_TRACKING_FLAG_ENABLE_SEATED_SUPPORT
the NuiInitialize function. The flags are an OR-ed
combination of the following constants: enables seated skeleton tracking.

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


Request audio data. are not tracked, resulting in less calculations (the default is to
NUI_INITIALIZE_FLAG_USES_COLOR track the whole body, 21 joints).
Request color data.
NUI_INITIALIZE_FLAG_USES_DEPTH When tracking a person seated in front of a computer,
Request depth data. this option can be used to reduce calculation time.
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX
The depth image stream, as well as other image streams,
Request depth data with a player index.
NUI_INITIALIZE_FLAG_USES_SKELETON are initialized through the following functions:
Request skeleton tracking Function NuiImageStreamOpen(
eImageType : NUI_IMAGE_TYPE;
eResolution : NUI_IMAGE_RESOLUTION;
For the purpose of this article, only the last 2 will be used. dwImageFrameFlags : DWORD;
The difference between the dwFrameLimit : DWORD;
hNextFrameEvent : THandle;
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX out phStreamHandle : THandle) : HRESULT;
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
(meaning that at most 7 players can be used) The NuiImageStreamOpen function opens an image
stream. Which images the stream returns is specified
Reading data from the device through the eImageType parameter, which can have one
The kinect API provides several data streams: of the following values:
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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Programming the Microsoft Kinect in Pascal (Continuation 2)
NUI_IMAGE_TYPE_COLOR a color image. The second parameter is a pointer to a
NUI_IMAGE_TYPE_COLOR_INFRARED an infrared image NUI_SKELETON_FRAME structure. On return, it points to a
NUI_IMAGE_TYPE_COLOR_RAW_BAYER record that describes the tracked skeletons.
a Raw Bayer color image (RGB) It is described as follows
NUI_IMAGE_TYPE_COLOR_RAW_YUV
a YUV color image; no conversion to RGB32. NUI_SKELETON_FRAME = record
NUI_IMAGE_TYPE_COLOR_YUV liTimeStamp: int64;
a YUV color image, converted to RGB32 dwFrameNumber,
NUI_IMAGE_TYPE_DEPTH a depth image. dwFlags: DWORD;
NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX vFloorClipPlane,
a depth image with player index encoded in the map. vNormalToGravity: Vector4;
SkeletonData : array[0..5] of NUI_SKELETON_DATA;
Various streams can be opened to capture data from the end;
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.
NUI_SKELETON_DATA = record
The dwImageFrameFlags parameter can be used to
eTrackingState: NUI_SKELETON_TRACKING_STATE;
specify some flags when capturing images, it accepts the
dwTrackingID,
same values as used in the dwEnrollmentIndex,
NuiImageStreamSetImageFrameFlags function. dwUserIndex: DWORD;
The hNextFrameEvent parameter is the handle of the Position: Vector4;
event that must be triggered when a new frame is ready.
SkeletonPositions: array[0..19] of Vector4;
Finally, the phStreamHandle is the handle of the image eSkeletonPositionTrackingState: array[0..19] of
stream that must be used in the NUI_SKELETON_POSITION_TRACKING_STATE;
NuiImageStreamGetNextFrame calls to read the image. dwQualityFlags: DWORD;
The NuiImageStreamSetImageFrameFlags method can be end;
used to modify the flags passed in the
dwImageFrameFlags parameter to The eTrackingState field describes whether the record
NuiImageStreamOpen: actually describes a skeleton.

NUI_IMAGE_STREAM_FLAG_DISTINCT_OVERFLOW It can have one of the following values:


_DEPTH_VALUES is undocumented. NUI_SKELETON_NOT_TRACKED
NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE Enable The record does not describe a tracked skeleton.
near mode. (enable depth detection close to the camera) NUI_SKELETON_POSITION_ONLY
NUI_IMAGE_STREAM_FLAG_SUPPRESS_NO_FRAME_DATA The record describes a skeleton whose position is tracked.
NUI_SKELETON_TRACKED
When set, the NuiImageStreamGetNextFrame method record describes a fully tracked skeleton.
will not return an E_NUI_FRAME_NO_DATA error when no
data is present, instead the call will block until data is To determine the skeletons, the eTrackingState field of
present or the timeout is reached. each record in the SkeletonData array of
NUI_IMAGE_STREAM_FLAG_TOO_FAR_IS_NONZERO NUI_SKELETON_FRAME must be checked. If it contains
is undocumented. NUI_SKELETON_TRACKED , it is a usable record.
For each skeleton, 20 joints are tracked.
After the image stream and skeleton stream have been set These joints are described in the SkeletonPositions
up, frames can be read by watching the event handles. and eSkeletonPositionTrackingState arrays.
In the example program later on, this will be done in a For each of the 20 joints, a constant is defined, for example:
separate thread.
NUI_SKELETON_POSITION_HEAD,
Interpreting skeleton frame data NUI_SKELETON_POSITION_HAND_LEFT,
When a skeleton frame is ready, it can be fetched with the NUI_SKELETON_POSITION_HAND_RIGHT.
following method of INuiSensor:
Function NuiSkeletonGetNextFrame( Each of these constants is an index in the
dwMillisecondsToWait : DWORD; SkeletonPositions and
pSkeletonFrame : PNUI_SKELETON_FRAME) : HRESULT; eSkeletonPositionTrackingState arrays.
The eSkeletonPositionTrackingState array
The first parameter is a timeout: determines which of the SkeletonPositions
if no frame is ready within the specified time, the call
elements contains a valid position vector.
returns with an error condition. When an event handle is
An element in the array can have one of the following
used to signal the completion of a frame, then the call
values:
should return at once.
Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS 4 35
Programming the Microsoft Kinect in Pascal (Continuation 3)
NUI_SKELETON_POSITION_NOT_TRACKED For the depth image, the data comes in the form of an array
The array element does not contain valid data. of word-sized values. The byte size of the array is reported
NUI_SKELETON_POSITION_INFERRED using BufferLen, the length of a single scan line can be
The position is calculated from other data. retrieved with the Pitch method. The actual array can be
NUI_SKELETON_POSITION_TRACKED
The position is tracked. retrieved with LockRect.
Since the array is managed by the kinect driver, it is locked
The last 2 values mean that the element with the same when it is retrieved. It must be unlocked using the
array index in the SkeletonPositions array, contains UnlockRect call when it is no longer needed.
a valid joint position. The skeleton tracking mechanism The data array is described by the following record
may result in ’jittery’ data. The results are vectors, and the NUI_LOCKED_RECT = record
positions will appear to have some random Brownian-like Pitch : integer;
motion. The INuiSensor interface offers the size : integer;
NuiTransformSmooth function to deal with this: pBits : pointer;
end;
Function NuiTransformSmooth( Where Pitch and Size correspond to the BufferLen and
pSkeletonFrame : PNUI_SKELETON_FRAME; Pitch methods of the INuiFrameTexture interface.
const pSmoothingParams : The pBits pointer points to the actual array.
PNUI_TRANSFORM_SMOOTH_PARAMETERS) : HRESULT;
Each element in the array is aWord value between
This function will attempt to reduce the randomness by NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE and
applying a transformation on the received coordinates. NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE
The transformation is controlled by the following if near mode is enabled.
NUI_TRANSFORM_SMOOTH_PARAMETERS record: In normal mode, the minimum and maximum values are
NUI_IMAGE_DEPTH_MINIMUM and
NUI_TRANSFORM_SMOOTH_PARAMETERS = record
NUI_IMAGE_DEPTH_MAXIMUM.
fSmoothing,
fCorrection,
When NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX
fPrediction, was used when creating the stream, the depth image’s
fJitterRadius, word values are shifted, and the last 3 bits are used to
fMaxDeviationRadius : single; encode a player index. (3 is the value of
end; NUI_IMAGE_PLAYER_INDEX_SHIFT).

The exact meaning of these parameters can be found in the If the last 3 bits are nonzero, then the pixel is considered
NUI API documentation on MSDN. part of a player’s body. The player index can be used for
example to color the corresponding pixels in a player-
Interpreting depth image data specific color.
If a depth image is requested, the INuiSensor’s method
NuiImageStreamGetNextFrame can be used to retrieve Putting everything together
the actual depth image. It is declared as follows: After the long description of the Kinect (Natural User
Function NuiImageStreamGetNextFrame(hStream:THandle; Interface) API, a small demonstration application will clarify
dwMillisecondsToWait : DWORD; things a bit.
pImageFrame : PNUI_IMAGE_FRAME) : HRESULT;
The hStream handle is an image stream handle created using the The sample application:
NuiImageStreamOpen function. Similar to the • Connects to the first found Kinect sensor.
NuiSkeletonGetNextFrame function, the • Requests and displays skeleton frames and a depth
dwMillisecondsToWait specifies a timeout, in case the image image stream.
is not yet ready. • Uses events to get a notification when the next frames
On return, the location pointed to by pImageFrame will be filled
are ready.
with a NUI_IMAGE_FRAME record:
• Displays the depth image with a specific color for all
NUI_IMAGE_FRAME = record players, and superimposes on that, for the first
liTimeStamp : int64; detected player, shapes representing the hands and head.
dwFrameNumber: DWORD; • Allows to set/get the camera elevation angle.
eImageType : NUI_IMAGE_TYPE;
eResolution : NUI_IMAGE_RESOLUTION;
pFrameTexture: INuiFrameTexture; The program is written in Lazarus, but it should work
dwFrameFlags : DWORD; equally well in Delphi. It is a simple form, with 2 panels,
ViewArea : NUI_IMAGE_VIEW_AREA; some controls, and 3 shapes on it. The OnCreate event
end; 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;
Function BufferLen: integer; FEDepth:=INVALID_HANDLE_VALUE;
Function Pitch: integer; FSDepth:=INVALID_HANDLE_VALUE;
Function LockRect(Level: UINT; LoadNuiLibrary;
pLockedRect: PNUI_LOCKED_RECT; TBAngle.Min:=NUI_CAMERA_ELEVATION_MINIMUM;
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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Programming the Microsoft Kinect in Pascal (Continuation 4)
The variables FESkeleton and FEDepth are the events NuiImageResolutionToSize(ImageResolution, w, h);
used to receive notifications when the skeleton and depth ClientWidth := w;
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 camera’s elevation angle is determined by FBDepth.Height:= h;
the NUI_CAMERA_ELEVATION_MINIMUM and Result:=true;
NUI_CAMERA_ELEVATION_MAXIMUM constants if Not Failed
(-27 and 27, respectively), these values are used to initialize a (FKinect.NuiCameraElevationGetAngle(@A))
then TBAngle.Position:= A;
track bar control which can be used to set the angle of the end;
Kinect’s 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.
kinect’s 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 :
Boolean = False): boolean; the EventDispatcherThread unit) which simply loops
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; This is one way of handling the events, another way would
if Failed(NuiGetSensorCount(C)) then exit; be to use the OnIdle event of the application, or a timer, to
I:=0;
While (FKinect=Nil) and (i<C) do check for new events. Instead of sending a message,
begin it is also possible to use the Synchronize or Queue methods
if Not Failed(NuiCreateSensorByIndex(i,NS))
then to let the main thread respond to the arrival of new data.
if (NS.NuiStatus=S_OK) then FKinect:= NS; The thread’s Execute method looks very simple:
Inc(I); procedure TEventDispatcherThread.Execute;
end; begin
if not Assigned(FKinect) then exit; if (FHWnd=INVALID_HANDLE_VALUE) or
if Failed(FKinect.NuiInitialize(NUIOptions)) (FESkeleton=INVALID_HANDLE_VALUE)
then then exit;
begin
FKinect:=Nil; While not terminated do begin
Exit; if (WaitForSingleObject(FESkeleton,50)=
end; WAIT_OBJECT_0)
then
This code is pretty straightforward, it requests the number begin
of kinect devices, and connects to the first available one. SendMessage(FHWnd,WM_USER,MsgSkeleton,0);
If none was detected, it exits. The first available sensor is ResetEvent(FESkeleton);
then initialized, the NUIOptions constant is defined as: end;
if (WaitForSingleObject(FEDepth,50)=
Const
WAIT_OBJECT_0)
NUIOptions = then
NUI_INITIALIZE_FLAG_USES_SKELETON or begin
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX; SendMessage(FHWnd,WM_USER,MsgDepth,0);
After the kinect was initialized, an event handle is created, ResetEvent(FEDepth);
and used to enable skeleton tracking: end;
end;
FESkeleton:= CreateEvent(nil,True,False,nil); end;
FKinect.NuiSkeletonTrackingEnable( If an event is received on either of the 2 handles,
FESkeleton,SkeletonOptions); a WM_USER message is sent to the main form, which will
then take appropriate action. The message parameters are
SkeletonOptions is a constant requesting seated support defined as constants. Note that the event is reset after the
and near range. The next thing to do is request a depth message is sent.
image, again using an event handle to get notifications: Reacting on the messages is done by implementing a
message handler method in the form for the WM_USER
FEDepth:= CreateEvent(nil,true,false,nil);
if Failed(FKinect.NuiImageStreamOpen( message:
ImageOptions, ImageResolution,0,2, procedure TMainForm.eventDispatcher(var msg:
FEDepth, FSDepth)) then TMessage);
Exit; begin
if EnableNear then if (msg.WParam=msgSkeleton)
if then OnNewSkeletonFrame
Failed(FKinect.NuiImageStreamSetImageFrameFlags else if (msg.WParam=msgDepth)
(FSDepth,ImageStreamOptions)) Exit; then OnNewDepthFrame;
DoTick(msg.WParam=msgDepth);
If all went well, a thread can be set up to check for events end;
on the FESkeleton and FEDEpth The message handler method simply examines the message
FTEvents:= parameter and calls the appropriate method to deal with
TEventDispatcherThread.CreateDispatcher(Handle, the message. After that it executes a tick, which collects and
FESkeleton, displays some statistics.
FEDEpth); The actual work is done in OnNewSkeletonFrame and
Lastly, the size of the depth image is used to create a OnNewDepthFrame. The first one is responsible for
bitmap (which will be used to draw the depth image) and set drawing the head and hand joints in the skeleton frame.
the width and height of the form: FEDEpth. It examines the received data and positions 3 shapes on the
form:
Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS 4 37
Programming the Microsoft Kinect in Pascal (Continuation 5)
procedure TMainForm.OnNewSkeletonFrame; procedure TMainForm.OnNewDepthFrame;
var i : integer; fr : NUI_SKELETON_FRAME; Const
PSD : PNUI_SKELETON_DATA; DS = NUI_IMAGE_PLAYER_INDEX_SHIFT;
tsp : NUI_TRANSFORM_SMOOTH_PARAMETERS; MINS = NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE shr DS;
begin MAXS = NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE shr DS;
FillChar(fr,sizeof(NUI_SKELETON_FRAME),0); var
if Failed( IFDepth : NUI_IMAGE_FRAME;
FKinect.NuiSkeletonGetNextFrame(0,@fr)) FT : INuiFrameTexture; lck : NUI_LOCKED_RECT;
then Exit; depth : pword; CD : Word; p : byte;
PSD:= Nil; x, y : integer; w, h, GS : cardinal;
I := 0; C : TCanvas;
begin
While (PSD=Nil)and (i<NUI_SKELETON_COUNT) do if (FSDepth=INVALID_HANDLE_VALUE) then Exit;
begin if Failed(
if (fr.SkeletonData[i].eTrackingState = FKinect.NuiImageStreamGetNextFrame(
NUI_SKELETON_TRACKED) then FSDepth,0,@IFDepth)) then Exit;
PSD:= @fr.SkeletonData[i]; NuiImageResolutionToSize(
Inc(I); IFDepth.eResolution, w, h);
end;
if Not Assigned(PSD) then Exit; The above code retrieves the image from the kinect and calculates
This code fetches the next NUI_SKELETON_FRAME a width and heigh with it. The next step is to retrieve the image
structure from the kinect, and initializes data from the INuiFrameTexture interface:
a pointer to the first skeleton (PSD). The following step is
’smoothing out’ the received try
coordinates: FT:=IFDepth.pFrameTexture;
if not assigned(FT) then Exit;
With tsp do begin if Failed(FT.LockRect(0,@lck,nil,0))
fCorrection := 0.3; then Exit;
fJitterRadius:= 1.0; try
fMaxDeviationRadius:= 0.5; if lck.Pitch<>(2*w) then Exit;
fPrediction := 0.4; depth:=lck.pBits;
fSmoothing := 0.7; The following steps transfer are a loop over the depth
end; data, transferring it as a grayscale to the bitmap.
if Failed(FKinect.NuiTransformSmooth(@fr,@tsp))
The depths that have a player index in them are
then Exit;
transferred to the bitmap using the player’s 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^;
NuiTransformSkeletonToDepthImage function: P:=(CD and NUI_IMAGE_PLAYER_INDEX_MASK);
if (P<>0) then
Procedure NuiTransformSkeletonToDepthImage(
C.Pixels[X,Y]:=FPlayerColors[p]
vPoint : TVector4;
out fDepthX : single; else if (CD>=NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE)
out fDepthY : single; and
eResolution : NUI_IMAGE_RESOLUTION); (CD<=NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE) then
begin
GS:=Round(((CD shr ds) - MINS) / MAXS * 255);
It receives a position vector, and a resolution. It returns an X,Y GS:=GS and $FF;
coordinate which is a coordinate on a depth bitmap GS:=GS or (GS shl 8) or (GS shl 16);
corresponding to the given resulution. C.Pixels[X,Y]:=GS;
All this is used in the ShowJoint function: end
else
Procedure TMainForm.ShowJoint(S : TShape; PSD : C.Pixels[X,Y]:=clBLack;
PNUI_SKELETON_DATA; HI : Integer); Inc(depth);
Var end;
x, y : single; C.Unlock;
begin
S.Visible:= Lastly, the retrieved depth image data is released,
PSD^.eSkeletonPositionTrackingState[HI]= and the bitmap is drawn on the panel:
NUI_SKELETON_POSITION_TRACKED;
if S.Visible then finally
begin FT.UnlockRect(0);
NuiTransformSkeletonToDepthImage( end;
PSD^.SkeletonPositions[HI],x,y, finally FKinect.NuiImageStreamReleaseFrame(
NUI_IMAGE_RESOLUTION_640x480); SDepth,@IFDepth);
S.Left:=Round(x)-(S.Width div 2); end;
S.Top:=Round(y)-(S.Height div 2); PImage.Canvas.Draw(0,0,FBDepth);
end; For X:=0 to PImage.ControlCount-1 do
end; if PImage.Controls[x] is TShape then
Finally, the depth image must be rendered: for this, the depth PImage.Controls[x].Repaint;
image needs to be interpreted and transferred to a bitmap, and end;
then the bitmap is drawn on a panel:

38 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Programming the Microsoft Kinect in Pascal (Continuation 6)
Finally, to make sure the shapes representing the head and Note that the trackbar position is reversed; it is positioned
hands are properly shown, they are repainted. vertically, with the minimum value (-27) at the top, and the
The form contains some logic to display a tick and average maximum value (27) at the bottom of the trackbar.
frames per second count, and to set the colors of the Everything put together, the running program results in a
players. This logic is not relevant to the operation of the figure like figure below.
kinect.
Conclusion
However, the routine to set the camera’s elevation angle
The kinect is a device which is one way of
needs some explanation: Setting the elevation angle of the
implementing a Natural User Interface: use the
camera takes some time: it is a mechanical operation,
human body to control the computer. While it is
involving a small motor inside the kinect. Setting a new
position while the previous position was not yet originally aimed at gaming, there may be
specialized uses for this device outside the
established, will result in an error. It is therefor important
not to send commands too often or too much. The gaming industry. For a more fine-grained control
following code attempts to do that: of the computer, the resolution of the Kinect’s
procedure TMainForm.TBAngleChange(Sender: TObject); skeleton detection is not fine enough:
Var A : Longint; it cannot detect individual fingers of the hand.
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))
then begin programmers, and there are certainly Object
if (A<>-TBAngle.Position) then Pascal game programmers that will consider the
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 4 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ž Gabrijelčič
starter expert SMART

Smart Mobile Studio 2.0 is available as a public beta at


A year and a half after the original release of
smartmobilestudio.com.
Smart Mobile Studio 1.0 I'm proud to present the
Pascal for Everything
first major upgrade – Smart Mobile Studio 2.0. A
I like to describe Smart Mobile Studio 2 as a development
lot of things have happened since the original
environment that allows you to run Pascal “everywhere”,
release. The team is now larger, counting five
developers. Lennart, the original father of the meaning that you can run Pascal programs on phones
“Pascal for the Internet” concept, has accepted (either from the browser, from the home screen or from
new challenges, and I have stepped in as a PhoneGap/Cordova-packaged applications), on desktops (in a
programming manager for the Smart team. This browser), on servers (with the help of Node.js) and even on
article is therefore not an objective outsider view, microprocessors (with the Espruino initiative).
but a bunch of notes written by a proud Smart Mobile Studio 2 (SmartMS for short) supports six
programmer 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 bitmap-
based 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
The middle ground holds the Professional edition.
It contains everything from the Basic edition plus the visual
Node.js project creates a server-side application designed project type and the visual designer.
to run as a part of the Node.js platform (nodejs.org). You The price is a bit higher, $149.
can run such applications on Windows, OS X and Linux
computers. The Visual components project creates a form- If you want to work with the databases we are also
and component-based application, just as you would do it offering the Enterprise edition which adds database
in Delphi or Lazarus. connectors to the Professional level. You can easily connect
A visual project can contain multiple forms and the user to RemObjects services or to DataSnap servers and
can navigate between them. Such projects can run in any SmartMS will create an appropriate Pascal connector for
modern browser. You can also use modal forms which do you automatically. (To use with the DataSnap server you also
not use full display (parts of old form are visible below).The have to have a Delphi/RAD Studio Enterprise installed on the
WebWorker project (thread) is a special light-weight project same computer.) This edition costs $399.
type which creates a web worker, a thread-like browser
entity that allows multithreaded computing in a browser.

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. There is also a command-line compiler, smsc, which is freely
You can, of course, use them without upgrading after the redistributable and which you can deploy on your servers or
year has passed. bundle with your solutions.
We are also offering an Educational license which is a As the main IDE, command-line compiler runs only
free license (functionally equivalent to Enterprise) for on Windows.
educational facilities (schools, universities, clubs …). When compiling inside the SmartMS IDE, you can use pre-
build and post-build scripts to execute external actions
(http://smartmobilestudio.com/2013/11/25/new-
New visual designer build-system/).
In the SmartMS version 1, visual designer was a bit rough For improved debugging experience, the IDE supports source
around the edges. It was functioning OK, but it was really map debugging with Chrome.
not working as smooth as users wanted. This too has That allows you to debug your programs in Chrome while
passed and with the version 2 users will be getting a new looking at the Pascal source, not at the JavaScript.
designer with better look and, besides all, better event (Sadly, this functionality is broken in the 2.0 beta.
support. Finally you'll be able to create events by double- We'll fix it for the next beta.)
clicking on a button or an event in the Object Inspector.
IDE improvements
You will also notice that some components are better Besides the new visual designer and package manager,
looking. The new designer allows us to display a better there's a bunch of smaller changes that make the IDE easier
representation of components. This functionality is not yet
for use. For example, you can Ctrl+Click on a symbol
finished and will be continually improved in 2.0 beta’s and
to jump on its definition. You can move lines around with
in later (2.1) releases. User-components can be organized
in packages and added via the new Packages menu. Alt+Shift+Up/Down keys.
Smart comes with an example package containing the The Project-related state (open tabs, current line in the editor,
TeeChart graphing component. bookmarks) is stored in a <projectname>.dsk file and is
restored when a project is opend.
Compiler changes We have changed the project file format.
Besides stability and code generation improvements, The new project format is incompatible with Smart 1 and uses
compiler now supports external variables and call a new extension .sproj.
variables. This simplifies interfacing with existing Forms are stored in xml-based .sfm files and can be edited
JavaScript libraries (such as used for Node.js integration). outside the SmartSM IDE. We have also changed the
HTML and CSS templates are now scriptable and run preferences file format which is now XML-based
through a DWScript preprocessor before they are used. (preferences.xml). The snippets system was also
CSS is compressed during the compilation so that redesigned, which now stores one snippet per folder and
minimum space is used. allows for a simple upgrade and backup.
We have added new ways to reference an external The Project management is now more flexible.
resource. In addition to the old command {$R You no longer have to choose the project path when creating a
'resource.js'} which copied the resource file into the new project. In fact, you can create and compile a project
deployment folder, the compiler now supports the without saving the source! (Resulting HTML/CSS/JS program is
{$R 'file:resource.js'} syntax. still save in a TEMP folder but is deleted when SmartMS exits.)
You can also specify an external file, which is not copied to Externally modified files are now displayed in a manner
the deployment folder, with a syntax
similar to the excellent DDevExtensions Delphi extension (big
{$R 'http://server/path/script.js'}.
thanks to Andreas Hausladen for showing us the way and allowing
Additionally, you can specify resource MIME type:
{$R 'text/javascript: us to steal his idea). You can compare changes with in-editor
http://server/path/script.js'}. 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 And for the end I'd like to show you how to write a Node.js
which does some run-time checking (as the Range checking application (the code also comes from the Mileage client-
compiler options was enabled). server demo).
If we, however, uncheck the Range checking and enable
Code packing and Code obfuscation options, we'll get procedure TServer.Run;
something much more interesting, a very compact begin
//load http module
JavaScript code.
var http := NodeJS.http.http;

,rl:function(S,E1,yco){var IB=0;S.ny=E1; //start http server


S.kG=yco;$ArraySetLenC(S.Vk,(S.kG+2), http.createServer(
function(){return[]});var $tR; procedure(request: JServerRequest; response:
for(IB=0,$tR=S.Vk.length;IB<$tR;IB++) JServerResponse)
{$ArraySetLen(S.Vk[IB],(S.ny+2),false)}} begin
if request.url.StartsWith('/Mileage/Read')
Next example (from the Mileage client-server demo) then
shows how to generate HTTP GET request and process the FetchData(response)
else
response. It also shows few features of the enhanced Pascal
if equest.url.StartsWith('/Mileage/Save')
language used in SmartMS – lambda expressions then StoreData(request, response)
(anonymous functions) and inline variable declaration. else response.end('MileageServer v0.1')
end)
procedure TMainForm.InitializeForm; .listen(80, '');
begin
var http := TW3HttpRequest.Create; console_.log([
http.OnDataReady := lambda (Sender) 'Server running at http://127.0.0.1:80/']);
for var line in end;
Sender.ResponseText.Split(#13) do begin
if line = '' then continue;
var data := line.Split(#9); Credits
var item := At the end I'd like to give the credit where it's due.
TListTemplate(lbData.Items[lbData.Add]); Smart Mobile Studio wouldn't be there without
item.Date := data[0]; Lennart Aasenden, the original author, Jørn Einar
item.Distance := data[1];
Angeltveit, our CEO, Eric Grange, author of the
item.Volume := data[2];
end; Pascal-to-JavaScript compiler, Christian Budde,
UpdateChart; who wrote lots of version 2 code including
end; complete v2 designer, and André Mussche, who
http.Get('http://localhost/Mileage/Read'); wrote RemObjects and DataSnap connectors.
end;

46 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.

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,
David seems to have a new job....
an annual program, so they can receive all of the
technologies when they are available.
Editor: Delphi and C++Builder developers have all the choices on
First to make it easy how to proceed and to stay up to date on the latest platform,
Could you explain the plans about updating buying etc of device and technology advances. Simply put, we are doing
Delphi and how you want to proceed in the near future? more for developers today than we have ever done in all the
years since Turbo Pascal version 1.0.
David I.
We are investing a lot of time and money to expand the When we released our most recent XE5 release, we were
reach of platforms we are supporting for Delphi and also testing iOS7 and next generation Android.
C++Builder. We released our iOS7 update in a very timely update soon
This is not just a Windows world anymore. after iOS7 appeared. We are committed to verifying that our
Over the past few years, mobile and device platforms have products work with the next generation Windows, OS X,
expanded the types of applications developers need to iOS and Android platforms.
build beyond just the desktop. Where we need to we will also update our products to take
At the same time the requirements for applications have advantage of new APIs, innovations and opportunities.
grown to include the Internet, web services, REST services Separate from platform support, our R&D teams are also
and cloud computing. innovating in IDEs, multi-device designers, tool chain
All of these new platforms and architectures have allowed enhancements, compiler technologies, components and in
us to help expand the reach of developers. the runtime libraries.
Along with these new innovations, we are delivering
enhancements to our products, IDEs, components, libraries All of this is being done to help our existing customers
and tools. 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
OS X, iOS and Android. Each of these platforms Delphi be 6 months from now on? If so there is the danger

FastReport
(and the devices that support them) continues to move ahead people will wait for quite some updates to engage again?
on their own pace of innovation and release schedules. We have our public roadmap that list some of the work we
This makes it harder to synchronize with all of them on a are doing with general time frames - for example
specific schedule. We must also work to support the C++Builder iOS and Android support this Winter.
has managed to create a Beta
release cycles of our R&D efforts and also the platforms ThereVersion for parts
are so many moving lazarus.
in the IDEs, compilers,
If you buy the full source codeFM,(for
themselves. RTL andDelphi) you
device/platform will
support thatbe able
it is not easy to
It was easier in the past, when we were a Windows predict when we will release updates, new versions and
to install the first even
only development product, to follow the cycles of
working
new products.
version.
Windows releases. We have allready tested it.
48 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
For example, we had originally planned to have Delphi for David I
iOS support in XE3, but we needed more time to complete I see no dangers anywhere. We are providing capabilities
the ARM compiler and mobile language enhancements, for all developers whether they are mature, youngsters or
so these were released when ready in April of this year. newbies. We are the only native code, optimizing compiler,
rapid application prototyping developer tools company on
Our first Android support was ready in the summer, the planet that offers one code base,
so it fit our "traditional" fall release. Some developers will multi-device targeting that is on all of the major platforms
remember the early years of Turbo Pascal and Borland Windows, OS X, iOS and Android.
Pascal where we released versions sometimes in
Springtime, sometimes when a new release of DOS or We give you a wide range of RTL functions and
Windows appeared, and even Turbo Pascal for the Mac components to improve your ability to build apps for the
that was released separately on its own schedule. platforms and at the same time we allow you to go right
down to the operating system and the hardware if you
Some of our customers have waited for releases that need to.
had capabilities they are interested in or need.
Other developers keep up with every release. We have been doing these things since the days of DOS
Some developers are still using very old Windows only (Turbo Vision) and Windows (Object Windows, VCL and now
versions of Delphi (3, 5, 6, 7, 2007, etc). FM). We know how to abstract the OS and hardware
If Microsoft dropped 32-bit from Windows, I would expect without keeping developers out of those spaces.
that most Delphi developers would move forward to use Everything, the APIs, devices, sensors, operating system
the 64-bit Delphi compiler. and other hardware features are available for your
programs.
We are very proud that our products are so good, that
they can be used for years and support multiple versions of We have millions of seasoned professionals and new
the platforms including Windows, OSX, iOS and Android generations of young developers using our products
versions. around the world..
Since we don't time out the releases that developers
purchase, it is their choice when to move forward. This year, South Africa has chosen Delphi as the
We continue to move forward and new and existing standard programming language and product for their
developers are coming with us. At the same time, we will High Schools computer classes. This means that a new
continue to help developers understand how to use the generation of South African students will learn object
current and latest capabilities for development. programming, component based development, event
programming and rapid application prototyping in high
Our DocWiki online documentation system and our school and will be well prepared for college and industry.
community/partner ecosystem still have all of the
information going back multiple versions, so developers Editor
can get the information they need to be successful Will you go there?
regardless of what version they are using.
David Intersimone :
We will keep innovating; continue to support the latest I have been to South Africa one time before and am
platforms and development technologies across all of the planning on going again sometime early next year.
platforms: Windows, OS X, iOS and Android. If someone I look forward to my return visit to South Africa.
is doing Windows only development on an older Windows
version, they can keep using the latest versions products. 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 4 49
A simple superscript text editor By David Dirkse
starter expert Delphi 7 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.
This article describes a text editor suitable for If no further action may take place, the procedure should
formulas with exponents. supply the code key := 0;
The formula is displayed in a paintbox named
formulabox. So, editing including cursor Neglecting this may result in unwanted changes of any
movement has to be programmed. active component on the form. VK stands for "virtual key"
The formula must be translated to "plain" text for .
further input to procedures for the painting of VK_LEFT is the code generated when pressing the left
graphics or calculations. arrow key. kbLeft, kbRight....are small procedures handling
Also a procedure takes care of the translation other codes. Showformula paints the formula in the
from plain text to superscript text. paintbox.

Figure 1: This is a picture of the project: 2.


procedure TForm1.FormKeyPress(Sender: TObject;
The top box is an editbox, holding plain text. var Key: Char);
The lower box is the paintbox with the superscript text. const fmask = ['a'..'z','A'..'Z','0'..'9','+',
'-','*','/','^','=',';',' ','.',',','(',')'];
Bitbuttons super and plain take care of the translation and
var mask : set of char;
display of texts. While typing text, the first concern is the begin
destination of the characters: edit- or paintbox. mask := fmask;
Therefore is
if plaintext then mask := mask + [#08];
//add backspace character
var plaintext: boolean; // true if plaintext editor if not (key in mask) then key := #0
else
Edit- and paintbox are components of Form1. if plaintext = false then
A form has a property keyPreview of type boolean. begin
addchar(key);
If true keyboard events and characters are , before being
key := #0;
delivered to the active component on the form, presented end;
in two procedures: end;
(after procedure to ...end the code is supplied by myself)
In this case key is the ascii code of the typed character.
1. If no further action may take place after this procedure then
procedure TForm1.FormKeyDown(Sender: TObject; make key := #0;
var Key: Word;
Shift: TShiftState);
begin
key is filtered to eliminate unused characters like #,$,%,&.
if plaintext = false then Problem is, that an editbox uses the backspace (#08) ascii
begin code for editing. In the case of plain text therefore, the
if (textlength = 0) then exit; backspace code has to be added to the filter.
clearcursor; procedure addchar() inserts the character in the
case key of superscript tekst.
VK_LEFT : kbLeft;
With plain text only the filter does it's work : the character
VK_RIGHT : kbRight;
is automatically send to the editbox which has it's own
VK_UP : kbUp;
VK_DOWN : kbDown; editing procedures.
VK_BACK : kbBack; Property activecontrol of a form indicates the active
VK_DELETE : kbDelete; control (the component that has focus). This control receives
VK_HOME : kbHome; the events from mouse and keyboard.
VK_END : kbEnd; form1.activecontrol := nil takes care that no
end;//case component is active. if assigned(activecontrol)
key := 0; shows if there is any control active.
showformula; In this little project, plaintext = true when activecontrol =
paintcursor; edit1 and plaintext = false when the forms' activecontrol is
end;//if nil. Actions for setting and clearing plain text vs.
end;
superscript text editing are in

50 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
A simple superscript text editor (Continuation 1)
procedure setplaintext; procedure kbLeft;
begin begin
with form1 do activecontrol := edit1; if cursX > 1 then
clearcursor; //verwijder cursor uit paintbox begin
plaintext := true; dec(cursX);
end; if cursX - bias < 1 then dec(bias);
setmodeF;
procedure setsupertext; end;
begin
end;
form1.activecontrol := nil;
clearcursor; //must be painted later If the cursor exits the box at the left side, the bias must be
plaintext := false; adjusted to keep the cursor inside.
end;
To paint superscript text in the paintbox, setmodeF sets variabele upmode true or false, which takes
in design time it's properties are set to care of the vertical cursor position. In this case, the right
width 660
height 30
character position (F = forward) is copied.
font "courier new", procedure kbBack handles the backspace:
font size 12
font style bold procedure kbBack;
which yields a fixed character width of 11 pixels. var i : byte;
The formulabox may display 60 characters at the time. begin
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);
cursX has the index of the character where it is placed if cursX - bias < 1 then dec(bias);
before in the text. Far left of the paintbox is position 1. setmodeB;
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 Opposite, when translating plain text to superscript text,
position which is remembered by the text must be scanned for ^(
var upmode : boolean = false; to detect a low to hi transition. Then the number of
For each character in de string, the position (up, down) parenthesis must be counted : +1 for ( and -1 for ).
must be recorded. Occurrence of a ) together with a zero count detects a hi
The text has a special dataformat to facilitate this: to low transition.
const charwidth = 11;
maxchardisplay = 60; //characters displayed in A mousedown event in the paintbox calls
paintbox
maxchar = 250; //max length of text
Formulabox.mousedown(...) which delivers the (x,y)
type TFormText = record coordinates. These are used to calculate the cursorpositie
ch : char; //character code in de superscript text:
up : boolean; //position
var hh : byte;
end;
begin
var formtext : array[1..maxchar] of TFormText; hh := bias + ((x+4) div charwidth) + 1;
textlength : byte = 0; //number of characters in text if hh > textlength + 1
then hh := textlength + 1;
Procedure showformula paints the complete line of cursX := hh;
text. For each character if cursX <= textlength
procedure paintchar(n) is called to paint character n then upmode := formtext[cursX].up
of the text. Please look at the source code for details. else upmode := false;

The specific procedures for cursor control are small but Please refer to the source code for more details.
tricky because accurate counting is needed. This concludes the description of this small editor project.
Below is procedure kbLeft to advance the cursor 1 place to
the left.
Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS 4 51
Interview with Gwan Tan - better office
So I did my final thesis with that faculty working on my
Gwan Tan is the owner of “better office”.
own project for a Chinese consumer word processor
We met a long time ago and had several times
very interesting discussions – especially after although I speak no Chinese.
meetings of first time Delphi presentations.
Editor
Because we want to present very interesting
Was there any follow up for this Chinese consumer?
people we meet and their companies using Delphi
and Pascal we want to know their opinion about
Gwan
the market, developments and critical annotations.
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
Editor
What does TUE mean ?
How did this cooperation start?
It is unusual that people have interest beyond the border –
Gwan
other countries.
Technical University Eindhoven (at that time it was the THE
Technische Hogeschool EIndhoven).
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.
Gwan At the time I had no direct interest in doing any work in
Electrical engineering is only taught at technical Germany. But as I got an invitation for a Christmas Party, I
universities. decided to go to Oldenburg (city in the north of Germany near
At technical Universities you can achieve the engineers title Bremen).
(Ir.) At general universities you can become a Master of So the contact was continued on German soil. With these
Science, (M Sc) Docterandus in Dutch (Drs.) first contacts I met other German developers (and
But electrical engineering is mostly an applied science. publishers) and I was invited to the first German Delphi
The more theoretical study is physics. Conference (EKON).
I always liked the applied sciences more than theory. During the years I got more and more involved with
In the beginning I wanted to specialize in data German developers and slowly got some clients there too.
communications. But during a training period in the USA As the German market was getting more and more
I got to my hands on the first Apple Macintosh and found interesting and there seemed to be good opportunities for
programming a much more interesting area, Dutch developers I sought a more structured approach to
especially with user interfaces and databases. the German market leading to better office benelux.
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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Interview met Gwan Tan - better office (Continuation 1)
Gwan Gwan
In the past years our main projects are in Germany What do you mean with combine with? We have done
although we still have an old client with sites in the projects were we have Pascal clients with Java middleware
Netherlands, Ireland, France, Germany and Thailand. etc. Is that what you want to know?
In Germany we specialize on ERP support programs.
We have a production planning system running in Editor
cooperation with the clients SAP system. Yes. And the databases?
We also have a system running with another client that
handles the sales departments, warehouse and distribution Gwan
of the clients main German factory. As for databases we mostly use InterBase or Firebird if we
have the freedom of choice. Often we have to connect and
Editor develop with MySQL, SQL Server or Oracle. Basically we
so you're mainly doing business with larger contractors? know InterBase and Firebird best but we are extending our
Is there any specializing like mobile or is that a new field? knowledge and experience in MySQL and SQL Server as
often clients expect us to work with these databases.
Gwan
We work most of all directly for a client. Editor
As our programs are often connected with the backbone Do you have a project that you are especially proud of?
systems of the client we have to cooperate with their other
contractors. Gwan
In the future one can foresee much more use of tablets, Depends on what to be proud of. Technically I'm very
handhelds and smartphones even within the factory. proud of the first production planning system we
The information people need during a production is not developed for a customer in Mainz.
confined to a working place and therefore to a single On a more general level I'm very proud of 'having
computer. survived' so far in the ERP-project just across the border.
The people need to have their info with them like they That project was partly challenged for technical reasons
have their wireless company phones. but even more for organizational reasons.
Within a warehouse it a already more obvious and the At the start the requirements seemed to be quite good
sales departments especially those sales persons in the defined but as the project went on it became clearer the
field need to be connected with the main company system. requirements were not as complete as expected.
So we are looking the area of mobile computing and are So much more interaction with the users was necessary to
therefore very interested in the (future) extensions of find out what had to be developed.
Delphi and Pascal in general. In the meantime especially top management put a lot of
At present our clients are not ready for mobile computing. pressure on getting the program(s) up and running.
They are in the field of IT mostly conservative, Especially Edwin van der Kraan, my main developer in
needing proven technology and do not want to be this project, had a very hard time.
pioneers. But now after 2.5 years the system is up and running and
So we have some time to develop applications that will get stable. So I'm proud of the project and especially proud of
their interest growing. Edwin.

Editor Editor
So you -up to now- are actually VCL users? What are fields you would especially point at for reasons
of learning and avoiding eventual upcoming problems?
Gwan How do you respond to pressure without getting squeezed?
Yes, in our German projects we are using VCL mostly.
Especially Jeroen is investigating/playing with the newer Gwan
extensions. Pressure is at almost all projects present. But sometimes
the pressure is very high. To keep the pressure bearable
Editor one has to keep the targets of the project well defined and
I got the impression that you are doing websites as well? plan sufficient moments where especially management can
What languages do you use for this? Intraweb maybe or see the progress made and the direction of the project.
php or... Depending on the part of the project and the pressure of
management one has to plan those moments more often.
Gwan But do not forget to explain to management that such
Websites are not our main area. It is mostly our German moments cost time and therefore will slow down the
colleagues who work in that area. project. These milestones should be useful for checking the
They use mostly “php” as far as I know. I have to admit progress and direction of the project.
that I'm not right person to ask about that area.
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 4 53
Interview met Gwan Tan - better office (Continuation 2)
Gwan Editor
The main issue here is management. We organized that last time in Leiden - Netherlands and
They are to be considered like normal users. that was a great success - because of meeting each other.
When you design a screen flow you image to be that user. Some reasons: we had 14 speakers and all the speakers as
What do I want/need to see when I have to do something. well the developers were very "touchable". Attendees
Management is the same like users they (should) know thought this was great - so we will do that over again at
what they need and like users they do not understand the our next conferences.
technical issues behind the programs. And now for something quite different: What is your
Therefore one has to prepare the meetings with opinion about FireMonkey? Do you ever use it in your
management not just by making nice looking planning projects?
sheets but also by showing them as much as possible what
has been developed and want the ideas are for the next Gwan
step(s). I believe FireMonkey might be the way to give Delphi a
Management is often very insecure about software future. I have no idea if it will be a success. I believe in the
projects. They need to have the feeling that they are still in Delphi IDE and I love Pascal but as Pascal is almost not
control. used at schools and universities one has to attract the
A large part of my job is 'holding their hand' during the future developers and IT-managers.
project. Giving them the confidence that the project will For that it has to be 'sexy' again. I will have a closer look at
turn out to be a success. it as soon as I can find more time.
I also hope to get more info from the upcoming
Editor conferences. Online fora are great but listening to the
Do you create a visual overview of what steps could be attendants of (Delphi-) conferences gives a better or at least
taken or what the flow will be? Do you use video for this other impression.
or a conferencing room? If the impression is positive I will have a more detailed
look in what way I can use FireMonkey, what kind of
Gwan projects, what kind of applications and especially in what
Depending on the milestone and the audience I will create kind of situations.
power point presentations but with a lot of switching to Clients might be asking about it in the future but I expect
the prototype. that in the beginning we will have to propose using it to
Even management knows that power point presentations potential clients.
do not necessarily cover reality. I like to use a conference They often do not know it.
room.
I want to be able to scan the audience for facial expressions Editor
and body language in general. Also in contrast to video We have plans for creating a Pascal learning program, at
conferencing I believe in the face-to-face communication. least for the Netherlands.
With video there is always a kind of wall in between, a
distance. Still, sometimes a video conference is more Gwan
practical. But I would only do that for smaller milestones. A Pascal learning program sounds great. The main issue
I still would want to have the main persons responsible for will be how to get it to the attention of teachers and even
the project with me in the same room. better students? For those who are already interested in
Pascal and want to learn it they will be very happy to find
Editor such a program as Pascal/Delphi-books are getting rare.
So this brings up another subject: But if you can get it to the schools and universities a major
nowadays Embarcadero is doing a lot of video step in getting future Pascal developers is made.
conferencing.
I must say it seemed interesting. But the details you just Editor
mentioned are not covered by that. What do you actually really miss in Delphi?
I find these video examples etc. very tiring and at a What would you you like to be changed?
distance.
I think there should be more as we had in the past Gwan
conferences you could personally attend to. I do not miss that much in Delphi. I'm still using Delphi XE
for the current main project.
Gwan Missing functionality is mostly covered by extensions like
I can see the organizational and financial reasons for video GExperts and component packs like Developer Express.
conferencing etc. but like you I like 'real' conferences much My main problem is the documentation of programs.
better. Most developers do not like to document and they barely
I still go to such events like the Delphi Tage in Leipzig. describe what is in each of the modules.
Saturday and the EKON in Cologne in November. I would like a way for developers to describe their
I really miss the old BorCons. reasoning for certain parts of the program.
The chance to meet other developers from all over the Next to the questions where did he do what.
world and discuss anything while having a beer in the bar I often have the question WHY?
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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Interview met Gwan Tan - better office (Continuation 3)
Editor Gwan
What's your special quality as company (in Delphi?) I have seen a demo of the Leap Motion in Leipzig at the
towards companies? Delphi Tage. I was quite impressed by its possibilities. It
does need some work to get a decent version out but I can
Gwan see some great opportunities with it.
As mentioned earlier we develop applications with I will have to think some more about itt as I have to find
databases, mostly Delphi and InterBase but sometimes possible uses for my customers. It might be interesting to
with Visual Studio and other databases like MySQL, SQL develop some managerial/marketing applications with it for
Server and Firebird. Although we have developed a instance to manipulate the views of graphs etc.
number of ERP-related applications we also have done a lot
of work for social housing corporations. Editor
We developed modules connecting with the main What's your idea of 3d printing? We want to make it
administration systems of those corporations. available for Delphi and are working hard to get that done...
We also created several applications that handle a lot of
statistical/market-oriented information and make that Gwan
information available to the corporation. I find 3D printing very interesting. I even thought about
I prefer not to give names of customers although with getting me one just to try it. Except from finding the time to
social housing corporations it would be possible. 'play' with it I did not have an immediate idea for something
Ok, there are many corporations we worked for. 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
Often it is easier to mention the cities they are at as that is
mostly their working area. searching for the best 3D-printer to do the job. I am certain
The main cities are: Maastricht, Nijmegen, Eindhoven, that 3D-printing will become more and more important in
Nieuwegein, Almelo, Enschede, Ede, Dantumadeel, the future.
Woudenberg and Raamsdonks Veer. I probably have The price has gone down a lot and the capabilities of those
'cheap' printers are improving a lot. To be able to develop
forgotten several corporations but this should give an idea
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 Editor
I can understand Embarcadero needs to get a better cash Sure! In this issue we have started our first articles about
flow in order to keep investments in development possible this. There is even a possibility of creating the so called 4th
but I do not believe the market will accept updates to be dimension: Interactive layer printing...Like printing a layer
released that often. Especially if they charge the normal that is able to react towards light or temperature. One could
cost. Developers (and their bosses) will not go for each even print layers that enable special things like creating an
update and will only update if one really needs to update. interactive object with printed chips on it...
Also if updates are released that often most updates will be
actually just patches. They should be free anyway and Gwan
made available as soon as possible. So if Embarcadero is The future capabilities of 3D-printing are going to be
really going for a release schedule of once every 6 months enormous. Already some creative minds have found new
and charges for each update their standard prices I expect ways to use 3D-printing. I wonder what will be done with it
part of the market to turn away from the product. in the future. So your articles will give everyone the chance
How large that part will be is hard to predict but as the to become part of that future.
Delphi market is already small any part will hurt. I do think that that is one of the main reasons for magazines,
conferences and fora etc. to be reading and discussing the
Editor future not just what can be done today but also what might
Did you ever consider the other Pascal? FPC- combined be possible in 5 or 10 years....
with Lazarus? Have you ever tried that?

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 4 55
productivity software building blocks
Using GEO services in Delphi applications
with TMS components Mapping
The 3 major services that provide digital maps are Google
Maps, Microsoft Bing and Open Street Maps. Digital maps
Introduction are provided via a HTTP service and can be displayed via a
In the past few years, a vast array of services browser. These mapping services are targeted mainly at
related to a position on our planet earth became browser usage, so if we want to take advantage of these
available. With a wide range of components, TMS maps from a Delphi application, the main two challenges
software offers seamless access to these services are calling a Javascript API from the Delphi application
from Delphi VCL desktop applications, IntraWeb that is executed in the browser and handle Javascript
web applications and FireMonkey mobile events that are being triggered from the map and that
applications running on iOS or Android. As such, ideally are exposed as class events to a Delphi application.
it's mostly your imagination that is the limitation For a Windows application, to call Javascript functions
of what you can do these days with geo services from a Delphi application for a map displayed in a
in your applications. browser, the HTMLWindow.execScript() function can be
used. To handle Javascript events from the Windows
Basis of geo services TWebBrowser at Delphi application level, it is required to
The fundament on which all geo services are built is the implement the IDocHostUIHandler interface. Fortunately,
determination of a position on our planet. For this, in this is somewhat easier from an IntraWeb application as
almost all cases, the system of longitude and latitude is the map is displayed typically on the same page where the
used. The latitude is a value between -90 and +90 that IntraWeb application page is rendered. For a FireMonkey
defines the angle of the position between south-pole and mobile application, a technique similar to the Windows
north-pole. The longitude is a value between -180 and +180 desktop application is necessary but in this case with the
that defines the position along the equator. These values browser that runs on the mobile device. Fortunately, TMS
are typically expressed in decimals or with degrees, software offers components for desktop, web and mobile
minutes and seconds. For ease of calculation, most services application with the same interface, so this makes using the
use the decimal notation. maps easy on any of these platforms.

If there is a classification to make for what geo services are To get started with using Google maps, drop the TMS
being used, we'd divide these in following categories: TWebGMaps component on the form and add the code:

- Mapping: techniques for displaying & manipulating webgmaps1.MapOptions.DefaultLatitude := 51.2;


maps and visualize information on maps webgmaps1.MapOptions.DefaultLongitude := 4.37;
- Geocoding / reverse geocoding: webgmaps1.Launch;
techniques for converting an address to a longitude and
latitude and vice versa This code snippet initializes the map for position 51.2 /
- Geolocation: techniques to obtain the longitude 4.37 that is Antwerp, Belgium. Next we can add a marker
and latitude of a computing device at this location. Markers are exposed via a markers
- Geo POI services: services that provide information / collection. By default a marker with a hint is added with
data about points of interest at a specific position this code:
- Routes: calculate the routes between two webgmaps1.Markers.Add(51.2,4.37,'Antwerp')
or more positions
Next task is to draw a polyline on this map. To illustrate
In this article, we have examples for using each of these this, code will be added to draw a triangle between 3 cities:
different services from Delphi applications. Antwerp, Gent, Brussels.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE 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)
Geocoding / reverse geocoding begin
Geocoding is the process of converting an // set the address
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
starting from a longitude and latitude WebGMapsGeocoding1.LaunchGeocoding;
coordinate. This is typically performed by a // pan map to location retrieved
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 ,
Bing, OpenAddresses, Yahoo PlaceFinder and WebGMapsGeocoding1 .ResultLongitude ,'Embarcadero' );
several smaller services. When looking at the end;
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
make using the Google geocoding and reverse WebGMaps1 .StreetViewOptions .DefaultLatitude :=
geocoding service very easy. This is WebGMapsGeocoding1 .ResultLatitude ;
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 4 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 • Wi-Fi service based: Similar as with cell phone based
that provide information about the location of a device. geolocation, Wi-Fi based geolocation could be an option
These days, most mobile devices have a GPS built-in and when a device is connected via a Wi-Fi access point and
this can return the longitude and latitude of the device the location of the Wi-Fi access point is known.
immediately. From Delphi XE4, the non-visual component An example of a service that collects information on the
TLocationSensor is provided that allows you to get this position of Wi-Fi access points is
information. Using TLocationSensor is easy. http://www.skyhookwireless.com
Set LocationSensor.Active = true and via the For mobile devices, typically a fallback mechanism is
event OnLocationChanged, the position is returned. used. First, there is a check if a GPS exists.
The accuracy of determining this location is around 10 When not, it can try to see if it can find a position based
metres typically. When no GPS is available, we must resort on the IP address, the connected cell phone access point or
to different techniques. These techniques can be: Wi-Fi access point. This is the mechanism that is built-in
• ISP IP address based: many ISPs have a database of these days in any HTML5 compliant browser. This allows
what IP address range is being used in what area. Services web applications to determine where the device is located
exist that gather this information that can be used to that connects to it. This is known in the HTML5 standard
retrieve location information based on an IP address. as HTML5 Geolocation API.
• Cell phone based: when a mobile device is connected
to a cell phone access point, the position of the cell phone To make it easy for desktop applications to determine as
access point is known and thus also the area the signal of good as possible the location of a machine, TMS software
this cell phone access point covers. There are also services offers a component TAdvIPLocation that uses the
that collect this information and make it accessible. FreeGEOIP service. To make it easy for IntraWeb web
OpenCellID is an example. applications, we have a component
See: http://www.opencellid.org/cell/map TTIWIPhoneGeolocation that uses the HTML5
Geolocation API to determine the location.

60 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 Next, when we click on a category in the listbox, we
TAdvIPLocation to obtain the location based on the IP perform a query of the top 10 points of interests nearby the
address of the machine and shows this location on a map computer location (obtained with TAdvIPLocation) and fill
and adds the location city name, ZIP code and country in a the listbox with this info:
memo.
procedure TForm1.ListBox1Click(Sender: TObject);
var
GEO POI services id: string;
Geo POI services is the name of services that provide point i: integer;
of interest information at a specific location. This includes la,lo:double;
things as railway stations, museums, restaurants, sports begin
infrastructure etc... Typically, a service can provide a list of id := listbox1.Items[listbox1.ItemIndex];
points of interest that matches a requested category and a
specific location. It can then offer information such as id := copy(id,pos('/',id)+1, 255);
address, description, recommendations, opening-hours of
listbox2.Items.Clear;
the points of interest. Many services exist that offer this
kind of information but the main suppliers with the biggest AdvIPLocation1.GetIPLocation;
amount of information are FourSquare, Google Place, Bing orgla := AdvIPLocation1.IPInfo.Latitude;
Spatial Data Services, Factual... orglo := AdvIPLocation1.IPInfo.Longitude;
As FourSquare is one of the leading services, TMS software
has a component TAdvFourSquare that makes using this // This fills the AdvFourSquare Venues
service very easy. Typically, all we need to do is specify a collection with points of interest:
location, i.e. longitude & latitude, specify category of AdvFourSquare1.GetNearbyVenues(
points of interest we're interested in and possibly also a orgla,orglo,'','',id);
radius. The service then returns a list of points of interests
of which we can query a description, photo, etc.. // Add the summary line to a listbox
for i := 0 to advfoursquare1.Venues.Count - 1
do
To illustrate this, we'll use the component begin
TAdvFourSquare, available in the TMS Cloud Pack. To listbox2.Items.Add(
start using this component, it is necessary to first obtain a AdvFourSquare1.Venues[i].Summary);
(free) FourSquare application key and secret. You can end;
register for this at https://developer.foursquare.com end;
First we obtain the different categories and subcategories
of points of interests that FourSquare has and fill a listbox
with this:
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 In this example, we use the TWebGMaps component as well
services we can consume from Delphi applications, is as the TWebGMapsDirectionList. TWebGMapsDirectionList
getting routing or directions information to travel from a is a component especially designed to visualize HTML
location A to a location B. Again, the three major suppliers formatted directions information returned by Google.
of these services are Google with the Google Directions Getting directions is as simple as calling
API, Microsoft with Bing Routes API and the Openroute WebGMaps.GetDirections with start and end address. Here
service (http://www.openrouteservice.org/). Such service we obtain directions information from San Francisco center
typically works in following way: we make a request to the Embarcadero offices in Scotts Valley
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.
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 About the author
(walking/bike/car) etc... that can be explored with the Bruno Fierens
Studied civil electronic engineering at university
TMS TWebGMaps component.
of Ghent, Belgium (1987-1992) and started a
Summary career as R&D digital hardware engineer.
It is amazing what amount of rich (and in many Besides the fascination for electronics,
cases free) geo information and geo services are Bruno Fierens set the first steps in
available to us Delphi programmers these days. programming with Turbo Pascal v3.0
With this information, we can add useful and used all Borland Pascal & Delphi versions
functionality to Delphi Windows based since that time.
applications, IntraWeb web based applications In 1996, he founded TMS software for the
and FireMonkey mobile applications for iOS and activity of application and component
Android devices. At TMS software, we offer a development with Delphi.
wide range of components that allow you to TMS software became Borland Technology
consume these services right-away. This allows Partner in 1998, developed Delphi Informant
you to avoid studying the various APIs yourself award-winning grid & scheduling components
and be productive immediately to integrate and now has an international team of software
these in your applications. The several products developers working on a large portfolio of
covered in this article that make use of these components.
services are: Bruno Fierens is from 2012 Embarcadero MVP
and frequent speaker at Delphi conferences
TMS WebGMaps: world-wide.
http://www.tmssoftware.com/site/webgmaps.asp He does and oversees VCL, IntraWeb,
TMS WebOSMaps: .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
type
This article is a supplement to the other article, PEnumArgs = ^ TEnumArgs;
"Designing an API: typical mistakes." on page 8 of TEnumArgs = record
this issue. In this article we will discuss how you ClassName: String;
can fix one of those mistakes listed in the Windows: TStrings;
end;
previous article, namely: the lack of user-
arguments for the callback-functions. function FindWindowsOfClass(Wnd: HWND;
lpData: LPARAM): Bool; stdcall;
var
Introduction to the callback-functions Args: PEnumArgs;
In computer programming, a callback is a piece of WndClassName, WndText: String;
executable code that is passed as an argument to other begin
Args := Pointer(lpData);
code, which is expected to call back (execute) the argument
at some convenient time. SetLength(WndClassName,
For example, if you want to set the timer by using the Length(Args.ClassName) + 2);
Windows API, you can call the SetTimer function, passing SetLength(WndClassName, GetClassName(Wnd,
PChar(WndClassName), Length(WndClassName)));
a pointer to the function to it, which will be the callback-
function. The system will call your function whenever the if WndClassName = Args.ClassName then
timer fires: begin
SetLength(WndText,
procedure MyTimerHandler(Wnd: HWND; uMsg: UINT; GetWindowTextLength(Wnd) + 1);
idEvent: UINT_PTR; dwTime: DWORD); stdcall; SetLength(WndText, GetWindowText(Wnd,
begin PChar(WndText), Length(WndText)));
// Will be called after 100 ms. Args.Windows.Add(Format ('%8x:%s', [Wnd,
WndText]));
end;
end;
procedure TForm1.Button1Click(Sender: TObject); Result := True;
begin end;
SetTimer(Handle, 1, 100, @MyTimerHandler);
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); Memo1.Lines.BeginUpdate;


begin
EnumWindows(@MyEnumFunc, 0); try
Memo1.Lines.Clear;
end;
EnumWindows(@FindWindowsOfClass,LPARAM(@Args));
finally
Since the callback function normally performs the same Memo1.Lines.EndUpdate;
task as the code that sets it, it turns out that the two pieces end;
of code has to work with the same data. Consequently, the end;
data from the code setting callback must be somehow
passed to the callback function. For this purpose, the
Note: the equivalent of user-parameters are the Tag property
callback functions provide so-called user-argument: either
and Data property, although their use is not always
a pointer or an integer (such as Native(U)Int, but not
ideologically correct (correct: a derived child class).
(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.

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 4 65
Correcting a bad API design (Continuation 1)
Static callback-method I Bad API design
nstead of callback-function If not too experienced developer did the design of the API,
As the modern applications are usually built as a set of he may not think about the need for a user-arguments in
classes - it would be nice to isolate the callback function: to callback-functions (as well as many other things - see
make it not global, but a member of the class. This is easily previous article). As a result, you may have the following
done as follows, using a static class methods: code on hand:

type type
TForm1 = class(TForm) TCallback = function (FoundData: TData): BOOL;
Button1: TButton; cdecl;
Memo1: TMemo;
Edit1: TEdit; function RegisterCallback (Callback: TCallback):
Integer; cdecl;
// Adapter
strict private As you can see, there are no user-parameter, and the only
class function
InternalEnumWindowsCallback(Wnd: HWND; lpData: argument for callback-function represents the actual data
LPARAM): Bool; stdcall; static; for the function.

// Hi-level interface Using global variables


protected
Of course, because the developer API - is not you, then you
function EnumWindowsCallback(const AWnd:
HWND): Boolean; virtual; cannot change the prototype of the callback function. What
function EnumWindows: Boolean; to do? Since we cannot send data through a local option,
end; the only option is a global parameter:
// ... var
GMemo: TStrings;
function TForm1.EnumWindows: Boolean;
begin function MyCallback(FoundData: TData): BOOL;
Result: = WinAPI.Windows.EnumWindows( cdecl;
@InternalEnumWindowsCallback, begin
LPARAM(Pointer(Self))); GMemo.Lines.Add(DataToString(FoundData));
end; end;

class function function TForm1.Button1Click(Sender: TObject);


TForm1.InternalEnumWindowsCallback(Wnd: HWND; begin
lpData: LPARAM): Bool; GMemo := Memo1;
var RegisterCallback(MyCallback);
Form: TForm1; end;
begin
Form: = TForm1(lpData);
Of course, we can make callback-function to be a member
Result := Form.EnumWindowsCallback(Wnd); of the class, as we did above (with help of “static”
end; keyword); but it should be understood that in this case it
will be all the same global function and variable, but a little
function TForm1.EnumWindowsCallback(
const AWnd: HWND): Boolean; disguised. Accordingly, we cannot access properties and
var methods of the class within callback.
WndClassName, WndText: String; This solution may be considered as "satisfactory" because it
begin works, but uses global variables - that is. Sometimes this
// Your code - it can work with the members of the class.
may be acceptable, but often we need to call the callback-
// For example:
function multi-threaded (which, however, can be solved
SetLength(WndClassName, through threadvar) or even just a couple of different calls
Length(Edit1.Text) + 2); within a single thread. In this case, it seems that there is no
SetLength(WndClassName, GetClassName( reliable way to identify function's data?
AWnd, PChar(WndClassName),
Length(WndClassName)));

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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Correcting a bad API design (Continuation 2)
The correct solution: dynamic code Creating a master template
However, an experienced programmer can offer First step: write the following dummy code:
guaranteed working version, "adding" user-parameter to
the existing "bad" API. type
This is not an impossible task as it may seem, but the TRealCallbackFunc = function(FoundData: TData;
Data: Pointer): BOOL; cdecl;
solution is not trivial.
The essence of the idea is that the user-parameter can be
function InternalCallback(FoundData: TData):
replaced by the actual function callback that will be unique BOOL; cdecl;
to each use. Thus, the call will not be identified by the var
parameter, but rather to which callback function is called. RealCallback: TRealCallbackFunc;
begin
Statement of the problem RealCallback: = Pointer ($ 12345678);
Of course, in a pre-compiled file there cannot be an Result: = RealCallback (
arbitrary number of functions for arbitrary user- FoundData, Pointer ($ 87654321));
end;
arguments. That is why this solution requires generating
code on the fly (in run-time). procedure TForm1.Button1Click (Sender: TObject);
Fortunately, it's not too difficult task because you can use begin
the services of the Delphi compiler to generate the InternalCallback (0);
template. What's more - you may not even know end;
assembler. But you need to have some idea of the memory
architecture in Windows. Here: InternalCallback - this is a callback-function, the
prototype of which is fully consistent with API.
So, suppose we have the following: TRealCallbackFunc – is a modified callback-function
with desired prototype, which is different from the API
type only by the presence of an additional parameter: it is our
TData = Integer; // just as an example, it could be
user-parameter. Although the prototype of RealCallback
// anything: a pointer, record, etc.
TCallback = can be arbitrary, but for ease of debugging, it is desirable
function(FoundData: TData): BOOL; cdecl; that he would be as similar to InternalCallback as possible.
TRegisterCallbackFunc = function(Callback: However, you can also put a user-parameter to be the first
TCallback): Integer; cdecl; parameter and change the call model from
TUnregisterCallbackFunc =
procedure(Callback: TCallback); cdecl; cdecl/stdcall to register - it will allow you to skip
var intermediate adapter in the future, if you ever want to
RegisterCallback: TRegisterCallbackFunc; make this callback-function a member of the class. In this
// imported from a DLL case, the Self will be transferred in user-parameter - thus
UnregisterCallback: TUnregisterCallbackFunc;
the signature of callback-function binary matches the
// imported from a DLL
signature of the conventional method of the class.
This is our "bad" API. Since the API is usually located in a We should just call RealCallback via fixed pointer,
separate DLL, then I made an example of a function- passing a fixed pointer as the user-parameter. The values of
variable, rather than a normal function. $12345678 and $87654321 are chosen for the simple reason
So our challenge: to add support of the user-parameter to that it will be easy to spot them in the native code. You can
the API. use any other "magic" values. Surely, in the case of 64-bit
Note: not always "bad" API is the result of developer mistake. code you need to use 16 bytes magic values instead of 8
Sometimes this kind of design is dictated by technical byte, for example: $1234567890ABCDEF and
constraints. $FEDCBA0987654321.
For example, the function SetWindowsHookEx can set The RealCallback function is not called directly, but
hooks both locally (in the current thread) and globally (for all indirectly - through a RealCallback variable. I will explain
processes in the system). below why it is done.
You simply cannot have a user-parameter with the design: So, add a direct call to InternalCallback in your code:
because the user-parameter during installation of the hook
procedure TForm1.Button1Click(Sender: TObject);
will not make sense at the time of the callback call - as the
begin
hook is called from another process. InternalCallback(0);
However, if you want to install a local trap in the same // here: TData = Integer (in this example),
process, the user-argument would be quite useful. // you can pass any value
For example, if you want to keep track of window end;
messages for some of your windows.
In this case, the hook will be limited only to your main Warning: do not run this code! This code will result in an
thread, but you may have many windows, so threadvar exception being thrown (Access Violation), because it calls
cannot help you. "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:
Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS 4 67
Correcting a bad API design (Continuation 3)

You can see that all of the code consists of four lines: begin · Address of the called function
(aka - the prologue), assigning the function to variable, (RealCallback): $12345678
the call, end (aka - the epilogue). Each line is labeled with · The value of user-parameter: $87654321
comment (in bold font), which shows the location in the
module and the code line itself. Lines below the comment All other code is static and it does not depend on anything,
is a machine (native) code that was compiled from that line. i.e. it will be exactly the same in all cases: for any user-
Do not worry, you absolutely do not need to arguments for any callback-functions.
understand what it says! All you need – is to determine the This means that if we want to generate the functions
beginning and end of the function. It is easy to do: start of themselves, such as InternalCallback, we can just
the function is at the very top and marked by a blue line. copy all the code and simply substitute two numbers: the
The end of the function is clearly in line Unit1.pas.43: end; address of the function and user-argument.
- 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. Address of the function and user-parameter is easy to spot
in machine code, since we used the magic value of
Note: If you do know assembler, then you know that the $12345678 and $87654321.
function ends in a line with a ret command (return control),
and the next line is just a garbage filler. Note:
Select the code with your mouse and copy it somewhere this is why I used the design with indirect function call
via the clipboard. Again, you do not need to run this code! instead of direct call: because in this design the call is
If you do not know assembly language, then all you need defined as "call the function at that address."
to know: There is clearly "this address" present - which means that it
The first column is the address of the instruction. can be easily changed. If the call was direct, the machine
These addresses belong to your exe (after all it is the exe code code were said to "call a function that lies in N bytes before
that we have called from Button1Click) and we are not this instruction". Altering such address would be much
interested in these. The second column: hex-code of more difficult.
machine code. That is, this is the machine code in a pure That is all that relates to parsing the template.
form, which will be executed by the processor (CPU) Please note that we did not use any special knowledge,
– and this is where we are concerned. apart from the ability to use a debugger and some common
The third column - is the assembly code corresponding to sense.
machine code. The great thing about this listing - we do Then proceed to the coding. First, you need to copy our
not need the assembly code, we need only native code. template into a byte array for later use in the program code.
Now I'll explain why... Now look: this function has only
two values of the variables:

68 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Correcting a bad API design (Continuation 4)
To do this, I first wrote out the entire machine code This weird code says that a CallbackTemplate pointer (and
(second column): any string - is a pointer, for a static array you'd have to take a
55 pointer explicitly via @) should be interpreted not as a string,
8BEC but as a function of the type TCallback.
83C4F8 So this function, therefore, should be called. Here is a
C745F878563412 // 12345678 longer version of the same code:
6821436587 // 87654321
8B4508
procedure TForm1.Button1Click (Sender: TObject);
50 var
FF55F8 Template: AnsiString;
83C408 Ptr: Pointer;
8945FC CB: TCallback;
8B45FC begin
59 Template := CallbackTemplate;
59 Ptr := Pointer(Template);
5D CB := TCallback(Ptr);
C3 CB(0);
end;
Remember, you do not need to write these codes manually
- select text within CPU-debugger with the mouse and use Set a breakpoint to the function call (in any form - long or
Ctrl + C - this will copy the selected lines to the clipboard. short), run the program, click, stop up on the breakpoint.
Then, go to the code editor and paste the text from the Do not press F7!
clipboard, and then remove the four comment lines, As we have no source code for the function encoded in
as well as the first and last columns, leaving only native CallbackTemplate, the debugger will try to execute the
code. whole function in a single pass - which will lead to Access
Note that x86 is little-endian architecture - which means Violation, as both of our pointer ($12345678 and
that the numbers "are written in reverse." $87654321) point to trash. Instead, call the CPU debugger
Of course, you do not have to change the byte order. (Ctrl + Alt + C or View / Debug Windows / CPU View), and
hit the F7 key few times - until you will be transferred to
After that, I removed the line breaks, combining all the the familiar code:
machine code in a long stream of bytes:
558BEC83C4F8C745F87856341268214365878B450850FF55F8 005B5C70 55 push ebp
83C4088945FC8B45FC59595DC3 005B5C71 8BEC mov ebp, esp
005B5C73 83C4F8 add esp, - $ 08
After that I put in the #$ every two characters and 005B5C76 C745F878563412 mov [ebp-$ 08], $
designed this constant as a string: 12345678
005B5C7D push $ 6821436587 87654321
const 005B5C82 8B4508 mov eax, [ebp + $ 08]
CallbackTemplate: RawByteString = 005B5C85 50 push eax
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 +
#$FC#$8B#$45#$FC#$59#$59#$5D#$C3; 005B5C93 59 pop ecx
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);
begin Machine code for x86-32 will not run on x86-64 and ARM
TCallback(Pointer(CallbackTemplate))(0); (and vice versa). That is why, if you are writing cross-
end; platform code, you will need to repeat the operation for
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 4 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 4 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 4 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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Correcting a bad API design (Continuation 8)
Voila! Da Magics! Win64
Unit1.pas.34:
A practical example begin
00000000006990B0 55 push rbp
Please note that the AllocTemplate and DisposeTemplate 00000000006990B1 4883EC30 sub rsp,$30
functions are universal and do not depend on your code. 00000000006990B5 488BEC mov rbp,rsp
00000000006990B8 894D40 mov
To illustrate the versatility of this - let's write code to [rbp+$40],ecx
monitor window messages using SetWindowsHookEx. 00000000006990BB 48895548 mov
[rbp+$48],rdx
We will use the WH_GETMESSAGE hooks. To do this, we
00000000006990BF 4C894550 mov
need to create a template first. Write this code: [rbp+$50],r8
Unit1.pas.35: GetMsgProc := Pointer({$IFDEF
type CPUX86}$12345678{$ENDIF}{$IFDEF
TGetMsgProc = function(Self: Pointer; code: CPUX64}$1234567890ABCDEF{$ENDIF});
00000000006990C3 48B8EFCDAB9078563412 mov
Integer; _wParam: WPARAM; _lParam: LPARAM):
rax,$1234567890abcdef
LRESULT; 00000000006990CD 48894520 mov
[rbp+$20],rax
function InternalGetMsgProc(code: Integer; Unit1.pas.36: Result :=
_wParam: WPARAM; _lParam: LPARAM): LRESULT; GetMsgProc(Pointer({$IFDEF
stdcall; CPUX86}$87654321{$ENDIF}{$IFDEF
var CPUX64}$FEDCBA0987654321{$ENDIF}), code, _wParam,
GetMsgProc: TGetMsgProc; _lParam);
begin 00000000006990D1 48B92143658709BADCFE mov
GetMsgProc := Pointer({$IFDEF rcx,$fedcba0987654321
00000000006990DB 8B5540 mov
CPUX86}$12345678{$ENDIF}{$IFDEF edx,[rbp+$40]
CPUX64}$1234567890ABCDEF{$ENDIF}); 00000000006990DE 4C8B4548 mov
Result := GetMsgProc(Pointer({$IFDEF r8,[rbp+$48]
CPUX86}$87654321{$ENDIF}{$IFDEF 00000000006990E2 4C8B4D50 mov
CPUX64}$FEDCBA0987654321{$ENDIF}), code, r9,[rbp+$50]
_wParam, _lParam); 00000000006990E6 FF5520 call qword ptr
end; [rbp+$20]
00000000006990E9 48894528 mov
[rbp+$28],rax
procedure TForm1.Button1Click(Sender: TObject); Unit1.pas.37: end;
begin 00000000006990ED 488B4528 mov
InternalGetMsgProc(0, 0, 0); rax,[rbp+$28]
end; 00000000006990F1 488D6530 lea
rsp,[rbp+$30]
00000000006990F5 5D pop rbp
We get such an assembly listing for this code: 00000000006990F6 C3 ret
Win32

Unit1.pas.34: begin Cut the machine code and make it into a constant:
005B5C64 55 push ebp const
005B5C65 8BEC mov ebp,esp GetMsgProcTemplate: RawByteString =
005B5C67 83C4F8 add esp,-$08 {$IFDEF CPUX86}
Unit1.pas.35: GetMsgProc := Pointer({$IFDEF
CPUX86}$12345678{$ENDIF}{$IFDEF #$55#$8B#$EC#$83#$C4#$F8#$C7#$45#$F8#$78#$56#$34#
CPUX64}$1234567890ABCDEF{$ENDIF});
$12#$8B#$45#$10#$50#$8B#$4D +
005B5C6A C745F878563412 mov [ebp-$08],$12345678
Unit1.pas.36: Result :=
GetMsgProc(Pointer({$IFDEF #$0C#$8B#$55#$08#$B8#$21#$43#$65#$87#$FF#$55#$F8#
CPUX86}$87654321{$ENDIF}{$IFDEF $89#$45#$FC#$8B#$45#$FC#$59 +
CPUX64}$FEDCBA0987654321{$ENDIF}), code, _wParam, #$59#$5D#$C2#$0C#$00;
_lParam); {$ENDIF}
005B5C71 8B4510 mov eax,[ebp+$10] {$IFDEF CPUX64}
005B5C74 50 push eax
005B5C75 8B4D0C mov ecx,[ebp+$0c]
#$55#$48#$83#$EC#$30#$48#$8B#$EC#$89#$4D#$40#$48#
005B5C78 8B5508 mov edx,[ebp+$08]
005B5C7B B821436587 mov eax,$87654321 $89#$55#$48#$4C#$89#$45#$50 +
005B5C80 FF55F8 call dword ptr [ebp-
$08] #$48#$B8#$EF#$CD#$AB#$90#$78#$56#$34#$12#$48#$89#
005B5C83 8945FC mov [ebp-$04],eax $45#$20#$48#$B9#$21#$43#$65 +
Unit1.pas.37: end;
005B5C86 8B45FC mov eax,[ebp-$04] #$87#$09#$BA#$DC#$FE#$8B#$55#$40#$4C#$8B#$45#$48#
005B5C89 59 pop ecx $4C#$8B#$4D#$50#$FF#$55#$20 +
005B5C8A 59 pop ecx
005B5C8B 5D pop ebp #$48#$89#$45#$28#$48#$8B#$45#$28#$48#$8D#$65#$30#
005B5C8C C20C00 ret $000c $5D#$C3;
{$ENDIF}

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 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 4 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 About the Author:
callback. There is no need to introduce a static class method Alexander Alexeev (Александр Алексеев)
adapter. But be careful: the compiler cannot check you Alexander started working with Delphi while still
here. If you make a mistake in the method's signature, you at university. Following graduation as a Master of
crash your application. Mathematics, he took several jobs in the Delphi
world. Today he works for the EurekaLab s.a.s.
That's all I wanted to say today. company, where he develops and supports
EurekaLog for Delphi/C++ Builder. Alex lives in
IMPORTANT NOTE the Russian Federation and maintains a personal
The approach described in this article is a hack. blog, where he writes about Delphi.
This means that it is a crutch, a workaround, Some of his articles are available in English at
a way to get the code to work somehow. eurekalog.blogspot.com. His favourite topics are
It is no excuse for writing "bad" API. If you have control debugging-related.
over the callback-function prototypes - change them! He is one of our best authors making you
Introduce the support for user-arguments. understand how Programming works...

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 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.

• 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.
maXbox or Hex in the Box It is generally easy to use for basic serial communications,
maXbox is a free scripter tool with an inbuilt alternative to the TurboPower ASYNCPro.
Delphi engine of Pascal Script in in one exe It includes 5 components:
(including several DLLs and helper files)! It is
designed for teaching, develop, test and TComPort,
analyzing apps and algorithms and runs under TComDataPacket,
Win and Linux (CLX) to set Delphi in a box TComComboBox,
without installation or administration. The tool is TComRadioGroup and
based on an educational program with examples TComLed.
and exercises (from biorhythm, form builder to
how encryption works). Units are precompiled With these tools you can build serial communication apps
and objects invokable! With a 26 part tutorial for easier and faster than ever.
coders or say it Pure Code for the Pascal First we start with the web server and second we explain the
Community (PC^2). COM port.
After creating the object in line 125 we use first methods to
Get the Code
configure our server calling Port and IP. The object makes a
So let's get a real example, the box has a lot of it.
bind connection with the Active method by passing a web
The tool is split up into the toolbar across the top,
server configuration.
the editor or code part in the centre and the output
window at the bottom. 125 HTTPServer:= TIdCustomHTTPServer.Create(self);
Change that in the menu /view at our own style. So the object HTTPServer has some methods and
We will start with an extract of a http-server demo, just to properties like Active you can find in the
show you several objects and a form in maXbox. TIdCustomHTTPServer.pas unit or IdHTTPServer library.
A library is a collection of code or classes, which you can
• In maXbox you will start the web server example as a include in your program or in maXbox already done. By
script, so the web server IS the script that starts the storing your commonly used code in a library, you can
Indy objects, configuration from ini-file and a browser reuse more code.
too; on board is also an add-on or more:
Options/Add_ons/Easy_Browser/. Let's get back to our HTTP Create in line 125. In line 131
• Before this starter code will work you will need to and 132 you see a Port and IP address configuration of a
download maXbox from a website. It can be down- const in line 13, instead of IP you can also set a host name
loaded from as parameter.
http://sourceforge.net/projects/maxbox 126 with HTTPServer do begin
(you'll find the download to maxbox3.zip on the top of the page). 127 if Active then Free;
Once the download has finished, unzip the file, making 128 if not Active then begin
129 bindings.Clear;
sure that you preserve the folder structure as it is. If you 130 bindings.Add;
double-click maxbox3.exe the box opens a default demo 131 bindings.items[0].Port:= APORT; // 8080
program. 132 bindings.items[0].IP:= IPADDR; // 192.168.1.53'
Test it with F9 / F2 or press Compile and you should 133 Active:= true;
134 onCommandGet:= @HTTPServerGet;
hear a sound. So far so good now we'll open the examples:
135 PrintF('Listening HTTP on %s:%d.'
305_webserver_arduino3ibz_rgb_led.txt ,[Bindings[0].IP, Bindings[0].Port]);
136 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 4 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 This problem might not be identified in the testing process,
code, it’s really not that bad. It’s compact and simple and since the average user installs to the default drive of the
easy to understand. However, within this code it is easy to archive and directory and testing might not include the
see the power of scripting because it's agile and high option of changing the installation directory.
available but you can't hide the functionality. By the way you find all info concerning a script or app in
If a maXbox script or app is programmed with the default menu /Program/Information/…
host standard, it is always started relative to the path
where the maXbox3.exe as the host itself is:
playMP3(ExePath+'examples\maxbox.mp3');

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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Coding with maXbox (Continuation 2) maXbox
Another solution to prevent hard coded literals is a We go on with the boot loader and his functionality.
constant or the call of a user dialog. An indirect reference, maXbox and the script loader system has default folders
such as a variable inside the program called 'FileName', which organize files logically on the hard disk.
could be expanded by accessing a "select browse for file" You can also copy all 6 folders and root files to a folder on
dialog window, and the program code would not have to your USB-stick that stores the same content in your
be changed whwn the file is moved. maXbox installation folder and it will start from the stick!
procedure GetMediaData(self: TObject);
begin 23.05.2013 <DIR> docs
if PromptForFileName(selectFile, 23.05.2013 <DIR> examples
09.05.2013 <DIR> exercices
'Media files (*.mp3)|*.mp3|*.mpg)|*.mpg', '',
09.05.2012 <DIR> crypt
'Select your mX3 media file Directory', 12.05.2013 <DIR> source
'D:\kleiner2005\download', False) 17.03.2013 <DIR> web
then begin 29.03.2013 97'370 bds_delphi.dci
// Display this full file/path value 11.12.2007 254'464 dbxint30.dll
11.11.2012 580'096 dmath.dll
However it is advisable for programmers and developers 09.04.2013 5'426 firstdemo3.txt
not to fix the installation path of a program or hard code 28.11.2010 3'866 firstdemo3.uc
some resources, since the default installation path is 27.10.2005 103'424 income.dll
different in different natural languages, and different 07.11.2010 138 maildef.ini
07.02.2013 10'544 maxbootscript_.txt
computers may be configured differently. It is a common 21.10.2011 59'060 maxbox.mp3
assumption that all computers running Win have the 02.01.2009 71'807 maxbox.png
primary hard disk labelled as drive C:\, 11.05.2013 11'887'616 maxbox3.exe
11.05.2013 5'133'220 maxbox3clx
but this is not the case.
12.05.2013 994 maxboxdef.ini
As you will see the configuration of maXbox is possible 03.12.2012 12'503 maxboxerrorlog.txt
with a boot loader script and a simple ini-file too. 12.05.2013 42'773 maxboxnews.htm
Extensions are possible with the Open Tools API and 12.05.2013 2'309'571 maxbox_functions_all.pdf
21.04.2012 9'533 maxdefine.inc
a small CLI (Command Line Interface). 14.11.2005 383'488 midas.dll
10.12.2012 17'202 pas_includebox.inc
12.05.2013 36'854 readmefirst_maxbox3.txt
Tutorials (for downloading to the harddisk) 11.10.2010 135'168 TIFFRead.dll
You can read or download tutorials 1 till 26 from the
program: ž /Help/Tutorials/: When you start the box a boot script is loaded.
from the Internet: Within the boot script, you can perform common source
http://sourceforge.net/apps/ control tasks, such as file check in, check out, and of course
maXbox
mediawiki/maxbox/ change IDE settings and synchronization of your current
Tutorial 00 Function-Coding (Blix the Programmer) version. This is a script where you can put all the global
Tutorial 01 Procedural-Coding
settings and styles of the IDE of maXbox, for example:
Tutorial 02 OO-Programming
Tutorial 03 Modular Coding with maxForm1 do begin
Tutorial 04 UML Use Case Coding caption:= caption +'Boot Loader Script
Tutorial 05 Internet Coding maxbootscript.txt';
Tutorial 06 Network Coding color:= clteal;
Tutorial 07 Game Graphics Coding IntfNavigator1Click(self);
Tutorial 08 Operating System Coding
tbtnCompile.caption:= 'Compile!';
Tutorial 09 Database Coding
tbtnUsecase.caption:= 'UML UC';
Tutorial 10 Statistic Coding
maxform1.ShellStyle1Click(self);
Tutorial 11 Forms Coding
memo2.font.size:= 16;
Tutorial 12 SQL DB Coding
Tutorial 13 Crypto Coding Info1Click(self);
Tutorial 14 Parallel Coding end;
Tutorial 15 Serial RS232 Coding Writeln('BOOTSCRIPT ' +BOOTSCRIPT+ ' loaded')
Tutorial 16 Event Driven Coding
When you want to see a complete copy of that file, look at:
Tutorial 17 Web Server Coding
Tutorial 18 Arduino System Coding
07.02.2013 10'544 maxbootscript_.txt
Tutorial 18_3 Arduino RGB LED Coding
Tutorial 19 WinCOM /Arduino Coding
Tutorial 20 Regular Expressions RegEx When you delete the underscore in the filename to
Tutorial 21 Android Coding (coming 2014) maxbootscript.txt the system performs next time when you
Tutorial 22 Services Coding load maXbox and presents you with a different view. This
Tutorial 23 Real Time Systems boot script results in the picture below for example. The
Tutorial 24 Clean Code
trick of renaming the file has a simple explanation. The ini-
Tutorial 25 maXbox Configuration
Tutorial 26 Socket Programming with TCP file default to load the boot script is YES so it can be easier
Tutorial 27 XML & TreeView (coming 2014) to rename the file instead of change the ini-file to set to YES,
Tutorial 28 Closures (coming 2014) cause of missing permissions, testing or so:
Tutorial 29 UML Scripting (coming 2014) BOOTSCRIPT=Y
Tutorial 30 Web of Things (coming 2014) 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.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 79
Coding with maXbox (Continuation 3) maXbox
maXbox configuration
<<extended>>

<<extended>>
Start maXbox

Load Boot
Script <<include>> bds_delphi.dci
(templates)
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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Coding with maXbox (Continuation 4) maXbox
• An advantage of using S_ShellExecute or
In this section we deal with multiple instances of maXbox
and its creation. You can create multiple instances of the ShellExecute3 is the no wait condition.
same app to execute multitask code, just type <F4>. For
example, you can launch a new instance within a script of In this article we show 4 steps to build a configuration:
the box in response to some user action, allowing each 1. First building block is the use of a
script to perform the expected response. boot script maxbootscript.txt.
2. Second we jump to the template file bds_delphi.dci
ExecuteShell(ExePath+'maxbox3.exe','"' 3. Third the Ini-file maxboxdef.ini environment
+ExePath+'examples\'+script+'"'); and code settings.
4. Forth we set an include file in our script, like
S_ShellExecute(ExePath+'maxbox3.exe', pas_includebox.inc to call external functions of
ExePath+'examples\'+script,secmdopen); a unit, it's very powerful, but tough to built.

There's no good way to launch one application (maXbox) Ok we have finished the boot script what about a template
with multiple scripts in it. file. The code template file bds_delphi.dci
Maybe with OLE Automation in that sense you open office stands for code completion templates.
programs (word, excel) or other external shell objects. In the Code Editor, type an object, class, structure or a
CreateOleObject creates a single uninitialized object of pattern name followed by <Ctrl J> to display the object.
the class specified by the ClassName parameter. But it's not a full code completion where you get after the
dot (.) a list of types, properties, methods, and events, if
ClassName specifies the string representation of the you are using the Delphi or C# languages.
Class ID (CLSID). It's more a template copy of your building blocks.
CreateOleObject is used to create an object of a In the menu /Debug/Code Completion List you get
specified type when the CLSID is known and when the the defaults, here is an extract:
object is on a local or in-proc server.
Only the objects that are not part of an aggregate are [cases | case statement |
Borland.EditOptions.Pascal]
created using CreateOleObject.
case | of
• Try the example of OLE Objects
: ;
318_excel_export3.TXT and the tutorial 19.
: ;
end;
An external script or a least a second one could also be a [try | try except |
test case to compare the behaviour. Borland.EditOptions.Pascal]
try |
Each test case and test project is reusable and rerun able, except
and can be automated through the use of shell scripts or end;
console commands.

Figure 4:

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 81
Coding with maXbox (Continuation 5) maXbox

Figure 5: mX bootscript GUI Loaded


I prefer\; myform<Ctrl J> which represents or copies a As you know the memo2 is the output window as the
form builder in your editor. shell, so the keypress is related to memo2; by the way
• Useless to say you can add your own templates memo1 is the editor itself!
to the file. Many of the Code Editor features are also Another function KeyPressed(VK: Integer): Boolean;
available when editing HTML and CSS files. Code returns True, if key VK has been pressed.
Completion (CTRL+J) and syntax highlighting are
available for HTML, XML and CSS files. Let's jump to the Ini-file. Many applications use ini
• Most of the Open Tools API declarations reside also files to store configuration information. Using ini files has
in that file bds_delphi.dci.(at the bottom) the advantage that they can be used in cross-platform
Note: Call the methods with maxForm1., e.g. applications and they are easy to read and edit.
:maxForm1.ShellStyle1Click(self); • The ini file format is still popular; many configuration
files (such as Desktop or Persistence settings file) are in
• A standard approach to break a running loop in a
this format.
script or configuration is the well known KeyPress or
This format is especially useful in cross-platform
IsKeyPressed function you can use and check: applications, where you can't always count on a system
procedure LoopTest; Registry for storing configuration information.
begin
I never was a friend of the Registry so you can also start
Randomize;
REPEAT maXbox from a stick.
Writeln(intToStr(Random(256*256))); In maXbox code, TIniFile is the game of advantage.
UNTIL isKeyPressed; //on memo2 output When you instantiate the TIniFile or TMemIniFile object,
if isKeypressed then writeln(Key has been you pass the name of the ini file as a parameter to the
pressed!'); constructor. If the file does not exist, it is automatically
end; created.
82 COMPONENTS
DEVELOPERS 4 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 4 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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Coding with maXbox (Continuation 8) maXbox
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 4 85
Coding with maXbox (Continuation 9) maXbox

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 'SetErrCode@dmath.dll'; { Sets error code }

function MyMessageBeep(para: integer): byte;


10: external 'MessageBeep@user32.dll 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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Coding with maXbox (Continuation 10) maXbox

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 : Demonstrates the functions of macros in header
* Date : 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:
//----------------------------------------------------------------------------
10181: //**************mX4 Macro Tags **************************
10182: //----------------------------------------------------------------------10183:
10184: #name, i#date, i#host, i#path, i#file, i#head, i#sign, i#teach
10185:
10186: SearchAndCopy(memo1.lines, '#name', getUserNameWin, 11);
10187: SearchAndCopy(memo1.lines, '#date', datetimetoStr(now), 11);
10188: SearchAndCopy(memo1.lines, '#host', getComputernameWin, 11);
10189: SearchAndCopy(memo1.lines, '#path', fpath, 11);
10190: SearchAndCopy(memo1.lines, '#file', fname, 11);
10191: SearchAndCopy(memo1.lines, '#locs', intToStr(getCodeEnd), 11);
10192: SearchAndCopy(memo1.lines, '#perf', perftime, 11);
10193: SearchAndCopy(memo1.lines, '#head',Format('%s: %s: %s %s ',
10194: [getUserNameWin, getComputernameWin, datetimetoStr(now), Act_Filename]),11);
10195: 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 4 87
Coding with maXbox (Continuation 11) maXbox
maXbox Macro Context

<<extended>>
Start maXbox

Actor HEX <<extended>> <<include>>


bds_delphi.dci
in the BOX
(templates)
Load Boot
Script

Parse Ini
File Macro
Macro Actor
Macro

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)
maxboxdef.ini ’#locs’, intToStr(getCodeEnd), 11)
’#perf’,perftime, internal func 11)
’#head’, Format(’%S: %S: %S: %S:)

Compile Script
Some DLL or Lib
PRE PROCESSING include file

<<include>>

Run App

Byte Code

• 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.

88 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Coding with maXbox (Continuation 12) maXbox
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; //---> reflection to box!
//memo1.Gutter.ShowLineNumbers:= true; //---> 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 4 89
Coding with maXbox (Continuation 13) maXbox

90 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Coding with maXbox (Continuation 14) maXbox
Feedback: max@kleiner.com About the Author:
Max Kleiner
Links of maXbox and DelphiWebStart:
The professional environment of Max Kleiner is in the
range OOP, UML and system design, including as a
http://www.softwareschule.ch/maxbox.htm trainer, developer and publisher. His focus is on IT
http://sourceforge.net/projects/maxbox security, navigation and simulators that need the power
of the Delphi compiler. As a teacher in a Fach Hochschule
http://sourceforge.net/apps/mediawiki/maxbox/
(University of Applied Sciences) and on behalf of a firm
http://sourceforge.net/projects/delphiwebstart 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.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


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

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


However, I continued to use Turbo Pascal at home and got
When I first met Ray I was impressed
because of his age. He seems so into object-oriented programming with Turbo Pascal 5.
young, knowing that he is one of the For the next few years, I bounced around a few jobs
very early adoptee’s of Delphi. trying to find a good fit for me. During this time, I also
Through the time that I spent with started reading a magazine called PC Techniques.
him I learned his secret: he is I really liked this magazine because it covered a lot of
enormously precise, always on the topics and it had articles on Turbo Pascal, which were
job, concentrated and very
pretty hard to find.
disciplined. Given his enormous
knowledge he surely is a man to learn At this time, most my Pascal work was isolated to my
from... own time and not involved with my job. I decided to write
Editor:
an article for the magazine and submitted it. My first
I would like to ask you about how you got started with Pascal?
published article was on creating a World Coordinate
You were one of the very early adapters I suppose?
System in Turbo Pascal. That was in 1993...
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. I submitted a couple more articles and then Jeff
Duntemann, publisher of PC Techniques, contacted me to
Editor:
see if I was interested in writing a column for the
How did you start, you met people from Borland by that time?
magazine. I thought this was really cool, but not as cool as
Ray Konopka: the name that Jeff came up with for the column.
Actually, meeting people from Borland came much later. The column was called Blazing Pascal.
During my time at University, I continued to use Turbo How cool is that!
Pascal and really fell in love with the language and the
product. I even bought a copy for myself so I could work It was through the magazine and column that I first got
with it at home. During my 3rd and 4th years at university involved in what was to become Delphi. I was very
I had the opportunity to work at Argonne National fortunate to work with very early versions
Laborator in the Physics department. of the product. I was even able to write articles about
I used Turbo Pascal to write software that Delphi in 1994, about 6 months before Delphi even
communicated with various pieces of scientific equipment shipped.
to capture data for various experiments. It was during this My first column that covered Delphi was published in
time that I switched from TurboPascal 3 to TurboPascal 4. September 1994. Shortly after that, Borland setup a Delphi
It was my first real world experience programming 95 preview day at their Scotts Valley campus.
outside of the classroom.... I was not going to miss that...
I graduated from University in 1989, where I majored It was at that preview event that I first met many of
in Computer Science and Mathematics, with a minor in the people behind Delphi. It was a great experience for me,
Physics. The following year I attended Northwestern especially when I had a chance to meet Anders Hejlsberg.
University on a fellowship and 9 months later complete Imagine my surprise when after I introduced myself to
my Masters degree in Computer Science. Anders, he responds, "Oh, yes, I know. I've been reading
I continued to use Turbo Pascal, and some other languages your articles." It was an experience I'll never forget.
throughout this time. After graduating from
Northwestern, I went to work for AT&T Bell Labs.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE 93


Interview with Ray Konopka (continuation 1)
Editor: I've been working with it since its inclusion into Delphi,
Jeff Duntermaan was also an author for Delphi books? and it is very flexible and has tremendous potential.
But it is also very different from the VCL.
Ray Konopka: When FMX was first released for desktop
Yes, he was. In fact, he wrote the Object-Oriented development, it was very hard to justify using it unless you
Programming Guide that shipped with Turbo Pascal. really needed the enhanced display functionality it offered,
I had talked with and emailed Jeff for quite a while before or if you really needed to go to Mac OSX.
we had a chance to meet. We finally met at the Borland
Conference in San Diego in 1995. Editor:
So whenn do you start writing articles about this?
Editor: There is a tremendous need for this...
You also wrote a very famous book. Could you tell something
about that? Will you try to do so again? Ray Konopka:
I sometimes regert that it isn’t available anymore... I know, I know
I've been presenting sessions on FM for quite some time,
Ray Konopka: so some material is out there, but those are not the same as
Very early on in my experience with Delphi, an article. One thing that I've been working on is a
I saw the potential that the VCL provided. redesign of the Raize Software website.
It was Jeff that suggested I write a book about Delphi, My plan is to have a formal blog and other resources
even before Delphi shipped, and I said we should do it on where I can provide some of this information to
how to build custom components. developers.
It was also at this time, that I decided to start working for I believe this approach will be easier for me to manage
myself as a Delphi consultant. given my time constraints and also provided developers
I started Raize Software Solutions (now called Raize with additional resources for Delphi development and also
Software) on February 7, 1995, a week before Delphi 1 provide developers...
officially launched. I consulted during the day and worked
on the book at nights. Editor:
One of my primary goals for the book was to come up with Maybe you could help explaining what firemomkey really is
real-world practical examples. (I really do not like contrived and what it means.
examples with nondescript names like Foo and Bar.)...
The complete title of the book was called Developing Ray Konopka:
Custom Delphi Components. Actually, that is something that I focus on in the first part
of my Creating Custom FM Controls sessions. A developer
Editor: has to have an understanding of what the framework
I have the first and the second edition... 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.
Developing Custom Delphi 3 A lot of people have vague
But it is also very different from the VCL.
Components came out in ideas about that...
1997. You can still find a
copy or two on eBay every once and a while. It is also Ray Konopka:
possible to get a PDF Edition of the second book. What is I understand. Although I do like to write things that are
really cool is that all of the material covered in the book more applied rather than simply provide a high level
still applies to developing Delphi components today. explanation.
Certainly there are more capabilities and features in the But, I do understand your point.
VCL and FMX, but the principles covered in the book still
apply. Of course, this begs the question of writing another Editor:
book. I am always trying to attract new people towards Pascal. Do you
have any ideas about that?
Editor:
Maybe you could do something about Firemonkey? Ray Konopka:
The biggest hurdle is the incorrect perception that Pascal is
Ray Konopka: an old language. Unfortunately, that is a hard hurdle to
The biggest factor in writing another book is time. Back get over because even if you look at 3 of the most popular
when I wrote the first two books, Raize Software was just implementations of Pascal:
starting out, we didn't even have a product out when the Delphi, Lazarus, FreePascal, only one is easily connected to
first book was written. Now, things are just so much Pascal. So someone may have heard about Delphi, but may
busier. What I have tried to do in place of writing a new not know its relationship to Pascal.
book is to cover the new topics in presentations and However, to make real in-roads, developers have to
articles. FireMonkey is very interesting. be able to see that using a particular tool will allow them to
become successful.

94 Nr 5 / 2013 BLAISE PASCAL MAGAZINE


Interview with Ray Konopka (continuation 2)
Ray Konopka: Editor:
What I mean by this is that the days of using a single That is a real problem. I would love to hear an answer from our
language are long gone. readers. I was thinking of starters and doing the VCL.
Developers will select the tool that allows them to deliver a Because we are starting this teaching project for schools:
particular solution. Just look at how popular Objective C PEP (Pascal Ecducation Program).
has become. The problem with FM is, that its still evolving...
It's an odd little language, and I much prefer Delphi and So you opt for the VCL?
Pascal, but if you wanted to target iPhones and iPads,
you needed to learn Objective C. Ray Konopka:
Now, that Delphi XE4 and XE5 are out with support for If you already have the VCL ones, done, then yes,
mobile development, that may change. If developers see I would go with that and get some feedback.
value in a tool, they will learn the language.
Editor:
Editor: Problem is there are about 550 to be done...
We need - easy to do exaples. Back to youre explainations: you had the chance to do buisnes
I think espacially for younger people. with the Walt Disney Company. How did that start?
And what is your job there?
Ray Konopka: Advisor or Developer or Creator?
Certainly, but again, they can't be contrived.
On the other hand, you cannot make them too complicated. Ray Konopka:
This is the problem that Apple has with their I started consulting with Walt Disney Parks and Resorts
documentation. There are lots of examples, but they are back in 2002.
overly complex and it is very difficult to extract the one Initially, I was brought in for Delphi training, but shortly
piece of information you need. after that, I became involved in the development of the
The other problem you have, and this applies to the FM FASTPASS system, which is used in all of the Disney Parks
Framework, is that the examples need to change as the world-wide.
framework changes. My work on FASTPASS led to other development
Think about the Generics presentation that I presented at projects, such as the “Free On Your Birthday” promotion
Be-Delphi this year. that ran at Disneyland and Walt Disney World in 2009,
I could have come up with contrived names like TTest or and the “Give a Day, Get a Disney Day” promotion
TFoo but that gives no context for the attendee. that ran in 2010. The success of these projects led to even
On the other hand, I did not want to create an overly more work, but as a consultant there were restrictions on
complex example that overloads the attendee with non- just how much I could take on.
relevant information. So, in October of 2011, I switched from consultant to
Finding that balance is key to a good presentation or part-time cast member, which is what employees of The
article.And no more DB Grids on the side of a 3D rotating Walt Disney Company are called.
cube. And yes, I even have a Disneyland name badge.
To be precise I am a Sr Application Developer
Editor: and I continue to work on the architecture and development
I loved your presentation, of the FASTPASS system as well as several other projects,
because of its simpliticity that you showd. including many new mobile projects that are
I told you about our Help Component project. used by Operations Cast Members in the parks.
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. Editor:
I was asked to do this for Firemonky at first. About your component book: you think its still a good book for
We actually already started on the VCL. starters in the field?
What would you suggest to start with? I was wondering if the book might still be worth to be reprinted?

Ray Konopka: Ray Konopka:


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

Nr 5 / 2013 BLAISE PASCAL MAGAZINE 95


Interview with Ray Konopka (continuation 3)
Editor:
How did your own company start?
Free Tools
http://www.raize.com/DevTools/FreeTools.asp
Raize Components came about as a direct result from
feedback I received from the first book. Shortly after it was BDS/Delphi Icons
published, I started to receive quite a few emails from Raize BDS Icons is an icon library that contains a
developers asking if it was okay for them to use the set of icons for the various file types used by the
components that I presented in the books in their own BDS and earlier versions of Delphi. The icons in this
applications. You will recall that I specifically wanted to new library are more consistent in their design and
create real-world practical examples in the book. From the show a direct association with the BDS/Delphi.
responses I got, I met my goal.
With this initial demand in place, I used the book's IDE Palette Menu
component exapmles as a foundation, extended those Are you tired of scrolling through the pages of the
components to provide even more functionality and added Borland Developer Studio, Delphi, or C++Builder
24 new components it. This became Raize Components for component palettes looking for the right page?
Delphi version 1.0. If so, then download and try out the Raize Palette
The product has grown a lot since that initial release. Menu expert, which when installed adds a new
menu to the selected IDE that provides instant
There are now more than 125 controls in the
access to any page on the component palette.
library and it is loaded with features and capabilities.
CodeSite also has its roots in some material that was
Raize Font
presented in the book, and was an internal tool for quite a
The Raize Font is a clean, crisp, fixed-pitched sans
while before it became a product. CodeSite was developed
serif screen font that is much easier to read than
to help debug Raize Components.
the fixed pitched fonts that come with Windows.
Back in the early days of Delphi, custom controls were
Ideally suited for programming, scripting, html
added to the IDE by rebuilding the component library-
writing, etc., the Raize Font can be used in any IDE
packages were not introduced until Delphi 3.
or text editor.
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). CodeSite is available in two editions: CodeSite Express
This is how CodeSite started. and CodeSite Studio. The Express edition includes core
But it wasn't until I showed CodeSite to Mark Miller logging functionality but does not include the full range of
(then of Eagle Software, now with Developer Express) and he functionality included in CodeSite Studio.
used to to track down issues he was having with his
• CodeSite Studio includes these additional features
CodeRush product that it became clear that CCodeSite
and capabilities:
clearly had value as a product of its own. • Using TraceMethod to record both an EnterMethod
CodeSite too has evolved since its original version. and ExitMethod message with a single statement
It has grown from just a debugging tool to a full featured • Recording time durations using a built-in high-
application logging system. And starting with RAD Studio precision timer
XE, CodeSite Express has been included "in the box". • Remote Destinations (i.e. transporting CodeSite
CodeSite Express is a fully functional version of CodeSite messages to a remote machine)
• Have logging classes directly connect to a remote
that provides basic logging functionality, while the
CodeSite Dispatcher
CodeSite Studio edition provides more powerful features • Special event in logging classes to hook into logging
and services. A list of the differences between the two process (VCL: OnSendMsg; .NET: Sending)
editions can be found the colum alongside and • Sending Color, Point, Size, Rectangle structures
http://www.raize.com/DevTools/CodeSite/ • Sending Bitmaps, Icons, Images, Screen Shots
Editions.asp • Sending Collections
• Sending Controls, Parents, and Window Handles
Editor (WinForms)
Yes I know this is really a great tool. • Sending Custom Data and the ICodeSiteCustomData
interface
I know developers that would want re-event it if they didnt
• Sending Text Files, Files, and Streams
have this tool. • SendIf methods
Thank you so much Ray for your explanations and help • Sending System Info, Memory Status,
and Stack Trace
With respect to Raize Software, there is a lot of • Sending Xml Data and Xml Files
• ExitMethodCollapse method
information on our website http://www.raize.com.
• Event Log Methods: LogError, LogEvent, LogWarning
As noted earlier, I started the company • Writing values to the CodeSite Scratch Pad
back in 1995 with the specific intent of providing • .NET Configuration File Support
tools and services to the Delphi community. • 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 Rotation takes place between a source and a destination
for us because we needed it for our Leap bitmap. In coarse mode, the source bitmap is scanned pixel
Motion Development. It was so much by pixel and projected on the destination bitmap.
interesting that we decided to publish the Therefore, not every pixel of the destination bitmap may be
basic Delphi code and handling... covered.
In medium mode, the pixels of the destination bitmap are
This article describes a Delphi project for scanned and their value is loaded from the source bitmap.
bitmap rotation. It does it in three different This insures that all pixels of the destination bitmap are
qualities: Simple, Good and Excellent covered.
Result for the Bitmap. In fine mode, the scanning is the same as in medium mode,
but each pixel is divided in 9 equal parts.
There are 3 units:
- unit1: exerciser to test the rotation procedures Parts may cover different pixels in the source map, the
- rotation_unit : procedures for bitmap rotation proportions are summed for the final color of the
- clock_unit : time measurement procedures destination pixel.

Exerciser Programming
The form has buttons for loading and saving bitmaps. The programmer has to create both the source and the
Also 3 modes of rotation are selectable destination bitmap. Before a rotation may take place, the
rotation_unit has to be informed about the names of the
- coarse: fast but less accurate bitmaps. This is done by a call to
- 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 Figure 3: Rotation theorie

98 COMPONENTS
DEVELOPERS 4 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 Figure 6: Rotation calculation
calculated. in the above figure, (x',y') is the corresponding source
Then these positions are used to calculate the map position which provides the color for (x,y) on the
corresponding pixels in the source and destination maps. destination map.
Let's observe a rotation of 30 degrees (clockwise) using the Rotation of the horizontal vector results in OD.
above bitmaps 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

For the other quadrants, the signs of xtx, ytx, xty, yty
Figure 5: Rotation of 30 degrees may change.
Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS
DEVELOPERS 4 99
Programming Bitmap Rotation (Continuation 2)
Addressing the pixels in the bitmaps Also an adjustment must be made to position the center of
Of course, Delphi property pixels[x,y] may be used to a bitmap over (0,0), the coordinate system origin.
change pixelvalues.
However, this way is incredibly slow. scx is the center x pixel of the source map
Also, the scanline[y] property, which returns the memory scy is the center y pixel of the source map
pointer of the first pixel in row y, is very slow. dcxy is the x and y center of the destination map
Better is to calculate the pointer to a certain pixel. Now, pixel (x,y) of the source map is addressed by
Because the bitmaps are in 32 bit format we first define
trunctx := trunc(tx); //tx is floating point format
type PDW = ^dword; truncty := trunc(ty); //...
ttx := scx + trunctx; //add center
PDW is a pointer to a 32 bit unsigned variable. tty := scy + truncty;
PS := PSBM - tty*Slinestep + (ttx shl 2); //pointer to
Address calculations are done with variables in source pixel
dword ( = cardinal) format. pix := PDW(PS)^;
First, the pointer to pixel [0,0] must be obtained.
For this, scanline[0] is used by the setmaps procedure. pix receives the color value of the source map pixel.
scanline[1] returns the pointer to pixel [0,1]. For quadrant 1, the pointer to the destination map is
scanline[1] - scanline[0] gives the pointer difference
between two rows. Ybase1 := PDBM - (dcxy + y)*Dlinestep;
PD := Ybase1 + ((dcxy+x) shl 2);
(note: row 1 pointer is smaller then the row 0 pointer). PDW(PD)^ := pix;
In the rotation procedure
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 4 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 4 101
Introduction to MVVM and the
Caliburn Micro for Delphi framework By Jeroen Pluimers
starter expert Delph XE5 It all comes down to Separation of Concerns:
Cutting business logic away from your UI
Introduction to MVVM Swapping your UI (VCL, FireMonkey, Mobile, Web, …)
The Model View ViewModel (MVVM) is an architectural
Making it easier to test user interaction without a View
pattern used in software engineering that originated from
layer
Microsoft as a specialization of the Presentation Model design
By pushing tests from a View to automated tests, you move
pattern introduced by Martin Fowler
them from the time consuming and labour intensive
MVVM in Delphi using the
acceptance test or end-to-end test phases into the automated
„Caliburn Micro for Delphi framework“.
unit test phase. This way it becomes much easier and
A bit of background on
cheaper to test the user interaction logic from the View
Model, View and View Model
MVVM is one of the ways to help split parts of your
Model. (The image was made thanks to Jonas Bandi)
application into three layers. Doing so makes it easier to

BUSINESS
FACING
"organize the mess", allows you to unit test not only the ACCEPTANCE
TESTS
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

TECHNOLOGY
with MVVM from scratch. You can apply the ideas from

FACING
MVVM into existing applications as well and evolve them UNIT TESTS INTEGRATION END TO END
TESTS TESTS
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. Let’s first focus on the
introduction though. GRANUARITY / SCOPE

My encounter with MVVM was in the .NET and later in Binding the 3rd object
the Delphi world with a self written framework similar to The question is: how do you bind View, Model and the 3rd
what Malcolm Grooves presented at CodeRage 7. object? That highly depends on what kind (or even
Back then I wasn't too impressed with what I achieved. flavour) of 3rd object architecture you use: MVC, MVP,
Even though I was used to naming conventions, most of MVVM, et cetera. A few of them are here:
the things were still manual labour. Too much tedious
manual labour.
Later I bumped into Caliburn for .NET, but at that VIEW MODEL
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 1 PRESENTER

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 VIEW MODEL
applications.
Before explaining more about MVVM, let’s take a step
back and look at the 3rd object.
VIEW MODEL
The 3rd object
(3rd object images thanks to Paul Stovell's 3rd object article)
2
All too often, applications - Delphi or otherwise -
are "structured" like these:
VIEW MODEL

VIEW = MODEL
VIEW MODEL
or, if you are lucky, like:
3
VIEW MODEL VIEW MODEL

Adding a 3rd object makes sense to make things


less monolithic and more maintainable: VIEW MODEL
4
VIEW MODEL
VIEW MODEL

THIRD OBJECT
VIEW MODEL
5
102 COMPONENTS
DEVELOPERS 4 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, MVVM: the View Model
and the View Model to the Model. Updates come When starting with MVVM, the term View Model wasn't making
from the Model to the View Model and from the View Model to much sense to me at first. I was missing the steering layer from the
the View. There is no binding between the View and Model at all. controller or presenter in MVC and MVP. And I'm not the only one,
This setup allows you to change any of the 3 layers and be very as you can see from this MVVM Wikipedia excerpt:
flexible.
The term "View model" is a major cause of confusion in
The traditional 3rd layer in Delphi is to separate user interface into
understanding the pattern when compared to the more
forms, business logic into data modules and
widely implemented MVC or MVP patterns. The role of the
your data into some form of storage. This has worked for 15 years, controller or presenter of the other patterns has been
but still needs a lot of plumbing and it is hard substituted with the framework binder (e.g., XAML) and
to change the UI as the binding is usually tied to the VCL view model as mediator and/or converter of the model to
platform. the binder.

with these class In MVVM the steering role is taken over by the framework,
and sequence and the View Model focuses a lot more on (testable!) logic
diagrams: 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 4 103
Steps in MindScape AppView Step - By - Step (Introduction 2)
Back in 2004, Martin Fowler published his famous Presentation Why this MVVM article?
Model article presenting a simple UI: This is very much like the MVVM combines refactoring, patterns, conventions and much
MVVM, with the exception that in MVVM the framework will take more into a way of developing software that is maintanable and
care of all the binding. (Martin Fowler testable in a way that makes the best use of modern language
kindly gave permission to use his pictures features without the need of the developer to master each and all
and the below book cover and portrait). of those features in great detail.
There are two reasons I started advocating using MVVM with
Refactoring Delphi. The primary one is that there are exciting times ahead for
Back in 1999 - in the middle of the XP era - Delphi developers. With added functionality like generics,
Martin Fowler wrote a great book titled attributes and a truckload of RTTI, new (and hopefully old!)
Refactoring: Improving the Design of platforms, lots of new possibilities - including MVVM - are
Existing Code. It influenced the way we already there, or on the horizon.
write software until today even in Delphi. It is so current that in Another reason is that these open source projects can use your
2012 it appeared in a special Kindle edition. The book has a help. Simple help like just using it and giving feedback can
catalog of simple recipes that describe how to improve certain bad tremendously move them forward. So here is my favourite
coding patterns into better code. Patterns like Extract Method or shortlist of projects you could contribute to:
Encapsulate Field are essential to how we develop code and give
us a set of conventions on how to do just that. • Spring4D
MVVM is all about patterns and conventions, so let’s look at • DSharp (including the Caliburn feature branch on which
another important book, but now at the start of the Agile era. 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.
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
Patterns
Spring4D work (both DSharp and Caliburn depend on Spring4D).
In 2004, the - instantly famous - Gang of Four (abbreviated to
Most of the Caliburn specific work has been done by Marko
GoF) published the book Design Patterns: Elements of Reusable
Vončina.
Object-Oriented Software. The book is another catalog, this time
Internally, all these frameworks heavily depend on these Delphi
on patterns, with a common vocabulary about recipes for
features:
developing new code. The Gang of Four consists of Erich Gamma, interfaces - attributes - generics - RTTI
Richard Helm, Ralph Johnson and John Vlissides. They expose many classes and interfaces (attributes are based on
Martin Fowler on the GoF book in relation to the 3rd object: classes). Applications based on Caliburn will use interfaces and
„In my view the Gang of Four is the best book ever written on attributes a lot.
object-oriented design - possibly of any style of design“. This book Make sure you read the Coding in Delphi book by Nick Hodges if
has been enormously influential. you feel unfamiliar with them. Actually: just read the book
The 3rd object is “just” a (relatively) new way of using patterns. anyway. I learned a lot while reading it!

104 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Steps in MindScape AppView Step - By - Step (Continuation 1)
The goal is to write an application using MVVM VCL project
that: Name the form AppView, the form unit AppViewForm and clean
is based on patterns and conventions by using RTTI up the uses list, private and public sections:
- is driven by the View Model (not the View)
- has the user interaction logic in the Model View unit AppViewForm;
- can have the View Model interface
- be unit tested in an early stage uses Forms;
- has the data storage in a Model type
- can have the Model TAppView = class(TForm)
- be unit tested in an early stage as well runs in Delphi end;
XE or higher builds warning and hint free. var
AppView: TAppView;
During the steps you will see some failures that will be implementation
fixed in subsequent steps. That is deliberate: it is nice
having working demos but in practice you will bump into {$R *.dfm}
things, so it is good to see the things you might bump
into upfront and how easy it is to solve them. end.
The projects are based on the Mindscape HQ
MVVM demos that use the Caliburn Micro for .NET
Name the application MindScape_AppViewVCL:
framework. Though Delphi is different than .NET,
the ideas in the framework are kept the same as much
program MindScape_AppViewVCL_Step00;
as possible. The demos will teach you where things differ
most. uses Forms,
So the Delphi application will eventually look similar to AppViewForm in 'AppViewForm.pas' {AppView};
this Caliburn Micro for .NET based WPF application: {$R *.res}
it can add, double and increment, all from a View Model begin
automagically updating the View. In fact the Delphi ReportMemoryLeaksOnShutdown := True;
application will also add a Model that is persistent in an Application.Initialize();
INI file! 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
set them apart from properties or fields. begin
Most of the times that is optional, but at times it is required, Application.Initialize();
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
might scare you at first. The scaring is not on purpose, Step 01: adding the interfaces and View Model
but the fine grained steps are: for me it is important to show you Add two units to the projects:
what it is involved, and what you might bump into. In practice AppInterfaces with the interface definitions and AppViewModel
these steps will take only a short amount of time. that contains the View Model.
For the project names, you can optionally include a _Step## prefix Later we will will add another unit with a Model as well.
where ## has the step number. After that make the modifications to the units and project file as
I've done that in the examples below as that makes it easier for shown in these sections.
you to find back the individual steps in the
The AppInterfaces unit
DSharp code repository.
For now, there will be one interface in it that defines the Model
Lets get on with the steps...
View. Note that you need a GUID for this
interface, as that will enable the compiler to generate RTTI
Step 00: an empty VCL and DUnit application
Create a new project group that contains an empty VCL project (RunTimeTypeInformation)
with one empty form, and a DUnit test project
that has no tests yet.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 105
Steps in MindScape AppView Step - By - Step (Continuation 2)
unit AppInterfaces; The first lines will initialize the logging.
interface
type
The last line will use the TAppViewModel to start the
IAppViewModel = interface application with. Later we will switch to the
['{38C5B8ED-8269-463D-847D-09F6A7B99584}'] IAppViewModel for that.
end; This is the main program you are after:
implementation program MindScape_AppViewVCL_Step01;
end. uses
The AppViewModel unit {$IFDEF CodeSite}
The AppViewModel unit contains the View Model for our DSharp.Logging.CodeSite,
{$ENDIF CodeSite}
application. It will drive the View. DSharp.PresentationModel.VCLApplication,
For now the View Model will be empty: Forms,
it's just the scaffolding for the rest of the steps. AppInterfaces in 'AppInterfaces.pas',
Since the TAppViewModel depends on the AppViewForm in 'AppViewForm.pas' {AppView},
IAppViewModel interface, the AppViewModel unit uses AppViewModel in 'AppViewModel.pas';
{$R *.res}
the AppInterfaces unit. begin
TAppViewModel descends from TScreen which is a ReportMemoryLeaksOnShutdown := True;
base class that can be bound to a container that can Application.Initialize();
be visible (usually a TForm or TFrame). Application.MainFormOnTaskbar := True;
{$IFDEF DEBUG}
The final step is in the initialization section of the unit: Application.WithDebugLogger();
it refers to the ClassName of the TAppViewModel. {$ENDIF DEBUG}
This effectively instructs the compiler and linker to include {$IFDEF CodeSite}
the class and the RTTI in the executable. Application.WithLogger<TCodeSiteLog>;
{$ENDIF CodeSite}
This allows Caliburn to use the RTTI and bind the View Application.Start<TAppViewModel>();
Model and View together. end.
unit AppViewModel;
interface Add unit AppViewModelTestCase to the test project
uses Like the View and View Model, the initial
AppInterfaces,
TAppViewModelTestCase is also empty.
DSharp.PresentationModel;
type The next steps will add tests each time functionality is
TAppViewModel = class(TScreen, IAppViewModel) added to the View Model.
end; For the test case to compile, also add the AppViewModel
implementation unit to the DUnit test project.
initialization
unit AppViewModelTestCase;
TAppViewModel.ClassName; interface
end. uses
No modifications for the form. TestFramework,
The really cool thing is that the form does not require any AppInterfaces,
AppViewModel;
modification. It does not need to implement the type
IAppViewModel interface (that is done by the TAppViewModelTestCase = class(TTestCase)
TAppViewModel class). strict private
The Caliburn framework will take care of binding the FAppViewModel: IAppViewModel;
strict protected
View Model and View for you. The project file also needs a property AppViewModel: IAppViewModel
few modifications: Insert these at the top of the uses list: read FAppViewModel;
{$IFDEF CodeSite} public
DSharp.Logging.CodeSite, procedure SetUp; override;
procedure TearDown; override;
{$ENDIF CodeSite}
end;
DSharp.PresentationModel.VCLApplication,
This enables the CodeSite logging tool that is included with implementation
procedure TAppViewModelTestCase.SetUp;
most recent Delphi versions.
begin
DSharp has support of other logging destinations like FAppViewModel := TAppViewModel.Create();
SmartInspect, Console, or OutputDebugString as well. end;
It will also extend the TApplication using class helpers so procedure TAppViewModelTestCase.TearDown;
the Caliburn framework can initialize the repository of begin
FAppViewModel := nil;
View Models and Views, and then start the application. end;
Finally replace initialization
Application.CreateForm(TAppView, AppView); RegisterTest(TAppViewModelTestCase.Suite);
Application.Run(); end.
with
{$IFDEF DEBUG}
Application.WithDebugLogger();
{$ENDIF DEBUG}
{$IFDEF CodeSite}
Application.WithLogger<TCodeSiteLog>;
{$ENDIF CodeSite}
Application.Start<TAppViewModel>();

106 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Steps in MindScape AppView Step - By - Step (Continuation 3)
Step 2: It almost is, and indeed it is the first step. And you get
making Caliburn recognize your form another error that too is part of the learning experience:
as a valid View Exception EResolveException
No component was registered for the service type:
IAppViewModel

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 (Sometimes you will get a series of other exceptions, we will
TAppViewModel. We know it is TAppView (it is one of investigate that to see where it needs fixing).
the Caliburn conventions), but Caliburn doesn't know about The cause of this exception is that Caliburn tried to find the
TAppView as it needs the RTTI for it.
class implementing IAppViewModel but
So, like we did with TAppViewModel, we need to
couldn't get to TAppViewModel.
make sure that the RTTI for TAppView is registered as
RTTI is not the only part in the foundation of Caliburn:
well. So we need to do the TAppView.ClassName trick.
there is also a composition framework inside
But that is not all: we also need to make sure that
Caliburn can add some extra functionality. DSharp that - not surprisingly - is mimicked after a .NET
For that, add the unit DSharp.Bindings.VCLControls framework as well. You need to decorate the
to the uses list. It contains (among a lot of other things) a IAppViewModel with the InheritedExport or
new TForm implementation that descends from InheritedExportAttribute attribute which works
Forms.TForm (this is called an interceptor class, something virtually identical to the InheritedExportAttribute
presented for instance by Delphi.about.com in 2009). in .NET.
The TForm interceptor adds extra behaviour to When booting your application, Caliburn composes a
Forms.TForm like notifications. graph of dependent objects (Models, View Models, Views)
that build your application.
In fact DSharp.Bindings.VCLControls contains
The InheritedExport attribute is in the
interceptor classes for these Delphi units:
ComCtrls DSharp.PresentationModel unit so that's why your
CommCtrl IAppViewModel declaration should look like this:
Controls
ExtCtrls unit AppInterfaces;
Forms interface
Grids uses
StdCtrls DSharp.PresentationModel;
type
So the AppViewForm unit now becomes this:
[InheritedExport]
unit AppViewForm; IAppViewModel = interface
interface
['{38C5B8ED-8269-463D-847D-09F6A7B99584}']
uses
end;
DSharp.Bindings.VCLControls;
implementation
type
end.
TAppView = class(TForm)
end;
If you now run the application, it looks nice again
implementation
{$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 4 107
Steps in MindScape AppView Step - By - Step (Continuation 4)
Wow, an empty form in just 3 steps! unit AppViewModel;
The cool things: interface
uses AppInterfaces, DSharp.PresentationModel;
• it is View Model driven type
(the next steps will show more of that) TAppViewModel = class(TScreen, IAppViewModel)
public
• Caliburn automagically did the binding - hence the constructor Create(); override;
Caption is now TAppViewModel, not AppView end;
• no global variables, Application.FormCreate implementation
and stuff like that any more: everything is dynamic constructor TAppViewModel.Create();
So lets move on! begin
inherited Create();
Step 4: DisplayName := IAppViewModel_DisplayName;
have the View Model drive the View's Caption end;
initialization
Add an Increments... caption TAppViewModel.ClassName;
Lets have the View Model steer a better Caption on the end.
View, like Increments...: 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;
It's a simple two-step process CheckEquals(IAppViewModel_DisplayName,
add a resourcestring to the AppInterfaces unit: LHaveDisplayName.DisplayName);
end;
resourcestring The IHaveDisplayName interface is one of the many interfaces
IAppViewModel_DisplayName = 'Increments...';
in Caliburn. This one exposes the DisplayName property.
TScreen implements that interface so it indicates it supports
then add a public constructor to the TAppViewModel like
having and supporting the DisplayName property. Now run
this:
and see if the first unit test on the View Model succeeds. Do
you get the same result
as left?

108 COMPONENTS
DEVELOPERS 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Steps in MindScape AppView Step - By - Step (Continuation 5)
Step 05: Step 06:
adding a Count property to the View Model fixing the binding.
and control to the View The previous step told about the importance of the
One of the Caliburn conventions is that if you have a interceptor classes in the DSharp.Bindings.VCLControls
control in the View with the same name as a property in unit. And that is exactly the reason why at run-time you got
the View Model, then they will automatically be bound Count into the caption of the TEdit:
together. it wasn't bound to the integer value of Count in the View
So lets start with adding a TEdit control called Count Model, as the interceptor classes could not do their
on the View, and notice that Delphi automatically work. The reason is that Delphi does not see them as they
extends the uses list for you: are obscured by the units that expose the actual control.
uses
DSharp.Bindings.VCLControls, The lesson is easy:
Classes, always make sure that units like
Controls, DSharp.Bindings.VCLControls that have
StdCtrls; interceptor classes are always the last in
type the uses list.
TAppView = class(TForm)
Count: TEdit; So the solution is very simple, modify the uses list from
end; uses
Now add the Count property to the View Model: DSharp.Bindings.VCLControls, Classes,
Controls, StdCtrls;
TAppViewModel = class(TScreen, IAppViewModel)
strict private
FCount: Integer; into
strict protected
function GetCount(): Integer; virtual; uses
procedure SetCount(const Value: Integer); Classes, Controls, StdCtrls,
virtual; DSharp.Bindings.VCLControls;
public Now run and enjoy the results of this step that was very
constructor Create(); override;
property Count: Integer read GetCount easy to perform, but had a high impact.
write SetCount;
end;
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; Step 07:
NotifyOfPropertyChange('Count'); add buttons to increment or decrement
end; the count
end
Here you will see that the Caliburn convention of naming
The DSharp.Bindings.VCLControls have class controls not only holds for properties in the View Model,
interceptors adding notification to most controls that ship but also for methods.
with Delphi, the View Model must also notifications. Lets start with the View Model: add two public methods
The above SetCount implementation shows this for here named DecrementCount and
properties; we will see later that this also can hold for IncrementCount
methods procedure TAppViewModel.DecrementCount;
Now let’s run the application and see if design-time begin
Count := Count - 1;
gets bound on run-time: 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 4 109
Steps in MindScape AppView Step - By - Step (Continuation 5)
Now run and see how the design-time gets translated: 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;
Caliburn binds public methods and properties in
the View Model to controls with the same name in The last step is very important: if you forget it the buttons
the View. Methods are bound to the action of a will not get disabled when the value of Count
control. Properties are bound to the content of gets at the edge of the allowed range.
the control.
Without it, you can get the run-time behaviour on the left,
This was almost too easy!. but you want the run-time behaviour on the right:
Let’s add some logic to limit the values to which Count can
be incremented or decremented in the next step.
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;
Adding unit tests
Now add these public properties to the View Model: I like having unit tests around the boundary cases, and to
have a certain symmetry. So these are the published test
property CanDecrementCount: Boolean
methods added:
read GetCanDecrementCount;
property CanIncrementCount: Boolean procedure Test_DecrementCount_MaximumCount();
read GetCanIncrementCount; procedure Test_DecrementCount_MaximumCount_Minus1();
and have them backed by get methods: procedure Test_DecrementCount_MaximumCount_Plus1();
procedure Test_DecrementCount_MinimumCount();
function TAppViewModel.GetCanDecrementCount(): procedure Test_DecrementCount_MinimumCount_Minus1();
Boolean; procedure Test_DecrementCount_MinimumCount_Plus1();
begin procedure Test_DisplayName();
Result := Count > MinimumCount; procedure Test_IncrementCount_MaximumCount();
end; procedure Test_IncrementCount_MaximumCount_Minus1();
procedure Test_IncrementCount_MaximumCount_Plus1();
function TAppViewModel.GetCanIncrementCount(): procedure Test_IncrementCount_MinimumCount();
Boolean; procedure Test_IncrementCount_MinimumCount_Minus1();
begin procedure Test_IncrementCount_MinimumCount_Plus1();
Result := Count < MaximumCount;
end with implementations like these:
It is always a good idea to make the View Model robust so procedure TAppViewModelTestCase.
that it can withstand unwanted calls. So update Test_DecrementCount_MaximumCount();
the DecrementCount and IncrementCount so they begin
AppViewModel.Count := MaximumCount;
throw an exception when they cannot perform their AppViewModel.DecrementCount();
respective action: end;
procedure TAppViewModel.DecrementCount;
begin procedure TAppViewModelTestCase.
if not CanDecrementCount then Test_DecrementCount_MaximumCount_Minus1();
raise EInvalidOperation.Create( begin
'not CanDecrementCount'); AppViewModel.Count := MaximumCount-1;
Count := Count - 1; AppViewModel.DecrementCount();
end; end;

procedure TAppViewModel.IncrementCount; procedure TAppViewModelTestCase.


begin Test_DecrementCount_MaximumCount_Plus1();
if not CanIncrementCount then begin
raise EInvalidOperation.Create( AppViewModel.Count := MaximumCount+1;
'not CanIncrementCount'); AppViewModel.DecrementCount();
Count := Count + 1; end;
end;
110 COMPONENTS
DEVELOPERS 4 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 4 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. About the Author
Lets fix that first, then take another look at the unit tests. Jeroen Pluimers
Since Caliburn is part of DSharp which depends on
Makes things work.
Spring4D which has a Guard.CheckRange method, Specialist in .NET, Win32, x64,
fixing Count is as easy as including this line at the start of SQL,
the SetCount method: Visual Studio and Delphi.

Guard.CheckRange((Value >= MinimumCount) Knows how to strike a balance between


and old and brand new technology to make
(Value <= MaximumCount), 'Value'); things work.
DOS, mobile, big systems, you name it.
BTW: Don't forget to add the unit Spring to the
Married to a cancer survivor.
implementation uses list of the AppViewModel unit. As curator responsible for his brother
who has an IQ < 50.
Now setting Count to an out-of-range value causes an 30+ year member of world class
EArgumentOutOfRangeException to be raised. marching band Adest Musica.
Trained and performed the 2013 half
marathon in New York.
That brings us to the unit tests: we need certain tests to
expect certain kinds of exceptions. DUnit can Twitter: jpluimers
do just that using the little known ExpectedException Blog: wiert.me
property, which means that some of the tests need LinkedIn: jwpluimers
to be modified.
Continuation:
First add the SysUtils and Classes units to the Future steps for the example will be in the next Blaise
implementation uses list of the AppViewModelTestCase issue. They will cover binding TAction with
unit. Add the line TActionManager, using the Caliburn logging, adding
unit tests for the new actions, adding increment-by-
ExpectedException := value with tests, adding a Model to the View Model,
EArgumentOutOfRangeException; and creating a FireMonkey UI with TActionList next to
at the start of these methods: the VCL UI.
procedure Test_DecrementCount_MaximumCount_Plus1();
procedure Test_DecrementCount_MinimumCount_Minus1();
procedure Test_IncrementCount_MaximumCount_Plus1();
procedure 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 4 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 4

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 4 113
kbmFMX for XE5 (Continuation 1)
COMPONENTS
DEVELOPERS 4

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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
kbmFMX for XE5 (Continuation 2)
COMPONENTS
DEVELOPERS 4

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 4 115
kbmFMX for XE5 (Continuation 3)
COMPONENTS
DEVELOPERS 4
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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
kbmFMX for XE5 (Continuation 4)
On
COMPONENTS
DEVELOPERS 4
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;
In this instance, the Database path on the device will be you need to add System.iOUtils to the uses list.
/data/data/com.fikrethasovic.AndroidFMXApp/ OK, back to kbmMW, you create standard QueryService,
files/myimages.sqlite and use SQLite specific components: Beside other
If your device is rooted, you can phisically access it, components, added are two buttons, which are connected to
but as default it is hidden from user, the alMedia ActionList, which is responsible for the
unless you root our device, which will void your warranty. camera and gallery manipulation.
Notice, to use the Tpath class, But, we need to use the Image captured by the Camera or
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 4 117
kbmFMX for XE5 (Continuation 5)
COMPONENTS
DEVELOPERS 4
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 4 Nr 5 / 2013 BLAISE PASCAL MAGAZINE
kbmFMX for XE5 (Continuation 6)
COMPONENTS
DEVELOPERS 4

Delphi will add the launcher related files, but you need to Here is one more:
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:

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 application, so this is the first screen, and together with kbmFMX components to create stunning
following is when you change tab to single photo: android business applications.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE COMPONENTS


DEVELOPERS 4 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 Supports Delphi/C++Builder/RAD Studio 2009 to
functionality multi tier product for serious system XE5 (32bit, 64bit and OSX where applicable).
development. kbmMW for XE5 includes full support for Android
and IOS (client and server).
- Native high performance 100% developer
kbmMemTable is the fastest and most feature rich
defined application server with support for in memory table for Embarcadero products.
loadbalancing and failover
- Native high performance JSON and XML - Easily supports large datasets
(DOM and SAX) for easy integration with - Easy data streaming support
external systems - Optional to use native SQL engine
- Native support for RTTI assisted object - Supports nested transactions and undo
marshalling to and from XML/JSON - Native and fast build in
aggregation/grouping features
- High speed, unified database access
- Advanced indexing features
(35+ supported database APIs) with connection
for extreme performance
pooling, metadata and data caching on all tiers
- Multi head access to the application server, Warning!
via AJAX, native binary, Publish/Subscribe, SOAP,
kbmMemTable and kbmMW
XML, RTMP from web browsers, embedded
devices, linked application servers, PCs, mobile are highly addictive!
devices, Java systems and many more clients Once used, and you are hooked for life!

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


COMPONENTS
DEVELOPERS
kbmMW IS THE PREMIERE N-TIER PRODUCT
4
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