5 / 6 2013
Publisher: Foundation for Supporting the Pascal Programming Languagein collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep) © Stichting Ondersteuning Programmeertaal Pascal
BLAISE PASCAL MAGAZINE
BLAISE PASCAL MAGAZINE
BLAISE PASCAL MAGAZINE
D E L P H I, L A Z A R U S, O X Y G E N E, S M A R T,A N D P A S C A L R E L A T E D L A N G U A G E S
A N D R O I D, I O S, M A C , W I N D O W S & L I N U X
31/32
31/32
31/32
maXbox
TWO ISSUES IN ONE
Book Review: Coding in Delphi
Coding Delphi Author: Nick Hodges
Designing an API: common mistakes
Newest Leap developments
3D Printing
Kinect ?!
Smart Mobile Studio 2.0
A simple superscript text editor
Using GEO services in Delphi applications with TMS components
Correcting a bad API design:
The maXbox Pure Code
Programming Bitmap Rotation
Introduction to Model, View and View Model (MVVM) and the Caliburn Micro for
Delphi framework
kbmFMX for XE5 (android)
By Jim Duff Book:
By Alexander Alexeev
Michael Van Canneyt
By Bj Rao
By Michael Van Canneyt
By Primož Gabrijelčič
By David Dirkse
By Bruno Fierens
By Alexander Alexeev
By Max Kleiner
By David Dirkse
By Jeroen Pluimers
By Fikret Hasovic
Interview with David I:
plans about updating buying etc
Interview with Gwan Tan - better office
ISSN 1876-0589
Royal Library -Netherlands Koninklijke Bibliotheek Den HaagEditor - in - chief
News and Press Releases
email only to Authors Editors Trademarks Caveat Subscriptions www. [email protected]
ABN AMRO Bank Account no. 44 19 60 863
IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 Subscription department
Detlef D. Overbeek, Netherlands
Tel:+31(0)30 890.66.44/Mobile +31(0)6 21.23.62.68
A Alexander
N Jeremy North, O Tim Opsteeg,
Howard Page-Clark, Jeroen Pluimers
Peter Bijlsma,
Howard Page-Clark, James D. Duff
( 2014 prices )
ncl. VAT 6%
(including code, programs and download magazine)
or by written order, or by sending an email to
Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.
or by credit card: Paypal or TakeTwo Name: Pro Pascal
Foundation-(Stichting Programmeertaal Pascal)
blaisepascal.eu
Alexeev Michaël Van Canneyt,
Daniele Teti F Bruno Fierens
Fikret Hasovic J Cary Jensen
L Wagner R. Landgraf, Sergey Lyubeznyy K Max Kleiner
M Kim Madsen, Felipe Monteiro de Cavalho Inoussa Ouedraogo
Foundation for Supporting the Pascal Programming Language (Stichting
Ondersteuning Programeertaal Pascal) B C Marco Cantù, D David Dirkse, G Primož Gabrijelčič, H P
S Rik Smit, Bob Swart,
W. (Wim) van Ingen Schenau, Rik Smit
All trademarks used are acknowledged as the property of their respective owners.
Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions. If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant. 1: Printed version: subscription € 60.-- I
(including code, programs and printed magazine, 6 issues per year excluding postage).
2: Electronic - non printed subscription € 45.-- Incl. VAT 21%
Subscriptions can be taken out online at
Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email.
Subscriptions can be paid by sending the payment to:
Edelstenenbaan 21 / 3402 XA IJsselstein,
The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68
Peter Bijlsma,
Correctors
Subscriptions run 365 days.
CONTENTS
Articles
All material published in Blaise Pascal is copyright © SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial purposes. Commercial use of program listings and code is prohibited without the written permission of the author.
Copyright notice
Advertisers
Barnsten Pag 20
BetterOffice Page 25
Components 4 Developers Page 120
ITDevCon Pag 39
Lazarus the complete guide Page 19
Learnto program Page 24
Raize Software Pag 92
Smart Mobile Studio Page 40 / 46
maXbox Page 77
2
COMPONENTS
DEVELOPERS
4
Nr 5 / 6 2013 BLAISE PASCAL MAGAZINE
BLAISE PASCAL MAGAZINE
BLAISE PASCAL MAGAZINE
BLAISE PASCAL MAGAZINE
D E L P H I, L A Z A R U S, O X Y G E N E, S M A R T, A N D P A S C A L R E L A T E D L A N G U A G E S A N D R O I D,
I O S, M A C , W I N D O W S & L I N U X
31/32
31/32
31/32
Editorial Page 3
Book Review: Coding in Delphi Page 4
Coding Delphi Author: Nick Hodges
Designing an API: common mistakes Page 8
Newest Leap developments Page 21
3D Printing Page 26
Kinect ?! Page 33
Smart Mobile Studio 2.0 Page 41
Page 48 A simple superscript text editor Page 50 Page 52 Using GEO services in Delphi applications
with TMS components Page 57
Correcting a bad API design: Page 65
The maXbox Pure Code Page Page 77
Page 93 Programming Bitmap Rotation Page 98 Introduction to Model, View and View Model (MVVM) and the Caliburn Micro for
Delphi framework Page 102
kbmFMX for XE5 (android) Page 113
By Jim Duff Book:
By Alexander Alexeev Michael Van Canneyt
By Bj Rao
By Michael Van Canneyt By Primož Gabrijelčič By David Dirkse By Bruno Fierens By Alexander Alexeev By Max Kleiner By David Dirkse By Jeroen Pluimers By Fikret Hasovic Interview with David I:
plans about updating buying etc
Interview with Gwan Tan - better office
Interview with Ray Konopka
maXbox
3
COMPONENTS
DEVELOPERS4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
irst of all a Happy New Year to you all.
This is a season of dark days, so I especially like the
F
light from our Christmas tree which adds to the warm ambience of those holiday opportunities to relaxing with your family and friends, times that are enhanced with little gifts and delicious meals...2013 was supposed to have been the end of the world – and though it was not the worst of years in the political sense, yet world crises seem not to be over, even if some of them are lessening in severity…
The immense horns Mr Snowden placed on us made us realize that our governments do not play by the rules. Ordinary mortals can do little beyond despairing at the invasion of privacy and taking what steps we can to combat it. Politicians must battle this erosion of individual's rights, and I think in the long run the battle will be won.
Simply because it has to be.
I think the sheer quantity of garbage the intelligence community is accumulating will collapse upon the perpetrators. It will take some time to reach critical mass; but in the world of programming we already have a solution for this: simply remove it by deletion (Garbage
Collection)!
So many complain about deception. But who knows what the truth is? We all lie now and then, because we do not want to damage our relationship to someone; and I think it in the end humans do not always want a black-and-white difference between Yes and No, we also prefer a gray zone. We find this even in scientific theory (although that is denied
as well).
String theory is a very good example of this:
it should be impossible for an entity to exist at two distinct places at the same time. And when you cast your mind back over history, and look at longer epochs, you will see that there is a pendulum effect: things move back and forth. Sometimes there is a slight improvement before the cycle starts all over again…
The universe as a whole seems to work on this principle. Some claim we will find people who can write programs that will protect us from any intrusion or decryption. Don’t believe them…
You would do better to spend your money on the newly developing future like the adventure of
the Leap Motion or even 3D Printing, and be creative (as
you probably have always been) in writing new programs with new code. People outside the programming world may not understand this need to innovate, or the many new possibilities in our Pascal world offers.
No Operating System is excluded any more.
Pascal is not only doing good it is getting back where it belongs: at the top of the range of available languages, because of its enormous potential in education, for solving problems, in facilitating Rapid Application Development, in bringing the “write-once-run-anywhere” dream closer for cross-platform programmers.
As the user group that publishes this Magazine we will always try to help you advance to the next step.
This year we will see 3D printing take off, and of course Leap Motion as a
human-computer interface used increasingly.
For Leap Motion we are developing a version that will run on ARM cpus: so we will be able to port Leap Motion software to the Raspberry-Pi and set up some new tools. This year we plan to offer our new group of Components for Leap Motion on several platforms.
I wrote a chapter about the history of computing for
our Learning Pascal book which has turned out to be
a lengthy story: about 70 pages. Naturally I had to
find out something about the founding father of
Pascal and write a few lines about him.
In Cologne recently I met Max Kleiner and we
discussed the origins of Pascal, and he told me
Professor Wirth was still alive, and so I thought it
would be interesting to interview the author of
Pascal: Professor Dr. Niklaus Wirth.
He will be 80 years old next month.
So we will visit him shortly to do an interview.
Mr. Wirth has agreed to this and you will be able to
read the outcome of our conversation in the next
issue. In his honour we will start a new project: PEP –
PASCAL EDUCATION PROGRAM. We will launch
PEP for the first time on 15th February, since that is
his birthday. In line with this educational theme we
plan in future issues to reserve about 4 pages for
Pascal beginners, explaining the fundamentals of
programming, and creating basic examples especially
for those new to programming.
Young people (12 to 18 years of age) and students
will be offered a free Blaise magazine subscription so
they can receive information without needing to pay.
Details will be available at our website.
Many of you always prefer Delphi's help as it was in
Delphi 5, 6 and 7. Here is some good news: we are
shortly releasing The Helper Component.
This is a component that presents help in the way we
think help should be organized. We plan to announce
the first trial version in the next issue: meantime you
can help us by letting us know your gripes and
wishes for an improved IDE help system.
There are many plans for 2014:
The next Pascon conference to follow the great
success of last summer's event;
The new Leap component –
Experimenting with Kinect
Many articles about Android development using
Pascal, and new opportunities for that OS…
It’s going to be a very busy year.
Very Exciting!
4
COMPONENTS
DEVELOPERS
4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Book Review: Coding in Delphi
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"
Book: Coding Delphi
Author: Nick Hodges
There are a couple of ways to preview what a
technical software book is all about before one goes
ahead to use it. The first quick act is to look at the list
of contents and then flick through some of the
chapters to preview the wording, code samples and
pictorial views. The next step is to analyse the
knowledge and validity of the author to make sure
that the contents and sample code are going to give
you the correct, accurate and productive assistance
for your development design and coding.
As a reviewer, the above second step is an
important one in order to provide the magazine
readers with a brief and positive (or negative - not in
this case) assessment of the book. So, here we go
with a summary of the first two write ups, namely
Forward, by Allen Bauer, Embarcadero's Chief
Scientist, and Introduction by author Nick Hodges.
In Allen's Forward chapter, he provides a
write up of Nick's background beginning with "Nick
holds the honor of producing one of, if not the first
non-Borland built component. In order to learn about
Delphi component building, he built TSmiley."
Looks like that must have worked OK as Nick
became a member of Borland-Embarcadero. And
near the end of Allen's summary, he made the
following link between the positive aspects of the
book and how such value would provide a beneficial
effect for developers.
The content of the book contains
the following chapters:
Forward
Introduction
Acknowledgements
Frameworks used in Coding in Delphi
1
Exceptions and Exception Handling
2
Using Interfaces
3
Understanding Generics
4
Understanding Anonymous Methods
5
Delphi Collections
6
Enumerators in Delphi
7
IEnumerable
8
Run-time Type Information
9
Attributes
10 Using TVirtualInterface
11 Introduction to Dependency Injection
12 A Deeper Look at Dependency Injection
13 Unit Testing
14 Testing with an Isolation Framework
Appendix A: Resources
Appendix B: My Delphi Story
The list of chapters shows that the book is aimed at
experienced Delphi developers rather than being a
beginner's guide. Having already mentioned the
Forward and Introduction items above, one more
item to mention in support of the qualified author is
the final Appendix - My Delphi Story. This is the
third part that once more gives the reader the
confidence in going ahead to take in the technical
aspects of the book.
5
COMPONENTS
DEVELOPERS4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Book Review: Coding in Delphi (Continuation 1)
So, finally getting into the technical documentation,
an introductory chapter Frameworks used in Coding
in Delphi provides "...a number of open source
frameworks to facilitate the coding techniques that
are discussed herein. I refer to them here at the
beginning of the book to enable you to retrieve the
code and get them set up before we begin."
Now, we are ready to go, and a major influence of
this book is having this subject matter as the first
main chapter:
1. Exceptions and Exception Handling Introduction
Structured Exception Handling How Not to Use Exceptions Don't Eat Exceptions
Don't Generally Trap Exceptions Don't Go Looking For Exceptions
Don't Use the Exception Handling System as a Generic Signaling System
How to Use Exceptions Properly
Use Exceptions to Allow Your Code to Flow Without the Interruption of Error Handling Code
Application Writers Should Trap Exceptions Trap only specific exceptions
Component Writers and Library Code Writers Raise Exceptions
Raise Your Own Custom Exceptions Let Your Users See Exception Messages Feel Free To Provide Good Exception Messages Provide Two Versions Of A Library Routine Conclusion
This provides not only the 'how' together with
sample code, but the reasoning behind having it as
a fundamental method of coding.
It reminds one of the basic input-process-output of
computer processing; if these steps don't work, then
the hardware/software doesn't work properly
and/or stops working - full stop.
Part of his final comments in the Conclusion
provides the 'why': "It's quite easy to fall into the
trap of using exception handling improperly. ... Use
exceptions wisely, and you'll be able to product
robust, clean code.
"
2. Using Interfaces Introduction
Decoupling
What are Interfaces? Interfaces Everywhere A Simple Example
Implementing an Interface Some Further Things to Note Interface Inheritance Other Things to Think About About TInterfacedObject How to Actually Use Interfaces Why Should You Be Using Interfaces?
This chapter has the only photo of the author, so it
just goes to show how important he believes the
following advice is.
This sample quote that soon follows the above one,
is provided to further confirm the reasonings
provided throughout the book, in line with the
sample code and instructions.
"All through this book, I'll talk a lot about decoupling
your code and why that is really good.
But a definition here is probably a good idea.
Code can be said to be decoupled when your classes
are designed in such a way that they don't depend on
the concrete implementations of other classes.
Two classes are loosely coupled when they know as
little about each other as possible, the dependencies
between them are as thin as possible, and the
communication lines between them are as simple as
possible."
A code sample and part of the associated instructions
that provide associated reasonings are now given as
the final quoted sample in this review.
" Note that the declaration of the interface has a
Globally Unique Identifier (GUID) right after the
initial declaration. This GUID is used by the compiler
to identify this interface.
You can use an interface without the GUID,
but much of the RTL and most frameworks that take
advantage of interfaces will require a GUID be
present. (You can generate a GUID any time you want in
the IDE by typing CTRL+SHIFT+G
)”
The remaining chapters follow in a logical sequence,
each with code samples plus the 'how' and 'why' to
use them.
type interface function string function string end = [ ] : ; : ; ; IName FirstName LastName '{671FDA43-BD31-417C-9F9D-83BA5156D5AD}'If I could teach a new programmer one thing it would be this:
Program to an interface, not an implementation.
Coding in
Summary
Hopefully, the selected quotations from the book
have provided the reader with positive views of the
author's ability to explain the 'how' and 'why' to
advance your coding capabilities.
Nick has also given a good background of his own
and Delphi's history, from the early days to the
present, to show how they and we readers can
advance our development capabilities as the
technical environment is accelerating in this day
and age.
The 'book' reviewed has been the pdf version and
there are several links in Appendix A such as Unit
Testing, Projects, Good Stuff etc. The following
link is also provide at the start of the book:
"Find out what other people are saying about the
book by clicking on this link to search for this
hashtag on Twitter:
Reviewed by Jim Duff
ADUG Melbourne Member
The following lines are taken out the book.
I had the pleasure of working closely with Nick
during his time at Borland and then Embarcadero.
https://twitter.com/search?q=#codingindelphi
Forward
I first met Nick during a Delphi 1 pre-launch “
boot-camp” that was held at the brand new Borland
campus in Scotts Valley, California.
We had invited a cadre of developers, authors
and long-term luminaries to get some in-depth
training and presentations directly from the
development team and product management.
Enthusiastic and exuberant are adjectives that don’t
fully characterize my first impressions of him.
He was never afraid of asking questions and
absorbed the information quickly.
I cannot talk about Nick without also discussing
TSmiley. Inquisitive and curious,
Nick embraced Delphi without reservation.
To that end, Nick wasn’t satisfied with what Delphi
did, but was just as keen on learning about how it did
it. To that end, Nick holds the honor of producing one
of, if not the first non-Borland built component.
In order to learn about Delphi component
building,
he built TSmiley. In this one simple component all the
core aspects of using Delphi’s Pascal language to
extend and enhance the VCL framework were
demonstrated.
You see, Delphi component building is all about the
code.
Nick held the position of Product Manager for a
while then managed a portion of the Delphi R&D
team. Nick was never afraid to challenge
assumptions and actively stand up for the Delphi
developer community.
Even though Nick’s position didn’t require him to
write code, he didn’t let that stop him.
He was always looking for ways to keep his
programming skills as sharp as possible. As new
features were developed, Nick was always right
there to give them a workout.
To this day there is a large amount of code written by
Nick that remains a key portion of the overall
regression and unit-testing process.
Nick discovered that some of the best ways to
learn about new code is to test that new code. It is not
without irony that this process requires code to be
written.
It stands to reason that Nick would end up here;
writing a book with a single focus on the code.
That is the engine that drives all the other
revolutionary features of Delphi.
Without the code, there would be no “Visual” in the
Visual Component Library (VCL).
In fact, Delphi has always been about getting the
developer to their code as quickly as possible. The
VCL and the newer FireMonkey component
frameworks make the use and construction of UI,
database, connection and others as simple as
possible. Its ultimate goal is to allow the developer to
focus on their task, which is to produce an
application that solves a specific problem. It is the
code that is written in between those UI, database
and connection components that make up the
application.
Developers at all levels will be able to use the
information contained within this book.
It 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.
This is right up there with the thrill experienced
when some thought, idea or concept comes to life
within the computer. When a developer sees their
running application, rarely do they think about the
few moments they spent arranging some controls on
the User Interface. They feel a sense of pride about
that one difficult and thorny problem they solved
through the clever use of code. At the end of the day,
to a developer, it is really all about the code.
Allen Bauer
Book Review: Coding in Delphi (Continuation 2)
6
COMPONENTS
DEVELOPERS
4
What you will find are ways to make your code much cleaner, much more powerful, and way easier to maintain. This book is all about the cool, new code you can write with Delphi. It won’t matter whether you are building a VCL or an FM application. I’ve titled it “Coding in Delphi” because I want to make it a book that shows you simple examples of how to use powerful features – that, is about the code. These language features are indeed advanced features – they are new relative to, say, the case statement – and thus many of you are beginners to them. By the end of this book, you won’t be.
The approach I’m taking for this book is to try to distill things down to the very basics. The examples will be very simple but the explanations deeper. I believe that if you can understand the basic idea in a simple example, it is but a small leap to using those ideas in more complex code.
There is no point in giving complex examples when you can get the main thrust using fundamental implementations that illustrate advanced ideas.
Between the code in this book and in the samples online (https://bitbucket.org/NickHodges/codinginde
lphi²) you can learn all the precepts and then begin applying them to your own code. In other words, nothing fancy here, just the basics – it is then up to you to use these ideas in your own code.
This book is not done – it’s instead a living document. Since it is self-published on a platform that makes iteration very easy, I plan on having frequent releases to fix typos (which will, I’m sure, sneak through despite my best efforts), improve examples and descriptions, and keep up with technology changes. Owners of the PDF should get notifications of new versions automatically.
If you are reading a paper version of this book, I’m sorry I can’t deliver fixes to your hard-copy – perhaps
some day that will be possible.
The book will be done when you guys say it is done. Maybe, it will never be done because Delphi keeps growing and expanding. I guess we’ll see.
As a result, I’m totally open to feedback – please feel free to contact me at
with suggestions corrections, and improvements. Please join the Google Plus group for the book.³ I may even add whole new chapters.
Thanks for your purchase – this book was a labor of love, so every sale is icing on the cake.
Nick Hodges Gilbertsville, PA [email protected]
²https://bitbucket.org/NickHodges
Coding in
Delphi
IntroductionOver the years, I’ve spoken in front of a lot of Delphi developers. The one thing that I notice is that there are a lot more of you Delphi guys than the average Delphi guy thinks. There are Delphi folks everywhere. Also, I have noticed that a lot of Delphi developers are “behind”. That is, they are either using an older version of Delphi, or they aren’t using or aren’t even aware of all the features in the newer versions of Delphi that they are using.
Something I like to do when I’m in front of folks is ask a few questions about what people are doing. I’m always saddened that the response to questions like “Who is doing unit testing?” or “Who is taking advantage of Generics?”
is pretty meager.
This is particularly true for the language features and the run-time library. It’s quite easy to move forward with an older code base, utilizing the new features in the IDE and adding new things to your app using the new high level frameworks and components that come in the newer versions.
For example, you might have been developing an application since Delphi 3, moving forward through various versions. Along the way, you may have added some DataSnap functionality, started using the Code Insight features in the IDE, and when you moved to XE2, you start poking around with FireMonkey.
But it’s fairly easy to ignore the new language features that come along with those new versions.
For instance, two powerful language features were added in Delphi 2009: generics and anonymous methods.
Both are features that enable the development of really cool code and frameworks.
But if you didn’t understand or feel the need for them, then it was pretty easy to simply not use them.
You can still do all kinds of great stuff in Delphi without them, but with them, well, you can write some
really beautiful, testable, and amazing code.
For instance, a relatively new framework that exists only because of these new language features is the Spring Framework for Delphi, or Spring4D, as I’ll refer to it in this book. Spring4D is a feature rich framework that provides a number of interesting services, including a wide variety of collections that leverage generics, an Inversion of Control container, encryption support, and much more.
I view Spring4Dsolid as much a part of the Delphi RTL as SysUtils is. Using Spring4D in your code will make many, many things easier and cleaner. But many Delphi developers don’t know this yet.
If the above is familiar, this book is for you: The Delphi developer that hasn’t quite yet made the leap over into the cool and amazing things that you can do with the latest versions of the Delphi language.
This book is all about introducing these new language features and some of the intriguing things you can do with them. It will take a look at these new language features, and then expand into some of the open source frameworks that have sprung up (no pun intended) as a result. It will then show you techniques that you can use to write SOLID¹, clean, testable code.
You 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.
Book Review: Coding in Delphi (Continuation 3)
7
COMPONENTS
DEVELOPERS4
8
COMPONENTS
DEVELOPERS
4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes
By Alexander Alexeev
When you want to create a public component for use by several potential applications, you can register it as a
COM object. (Component Object Model (COM) is a
binary-interface standard for software components introduced by Microsoft in 1993)
Because COM was designed to simplify exactly this task, creating a publicly offered component as a COM object is an obvious way to do it. However there are drawbacks to development using COM.
Firstly COM technology exists only on Windows, and secondly it has a steep learning curve - which means that you need to learn many things before you can begin to develop your public component.
Also if you are only developing a simple component COM may be overkill.
Consequently a developer may avoid the COM route, choosing to implement his component as a simple DLL. When you create a new DLL you need to decide: - what functions you want it to export;
- what arguments the functions will have;
- how you want to transfer data, and other issues. Taken together, we call this the API of your DLL (it is also termed a protocol or contract).
The API (Application Programming Interface) is a set of rules (the contract), which you as developer of the DLL and the user (as caller of your DLL) agree to follow in order to understand each other and to interact successfully.
All DLL services are provided only under this contract.
This article describes typical mistakes, features and pitfalls developers encounter as they develop a public DLL API. In general, this article serves as a kind of check list for you. You can compare your code with this list and find out how good it is, and if it contains any of the mistakes typically found in such DLLs.
Unwritten rules
(This section is based on
)
There are some basic ground rules that apply to all programming, which are so obvious that most
documentation and books do not bother explaining them (because these rules should have been internalized by
practitioners of the art to the point where they don't need to be expressed).
A driver planning what route to take wouldn't even consider taking a shortcut through somebody's backyard or going the wrong way down a one-way street.
In the same way that an experienced chess player doesn't even consider illegal options when deciding his next move, an experienced programmer doesn't even consider
violating the following basic rules without explicit permission in the documentation which allows contravening the rule:
• Everything not defined is undefined. This may be a tautology, but it is a useful one. Many of the rules below are just special cases of this rule.
• All parameters must be valid.
The contract for a function applies only when the caller adheres to the conditions, and one of the conditions is that the parameters are actually what they claim to be. This is a special case of the "everything not defined is undefined" rule.
o Pointers are not nil unless explicitly permitted otherwise.
o Pointers actually point to what they purport to point to.
If a function accepts a pointer to a
CRITICAL_SECTION, then you must pass a pointer which points to a valid CRITICAL_SECTION.
http://blogs.msdn.com/b/oldnewthing/archive/ 2006/03/20/555511.aspx
e assume you are developing a public DLL. So you will have a .dll file, you will have the
W
header files (at least *.h and *.pas), and you will have documentation.The header files (or headers) form a set of source files containing structure and function declarations used in the API for your DLL.
Typically they contain no implementation.
The headers should be available in several languages. As a rule, this means the language used to create the DLL (in our case - Delphi), C (as standard) and perhaps additional languages (such as Basic, etc.).
All these header files are equivalent to each other,
representing only translation of the API from one language to another.
The more languages you include the better.
If you don't provide header files for a particular language, then developers using that language will be unable to use your DLL, (unless they are able to translate your header files
from a language you provide (Delphi or C) into their language). This means that failing to offer headers for a particular language is usually a big enough obstacle that developers in that language will not use your DLL, although it is not an absolute block to such usage. From this perspective COM looks more attractive (the API description is stored in
type libraries in the universal TLB format).
You do not have to do anything beyond distributing a .tlb file (which can also be embedded in the .dll itself).
If the 'foreign' language can work with COM,
then it can import information from the TLB and generate appropriate header files for itself.
A TLB file is a binary file which has been created and edited by a TLB editor (or generated by an IDE). It is also possible to "compile" a TLB file from a text description - an IDL file (.idl or .ridl).
If you are smart enough, you can "borrow" this feature from COM.
Your documentation is a textual description of the DLL API written by the DLL developer for other developers who will use the DLL (i.e. it is one-sided).
Of course, you can also document internal details for yourself; but that is not the concern of this article. So, you should provide documentation which at least contains a formal description of the API, listing of all the functions, methods, interfaces, and data types, along with explanations of "how" and "why" (the so-called Reference).
Additionally, the documentation may include an informal description of the code-development process (a guide, a how-to, etc.). In the simplest cases, the documentation is written directly in the header files as comments, but more often it is provided as a separate file (or files) in chm, html, or pdf format.
expert
9
COMPONENTS
DEVELOPERS4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Designing an API: common mistakes (Continuation 1)
o When invoking a method on an object, the Self parameter is the object. Again, this is something modern compilers handle automatically, though people using COM from C (and yes they exist) have to pass the Self parameter manually,
and occasionally they mess up.
·• Function parameter lifetime:
o The called function can use the parameters during the execution of the function.
o The called function cannot use the parameters once the function has returned. Of course, if the caller and the callee have agreed on a means of extending the lifetime,
then those agreed rules apply.
§ The lifetime of a parameter that is a pointer to a COM object can be extended bythe use of the IUnknown.AddRef method.
§ Many functions are passed parameters with the express intent that they be used after the function returns.
It is then the caller's responsibility
to ensure that the lifetime of the parameter is at least as long as the function needs it. For example, if you register a callback function, then the callback function needs to be valid until you deregister
the callback function.
Error:
Providing no dedicated initialize and finalize functions
The first mistake you can make as an API developer is not to provide standalone functions to initialize and finalize your DLL, but instead use the DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH notifications from the DllMain callback-function.
DllMain is a special function in the DLL,
called automatically by the system in response to certain events. Among those events are DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH
- these are notifications about the loading and unloading of your DLL.
If you are using Delphi, then the initialization section and the finalization section of Pascal units in the DLL are executed in response to DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH, which the system sends to the DllMain function of your DLL.
Of course, you do not see this process, it is happening under the hood of the RTL (language support library). You just see that when the DLL is loaded, all units are initialized, and when it is unloaded, they are finalized. o Pointers must be properly aligned.
Pointer alignment is a fundamental
architectural requirement, yet something many people overlook, having been pampered by a processor architecture that is very forgiving of alignment errors.
o The caller has the right to use the memory being pointed to.
This means no pointers to memory
that has been freed or to memory that the caller does not have control over.
o All buffers are as big as the declared (or implied) size.
If you pass a pointer to a buffer and say that it is ten bytes in length, then the buffer really needs to be ten bytes in length (or more). o Handles refer to valid objects that
have not been destroyed.
If a function wants a window handle, then you must pass a valid window handle. • All parameters are stable.
o You cannot change a parameter while the function call is in progress.
o If you pass a pointer, the pointed-to memory will not be modified by another thread for the duration of the call.
o You cannot free the pointed-to memory either.
• The correct number of parameters is passed with the correct calling convention.
This is another special case of the "everything not defined is undefined" rule.
o Thank goodness, modern compilers
refuse to pass the wrong number of parameters, though you would be surprised how many people manage to sneak the wrong number of parameters past the compiler anyway, usually by devious casting.
• Input buffers:
o A function is permitted to read from the full extent of the buffer provided by the caller, even if the entire buffer is not required to determine the result. • Output buffers:
o An output buffer cannot overlap an input buffer or another output buffer.
o A function is permitted to write to the full extent of the buffer provided by the caller, even if not all of the buffer is required to hold the result. o If a function needs only part of a buffer to hold
the result of a function call, the contents of the unused portion of the buffer are undefined. o If a function fails and the documentation does not
specify the buffer contents on failure, then the contents of the output buffer are undefined.
This is a special case of the
"everything not defined is undefined" rule. o Note that COM imposes its own rules
on output buffers. COM requires that all output buffers be in a marshallable state even on failure. For objects that require nontrivial marshalling (interface pointers and BSTR/WideStrings being the
most common examples), this means that the output pointer must be nil on failure.
(Remember, every statement here is a basic ground rule, not an
absolute inescapable fact. Assume every sentence here is prefaced with "In the absence of indications to the contrary". If the caller and callee have agreed on an exception to the rule, then that exception applies.)
10
COMPONENTS
DEVELOPERS
4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Error:
Using ancient techniques for memory management and error handling
In any API, there are two very important aspects: how you pass data of arbitrary (dynamic) size, and how you report any errors from API function calls.
A typical WinAPI function contains this logic: The caller must call a function with
lpData = nil and cbSize = 0, whereupon the function returns an ERROR_INSUFFICIENT_BUFFER error, and cbSize will contain the amount of memory in bytes required to store all the data.
The caller can then allocate sufficient memory and call the function again, passing to lpData a pointer to a block of data, and passing the size of the block to cbSize. This approach is complex in itself (calling the function twice
instead of once), and imagine how it would work for frequently changing data.
What happens if the data increases in size between your two calls? Then the second call will return an ERROR_INSUFFICIENT_BUFFER error again, and again you will need to re-allocate more memory and repeat the call. That is - to reliably call the function - you have to write a loop.
Why do most of the WinAPI functions use such a terribly complicated method? Answer: history.
When these functions were first designed there were no modern de-facto standards; moreover the Windows developers sacrificed convenience for the sake of micro-optimizations
Similarly, a typical WinAPI function reports that it ...returns True on success and False on failure. The cause of the error can be obtained by calling GetLastError.
http://blogs.msdn.com/b/oldnewthing/ archive/2004/08/23/218837.aspx
and some of the functions even report:
...in case of success, the function returns the size of the data, or 0 if there is no data. In the case of error, the function returns 0. To determine the exact cause, you should call GetLastError,
which returns ERROR_SUCCESS for a successful call, or an error code.
What a nightmare!
Once again, we encounter two function calls rather than one, not to mention the complexity of the extensibility (to
use our own error codes) and the inability to receive more context data about the error.
Accordingly, many people when designing their own API see how it is done in the operating system and imagine they should do the same. "Well, if that is how
Microsoft does it, then we will copy them because (perhaps) it is correct and necessary to do it that way."
However, they fail to realize that today's WinAPI was created a long, long time ago.
A huge number of functions began their life in 16-bit code. These functions were designed for requirements that simply do not exist today.
There is no need to use such ancient and uncomfortable approaches. Instead use a modern approach.
Here (in descending order of preference) is how you can transfer varyingly sized data without sacrificing the convenience of a call:
• [Special case, only for strings] BSTR/WideString. • The interface with the lpData and cbSize properties. • A DLL can export a function for freeing memory
which the DLL itself allocated.
• You can use system memory management (most often: HeapAlloc/HeapFree).
Here is a list for error handling (in descending order of
preference):
• COM style: HRESULT + ICreateErrorInfo. Delphi may additionally take advantage of the "magic" safecall call model.
• The function returns HRESULT.
• Functions return a sign of success/failure, the error code is obtained from GetLastError.
• Similar to the previous item, but implementing your own error code function.
Moreover, there is no need to use the naked functions - because today we have interfaces. Interfaces offer solid advantages:
• Automatic memory management
- no problems with the allocation / release. • Delphi gives you a safecall calling convention
and virtual methods
- for customizing the compiler "magic". • Simplified versioning, because an interface
can be uniquely identified by a GUID (aka IID). • Data is grouped with methods for handling this data. • Performance does not suffer (a lot).
What is the problem here?
DllMain is no ordinary callback-function. It is invoked by a very specific point (
).
There are very few actions that you can take in DllMain with guaranteed safety.
For example, loading libraries, starting threads, thread synchronization, COM calls, even calls to other libraries (except kernel32) – performing any of these actions inside DllMain may lead to blocking (deadlock/hang/freeze).
When you realize that developers usually do not consider whether the code they put in the initialization and finalization sections of their units is truly admissible there, you also realize that relying on such behaviour (automatic initialization of units from DllMain) is not the best design solution.
That's why your DLL must export the two functions like Init and Done, which will contain the actions that you otherwise would have inserted in the initialization and finalization sections.
Whoever loads your DLL should immediately import and call the Init function.
Later he should also call Done just before unloading your DLL.
http://blogs.msdn.com/b/oleglv/archive/2003 /10/24/56141.aspx
Error:
Using language-specific data types or other language-specific constructs
Obviously if you create an API to be used from various programs each written in a different language, then you cannot use structures that exist only in your language. For example, string (as well as AnsiString, UnicodeString,
ShortString, String[number]), array of (dynamic and open
arrays), TObject (i.e. any objects), TForm (and components), etc.
If you use a data type (or language construct) that has no counterpart in other languages, then code in this very different language simply will not be able to use your API. It would have to emulate the language constructs of your language.
So, what can be used in an API? The following:
• integer types
(Integer, Cardinal, Int64, UInt64, NativeInt, NativeUInt,
Byte, Word, etc. - with the exception of Currency); • real types
(Single and Double - except Extended, Real, Real48
and Comp); • static arrays
(array[number .. number] of) of the acceptable types; • set, enumerated and subrange-types
(with some reservations - see below;
it is preferable to replace them with integer types); • character types
(AnsiChar and WideChar, but not Char); • strings
(only in the form of WideString; strings
as PWideChar - allowed, but not recommended; PAnsiChar is valid only for ASCII-strings;
;
); • Boolean type
(BOOL, but not Boolean; ByteBool, WordBool and
LongBool are acceptable, but not recommended); • interfaces which use and operate
with acceptable types only;
• records with fields of acceptable types; • pointers to data of acceptable types; • untyped pointers;
• data types from the Winapi.Windows.pas unit (or a similar unit for non-Windows platforms);
• data types from other system headers
(they are located in the \Source\ RTL\Win of your Delphi;
replace "Win" path with OSX, iOS, etc. - for other platforms).
PChar strictly ANSI-string
prohibited is prohibited
Error:
Using a shared memory manager and/or packages
Any shared memory manager
(such as ShareMem, FastShareMem, SimpleShareMem, etc.) is a language-specific facility which does not exist in other
languages. So (as in the previous section) you should never use any of them in your API. The same applies to run-time packages (.bpl packages). This package concept exists only in Delphi (and C++ Builder).
Error:
Failing to protect each exported function with a try/except block
The explanation above should have made it clear that you cannot pass any objects between modules.
An exception is also an object (in Delphi).
Adding two plus two, we realize that you cannot throw an exception in one module and then catch it in another. The other module has no idea how to work with an object that was created in a different programming language. This means, of course, that you have to think carefully how you will report errors (as mentioned above). Here I am talking only about the particular case of an exception. Because an exception cannot (that is: should not) leave the module, you must implement this rule religiously: put a try/except block to catch all exceptions around the code of each exported function.
Note:
• The function can be exported explicitly (“exports
function-name”) or implicitly (e.g. as a
callback-function or other callback-function which returns a pointer to the calling code).
Both options must be wrapped in a try/except block. • "Catch all exceptions," of course, does not mean that you wrap the function in an empty try/except block. You must not turn off all exceptions. You have to catch them (yes, catch them all) and transform rather than suppress them.
You must convert each exception to something prescribed by your protocol (perhaps an error code, an
HRESULT, or whatever).
Note also that if you use the method recommended above (interfaces with safecall) then this issue is automatically covered for you. The safecall calling convention assures you that every method you write will be wrapped by a hidden try-except block through compiler "magic"; and no exception escapes your module.
What is the purpose of a shared memory manager? In a sense, a shared memory manager is a "hack". It is a quick and dirty way to solve the problem of exchanging dynamic data between modules.
Never use a shared memory manager at the beginning of a new DLL project.
A shared memory manager is a means of backward compatibility, but does not fit with new code. If you need to exchange objects (including exceptions), strings or other language-specific constructs you have to use BPL-packages, not DLLs.
If you have used a DLL, then you must not use Delphi-specific types and, therefore, must not use the shared memory manager.
Hence the comment at the beginning of the earlier section about the forbidden use of UnicodeStrings (and so on) in DLLs.
You can easily transfer data of varying size if you follow the guidance above; and you already know you should not use Delphi-specific types in a DLL.
Therefore, there is no need to use a shared memory manager at all (either alone or using run-time packages).
11
COMPONENTS
DEVELOPERS4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Error:
Using ANSI-encoding(s)
Support for Unicode appeared in the Windows API in 1996 (in Windows NT 4), and in 2000 Unicode support came to the client OS (Windows 2000).
Although Windows 95 did contain Unicode functions, there was no full support.
The mobile OS market was Unicode only from the start (Windows CE, 1996) through the PocketPC,
Windows Mobile, and up to Windows Phone - all these systems support exclusively Unicode, but not ANSI. That is, for more than 13 years Unicode has been the default choice in all Windows versions.
For more than 13 years the ANSI API Windows functions have been nothing more than stubs that do nothing beyond converting the string encoding before invoking Unicode-variants of themselves.
Support for Unicode in Delphi has been present since Delphi 3 - as part of the COM support (that is from 1997). Although until 2008 (Delphi 2009), the entire language support library (RTL) and the component library (VCL) worked in ANSI.
However, in spite of the wide open opportunity to use Unicode when constructing their own APIs most Delphi developers even since 1997 (over 16 years), have not hesitated to use the "familiar types" - that is, at best a PChar (equivalent to PAnsiChar on the systems of that time), and at worst – a string with the shared memory manager. Of course, those who were smart used PAnsiChar, and PWideChar (since 2000).
But WideString was hardly used - despite its undeniable advantages: there are no problems with the exchange of strings between modules, auto-conversion to string and back again, built-in support for Unicode, built-in pointer length.
Why the avoidance of WideString?
Probably because PWideChar is sufficient for easy transfer of data inside the called function, and returning data from called functions was required much less frequently.
To sum up:
always use Unicode in your API for strings - even if you are using Delphi 7 and work with ANSI-strings inside: it does not matter.
In 2013, the API must be Unicode. It is not 1995. Well, what about the ANSI-adapters (stubs) to Unicode-functions: are they necessary? No. Remember why they are there in the WinAPI: as a means of backward compatibility for legacy code which is not familiar with Unicode. This era ended in 2000 with the release of Windows 2000.
There is no need to create a means of
backward compatibility for something which never existed in the first place.
No code was using your 2013 API functions with ANSI – so there is no need to add ANSI-adapters to your API. Note that if you are using the recommendations in the preceding paragraphs you have already covered this issue. By now, you should be using WideString strings (aka -
BSTR) to pass string data between modules; or, in extreme cases, PWideChar.
You should not use PChar and PAnsiChar.
Gotcha:
Enumerated types
An enumerated type is a convenient way to declare case-types (rather than using integers). What is the problem here? Look at this code:
Question:
What is the size of the variable T in bytes?
This is an important question because size affects the position of the fields in records, when passing arguments to functions, and in much other code.
The answer is that in general you do not know the size. It depends on the compiler and its settings. By default, it is 1 byte for Delphi. However, another compiler and/or setting may result in 4 bytes.
Here is another question: since this type occupies 1 byte in Delphi, it can hold up to 255 values.
But what if your enumerated type has more than 255 values?
Answer: then the variable will occupy 2 bytes. Do you see where this is leading?
Suppose you have used 50 values in version 1 of your DLL, so the fields of this type occupied 1 byte.
In version 2 you have extended the possible values up to 300 - and the field now occupies 2 bytes.
It would not matter if we used this type only inside our own code.
But since you are sharing it with other code, such a change in the size of the data will be a complete surprise to the other code.
Overwriting (data corruption) and Access Violations are the result.
Note:
in fact the problem arises even with far less than 300 elements. It is sufficient that the type has a 300-th element:
You can solve this problem in two ways: 1. You can place the compiler directive {$Z4}
(or {$MINENUMSIZE 4}) at the beginning of each header file.
This will cause the compiler to make every enumerated type 4 bytes in size, even if you do not need so much. 2. You can use LongWord (or Cardinal) and a
set of numeric constants (const T1 = 0).
It is possible that the second method is preferable because it clearly answers the question:
What are the numerical values of the type's elements? type var type 300 var begin = ( , , ); : ; = ( , , , = ); : ; ( ( )); TTest T1 T2 T3 T TTest TTest T1 T2 T3 T4 T TTest
WriteLn SizeOf T // shows 2
12
COMPONENTS
DEVELOPERS
4
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
Gotcha: Records
The uncertainty about the size of enumerated types also applies to records:
What is the size of this record? 9? 10? 12? 16?
The amount of unused space between fields (filler bytes) also depends on the compiler and its settings.
Overall, I would recommend using interfaces instead of records when possible.
If you need to use a record:
either insert the directive {$A8} ({$ALIGN 8}) to the beginning of each header file, or use the keyword packed for the record.
The latter may be preferable - because the alignment rules in Delphi might be different from the alignment rules in another language (for instance consider the case of problems
similar to this bug:
). type record end = : ; : ; ; TTestRec A Byte B Int64 http://qc.embarcadero.com/wc/ qcmain.aspx? d = 75838 Gotcha: Sets
We can ask the same question about sets: how many bytes do they occupy?
Here, however, everything is more complicated, because a set can take up to 32 bytes,
and there is no compiler directive to control the size of sets. Overall, the set is a syntactic convenience for dealing with flags. So instead of sets you can use an integer type (again:
LongWord or Cardinal) and a set of numeric constants, combining them with OR for inclusion in the "set" and checking their presence in the "set" with AND.
Error:
Failing to provide user-arguments in callback-functions
A callback-function is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time.
For example, if you want to find all the windows on your desktop, you can use EnumWindows:
Since the callback function normally performs the same task as the code that sets it, it turns out that the two pieces of code are working with the same data.
Consequently, the data from the code setting the callback must somehow be passed to the callback function. function stdcall begin end procedure begin 0 end ( : ; : ): ; ; ; . ( : ); (@ , ); ;
MyEnumFunc Wnd HWND lpData LPARAM Bool
TForm1 Button1Click Sender TObject EnumWindows MyEnumFunc
/ / This is called once for each window in the system
Gotcha:
Mixing manual and automatic control of an entity's lifetime
In general, you should try to use automatic control of a lifetime.
You have less chance to screw up, because the compiler emits code to keep track of your objects and there is less (fallible) human work to do.
But in any case, there will always be places where you want manual control of a lifetime.
The junction of these two control mechanisms is what can cause problems.
As you know, TComponent does not use automatic reference counting and you must control its lifetime manually.
The problem in the above code is in the line GetSomething DoSomething.
A temporary (hidden) variable of the interface type is created (for storing the result of the GetSomething call), which is cleared in the last line (at “end;”) - after the object has been released.
Look at this code: type interface procedure end class procedure end var function begin end begin nil try finally end end = ; ; = ( , ) ; ; : ; : ; := ; ; := . ( ); . ; ( ); ; ; ISomething DoSomething
TSomething TComponent ISomething DoSomething
Comp TSomething
GetSomething ISomething Result Comp
Comp TSomething Create GetSomething DoSomething FreeAndNil Comp
.
For this purpose, a callback function is provided with a so-called user-argument: either a pointer or an integer (such as
Native(U)Int, but not (U)Int), which are not used by the API itself and transparently passed directly to the callback-function.
Or (in rare cases), it can be any value that uniquely identifies the callback function call.
For example, the system function SetTimer has idEvent, while EnumWindows function has lpData.
These parameters are not used by the functions and are simply passed directly to the callback-function unchanged. That is why we can use these parameters to pass arbitrary data.
If you do not implement user-parameters in your API, then the calling code cannot associate a callback-function with the data.
We will look at this issue in more detail in the next article.