Blaise 31UK PDF
Blaise 31UK PDF
Blaise 31UK PDF
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE
31/32
D E L P H I,
L A Z A R U S,
O X Y G E N E,
S M A R T,
A N D
P A S C AL
R E L A T E D
L A N G U A G E S
A N D R O I D,
I O S,
M A C,
W I N D O W S
&
L I N U X
maXbox
5 / 6 2013
Publisher: Foundation for Supporting the Pascal Programming Language
in collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep)
Stichting Ondersteuning Programmeertaal Pascal
BLAISE
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE
31/32
D E L P H I,
L A Z A R U S,
O X Y G E N E,
S M A R T, A N D
P A S C AL
R E L A T E D
L A N G U A G E S
A N D R O I D,
I O S,
M A C,
W I N D O W S
&
L I N U X
CONTENTS
ISSN 1876-0589
Royal Library -Netherlands
Koninklijke Bibliotheek Den Haag
Articles
Editorial
Book Review: Coding in Delphi
By Jim Duff Book:
Coding Delphi Author: Nick Hodges
Designing an API: common mistakes
By Alexander Alexeev
Newest Leap developments
Michael Van Canneyt
3D Printing By Bj Rao
Kinect ?! By Michael Van Canneyt
Smart Mobile Studio 2.0
By Primo Gabrijeli
Interview with David I:
plans about updating buying etc
A simple superscript text editor
By David Dirkse
Interview with Gwan Tan - better office
Using GEO services in Delphi applications
with TMS components
By Bruno Fierens
Correcting a bad API design:
By Alexander Alexeev
Page 3
Page 4
Page 8
Page 21
Page 26
Page 33
Page 41
Page 48
Page 50
Page 52
Page 57
Page 65
maXbox
Alexander Alexeev
Peter Bijlsma,
Michal Van Canneyt, Marco Cant,
David Dirkse, Daniele Teti
Bruno Fierens
Primo Gabrijeli,
Fikret Hasovic
Cary Jensen
Wagner R. Landgraf, Sergey Lyubeznyy
Max Kleiner
Kim Madsen, Felipe Monteiro de Cavalho
Jeremy North,
Tim Opsteeg, Inoussa Ouedraogo
Howard Page-Clark, Jeroen Pluimers
Rik Smit, Bob Swart,
Editors
maXbox
Correctors
Howard Page-Clark, James D. Duff
Trademarks
All trademarks used are acknowledged as the property
of their respective owners.
Caveat Whilst we endeavour to ensure that what is
published in the magazine is correct, we cannot accept
responsibility for any errors or omissions. If you notice
something which may be incorrect, please contact the
Editor and we will publish a correction where relevant.
Subscriptions ( 2014 prices )
1: Printed version: subscription 60.-- Incl. VAT 6%
(including code, programs and printed magazine, 6
issues per year excluding postage).
2: Electronic - non printed subscription 45.-Incl. VAT 21% (including code, programs and
download magazine)
Subscriptions can be taken out online at
www.blaisepascal.eu
or by written order, or by sending an email to
Advertisers
Barnsten
BetterOffice
Components 4 Developers
ITDevCon
Lazarus the complete guide
Learnto program
maXbox
Raize Software
Smart Mobile Studio
Pag
Page
Page
Pag
Page
Page
Page
Pag
Page
20
25
120
39
19
24
77
92
40 / 46
Copyright notice
All material published in Blaise Pascal is copyright SOPP Stichting
Ondersteuning Programeertaal Pascal unless otherwise noted and may
not be copied, distributed or republished without written permission.
Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of
the PGG for download, and that articles and code will be placed on
distributable data storage media. Use of program listings by subscribers
for research and study purposes is allowed, but not for commercial
purposes. Commercial use of program listings and code is prohibited
without the written permission of the author.
Editor - in - chief
Detlef D. Overbeek, Netherlands
Tel:+31(0)30 890.66.44/Mobile +31(0)6 21.23.62.68
COMPONENTS
DEVELOPERS
Subscription department
Edelstenenbaan 21 / 3402 XA IJsselstein,
The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile:
+ 31 (0) 6 21.23.62.68
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
Forward
Introduction
Acknowledgements
Frameworks used in Coding in Delphi
1
Exceptions and Exception Handling
2
Using Interfaces
3
Understanding Generics
4
Understanding Anonymous Methods
5
Delphi Collections
6
Enumerators in Delphi
7
IEnumerable
8
Run-time Type Information
9
Attributes
10 Using TVirtualInterface
11 Introduction to Dependency Injection
12 A Deeper Look at Dependency Injection
13 Unit Testing
14 Testing with an Isolation Framework
Appendix A: Resources
Appendix B: My Delphi Story
The list of chapters shows that the book is aimed at
experienced Delphi developers rather than being a
beginner's guide. Having already mentioned the
Forward and Introduction items above, one more
item to mention in support of the qualified author is
the final Appendix - My Delphi Story. This is the
third part that once more gives the reader the
confidence in going ahead to take in the technical
aspects of the book.
Coding in Delphi
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
What you will find are ways to make your code much
cleaner, much more powerful, and way easier to maintain.
This book is all about the cool, new code you can write
with Delphi. It wont matter whether you are building a
VCL or an FM application. Ive titled it Coding in Delphi
because I want to make it a book that shows you simple
examples of how to use powerful features that, is about
the code. These language features are indeed advanced
features they are new relative to, say, the case statement
and thus many of you are beginners to them. By the end
of this book, you wont be.
The approach Im taking for this book is to try to
distill things down to the very basics. The examples will be
very simple but the explanations deeper. I believe that if
you can understand the basic idea in a simple example, it is
but a small leap to using those ideas in more complex code.
There is no point in giving complex examples
when you can get the main thrust using fundamental
implementations that illustrate advanced ideas.
Between the code in this book and in the samples online
(https://bitbucket.org/NickHodges/codinginde
lphi) you can learn all the precepts and then begin
applying them to your own code. In other words, nothing
fancy here, just the basics it is then up to you to use these
ideas in your own code.
This book is not done its instead a living document.
Since it is self-published on a platform that makes iteration
very easy, I plan on having frequent releases to fix typos
(which will, Im sure, sneak through despite my best efforts),
improve examples and descriptions, and keep up with
technology changes. Owners of the PDF should get
notifications of new versions automatically.
If you are reading a paper version of this book, Im sorry I
cant deliver fixes to your hard-copy perhaps
some day that will be possible.
The book will be done when you guys say it is done.
Maybe, it will never be done because Delphi keeps
growing and expanding. I guess well see.
As a result, Im totally open to feedback please feel free to
contact me at [email protected]
with suggestions corrections, and improvements.
Please join the Google Plus group for the book.
I may even add whole new chapters.
Thanks for your purchase this book was a labor of love,
so every sale is icing on the cake.
Nick Hodges
Gilbertsville, PA
https://bitbucket.org/NickHodges
Coding in Delphi
Introduction
Over the years, Ive spoken in front of a lot of Delphi
developers. The one thing that I notice is that there are a lot
more of you Delphi guys than the average Delphi guy
thinks. There are Delphi folks everywhere. Also, I have
noticed that a lot of Delphi developers are behind. That
is, they are either using an older version of Delphi, or they
arent using or arent even aware of all the features in the
newer versions of Delphi that they are using.
Something I like to do when Im in front of folks is
ask a few questions about what people are doing. Im
always saddened that the response to questions like Who
is doing unit testing? or Who is taking advantage of
Generics?
is pretty meager.
This is particularly true for the language features and the
run-time library. Its quite easy to move forward with an
older code base, utilizing the new features in the IDE and
adding new things to your app using the new high level
frameworks and components that come in the newer
versions.
For example, you might have been developing an
application since Delphi 3, moving forward through
various versions. Along the way, you may have added
some DataSnap functionality, started using the Code
Insight features in the IDE, and when you moved to XE2,
you start poking around with FireMonkey.
But its fairly easy to ignore the new language features that
come along with those new versions.
For instance, two powerful language features were added
in Delphi 2009: generics and anonymous methods.
Both are features that enable the development of really cool
code and frameworks.
But if you didnt understand or feel the need for them,
then it was pretty easy to simply not use them.
You can still do all kinds of great stuff in Delphi without
them, but with them, well, you can write some
really beautiful, testable, and amazing code.
For instance, a relatively new framework that
exists only because of these new language features is the
Spring Framework for Delphi, or Spring4D, as Ill refer to it
in this book. Spring4D is a feature rich framework that
provides a number of interesting services, including a wide
variety of collections that leverage generics, an Inversion of
Control container, encryption support, and much more.
I view Spring4Dsolid as much a part of the Delphi
RTL as SysUtils is. Using Spring4D in your code will make
many, many things easier and cleaner. But many Delphi
developers dont know this yet.
If the above is familiar, this book is for you:
The Delphi developer that hasnt quite yet made the leap
over into the cool and amazing things that you can do with
the latest versions of the Delphi language.
This book is all about introducing these new
language features and some of the intriguing things you
can do with them. It will take a look at these new language
features, and then expand into some of the open source
frameworks that have sprung up (no pun intended) as a
result. It will then show you techniques that you can use to
write SOLID, clean, testable code.
You wont find much of anything about the IDE or
the higher level frameworks here. Screen shots will be few
but code examples many. You wont find anything about
how to build better user interfaces or fancy components.
COMPONENTS
DEVELOPERS
expert
Unwritten rules
(This section is based on
http://blogs.msdn.com/b/oldnewthing/archive/
2006/03/20/555511.aspx )
There are some basic ground rules that apply to all
programming, which are so obvious that most
documentation and books do not bother explaining them
(because these rules should have been internalized by
practitioners of the art to the point where they don't need to be
expressed).
A driver planning what route to take wouldn't even
e assume you are developing a public DLL.
consider taking a shortcut through somebody's backyard or
So you will have a .dll file, you will have the
going the wrong way down a one-way street.
header files (at least *.h and *.pas), and you will
In the same way that an experienced chess player doesn't
have documentation.
even consider illegal options when deciding his next move,
The header files (or headers) form a set of source files
containing structure and function declarations used in the an experienced programmer doesn't even consider
violating the following basic rules without explicit
API for your DLL.
permission in the documentation which allows
Typically they contain no implementation.
contravening the rule:
The headers should be available in several languages.
Everything not defined is undefined.
As a rule, this means the language used to create the DLL
This may be a tautology, but it is a useful one.
(in our case - Delphi), C (as standard) and perhaps additional
Many of the rules below are just special cases
languages (such as Basic, etc.).
of this rule.
All these header files are equivalent to each other,
All parameters must be valid.
representing only translation of the API from one language
The contract for a function applies only when the caller
to another.
adheres to the conditions, and one of the conditions is
The more languages you include the better.
that the parameters are actually what they claim to be.
If you don't provide header files for a particular language,
This is a special case of the "everything not defined is
then developers using that language will be unable to use
undefined" rule.
your DLL, (unless they are able to translate your header files
o Pointers are not nil unless explicitly
from a language you provide (Delphi or C) into their language).
permitted otherwise.
This means that failing to offer headers for a particular
o Pointers actually point to what they
language is usually a big enough obstacle that developers
purport to point to.
in that language will not use your DLL, although it is not
If a function accepts a pointer to a
an absolute block to such usage. From this perspective
CRITICAL_SECTION, then you must pass a pointer
COM looks more attractive (the API description is stored in
which points to a valid CRITICAL_SECTION.
type libraries in the universal TLB format).
This article describes typical mistakes,
features and pitfalls developers encounter
as they develop a public DLL API. In
general, this article serves as a kind of
check list for you. You can compare your
code with this list and find out how good it
is, and if it contains any of the mistakes
typically found in such DLLs.
COMPONENTS
DEVELOPERS
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.
COMPONENTS
DEVELOPERS
10
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
11
Gotcha:
Enumerated types
An enumerated type is a convenient way to declare casetypes (rather than using integers). What is the problem here?
Look at this code:
type
TTest = (T1, T2, T3);
var
T: TTest;
Question:
What is the size of the variable T in bytes?
This is an important question because size affects the
position of the fields in records, when passing arguments
to functions, and in much other code.
The answer is that in general you do not know the size.
It depends on the compiler and its settings. By default, it is
1 byte for Delphi. However, another compiler and/or
setting may result in 4 bytes.
Here is another question: since this type occupies 1 byte in
Delphi, it can hold up to 255 values.
But what if your enumerated type has more than 255
values?
Answer: then the variable will occupy 2 bytes.
Do you see where this is leading?
Suppose you have used 50 values in version 1 of your
DLL, so the fields of this type occupied 1 byte.
In version 2 you have extended the possible values up to
300 - and the field now occupies 2 bytes.
It would not matter if we used this type only inside our
own code.
But since you are sharing it with other code, such a change
in the size of the data will be a complete surprise to the
other code.
Overwriting (data corruption) and Access
Violations are the result.
Note:
in fact the problem arises even with far less than 300
elements. It is sufficient that the type has a 300-th element:
type
TTest = (T1, T2, T3, T4 = 300);
var
T: TTest;
begin
WriteLn(SizeOf(T)); // shows 2
Well, what about the ANSI-adapters (stubs) to Unicodefunctions: are they necessary? No. Remember why they are
You can solve this problem in two ways:
there in the WinAPI: as a means of backward compatibility
1. You can place the compiler directive {$Z4}
for legacy code which is not familiar with Unicode.
(or {$MINENUMSIZE 4}) at the beginning of each
This era ended in 2000 with the release of Windows 2000.
header file.
There is no need to create a means of
This will cause the compiler to make every enumerated
backward compatibility for something which
type 4 bytes in size, even if you do not need so much.
never existed in the first place.
No code was using your 2013 API functions with ANSI
2. You can use LongWord (or Cardinal) and a
so there is no need to add ANSI-adapters to your API.
set of numeric constants (const T1 = 0).
Note that if you are using the recommendations in the
preceding paragraphs you have already covered this issue.
By now, you should be using WideString strings (aka BSTR) to pass string data between modules;
or, in extreme cases, PWideChar.
You should not use PChar and PAnsiChar.
12
COMPONENTS
DEVELOPERS
For this purpose, a callback function is provided with a socalled user-argument: either a pointer or an integer (such as
Native(U)Int, but not (U)Int), which are not used by the API
itself and transparently passed directly to the callbackfunction.
Or (in rare cases), it can be any value that uniquely identifies
the callback function call.
For example, the system function SetTimer has idEvent,
while EnumWindows function has lpData.
These parameters are not used by the functions and are
simply passed directly to the callback-function unchanged.
That is why we can use these parameters to pass arbitrary
data.
If you do not implement user-parameters in your API, then
the calling code cannot associate a callback-function with
the data.
We will look at this issue in more detail in the next article.
Gotcha:
Mixing manual and automatic control
of an entity's lifetime
In general, you should try to use automatic control of a
lifetime.
You have less chance to screw up, because the compiler
emits code to keep track of your objects and there is less
(fallible) human work to do.
But in any case, there will always be places where you
want manual control of a lifetime.
The junction of these two control mechanisms is what can
cause problems.
Gotcha:
Sets
We can ask the same question about sets:
how many bytes do they occupy?
Look at this code:
Here, however, everything is more complicated,
type
because a set can take up to 32 bytes,
ISomething = interface
and there is no compiler directive to control the size of sets.
procedure DoSomething;
end;
Overall, the set is a syntactic convenience for dealing with
flags. So instead of sets you can use an integer type (again:
TSomething = class(TComponent, ISomething)
LongWord or Cardinal) and a set of numeric constants,
procedure DoSomething;
combining them with OR for inclusion in the "set" and
end;
checking their presence in the "set" with AND.
Error:
Failing to provide user-arguments
in callback-functions
A callback-function is a piece of executable code that is
passed as an argument to other code, which is expected to
call back (execute) the argument at some convenient time.
For example, if you want to find all the windows on your
desktop, you can use EnumWindows:
function MyEnumFunc (Wnd: HWND; lpData: LPARAM):
Bool; stdcall;
begin
/ / This is called once for each window in the system
end;
procedure TForm1.Button1Click (Sender: TObject);
begin
EnumWindows (@MyEnumFunc, 0);
end;
var
Comp: TSomething;
function GetSomething: ISomething;
begin
Result := Comp;
end;
begin
Comp := TSomething.Create(nil);
try
GetSomething.DoSomething;
finally
FreeAndNil(Comp);
end;
end;
COMPONENTS
DEVELOPERS
13
14
COMPONENTS
DEVELOPERS
Gotcha:
Identifying interfaces
Interfaces are different from other data types.
They have two levels of identification.
On the programming language level an interface is
identified by an identifier name (such as IUnknown,
IApplication etc.) and is no different in this aspect from
any other data type in Delphi.
Two interfaces with the same declaration but with different
type identifiers are considered to be different data types by
the compiler.
On the other hand, the interfaces may also be identified not
only at the level of programming language, but at run-time
(by the machine code) via meta-information: the
interface's GUID (IID). Two completely different
declarations, but with the same IID will be considered to be
identical by run-time machine code.
Gotcha: The immutability of interfaces
Once you have published an interface ("published"
means you publicly release a version of the DLL with this
interface definition), you cannot change it (either its IID or its
structure) because the interface is used by third party code.
Changing the interface declaration will break the other
(third party's) code.
Instead of changing the interface, you need to create a new
interface with a new GUID. You should create a new
independent interface (preferably and usually) or inherit
from your old interface (allowed).
Gotcha:
Expanding interfaces
Look at this code:
type
IColorInfo = interface
{ABC}
function GetBackgroundColor: TColorRef;
safecall;
...
end;
IGraphicImage = interface
{XYZ}
...
function GetColorInfo: IColorInfo; safecall;
end;
Gotcha:
Function return values
Functions or methods that return an interface (as in the
previous paragraph) present a problem for the extension.
Of course, in the beginning it is a convenient solution:
you can call the function "normally" and even hook them
into chains like this:
Control.GetPicture.GetImage.GetColorInfo.GetBackgroundColor
However, this state of affairs will exist only in the very first
version of the system. As you begin to develop the system,
you will begin to create new methods and new interfaces.
In the not too distant future you'll have plenty of advanced
interfaces; and base interfaces that were originally in the
program, at the time of its birth will provide only trivially
uninteresting functions.
Overall, very often the caller will need the newer interfaces
rather than the original ones.
What does this mean? It means that almost all the code has to
call the original function to get the original interface, and then
request a new one (via Supports/QueryInterface) and only then
use the new interface.
The result is not so comfortable, and even more uncomfortable
is the fact we now have a triple calls (original/old + conversion +
desired/new).
Let us look again at the previous point:
the modification of one interface makes it necessary to make
copies of all the interfaces that use it as a return value
- even if they themselves do not change.
The best solution for both cases is that the callee code indicates
to the called function which interface it wants
- the new or the old.
This can be done, of course, by specifying the IID:
type
IGraphicImage = interface
{XYZ}
...
procedure GetColorInfo
(const AIID: TGUID; out AColorInfo);
safecall;
end;
Note that now you cannot use the result of the function, as the
result has to have a specific type (of course it does not have it we should return interfaces of different types), that's why we use
the raw data output parameter.
Then, you can write code like this:
var
Image: IGraphicImage;
ColorInfo: IColorInfoV1;
begin
...
Image.GetColorInfo(IColorInfoV1, ColorInfo);
Color := ColorInfo.GetBackgroundColor;
...
var
Image: IGraphicImage;
ColorInfo: IColorInfoV2;
begin
...
Image.GetColorInfo(IColorInfoV2, ColorInfo); //
throw a "no interface" exception, if you run on the V1
ColorInfo.AdjustColor(OldColor, NewColor);
...
COMPONENTS
DEVELOPERS
15
Error:
Interfaces without an IID
Every interface in your API must be declared with a GUID
(interface identifier - IID).
You may be tempted to skip the IID for interfaces that are
returned from functions explicitly, without request by IID.
But, as we saw above, you need to design your API in such
way that you have no functions that return an interface via
Result - because it is extremely difficult to further expand
the system. Therefore, all of your interfaces must always
have an IID.
Error:
Missing interface declarations when declaring
the implementing class
As you probably know already, there are two ways to
implement interfaces in a class:
1. Automatically.
You simply declare
The problem in all these cases is that Delphi and C++ differ
TMyClass = class(the base class, the list of interfaces).
in their interpretation of the calling convention model with
Once you have declared interface support,
regard to returning complex types.
there is nothing more to do.
Delphi's documentation indicates that the following code:
2. Manually.
You override the virtual class method QueryInterface,
function Test: IInterface; stdcall;
analyse the parameters and then construct
is interpreted as:
and return the interface.
function Test: IInterface; stdcall;
while MS C++ literally follows the syntax and returns the
You would think that with the automatic method,
interface directly (EAX for x86-32).
we would surely have no problems; but look at the following
Thus, instead of declaring functions like this (for example): code (the key points are noted in comments):
function Test1: IInterface; stdcall;
function Test2: WideString; stdcall;
function Test3: TSomeRecord; stdcall;
or this one:
function Test1: IInterface; safecall;
function Test2: WideString; safecall;
function Test3: TSomeRecord; safecall;
The latter is valid for the simple reason that such code is
equivalent to:
function Test1(out Rslt: IInterface): HRESULT;
stdcall;
function Test2(out Rslt: WideString): HRESULT;
stdcall;
function Test3(out Rslt: TSomeRecord): HRESULT;
stdcall;
type
ISomeInterfaceV1 = interface
['{A80A78ED-5836-49C4-B6C2-11F531103FE7}']
procedure A;
end;
ISomeInterfaceV2 = interface(ISomeInterfaceV1)
// ISomeInterfaceV2 inherited from ISomeInterfaceV1
['{EBDD52A1-489B-4564-998E-09FCCF923F48}']
procedure B;
end;
TObj = class(TInterfacedObject, ISomeInterfaceV2)
// List ISomeInterfaceV2, but not ISomeInterfaceV1
protected
procedure A;
// necessary because object implements ISomeInterfaceV1.
Otherwise - a compilation error
procedure B;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
SI1: ISomeInterfaceV1;
SI2: ISomeInterfaceV2;
begin
SI2: = TObj.Create;
Supports(SI2, ISomeInterfaceV1, SI1);
Assert(Assigned (SI1));
// Fails, SI1 = nil (Supports call returned False)
SI1.A;
end;
16
COMPONENTS
DEVELOPERS
and
type
ISomeInterfaceV1 = interface
['{A80A78ED-5836-49C4-B6C2-11F531103FE7}']
procedure A;
end;
ISomeInterfaceV2 = interface (ISomeInterfaceV1)
['{EBDD52A1-489B-4564-998E-09FCCF923F48}']
procedure B;
end;
Gotcha:
Polymorphism and implementation
of interfaces
When your object descends from a class its polymorphic
behaviour is achieved by virtual means. But when you use
interfaces, all the methods of the interface are already virtual (by
definition).
Therefore, there is no need to use virtual methods to implement
interfaces (though virtual methods may be required for other reasons
- for example, to inherit functionality).
For example:
type
ISomeInterfaceV1 = interface
['{C25F72B0-0BC9-470D-8F43-6F331473C83C}']
procedure A;
procedure B;
end;
TObj1 = class(TInterfacedObject, ISomeInterfaceV1)
protected
procedure A;
procedure B;
end;
TObj2 = class(TObj1, ISomeInterfaceV1)
protected
procedure B;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
SI: ISomeInterfaceV1;
begin
SI := TObj2.Create;
SI.A; // calls TObj1.A
SI.B; // calls TObj2.B
end;
COMPONENTS
DEVELOPERS
17
Note:
of course, you can "solve" this problem by removing const
from the parameter declaration, but you need to
understand that providing a well-formed argument is the
task of the caller, not the callee.
In general, guided by the rule "Give way to a fool",
I would recommend not to use the const modifier for the
parameters of interface types, and, of course, not to use the
constructor directly when passing arguments to such
functions.
18
COMPONENTS
DEVELOPERS
4.
5.
6.
The destruction of the object a second time leads to fullscale chaos. If you are lucky, the crash inside the recursive
destruction will be able to identify its source; but if you are
unlucky, it may cause damage to the heap, which will
remain undetected for some time, after which you'll be
scratching your head.
Therefore, as a minimum, you should insert an Assert call
into your _AddRef method, to ensure that you do not
increase the reference count from zero during the
execution of a destructor:
Note:
such a check is not present in TInterfacedObject.
TInterfacedObject allows your code to run and call
the destructor twice.
This check will help you to easily catch "cases of
mysterious double calls to the destructor of the object."
But when you identify the problem, then what do you do
with it?
Here's one recipe:
http://blogs.msdn.com/b/oldnewthing/archive
/2005/09/28/474855.aspx
19
http://www.barnsten.com/smashing-deal
Or call: +31 (0)23 542 22 27
20
COMPONENTS
DEVELOPERS
expert
MOTION
When first started, the Lazarus IDE will prompt for the
location of the fpc compiler and the sources. For a default
installation, this is /usr/local/bin/fpc and
/usr/local/share/fpcsrc, respectively.
Once installed and started, the IDE is ready for use.
fpc-2.6.2.intel-macosx.dmg
The Free Pascal compiler.
The IDE calls the free pascal compiler
when it needs to compile code.
Pointables
on the move
fpcsrc-2.6.2-i386-macosx.dmg
The Free Pascal Sources.
The IDE needs this to provide code insight.
lazarus-1.0.14-20131116-i386-macosx.dmg
The actual Lazarus IDE.
The version numbers may change as FPC and Lazarus
evolve.
21
Under Mac
22
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
23
PUBLISHED BY
D E L P H I, L A Z A R U S, S M A R T M O B I L E S T U D I O
OXYGENE AND PASCAL RELATED LANGUAGES
As well as sharing with the author ... the passion of a few in the programming
community for good documentation..., it is good to see the following comment within the
Foreword section that is applicable to both today's learners and experienced developers,
now that their targets have gone beyond the single Windows Operating System : "A
maturing Lazarus has also helped slowly to convince developers for MacOS, Linux and
more recently Android and other platforms that Pascal can truly serve their needs."
In the opening chapter, the following is quoted to explain the contents and objectives of the book.
"This is a tutorial guide rather than a reference book.
When you've worked your way through the following chapters you should be able to understand how
to use Lazarus to tackle increasingly complex programming challenges, and have learned
not only how to use the Lazarus IDE and the Pascal language, but how to go about finding out what
you still need to learn." Below is the list of chapters, each one containing several topics, and
finishing off with either Review Questions or Review Exercises to keep the reader awake. A couple of
sub-topic lists are also included to show the levels of detail provided within those chapters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Starting to program
Lazarus and Pascal
Types, variables, constants and assignments
a.
Pascal types, b. Ordinal types, c. The boolean type,
d.
Enumerated types, e. Type conversion, f. Typecasts,
g.
Variables, h. Initialised variables, i. Assignment: placing a value in a variable,
j.
Extended numerical assignment operators,
k.
Constants and literal values, l. A program example: simple_types,
m. Typed constants, n. Pointers, o. Review Questions
Structured types
Expressions and operators
Pascal statements
Routines: functions and procedures
Class: An elaborate type
Polymorphism
Units, GUI programs and the IDE
Display Controls
a.
TLabel, b. exploring TLabel properties, c. TStaticText,
d.
TBevel and TDividerBevel, e. TListBox, f. TStatusBar,
g.
Further options, h. Review Questions
GUI Edit controls
Lazarus GUI projects
Component Containers
Non-visual GUI Support classes
Files and Errors
Working within known limits
Algorithms and Unit tests
Debugging techniques
Further resources
LEARN TO PROGRAM
Nr Nr
5 /56/ 2013 BLAISE PASCAL MAGAZINE
USING COMPONENTS
LAZARUS
ISBN 978-94-90968-04-5
4
24
96
DEVELOPERS
PUBLISHED BY
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
COMPONENTS
DEVELOPERS
25
Perception vs Proof
26
3D Printing
a More Tangible Idea
What does 3D printing have to do with
Pascal? Well...not much really. That is
unless Pascal somehow starts finding
its way into developing solutions here.
3D printing is an exciting and
emerging manufacturing technology.
One that Pascal could take part in.
In terms of software development,
Pascal provides all the solution.
But how that might eventually happen
will depend on you. With that in mind,
the incentive here is to present a very
brief but in-depth and critical
overview about 3D printing.
Something that sheds light on how it
essentially works, what it can do as
well as what it can't do (yet).
Misprint?
Why write another article about 3D printing?
The internet already offers an abundance of
information on the subject. Mountains, in fact.
Everyone has been talking and writing about it.
And, companies like MakerBot, Ultimaker and
several others are building a vast knowledge base
in the peer production realm. All True. But there
may be more to be said...and said again.
These days we can print out 3D objects in plastic,
metal, ceramics and even organic material. We can
print in just about any shape we can imagine.
All this suggests that we will soon be able to print
out trains, planes, automobiles and even complex
body parts. We also hear claims like; We will print
our own phones and Conventional manufacturing
techniques will soon be rendered obsolete. 3D
printing is the holy grail to manufacturing and the
distribution of products. Well...yes and no.
Talk is cheap and the web has a way of taking that
to the next level. The internet flattens things out.
Sure, truth has a way of eventually rising above it
all. Only the most robust of ideas will move up
through the ranks and prevail. Moving from
perception to proof is what brings value to
something. It, at the very least, makes it more
tangible to talk about. But that process takes time.
27
28
29
30
Depending on the technique, the Post-Process stage For the average user it may turn out to be an
impossible task to perform, especially these days.
relates to things like cleaning, assembling or
In fact, even when it comes to 3D modeled data
post-curing of the printed material after the
drawn in a graphics or CAD application, things can
Main-Process is completed.
take time to prepare and involve a lot of work.
Desktop 3D printing, certainly for home use, is still
In particular it is the Pre-Processing and
equally an art as it is a technology.
Main-Processing stages were the magic
happens and where Pascal can provide
The block diagram provides a birds-eye view of the
solutions. The Main-Processing stage pertains to
typical Pre-Process elements of 3D printing data
the control of the machine, the printer.
preparation. The 3D scan data process flow has also
been included to illustrate what's typically involved.
If you are new to 3D printing then you may be
inclined to think about the washer and dryer
solution. The 3D scan and 3D print solution. This is Solid Model
The 3D data used for printing must be a solid
a scenario where a 3D scanner is used to scan in
some usually trivial (typically broken) part and then model, a closed vessel. It must be leak-proof.
Think of this as modeling your house for
that part is reproduced through 3D printing.
3D printing.
A perfectly reasonable idea.
You may have modeled the front side but what
And, this line of thought is correct. It is certainly
about the back side and all in between. Modeling
where we are headed.
only one open side would not make sense to a 3D
But, in practice, and for most applications there is a printer.
lot more involved in the process. In many cases this A solid model is that which fully describes the
model in 3D space from all angles. Anything less
approach is simply too impractical, too difficult.
than that is in the strictest sense not a 3D model,
3D scanning can be (very) complex.
its not solid. It is certainly possible to force close a
In many ways it is still an art.
model, assuming that these closed sides are not of
This is certainly true when high levels of accuracy
are needed. Also, preparing the data for 3D printing interest.
can be equally, if not more, complex.
Sure, there are exceptions. But these are usually
not the rule.
MAIN PROCESS
3D PRINTER
PRE PROCESS
3D DATA
MERGE
3D SCAN
DATA
DECIMATE
SOLID
MODEL
MODEL
INTEGRITY
CHECK
SIZE,
ORIENTATE
ADD
SUPPORT
MATERIAL
SLICE
LAYERS
SMOOTH
G-CODE
EDIT
3D
PRINTER
3D
GRAPHICS
CAD
POST PROCESS
Nr 5 / 2013 BLAISE PASCAL MAGAZINE
31
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
KINECT
KINECT
KINECT
expert
Delphi,
Lazarus
COMPONENTS
DEVELOPERS
33
NUI_INITIALIZE_FLAG_USES_AUDIO
Request audio data.
NUI_INITIALIZE_FLAG_USES_COLOR
Request color data.
NUI_INITIALIZE_FLAG_USES_DEPTH
Request depth data.
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX
and
NUI_INITIALIZE_FLAG_USES_DEPTH
Function NuiImageStreamSetImageFrameFlags(
is that the former encodes a player index in the depth map:
hStream : THandle;
the depths are returned as word-sized values, and the 3 last dwImageFrameFlags : DWORD) : HRESULT;
bits of the word are used to encode a player index
The NuiImageStreamOpen function opens an image
(meaning that at most 7 players can be used)
stream. Which images the stream returns is specified
through the eImageType parameter, which can have one
Reading data from the device
of the following values:
The kinect API provides several data streams:
video, audio, depth map, skeleton data.
The API uses event handles to report the presence of data
in one of these streams.
34
COMPONENTS
DEVELOPERS
NUI_SKELETON_DATA = record
eTrackingState: NUI_SKELETON_TRACKING_STATE;
dwTrackingID,
dwEnrollmentIndex,
dwUserIndex: DWORD;
Position: Vector4;
SkeletonPositions: array[0..19] of Vector4;
eSkeletonPositionTrackingState: array[0..19] of
NUI_SKELETON_POSITION_TRACKING_STATE;
dwQualityFlags: DWORD;
end;
NUI_SKELETON_POSITION_HEAD,
NUI_SKELETON_POSITION_HAND_LEFT,
NUI_SKELETON_POSITION_HAND_RIGHT.
Each of these constants is an index in the
SkeletonPositions and
eSkeletonPositionTrackingState arrays.
The eSkeletonPositionTrackingState array
determines which of the SkeletonPositions
elements contains a valid position vector.
An element in the array can have one of the following
values:
COMPONENTS
DEVELOPERS
35
For the depth image, the data comes in the form of an array
of word-sized values. The byte size of the array is reported
using BufferLen, the length of a single scan line can be
retrieved with the Pitch method. The actual array can be
retrieved with LockRect.
Since the array is managed by the kinect driver, it is locked
when it is retrieved. It must be unlocked using the
UnlockRect call when it is no longer needed.
The data array is described by the following record
NUI_LOCKED_RECT = record
Pitch : integer;
size : integer;
pBits : pointer;
end;
Where Pitch and Size correspond to the BufferLen and
Pitch methods of the INuiFrameTexture interface.
The pBits pointer points to the actual array.
Each element in the array is aWord value between
NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE and
NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE
if near mode is enabled.
In normal mode, the minimum and maximum values are
NUI_IMAGE_DEPTH_MINIMUM and
NUI_IMAGE_DEPTH_MAXIMUM.
When NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX
was used when creating the stream, the depth images
word values are shifted, and the last 3 bits are used to
encode a player index. (3 is the value of
NUI_IMAGE_PLAYER_INDEX_SHIFT).
NUI_IMAGE_FRAME = record
liTimeStamp : int64;
dwFrameNumber: DWORD;
: NUI_IMAGE_TYPE;
eImageType
eResolution : NUI_IMAGE_RESOLUTION;
pFrameTexture: INuiFrameTexture;
dwFrameFlags : DWORD;
: NUI_IMAGE_VIEW_AREA;
ViewArea
end;
36
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
37
procedure TMainForm.OnNewDepthFrame;
Const
DS
= NUI_IMAGE_PLAYER_INDEX_SHIFT;
MINS = NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE shr DS;
MAXS = NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE shr DS;
var
IFDepth : NUI_IMAGE_FRAME;
FT : INuiFrameTexture; lck : NUI_LOCKED_RECT;
depth : pword; CD : Word; p : byte;
x, y : integer; w, h, GS : cardinal;
C : TCanvas;
begin
if (FSDepth=INVALID_HANDLE_VALUE) then Exit;
if Failed(
FKinect.NuiImageStreamGetNextFrame(
FSDepth,0,@IFDepth)) then Exit;
NuiImageResolutionToSize(
IFDepth.eResolution, w, h);
The above code retrieves the image from the kinect and calculates
a width and heigh with it. The next step is to retrieve the image
data from the INuiFrameTexture interface:
try
FT:=IFDepth.pFrameTexture;
if not assigned(FT) then Exit;
if Failed(FT.LockRect(0,@lck,nil,0))
then Exit;
try
if lck.Pitch<>(2*w) then Exit;
depth:=lck.pBits;
38
COMPONENTS
DEVELOPERS
if (P<>0) then
C.Pixels[X,Y]:=FPlayerColors[p]
else if (CD>=NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE)
and
(CD<=NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE) then
begin
GS:=Round(((CD shr ds) - MINS) / MAXS * 255);
GS:=GS and $FF;
GS:=GS or (GS shl 8) or (GS shl 16);
C.Pixels[X,Y]:=GS;
end
else
C.Pixels[X,Y]:=clBLack;
Inc(depth);
end;
C.Unlock;
Conclusion
The kinect is a device which is one way of
implementing a Natural User Interface: use the
human body to control the computer. While it is
originally aimed at gaming, there may be
specialized uses for this device outside the
gaming industry. For a more fine-grained control
of the computer, the resolution of the Kinects
procedure TMainForm.TBAngleChange(Sender: TObject); skeleton detection is not fine enough:
it cannot detect individual fingers of the hand.
Var A : Longint;
begin
This gap may be better filled by the Leap Motion
If TBAngle.Position=FLastPosition then Exit;
device.
If Not Failed
Both devices are available to Object Pascal
(FKinect.NuiCameraElevationGetAngle(@A))
programmers, and there are certainly Object
then begin
Pascal game programmers that will consider the
if (A<>-TBAngle.Position) then
A:=-TBAngle.Position;
ability to use the kinect a nice addition to their
begin
if Failed
(FKinect.NuiCameraElevationSetAngle(A))
then ShowMessage(Format(SErrSetAngle,[A]));
FLastPosition:=-A;
end;
end
else
ShowMessage(SErrGetAngle);
end;
COMPONENTS
DEVELOPERS
39
30
expert
SMART
41
Figure 8: Turning
Figure 5: SmartMS-design
44
45
And for the end I'd like to show you how to write a Node.js
application (the code also comes from the Mileage clientserver demo).
,rl:function(S,E1,yco){var IB=0;S.ny=E1;
S.kG=yco;$ArraySetLenC(S.Vk,(S.kG+2),
function(){return[]});var $tR;
for(IB=0,$tR=S.Vk.length;IB<$tR;IB++)
{$ArraySetLen(S.Vk[IB],(S.ny+2),false)}}
46
procedure TServer.Run;
begin
//load http module
var http := NodeJS.http.http;
console_.log([
'Server running at http://127.0.0.1:80/']);
end;
Credits
At the end I'd like to give the credit where it's due.
Smart Mobile Studio wouldn't be there without
Lennart Aasenden, the original author, Jrn Einar
Angeltveit, our CEO, Eric Grange, author of the
Pascal-to-JavaScript compiler, Christian Budde,
who wrote lots of version 2 code including
complete v2 designer, and Andr Mussche, who
wrote RemObjects and DataSnap connectors.
www.SmartMobileStudio.com
Editor:
First to make it easy
Could you explain the plans about updating buying etc of
Delphi and how you want to proceed in the near future?
David I.
We are investing a lot of time and money to expand the
reach of platforms we are supporting for Delphi and
C++Builder.
This is not just a Windows world anymore.
Over the past few years, mobile and device platforms have
expanded the types of applications developers need to
build beyond just the desktop.
At the same time the requirements for applications have
grown to include the Internet, web services, REST services
and cloud computing.
All of these new platforms and architectures have allowed
us to help expand the reach of developers.
Along with these new innovations, we are delivering
enhancements to our products, IDEs, components, libraries
and tools.
FastReport
48
COMPONENTS
DEVELOPERS
David I
I see no dangers anywhere. We are providing capabilities
for all developers whether they are mature, youngsters or
newbies. We are the only native code, optimizing compiler,
rapid application prototyping developer tools company on
the planet that offers one code base,
multi-device targeting that is on all of the major platforms
Windows, OS X, iOS and Android.
We give you a wide range of RTL functions and
components to improve your ability to build apps for the
platforms and at the same time we allow you to go right
down to the operating system and the hardware if you
need to.
We have been doing these things since the days of DOS
(Turbo Vision) and Windows (Object Windows, VCL and now
FM). We know how to abstract the OS and hardware
without keeping developers out of those spaces.
Everything, the APIs, devices, sensors, operating system
and other hardware features are available for your
programs.
We have millions of seasoned professionals and new
generations of young developers using our products
around the world..
This year, South Africa has chosen Delphi as the
standard programming language and product for their
High Schools computer classes. This means that a new
generation of South African students will learn object
programming, component based development, event
programming and rapid application prototyping in high
school and will be well prepared for college and industry.
Editor
Will you go there?
David Intersimone :
I have been to South Africa one time before and am
planning on going again sometime early next year.
I look forward to my return visit to South Africa.
Thanks David!
COMPONENTS
DEVELOPERS
49
expert
Delphi 7
plaintext: boolean;
50
COMPONENTS
DEVELOPERS
2.
procedure TForm1.FormKeyPress(Sender: TObject;
var Key: Char);
const fmask = ['a'..'z','A'..'Z','0'..'9','+',
'-','*','/','^','=',';',' ','.',',','(',')'];
var mask : set of char;
begin
mask := fmask;
if plaintext then mask := mask + [#08];
//add backspace character
if not (key in mask) then key := #0
else
if plaintext = false then
begin
addchar(key);
key := #0;
end;
end;
660
30
"courier new",
size 12
style bold
procedure kbLeft;
begin
if cursX > 1 then
begin
dec(cursX);
if cursX - bias < 1 then dec(bias);
setmodeF;
end;
end;
If the cursor exits the box at the left side, the bias must be
adjusted to keep the cursor inside.
setmodeF sets variabele upmode true or false, which takes
care of the vertical cursor position. In this case, the right
character position (F = forward) is copied.
procedure kbBack handles the backspace:
procedure kbBack;
var i : byte;
which yields a fixed character width of 11 pixels.
begin
The formulabox may display 60 characters at the time.
if cursX > 1 then
However, a formula may be much longer,(up to 250
begin
characters) , in which case part of the formula is outside the
for i := cursX-1 to textlength-1 do
box. The number of characters left outside the box is
formtext[i] :=
var bias : byte;
formtext[i+1];
Also the position of the cursor has to be remembered
formtext[textlength].ch := #0;
var cursX : byte;
dec(textlength);
Accuracy is important:
dec(cursX);
if cursX - bias < 1 then dec(bias);
cursX has the index of the character where it is placed
setmodeB;
before in the text. Far left of the paintbox is position 1.
end;
end;
COMPONENTS
DEVELOPERS
51
Editor
Gwan
Could you give some details about you and your company? Well, I started my first company with two friends just after
my study in 1989/1990.
Gwan
That company was called Co-Assist and we did a lot of
Well, I'm 54 years old. I started programming at university work with Turbo Pascal but later mostly with Paradox
(TUE) in 1977. There we learned languages like ALGOL60 (Borland's database program at the time).
and APL and programming on punch cards for a
In 1997 I left Co-Assist and founded Sibylle IT.
Burroughs mainframe. Later in the study (electrical
A number of years ago I also started a cooperation with a
engineering) I learned Pascal especially Turbo Pascal.
German company called better office.
Turbo Pascal was actually the first program I bought
That cooperation led to founding the company better
legally because of the amount of handbooks coming with
office benelux and is a cooperation between better
it. Copying them at the time was almost as expensive as
office (Germany), Sibylle IT and PSO (Jeroen
buying the program.
Pluimers'company).
Editor
What does TUE mean ?
Gwan
Technical University Eindhoven (at that time it was the THE
Technische Hogeschool EIndhoven).
Editor
How did this cooperation start?
It is unusual that people have interest beyond the border
other countries.
Gwan
Well it started actually at a BorCon (Borland Conference) in
Editor
the USA. I met the owners/directors of better office and on
The technical part of it, was that of any influence?
personal level we had a click.
At the time I had no direct interest in doing any work in
Gwan
Germany. But as I got an invitation for a Christmas Party, I
Electrical engineering is only taught at technical
decided to go to Oldenburg (city in the north of Germany near
universities.
Bremen).
At technical Universities you can achieve the engineers title
So the contact was continued on German soil. With these
(Ir.) At general universities you can become a Master of
first contacts I met other German developers (and
Science, (M Sc) Docterandus in Dutch (Drs.)
publishers) and I was invited to the first German Delphi
But electrical engineering is mostly an applied science.
Conference (EKON).
The more theoretical study is physics.
During the years I got more and more involved with
I always liked the applied sciences more than theory.
German developers and slowly got some clients there too.
In the beginning I wanted to specialize in data
As the German market was getting more and more
communications. But during a training period in the USA
interesting and there seemed to be good opportunities for
I got to my hands on the first Apple Macintosh and found
Dutch developers I sought a more structured approach to
programming a much more interesting area,
the German market leading to better office benelux.
especially with user interfaces and databases.
Back at university I continued in that area,
Editor
also as at the time there was no department of Information
So now I understood you are mainly working in Germany.
Technology.
What special fields of interest?
That area was divided between the mathematical
department and the electro-technical department (faculty of
Digital Systems).
52
COMPONENTS
DEVELOPERS
Gwan
What do you mean with combine with? We have done
projects were we have Pascal clients with Java middleware
etc. Is that what you want to know?
Editor
Yes. And the databases?
Gwan
As for databases we mostly use InterBase or Firebird if we
have the freedom of choice. Often we have to connect and
develop with MySQL, SQL Server or Oracle. Basically we
know InterBase and Firebird best but we are extending our
knowledge and experience in MySQL and SQL Server as
often clients expect us to work with these databases.
Editor
Do you have a project that you are especially proud of?
Gwan
Depends on what to be proud of. Technically I'm very
proud of the first production planning system we
developed for a customer in Mainz.
On a more general level I'm very proud of 'having
survived' so far in the ERP-project just across the border.
That project was partly challenged for technical reasons
but even more for organizational reasons.
At the start the requirements seemed to be quite good
defined but as the project went on it became clearer the
requirements were not as complete as expected.
So much more interaction with the users was necessary to
find out what had to be developed.
In the meantime especially top management put a lot of
pressure on getting the program(s) up and running.
Especially Edwin van der Kraan, my main developer in
this project, had a very hard time.
But now after 2.5 years the system is up and running and
stable. So I'm proud of the project and especially proud of
Edwin.
Editor
What are fields you would especially point at for reasons
of learning and avoiding eventual upcoming problems?
How do you respond to pressure without getting squeezed?
Gwan
Pressure is at almost all projects present. But sometimes
the pressure is very high. To keep the pressure bearable
one has to keep the targets of the project well defined and
plan sufficient moments where especially management can
see the progress made and the direction of the project.
Depending on the part of the project and the pressure of
management one has to plan those moments more often.
But do not forget to explain to management that such
moments cost time and therefore will slow down the
project. These milestones should be useful for checking the
progress and direction of the project.
Editor
Editor
I think planning and explaining the plans are the most
If you work with Pascal what other languages you have to difficult and therefore very often underestimated subjects
combine with?
for a project. Do you have any special advice or helpful
ideas for developers?
COMPONENTS
DEVELOPERS
53
54
COMPONENTS
DEVELOPERS
Editor
We organized that last time in Leiden - Netherlands and
that was a great success - because of meeting each other.
Some reasons: we had 14 speakers and all the speakers as
well the developers were very "touchable". Attendees
thought this was great - so we will do that over again at
our next conferences.
And now for something quite different: What is your
opinion about FireMonkey? Do you ever use it in your
projects?
Gwan
I believe FireMonkey might be the way to give Delphi a
future. I have no idea if it will be a success. I believe in the
Delphi IDE and I love Pascal but as Pascal is almost not
used at schools and universities one has to attract the
future developers and IT-managers.
For that it has to be 'sexy' again. I will have a closer look at
it as soon as I can find more time.
I also hope to get more info from the upcoming
conferences. Online fora are great but listening to the
attendants of (Delphi-) conferences gives a better or at least
other impression.
If the impression is positive I will have a more detailed
look in what way I can use FireMonkey, what kind of
projects, what kind of applications and especially in what
kind of situations.
Clients might be asking about it in the future but I expect
that in the beginning we will have to propose using it to
potential clients.
They often do not know it.
Editor
We have plans for creating a Pascal learning program, at
least for the Netherlands.
Gwan
A Pascal learning program sounds great. The main issue
will be how to get it to the attention of teachers and even
better students? For those who are already interested in
Pascal and want to learn it they will be very happy to find
such a program as Pascal/Delphi-books are getting rare.
But if you can get it to the schools and universities a major
step in getting future Pascal developers is made.
Editor
What do you actually really miss in Delphi?
What would you you like to be changed?
Gwan
I do not miss that much in Delphi. I'm still using Delphi XE
for the current main project.
Missing functionality is mostly covered by extensions like
GExperts and component packs like Developer Express.
My main problem is the documentation of programs.
Most developers do not like to document and they barely
describe what is in each of the modules.
I would like a way for developers to describe their
reasoning for certain parts of the program.
Next to the questions where did he do what.
I often have the question WHY?
Gwan
I have seen a demo of the Leap Motion in Leipzig at the
Delphi Tage. I was quite impressed by its possibilities. It
does need some work to get a decent version out but I can
see some great opportunities with it.
I will have to think some more about itt as I have to find
possible uses for my customers. It might be interesting to
develop some managerial/marketing applications with it for
instance to manipulate the views of graphs etc.
Editor
What's your idea of 3d printing? We want to make it
available for Delphi and are working hard to get that done...
Gwan
I find 3D printing very interesting. I even thought about
getting me one just to try it. Except from finding the time to
'play' with it I did not have an immediate idea for something
to print that I could use. I will first have to spend some time
to think about what I would like to print with it before
searching for the best 3D-printer to do the job. I am certain
that 3D-printing will become more and more important in
the future.
The price has gone down a lot and the capabilities of those
'cheap' printers are improving a lot. To be able to develop
applications with Delphi for it would be great as I expect
Editor
customers to ask for those kinds of applications in the near
How do you feel about the upcoming updates of versions in future. So if you have something for Delphi applications
Delphi every 6 mays?
with 3D-printers let me know. I would be very interested in
The next XE6 will probably be released in April...
trying it.
Gwan
I can understand Embarcadero needs to get a better cash
flow in order to keep investments in development possible
but I do not believe the market will accept updates to be
released that often. Especially if they charge the normal
cost. Developers (and their bosses) will not go for each
update and will only update if one really needs to update.
Also if updates are released that often most updates will be
actually just patches. They should be free anyway and
made available as soon as possible. So if Embarcadero is
really going for a release schedule of once every 6 months
and charges for each update their standard prices I expect
part of the market to turn away from the product.
How large that part will be is hard to predict but as the
Delphi market is already small any part will hurt.
Editor
Did you ever consider the other Pascal? FPC- combined
with Lazarus? Have you ever tried that?
Editor
Sure! In this issue we have started our first articles about
this. There is even a possibility of creating the so called 4th
dimension: Interactive layer printing...Like printing a layer
that is able to react towards light or temperature. One could
even print layers that enable special things like creating an
interactive object with printed chips on it...
Gwan
The future capabilities of 3D-printing are going to be
enormous. Already some creative minds have found new
ways to use 3D-printing. I wonder what will be done with it
in the future. So your articles will give everyone the chance
to become part of that future.
I do think that that is one of the main reasons for magazines,
conferences and fora etc. to be reading and discussing the
future not just what can be done today but also what might
be possible in 5 or 10 years....
Gwan
I have not tried Lazarus yet but especially if Embarcadero
is moving to a 6 months update schedule Lazarus will be a
possible alternative. I'm sure I will have a look at Lazarus
in the next 3-6 months if only to see what it can and how
much work it would be to switch from Delphi to Lazarus.
For now I would not switch to Lazarus but it depends very
much on Embarcadero and their path for the future.
Editor
I have two final questions:
We have been busy creating the new extra userinterface,
not for everything useful but can be the Leap Motion...
Have you heard about that or seen something?
COMPONENTS
DEVELOPERS
55
Introduction
In the past few years, a vast array of services
related to a position on our planet earth became
available. With a wide range of components, TMS
software offers seamless access to these services
from Delphi VCL desktop applications, IntraWeb
web applications and FireMonkey mobile
applications running on iOS or Android. As such,
it's mostly your imagination that is the limitation
of what you can do these days with geo services
in your applications.
webgmaps1.MapOptions.DefaultLatitude := 51.2;
webgmaps1.MapOptions.DefaultLongitude := 4.37;
webgmaps1.Launch;
57
58
COMPONENTS
DEVELOPERS
59
Geolocation
Geolocation is the name used for all kinds of techniques
that provide information about the location of a device.
These days, most mobile devices have a GPS built-in and
this can return the longitude and latitude of the device
immediately. From Delphi XE4, the non-visual component
TLocationSensor is provided that allows you to get this
information. Using TLocationSensor is easy.
Set LocationSensor.Active = true and via the
event OnLocationChanged, the position is returned.
The accuracy of determining this location is around 10
metres typically. When no GPS is available, we must resort
to different techniques. These techniques can be:
ISP IP address based: many ISPs have a database of
what IP address range is being used in what area. Services
exist that gather this information that can be used to
retrieve location information based on an IP address.
Cell phone based: when a mobile device is connected
to a cell phone access point, the position of the cell phone
access point is known and thus also the area the signal of
this cell phone access point covers. There are also services
that collect this information and make it accessible.
OpenCellID is an example.
See: http://www.opencellid.org/cell/map
60
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;
61
Routes
A final important part in useful geo information based
services we can consume from Delphi applications, is
getting routing or directions information to travel from a
location A to a location B. Again, the three major suppliers
of these services are Google with the Google Directions
API, Microsoft with Bing Routes API and the Openroute
service (http://www.openrouteservice.org/). Such service
typically works in following way: we make a request
based on two locations, either specified as two sets of
longitude/latitude of two sets of addresses, the start
address and end address. The service then returns one
route or a set of routes that can be used to travel from start
point to end point. Note that some services also support
waypoints, i.e. points between the start point and end
point the route must go along. A route is typically
returned as a series of textual descriptions of the route to
follow. Each part of the route that is described is called a
leg. A set of routes can be returned when alternative routes
exist. Along the textual description, typically also polygon
data is returned and this polygon data can be used to
visualize the route on a map.
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
http://www.tmssoftware.com/site/webosmaps.asp
63
64
type
PEnumArgs = ^ TEnumArgs;
TEnumArgs = record
ClassName: String;
Windows: TStrings;
end;
function FindWindowsOfClass(Wnd: HWND;
lpData: LPARAM): Bool; stdcall;
var
Args: PEnumArgs;
WndClassName, WndText: String;
begin
Args := Pointer(lpData);
SetLength(WndClassName,
Length(Args.ClassName) + 2);
SetLength(WndClassName, GetClassName(Wnd,
PChar(WndClassName), Length(WndClassName)));
if WndClassName = Args.ClassName then
begin
SetLength(WndText,
GetWindowTextLength(Wnd) + 1);
SetLength(WndText, GetWindowText(Wnd,
PChar(WndText), Length(WndText)));
Args.Windows.Add(Format ('%8x:%s', [Wnd,
WndText]));
end;
Result := True;
end;
Memo1.Lines.BeginUpdate;
try
Memo1.Lines.Clear;
EnumWindows(@FindWindowsOfClass,LPARAM(@Args));
finally
Memo1.Lines.EndUpdate;
end;
end;
COMPONENTS
DEVELOPERS
65
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Edit1: TEdit;
type
TCallback = function (FoundData: TData): BOOL;
cdecl;
function RegisterCallback (Callback: TCallback):
Integer; cdecl;
// Adapter
strict private
class function
InternalEnumWindowsCallback(Wnd: HWND; lpData:
LPARAM): Bool; stdcall; static;
// Hi-level interface
protected
function EnumWindowsCallback(const AWnd:
HWND): Boolean; virtual;
function EnumWindows: Boolean;
end;
// ...
var
GMemo: TStrings;
66
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
67
You can see that all of the code consists of four lines: begin
(aka - the prologue), assigning the function to variable,
the call, end (aka - the epilogue). Each line is labeled with
comment (in bold font), which shows the location in the
module and the code line itself. Lines below the comment
is a machine (native) code that was compiled from that line.
Do not worry, you absolutely do not need to
understand what it says! All you need is to determine the
beginning and end of the function. It is easy to do: start of
the function is at the very top and marked by a blue line.
The end of the function is clearly in line Unit1.pas.43: end;
- i.e. you need to take another 6 lines after it - up to a line
Unit1.pas.46: begin - line which starts some other function.
Note: If you do know assembler, then you know that the
function ends in a line with a ret command (return control),
and the next line is just a garbage filler.
Select the code with your mouse and copy it somewhere
via the clipboard. Again, you do not need to run this code!
If you do not know assembly language, then all you need
to know:
The first column is the address of the instruction.
These addresses belong to your exe (after all it is the exe code
that we have called from Button1Click) and we are not
interested in these. The second column: hex-code of
machine code. That is, this is the machine code in a pure
form, which will be executed by the processor (CPU)
and this is where we are concerned.
The third column - is the assembly code corresponding to
machine code. The great thing about this listing - we do
not need the assembly code, we need only native code.
Now I'll explain why... Now look: this function has only
two values of the variables:
68
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
69
{$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
COMPONENTS
DEVELOPERS
71
72
COMPONENTS
DEVELOPERS
Win64
Unit1.pas.34:
begin
00000000006990B0 55
push rbp
00000000006990B1 4883EC30
sub rsp,$30
00000000006990B5 488BEC
mov rbp,rsp
00000000006990B8 894D40
mov
[rbp+$40],ecx
00000000006990BB 48895548
mov
[rbp+$48],rdx
00000000006990BF 4C894550
mov
[rbp+$50],r8
Unit1.pas.35: GetMsgProc := Pointer({$IFDEF
CPUX86}$12345678{$ENDIF}{$IFDEF
CPUX64}$1234567890ABCDEF{$ENDIF});
00000000006990C3 48B8EFCDAB9078563412 mov
rax,$1234567890abcdef
00000000006990CD 48894520
mov
[rbp+$20],rax
Unit1.pas.36: Result :=
GetMsgProc(Pointer({$IFDEF
CPUX86}$87654321{$ENDIF}{$IFDEF
CPUX64}$FEDCBA0987654321{$ENDIF}), code, _wParam,
_lParam);
00000000006990D1 48B92143658709BADCFE mov
rcx,$fedcba0987654321
00000000006990DB 8B5540
mov
edx,[rbp+$40]
00000000006990DE 4C8B4548
mov
r8,[rbp+$48]
00000000006990E2 4C8B4D50
mov
r9,[rbp+$50]
00000000006990E6 FF5520
call qword ptr
[rbp+$20]
00000000006990E9 48894528
mov
[rbp+$28],rax
Unit1.pas.37: end;
00000000006990ED 488B4528
mov
rax,[rbp+$28]
00000000006990F1 488D6530
lea
rsp,[rbp+$30]
00000000006990F5 5D
pop rbp
00000000006990F6 C3
ret
{$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}
COMPONENTS
DEVELOPERS
73
74
COMPONENTS
DEVELOPERS
{$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.
COMPONENTS
DEVELOPERS
75
maXbox
PURE CODE FOR
OBJECT SCRIPTING
Features
http://sourceforge.net/projects/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;
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
COMPONENTS
DEVELOPERS
77
maXbox
78
COMPONENTS
DEVELOPERS
maXbox
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
<DIR>
97'370
254'464
580'096
5'426
3'866
103'424
138
10'544
59'060
71'807
11'887'616
5'133'220
994
12'503
42'773
2'309'571
9'533
383'488
17'202
36'854
135'168
docs
examples
exercices
crypt
source
web
bds_delphi.dci
dbxint30.dll
dmath.dll
firstdemo3.txt
firstdemo3.uc
income.dll
maildef.ini
maxbootscript_.txt
maxbox.mp3
maxbox.png
maxbox3.exe
maxbox3clx
maxboxdef.ini
maxboxerrorlog.txt
maxboxnews.htm
maxbox_functions_all.pdf
maxdefine.inc
midas.dll
pas_includebox.inc
readmefirst_maxbox3.txt
TIFFRead.dll
When you want to see a complete copy of that file, look at:
07.02.2013
10'544 maxbootscript_.txt
COMPONENTS
DEVELOPERS
79
maXbox
maXbox configuration
<<extended>>
Start maXbox
Load Boot
Script
<<extended>>
bds_delphi.dci
(templates)
<<include>>
Actor HEX
in the BOX
Parse Ini
File
maxbootscript.txt
maxboxdef.ini
PRE PROCESSING
Compile Script
maxboxdef.ini
include file
<<include>>
Run App
Byte Code
Figure 3: Configuration Step by Step
80
COMPONENTS
DEVELOPERS
maXbox
Figure 4:
COMPONENTS
DEVELOPERS
81
maXbox
82
COMPONENTS
DEVELOPERS
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;
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.
COMPONENTS
DEVELOPERS
83
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.
84
COMPONENTS
DEVELOPERS
maXbox
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.
COMPONENTS
DEVELOPERS
85
maXbox
procedure TForm1_FormCreateShowRunDialog;
var ShellApplication: Variant;
begin
ShellApplication:= CreateOleObject('Shell.Application');
ShellApplication.FileRun;
end;
86
COMPONENTS
DEVELOPERS
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
: 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
*********************************************
All macros are marked with red. One of my favour is #locs means
lines of code and you get always the certainty if something has
changed by the numbers of line.
So the editor has a programmatic macro system which allows the pre
compiler to be extended by user code I would say user tags.
Below an internal extract from the help file All Functions List
maxbox_functions_all.pdf:
//---------------------------------------------------------------------------//**************mX4 Macro Tags **************************
//----------------------------------------------------------------------10183:
#name, i#date, i#host, i#path, i#file, i#head, i#sign, i#teach
10181:
10182:
10184:
10185:
10186:
10187:
10188:
10189:
10190:
10191:
10192:
10193:
10194:
10195:
COMPONENTS
DEVELOPERS
87
maXbox
Start maXbox
Actor HEX
in the BOX
<<extended>>
<<include>>
bds_delphi.dci
(templates)
Load Boot
Script
Parse Ini
File
Macro
Macro
Macro Actor
Macro
maxbootscript.txt
Macros
#name, get_UserNameWin, 11)
#date, datetimetoStr(now), 11)
#host, get_ComputernameWin, 11)
#path, fpath, internal func 11)
#file, fname,internal func 11)
#locs, intToStr(getCodeEnd), 11)
#perf,perftime, internal func 11)
#head, Format(%S: %S: %S: %S:)
maxboxdef.ini
Compile Script
Some DLL or Lib
<<include>>
Run App
Byte Code
88
Some macros produce simple combinations of one liner tags but at least they replace
the content by reference in contrary to templates which just copy a content by value.
COMPONENTS
DEVELOPERS
maXbox
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;
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.
COMPONENTS
DEVELOPERS
89
90
COMPONENTS
DEVELOPERS
maXbox
maXbox
COMPONENTS
DEVELOPERS
91
Ray Konopka
Interview with the famous writer of Developing Custom Delphi Components
Editor:
I would like to ask you about how you got started with Pascal?
You were one of the very early adapters I suppose?
Ray Konopka:
My very first experience with Pascal was Berkley Software
Distribution Pascal atI llinois Benedictine College (now
called Benedictine University) in 1986 in what I refer to as
my first "real" programming course.
I had a programming course in high school that used
AppleSoft Basic on Apple IIe computers, but there was a
real elegance to Pascal that I really liked. Soon after, I
started working with Turbo Pascal 3.
Editor:
How did you start, you met people from Borland by that time?
Ray Konopka:
Actually, meeting people from Borland came much later.
During my time at University, I continued to use Turbo
Pascal and really fell in love with the language and the
product. I even bought a copy for myself so I could work
with it at home. During my 3rd and 4th years at university
I had the opportunity to work at Argonne National
Laborator in the Physics department.
I used Turbo Pascal to write software that
communicated with various pieces of scientific equipment
to capture data for various experiments. It was during this
time that I switched from TurboPascal 3 to TurboPascal 4.
It was my first real world experience programming
outside of the classroom....
I graduated from University in 1989, where I majored
in Computer Science and Mathematics, with a minor in
Physics. The following year I attended Northwestern
University on a fellowship and 9 months later complete
my Masters degree in Computer Science.
I continued to use Turbo Pascal, and some other languages
throughout this time. After graduating from
Northwestern, I went to work for AT&T Bell Labs.
93
Ray Konopka:
FireMonkey is very interesting.
Excellent. The first edition
Editor:
I've been working with it since its inclusion
was published in 1995 and
Maybe even explain what a
into Delphi, and it is very flexible and has
the second edition titled
framework itself means.
tremendous potential.
A lot of people have vague
Developing Custom Delphi 3
But it is also very different from the VCL.
ideas about that...
Components came out in
1997. You can still find a
Ray Konopka:
copy or two on eBay every once and a while. It is also
I understand. Although I do like to write things that are
possible to get a PDF Edition of the second book. What is
more applied rather than simply provide a high level
really cool is that all of the material covered in the book
explanation.
still applies to developing Delphi components today.
But, I do understand your point.
Certainly there are more capabilities and features in the
VCL and FMX, but the principles covered in the book still
apply. Of course, this begs the question of writing another Editor:
I am always trying to attract new people towards Pascal. Do you
book.
have any ideas about that?
Editor:
Maybe you could do something about Firemonkey?
Ray Konopka:
The biggest factor in writing another book is time. Back
when I wrote the first two books, Raize Software was just
starting out, we didn't even have a product out when the
first book was written. Now, things are just so much
busier. What I have tried to do in place of writing a new
book is to cover the new topics in presentations and
articles. FireMonkey is very interesting.
94
Ray Konopka:
The biggest hurdle is the incorrect perception that Pascal is
an old language. Unfortunately, that is a hard hurdle to
get over because even if you look at 3 of the most popular
implementations of Pascal:
Delphi, Lazarus, FreePascal, only one is easily connected to
Pascal. So someone may have heard about Delphi, but may
not know its relationship to Pascal.
However, to make real in-roads, developers have to
be able to see that using a particular tool will allow them to
become successful.
Editor:
That is a real problem. I would love to hear an answer from our
readers. I was thinking of starters and doing the VCL.
Because we are starting this teaching project for schools:
PEP (Pascal Ecducation Program).
The problem with FM is, that its still evolving...
So you opt for the VCL?
Ray Konopka:
If you already have the VCL ones, done, then yes,
I would go with that and get some feedback.
Editor:
Problem is there are about 550 to be done...
Back to youre explainations: you had the chance to do buisnes
with the Walt Disney Company. How did that start?
And what is your job there?
Advisor or Developer or Creator?
Ray Konopka:
I started consulting with Walt Disney Parks and Resorts
back in 2002.
Initially, I was brought in for Delphi training, but shortly
after that, I became involved in the development of the
FASTPASS system, which is used in all of the Disney Parks
world-wide.
My work on FASTPASS led to other development
projects, such as the Free On Your Birthday promotion
that ran at Disneyland and Walt Disney World in 2009,
and the Give a Day, Get a Disney Day promotion
that ran in 2010. The success of these projects led to even
more work, but as a consultant there were restrictions on
just how much I could take on.
So, in October of 2011, I switched from consultant to
part-time cast member, which is what employees of The
Walt Disney Company are called.
And yes, I even have a Disneyland name badge.
To be precise I am a Sr Application Developer
and I continue to work on the architecture and development
of the FASTPASS system as well as several other projects,
including many new mobile projects that are
used by Operations Cast Members in the parks.
Editor:
About your component book: you think its still a good book for
starters in the field?
I was wondering if the book might still be worth to be reprinted?
Ray Konopka:
The first few chapters in the book are certainly useful for
any Delphi developer, but the book as a whole does focus on
a specific kind of development, namely reusable component
building.
As a result, its a book that is great to have in your
toolbox, but not necessarily the best choice for a developer
just trying to learn Delphi. Marco Cantus written several
editions of his Mastering Delphi book that would be better
Editor:
suited for that. However, one of the points that I make early
We already started with the first 50 components.
in the book is that there is a lot of value in gaining a better
understanding of how Delphi and the VCL is organized and
Ray Konopka:
You also need to consider which version of FM you want to architected. So while a developer may not have a specific
need to develop their own custom component,
support. Are you going to suppor them all?
understanding what all is involved will make the reader a
Or, just the most recent?
more effective Delphi developer.
That will dictate which version of Delphi is required,
Ray Konopka:
I suppose that would depend on what type of apps the
people testing it out are building.
The VCL is certainly a safe bet as there is still a lot of
development being done with the VCL.
But if your target audience is doing more mobile
development, then FM might make sense.
95
Free Tools
http://www.raize.com/DevTools/FreeTools.asp
BDS/Delphi Icons
Raize BDS Icons is an icon library that contains a
set of icons for the various file types used by the
BDS and earlier versions of Delphi. The icons in this
new library are more consistent in their design and
show a direct association with the BDS/Delphi.
IDE Palette Menu
Are you tired of scrolling through the pages of the
Borland Developer Studio, Delphi, or C++Builder
component palettes looking for the right page?
If so, then download and try out the Raize Palette
Menu expert, which when installed adds a new
menu to the selected IDE that provides instant
access to any page on the component palette.
Raize Font
The Raize Font is a clean, crisp, fixed-pitched sans
serif screen font that is much easier to read than
the fixed pitched fonts that come with Windows.
Ideally suited for programming, scripting, html
writing, etc., the Raize Font can be used in any IDE
or text editor.
96
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
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:
98
COMPONENTS
DEVELOPERS
For the other quadrants, the signs of xtx, ytx, xty, yty
may change.
COMPONENTS
DEVELOPERS
99
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
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
COMPONENTS
DEVELOPERS
101
Introduction to MVVM
The Model View ViewModel (MVVM) is an architectural
pattern used in software engineering that originated from
Microsoft as a specialization of the Presentation Model design
pattern introduced by Martin Fowler
MVVM in Delphi using the
Caliburn Micro for Delphi framework.
A bit of background on
Model, View and View Model
MVVM is one of the ways to help split parts of your
application into three layers. Doing so makes it easier to
"organize the mess", allows you to unit test not only the
business logic but also the interaction logic in an early
stage, and makes it easier to switch user interfaces when
aiming for cross-platform versions of your applications.
The really cool thing is that you do not have to start
with MVVM from scratch. You can apply the ideas from
MVVM into existing applications as well and evolve them
into a fully fledged MVVM application.
It means MVVM gives you a lot of freedom on which I
will write more at a later stage. Lets first focus on the
introduction though.
My encounter with MVVM was in the .NET and later in
the Delphi world with a self written framework similar to
what Malcolm Grooves presented at CodeRage 7.
Back then I wasn't too impressed with what I achieved.
Even though I was used to naming conventions, most of
the things were still manual labour. Too much tedious
manual labour.
Later I bumped into Caliburn for .NET, but at that
time I was working with applications that hadn't been
maintained since around 2005 in a way that was hard to
move to MVVM. Last year, I was really glad to see DSharp
was having an MVVM presentation model, and even more
happy that it was being revamped into something very
much alike Caliburn Micro, which was the successor of
Caliburn.
Pieces of the puzzle started falling into place when I
recognized how easily you could integrate it with existing
applications.
Before explaining more about MVVM, lets take a step
back and look at the 3rd object.
The 3rd object
expert
TECHNOLOGY
FACING
starter
ACCEPTANCE
TESTS
UNIT TESTS
INTEGRATION
TESTS
END TO END
TESTS
GRANUARITY / SCOPE
VIEW
PRESENTER
MODEL
VIEW
VIEW MODEL
MODEL
VIEW
VIEW = MODEL
VIEW MODEL
MODEL
VIEW
MODEL
VIEW
VIEW MODEL
MODEL
VIEW
MODEL
VIEW
THIRD OBJECT
5
102
COMPONENTS
DEVELOPERS
VIEW MODEL
Decoupling
COMPONENTS
DEVELOPERS
103
Patterns
In 2004, the - instantly famous - Gang of Four (abbreviated to
GoF) published the book Design Patterns: Elements of Reusable
Object-Oriented Software. The book is another catalog, this time
on patterns, with a common vocabulary about recipes for
developing new code. The Gang of Four consists of Erich Gamma,
Richard Helm, Ralph Johnson and John Vlissides.
Martin Fowler on the GoF book in relation to the 3rd object:
In my view the Gang of Four is the best book ever written on
object-oriented design - possibly of any style of design. This book
has been enormously influential.
The 3rd object is just a (relatively) new way of using patterns.
104
COMPONENTS
DEVELOPERS
VCL project
Name the form AppView, the form unit AppViewForm and clean
up the uses list, private and public sections:
unit AppViewForm;
interface
uses Forms;
type
TAppView = class(TForm)
end;
var
AppView: TAppView;
implementation
{$R *.dfm}
end.
Name the application MindScape_AppViewVCL:
program MindScape_AppViewVCL_Step00;
uses Forms,
AppViewForm in 'AppViewForm.pas' {AppView};
{$R *.res}
begin
ReportMemoryLeaksOnShutdown := True;
Application.Initialize();
Application.MainFormOnTaskbar := True;
Application.CreateForm(TAppView, AppView);
Application.Run();
end.
DUnit testing project
This is based on the standard DUnit project template with
a twist: it reports memory leaks, and a bit more cleaned
up code when switching between console and GUI
applications.
program MindScape_AppViewTests_Step00;
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
Forms, TestFramework, GUITestRunner,
TextTestRunner;
{$R *.res}
begin
ReportMemoryLeaksOnShutdown := True;
A common thing is that Caliburn Micro for Delphi depends
if IsConsole then
heavily on RTTI (Run Time Type Information)
with TextTestRunner.RunRegisteredTests do
to make the conventions and patterns work for you.
Free()
In the steps, I will put () parenthesis to parameterless methods to
else
begin
set them apart from properties or fields.
Application.Initialize();
Most of the times that is optional, but at times it is required,
and for me it makes it easier to see what the code is aimed at:
GUITestRunner.RunRegisteredTests();
method calls often have side effects, but most of the time accessing
end;
fields and properties should not.
end.
Some of the steps might sound overly detailed, and the total
Step 01: adding the interfaces and View Model
might scare you at first. The scaring is not on purpose,
Add two units to the projects:
but the fine grained steps are: for me it is important to show you
AppInterfaces with the interface definitions and AppViewModel
what it is involved, and what you might bump into. In practice
that contains the View Model.
these steps will take only a short amount of time.
For the project names, you can optionally include a _Step## prefix Later we will will add another unit with a Model as well.
After that make the modifications to the units and project file as
where ## has the step number.
shown in these sections.
I've done that in the examples below as that makes it easier for
you to find back the individual steps in the
DSharp code repository.
Lets get on with the steps...
Step 00: an empty VCL and DUnit application
Create a new project group that contains an empty VCL project
with one empty form, and a DUnit test project
that has no tests yet.
COMPONENTS
DEVELOPERS
105
with
{$IFDEF DEBUG}
Application.WithDebugLogger();
{$ENDIF DEBUG}
{$IFDEF CodeSite}
Application.WithLogger<TCodeSiteLog>;
{$ENDIF CodeSite}
Application.Start<TAppViewModel>();
106
COMPONENTS
DEVELOPERS
It almost is, and indeed it is the first step. And you get
another error that too is part of the learning experience:
Exception EResolveException
No component was registered for the service type:
IAppViewModel
{$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>();
COMPONENTS
DEVELOPERS
107
108
COMPONENTS
DEVELOPERS
unit AppViewModel;
interface
uses AppInterfaces, DSharp.PresentationModel;
type
TAppViewModel = class(TScreen, IAppViewModel)
public
constructor Create(); override;
end;
implementation
constructor TAppViewModel.Create();
begin
inherited Create();
DisplayName := IAppViewModel_DisplayName;
end;
initialization
TAppViewModel.ClassName;
end.
Step 06:
fixing the binding.
The previous step told about the importance of the
interceptor classes in the DSharp.Bindings.VCLControls
unit. And that is exactly the reason why at run-time you got
Count into the caption of the TEdit:
it wasn't bound to the integer value of Count in the View
Model, as the interceptor classes could not do their
work. The reason is that Delphi does not see them as they
are obscured by the units that expose the actual control.
The lesson is easy:
always make sure that units like
DSharp.Bindings.VCLControls that have
interceptor classes are always the last in
the uses list.
So the solution is very simple, modify the uses list from
uses
DSharp.Bindings.VCLControls, Classes,
Controls, StdCtrls;
into
uses
Classes, Controls, StdCtrls,
DSharp.Bindings.VCLControls;
Now run and enjoy the results of this step that was very
easy to perform, but had a high impact.
Step 07:
add buttons to increment or decrement
the count
Here you will see that the Caliburn convention of naming
controls not only holds for properties in the View Model,
but also for methods.
Lets start with the View Model: add two public methods
here named DecrementCount and
IncrementCount
procedure TAppViewModel.DecrementCount;
begin
Count := Count - 1;
end;
procedure TAppViewModel.IncrementCount;
begin
Count := Count + 1;
end;
Now add two buttons with the same name in the View:
type
TAppView = class(TForm)
Count: TEdit;
IncrementCount: TButton;
DecrementCount: TButton;
end;
You see that it doesn't, and that's what the next step
will fix.
COMPONENTS
DEVELOPERS
109
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;
Test_DecrementCount_MaximumCount();
Test_DecrementCount_MaximumCount_Minus1();
Test_DecrementCount_MaximumCount_Plus1();
Test_DecrementCount_MinimumCount();
Test_DecrementCount_MinimumCount_Minus1();
Test_DecrementCount_MinimumCount_Plus1();
Test_DisplayName();
Test_IncrementCount_MaximumCount();
Test_IncrementCount_MaximumCount_Minus1();
Test_IncrementCount_MaximumCount_Plus1();
Test_IncrementCount_MinimumCount();
Test_IncrementCount_MinimumCount_Minus1();
Test_IncrementCount_MinimumCount_Plus1();
function TAppViewModel.GetCanIncrementCount():
Boolean;
begin
Result := Count < MaximumCount;
end
procedure TAppViewModelTestCase.
Test_DecrementCount_MaximumCount();
begin
AppViewModel.Count := MaximumCount;
AppViewModel.DecrementCount();
end;
procedure TAppViewModel.DecrementCount;
begin
if not CanDecrementCount then
raise EInvalidOperation.Create(
'not CanDecrementCount');
Count := Count - 1;
end;
procedure TAppViewModelTestCase.
Test_DecrementCount_MaximumCount_Minus1();
begin
AppViewModel.Count := MaximumCount-1;
AppViewModel.DecrementCount();
end;
procedure TAppViewModel.IncrementCount;
begin
if not CanIncrementCount then
raise EInvalidOperation.Create(
'not CanIncrementCount');
Count := Count + 1;
end;
110
COMPONENTS
DEVELOPERS
procedure TAppViewModelTestCase.
Test_DecrementCount_MaximumCount_Plus1();
begin
AppViewModel.Count := MaximumCount+1;
AppViewModel.DecrementCount();
end;
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();
COMPONENTS
DEVELOPERS
111
and
(Value <= MaximumCount), 'Value');
BTW: Don't forget to add the unit Spring to the
implementation uses list of the AppViewModel unit.
Now setting Count to an out-of-range value causes an
EArgumentOutOfRangeException to be raised.
That brings us to the unit tests: we need certain tests to
expect certain kinds of exceptions. DUnit can
do just that using the little known ExpectedException
property, which means that some of the tests need
to be modified.
First add the SysUtils and Classes units to the
implementation uses list of the AppViewModelTestCase
unit. Add the line
ExpectedException :=
EArgumentOutOfRangeException;
Continuation:
Future steps for the example will be in the next Blaise
issue. They will cover binding TAction with
TActionManager, using the Caliburn logging, adding
unit tests for the new actions, adding increment-byvalue with tests, adding a Model to the View Model,
and creating a FireMonkey UI with TActionList next to
the VCL UI.
Test_DecrementCount_MaximumCount_Plus1();
Test_DecrementCount_MinimumCount_Minus1();
Test_IncrementCount_MaximumCount_Plus1();
Test_IncrementCount_MinimumCount_Minus1();
112
COMPONENTS
DEVELOPERS
expert
Delphi XE5
COMPONENTS
DEVELOPERS
COMPONENTS
DEVELOPERS
113
COMPONENTS
kbmFMX for XE5 (Continuation 1)
DEVELOPERS
After selecting the folder, the Delphi designer will open, and here is the main window with the mobile application unit created:
So, the new form in design mode will look like this:
114
COMPONENTS
DEVELOPERS
COMPONENTS
kbmFMX for XE5 (Continuation 2)
DEVELOPERS
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;
COMPONENTS
DEVELOPERS
115
COMPONENTS
kbmFMX for XE5 (Continuation 3)
DEVELOPERS
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
COMPONENTS
kbmFMX for XE5 (Continuation 4)
DEVELOPERS
On
http://www.components4programmers.com/produ
cts/kbmmw/download/sampleprojects.htm
you can find the LocalServer downloadable sample,
explaining how to use kbmMW based server embedded
in client application.
I have used the same schema here, only used SQLite
specific connectivity instead of BDE in sample.
So, after adding required components on main form, this is
how it looks like now:
{$IF DEFINED(ANDROID)}
kbmMWSQLiteConnectionPool1.Database := \TPath.Combine(TPath.GetDocumentsPath, 'myimages.sqlite');
{$ENDIF}
end;
you need to add System.iOUtils to the uses list.
In this instance, the Database path on the device will be
/data/data/com.fikrethasovic.AndroidFMXApp/ OK, back to kbmMW, you create standard QueryService,
and use SQLite specific components: Beside other
files/myimages.sqlite
components, added are two buttons, which are connected to
If your device is rooted, you can phisically access it,
the alMedia ActionList, which is responsible for the
but as default it is hidden from user,
unless you root our device, which will void your warranty. camera and gallery manipulation.
But, we need to use the Image captured by the Camera or
Notice, to use the Tpath class,
picked from the Gallery (or CameraRoll), so we will use
OnDidFinishTaking Event of standard media actions:
procedure
TTabbedwithNavigationForm.TakePhotoFromLibraryAct
ion1DidFinishTaking(
Image: TBitmap);
begin
if qClientSide.State <> dsEdit then
qClientSide.Insert;
(qClientSide.FieldByName('Image') as
TBlobField).Assign(Image);
end;
COMPONENTS
DEVELOPERS
117
COMPONENTS
kbmFMX for XE5 (Continuation 5)
DEVELOPERS
You can use the same code for both Media actions here.
Here you notice kbmFMXDBGrid, kbmFMXDBImage and kbmFMXDBMemo components which are connected to
qClientSide kbmMWQuery responsible for fetching data from SQLite database.
I have removed some tabs I don't need, and left two only, so my interface looks like this:
Before we try to run it, don't forget to add following code to OnCreate method of main form:
kbmMWServer1.RegisterService(TkbmMWInventoryService,false);
kbmMWServer1.RegisterServiceByName('KBMMW_QUERY',TTestQuery,false);
kbmMWServer1.Active := True;
You can go without Inventory service, but you might want to test it here, by creating some test procedure, just use standard kbmMW
code... Since kbmFMX components are ReadOnly, I have used a standard Memo component to enter note/comment for pictures, so I
needed some code like this:
procedure TTabbedwithNavigationForm.qClientSideAfterScroll(DataSet: TDataSet);
begin
Memo1.Text := DataSet.FieldByName('Comment').AsString;
end;
procedure TTabbedwithNavigationForm.qClientSideBeforePost(DataSet: TDataSet);
begin
qClientSide.FieldByName('Comment').AsString := Memo1.Text;
end;
Application source code and compiled aplication dpk will be available for download from BlaisePascal, so let me try to run app and
show few screenshots here... Don't forget to add needed files for deployment, using Deployment tool:
118
COMPONENTS
DEVELOPERS
COMPONENTS
kbmFMX for XE5 (Continuation 6)
Delphi will add the launcher related files, but you need to
add some files, in this case SQLite database, or to add code
to create it from your application.
I have added db file here, and RemotePath should be
assets\internal\ so the client application will be able
to connect to.
OK, here are a few screenshots from my Samsung Galaxy
Tab 2 7 P3110:
DEVELOPERS
COMPONENTS
DEVELOPERS
119
Warning!
COMPONENTS
DEVELOPERS