Here are two great videos from ITDevCon 2012 by Ray Konopka that talk about how FireMonkey does what it does. Ray gives an overview of the high-level differences between VCL and FMX. In part 2 he shows how to create custom FMX controls.
I love the way Ray gets frustrated. He's just like us.
Creating Custom FireMonkey Controls Part 1
https://www.youtube.com/watch?v=m0NeWCmKAnU
Creating Custom FireMonkey Controls Part 2
https://www.youtube.com/watch?v=ufCKmYAxyCA
Update: 10/03/2015
I spoke with Ray and he gave me the link so we can download the source code he used in the demo. Thanks Ray!
http://www.raize.com/Sessions/CustomFMXControls.zip
Enjoy - Semper Fi
Gunny Mike
Husband, Father, Grandfather, U.S. Marine Corps Gunnery Sergeant (Retired), Software Artisan, Storyteller, Toastmaster, and Author (in progress) #capecodgunny #pilotlight #enlightment #delphi
Monday, September 28, 2015
Sunday, September 27, 2015
Blaise Pascal Magazine: Complete Article Index
I subscribed to Blaise Pascal Magazine last year. I also purchased the complete back issue library which contains all previous issues of the magazine. Unfortunately the index that came with the Lib Stick didn't work. So I made my own index as a quick reference to help me find the articles I'm interested in.
Download Excel File
Enjoy - Semper Fi
Gunny Mike
Download Excel File
Article | Author | Issue | Page | Type | Year |
Delphi 2007 Handbook | Frans Doove | 1 | 4 | Book Review | 2008 |
Delphi Win 32 / .NET Component Development | Frans Doove | 1 | 4 | Book Review | 2008 |
Representing graphics for math functions | Peter Bijlsma | 1 | 6 | Article | 2008 |
Client Dataset Toolkit | Detlef Overbeek | 1 | 8 | Article | 2008 |
Website name checking | Henk Schreij | 1 | 9 | Article | 2008 |
Database normalisation | Herman Peeren | 1 | 11 | Article | 2008 |
Delphi 2007 and VCL Component Building | Bob Swart | 1 | 13 | Article | 2008 |
Wallpaper | Detlef Overbeek | 1 | 16 | Article | 2008 |
Coding for two or more types | Henk Schreij | 1 | 17 | Article | 2008 |
Recipe for creating a cookbook | Tim Opsteeg | 1 | 21 | Article | 2008 |
Exploring Rave Reports | Rik Smit | 1 | 25 | Article | 2008 |
Drawing on a changing background | David Dirkse | 1 | 33 | Article | 2008 |
Mini application: Opening files | Detlef Overbeek | 1 | 35 | Article | 2008 |
Rotating images | Detlef Overbeek | 1 | 36 | Article | 2008 |
Delphi for PHP .2 new version | Herman Peeren | 2 | 3 | Article | 2008 |
Delphi 7 for Win 32 Development Essentials | Frans Doove | 2 | 6 | Book Review | 2008 |
Essential Pascal | Frans Doove | 2 | 6 | Book Review | 2008 |
Applied Mathematics for Database Professionals | Frans Doove | 2 | 7 | Book Review | 2008 |
Illustrated C# 2008 | Frans Doove | 2 | 7 | Book Review | 2008 |
Efficient Pascal – For-loops | Hallvard Vassbotn | 2 | 9 | Article | 2008 |
Hack #1: Write access to a read-only property | Hallvard Vassbotn | 2 | 11 | Article | 2008 |
Resizing Bitmaps | David Dirkse | 2 | 12 | Article | 2008 |
Making right-justified Edits | Henk Schreij | 2 | 14 | Article | 2008 |
Delphi 2007 VCL Component Building - II | Bob Swart | 2 | 16 | Article | 2008 |
ClientDataset filtering | Martin De Bont | 2 | 20 | Article | 2008 |
Binary Heap | Julian Bucknall | 2 | 21 | Article | 2008 |
Intraweb and AJAX | Bob Swart | 2 | 25 | Article | 2008 |
Solving unknown variables | Peter Bijlsma | 2 | 28 | Article | 2008 |
Obfuscator? | Rob van den Bogert | 2 | 30 | Article | 2008 |
Restricting Edits to numbers | Henk Schreij | 2 | 32 | Article | 2008 |
Monitor aspects | Jeremy North | 2 | 33 | Article | 2008 |
Euro symbol generation with the Alt key | Henk Schreij | 2 | 38 | Article | 2008 |
VCL Ribbon controls | Jeremy North | 3 | 3 | Article | 2008 |
The Tomes of Delphi: Algorithms and Data Structures | Frans Doove | 3 | 5 | Book Review | 2008 |
Advantage Database Server, A Developer's Guide | Frans Doove | 3 | 5 | Book Review | 2008 |
Combining visual components in an object | Wim Zandt | 3 | 6 | Article | 2008 |
A simple component for color selection | David Dirkse | 3 | 9 | Article | 2008 |
Introduction to Enumerators | Primož Gabrijelcic | 3 | 10 | Article | 2008 |
Images in a DbGrid | Henk Schreij | 3 | 12 | Article | 2008 |
Change indication in a multiuser system | Henk Schreij | 3 | 15 | Article | 2008 |
Curve Fitting made easy | Peter Bijlsma | 3 | 16 | Article | 2008 |
Advantage Database Server 9 | Rob van den Bogert | 3 | 19 | Article | 2008 |
VCL Property Editors | Bob Swart | 3 | 21 | Article | 2008 |
Hack#2: Access to protected methods | Hallvard Vassbotn | 3 | 26 | Article | 2008 |
Hack#1 update | Hallvard Vassbotn | 3 | 27 | Article | 2008 |
Delphi Stream Classes | Marco Cantù | 3 | 28 | Article | 2008 |
TExpandButton | Jeremy North | 3 | 30 | Article | 2008 |
Extending streams | Julian Bucknall | 3 | 32 | Article | 2008 |
Service Oriented Architecture | Fikret Hasovic | 3 | 36 | Article | 2008 |
Advanced Enumerators | Primož Gabrijelcic | 4 | 4 | Article | 2008 |
Text encryption | David Dirkse | 4 | 7 | Article | 2008 |
First In First Out | Frank Dubbeld | 4 | 8 | Article | 2008 |
Delphi and the Keyboard | David Dirkse | 4 | 9 | Article | 2008 |
RichEdit reports | Henk Schreij | 4 | 11 | Article | 2008 |
BookmarkStr | Henk Schreij | 4 | 15 | Article | 2008 |
Continuation Curve Fitting made easy | Peter Bijlsma | 4 | 17 | Article | 2008 |
Delphi 2009, Development Essentials | Frans Doove | 4 | 20 | Book Review | 2008 |
Delphi 2009 Language Enhancements | Bob Swart | 4 | 21 | Article | 2008 |
Hacks3-Access to private fields | Hallvard Vassbotn | 4 | 26 | Article | 2008 |
Creating TView object | Jeremy North | 4 | 27 | Article | 2008 |
Delphi Memory Streams | Marco Cantù | 4 | 30 | Article | 2008 |
SOA part two | Fikret Hasovic | 4 | 33 | Article | 2008 |
Delphi 2009 Handbook | Frans Doove | 5 | 4 | Book Review | 2009 |
Delphi 2009 Development Essentials | Frans Doove | 5 | 4 | Book Review | 2009 |
Specifying more than one shortcut key | Henk Schreij | 5 | 6 | Article | 2009 |
Solution for slow scrolling in DbGrids | Henk Schreij | 5 | 7 | Article | 2009 |
The Xbitmap component | David Dirkse | 5 | 9 | Article | 2009 |
Painting frames | David Dirkse | 5 | 12 | Article | 2009 |
Creating Stereographic Images | Peter Bijlsma | 5 | 13 | Article | 2009 |
Dialogue boxes | Adrian Kummerlaender | 5 | 15 | Article | 2009 |
The “Oracle” of Delphi | Thomas Pfister | 5 | 16 | Article | 2009 |
Advanced Enumerators | Primož Gabrijelcic | 5 | 19 | Article | 2009 |
Using TReader en TWriter | Marco Cantù | 5 | 22 | Article | 2009 |
ASP.NET Web Services with Delphi Prism 2009 | Bob Swart | 5 | 24 | Article | 2009 |
Ceating a progress bar | Jeremy North | 5 | 30 | Article | 2009 |
SOA part three | Fikret Hasovic | 5 | 33 | Article | 2009 |
An Advantage for Delphi Developers | Cary Jensen | 6 | 5 | Article | 2009 |
Delphi Roadmap | Marco Cantù | 6 | 8 | Article | 2009 |
A Twitter REST Client in Delphi | Marco Cantù | 6 | 11 | Article | 2009 |
Resolving in “kbmMW” | Fikret Hasovic | 6 | 22 | Article | 2009 |
JSDialog Pack | Jeremy North | 6 | 31 | Article | 2009 |
Visual Forms | Jeremy North | 6 | 33 | Article | 2009 |
RemObjects Hydra: Win32 and .NET Plugins | Bob Swart | 6 | 36 | Article | 2009 |
Controlling the parallel port | Thiago Batista Limeira | 6 | 51 | Article | 2009 |
The Xbitmap class (part 2) Lines and Ellipses | David Dirkse | 6 | 96 | Article | 2009 |
The Xbitmap class (part 3) Polygons | David Dirkse | 7 | 13 | Article | 2009 |
FastReport, A report generator for Delphi and .NET | Marco Roessen | 7 | 18 | Article | 2009 |
When as var, const, out: or not to declare | Henk Schreij | 7 | 24 | Article | 2009 |
Using Colours in DBGrid | Henk Schreij | 7 | 25 | Article | 2009 |
Writing Delphi Components: an Introduction | Marco Cantù | 7 | 27 | Article | 2009 |
Windows Communication Foundation with Delphi Prism | Bob Swart | 7 | 30 | Article | 2009 |
Datastore metadata | Fikret Hasovic | 7 | 34 | Article | 2009 |
using the Alt Key | Frans Dubbeid | 7 | 38 | Article | 2009 |
Delphi 2010 – what a feeling! Gestures | Bob Swart | 8 | 7 | Article | 2009 |
Counters | David Dirkse | 8 | 11 | Article | 2009 |
Virus in Delphi? | Nick Hodges | 8 | 14 | Article | 2009 |
Dezign for databases | Marco Roessen | 8 | 16 | Article | 2009 |
Customizing the T-Field data display | Henk Schreij | 8 | 18 | Article | 2009 |
Using Free Pascal and Lazarus to create applications for OSX | Jeremy North | 8 | 20 | Article | 2009 |
Writing Delphi Components II | Marco Cantù | 8 | 22 | Article | 2009 |
My Top Five Delphi 2010 New Features | Pawel Glowacki | 8 | 24 | Article | 2009 |
Fast Graphic deformation by using Scanlines | Peter Bijlsma | 8 | 28 | Article | 2009 |
Wide Information Bus (Introduction) | Fikret Hasovic | 8 | 33 | Article | 2009 |
Freehand Drawing (Introduction) | David Dirkse | 8 | 36 | Article | 2009 |
The 3D spheres generator | David Dirkse | 9 | 5 | Article | 2009 |
What is kbmMW? | Benno Evers | 9 | 7 | Article | 2009 |
Delphi 2010 Feature Highlight - Debugger Visualizers | Jeremy North | 9 | 12 | Article | 2009 |
Introduction to multithreading | Primož Gabrijelcic | 9 | 18 | Article | 2009 |
Writing Delphi Components III | Marco Cantù | 9 | 21 | Article | 2009 |
Talking Delphi | Henk Schreij | 9 | 25 | Article | 2009 |
LCD Interfacing: Driving a HD44780 LCD in Delphi | Thiago Batista Limeira | 9 | 28 | Article | 2009 |
Exploring the inplace editing capabilities of TAdvStringGrid | Bruno Fierens | 9 | 34 | Article | 2009 |
Remembering input | Henk Schreij | 10 | 6 | Article | 2010 |
Rotating photo cube | Peter Bijlsma | 10 | 7 | Article | 2010 |
Non overlapping rectangles | David Dirkse | 10 | 11 | Article | 2010 |
The Wide Information Bus part 2 | Fikret Hasovic | 10 | 12 | Article | 2010 |
Library Unit | Henk Schreij | 10 | 16 | Article | 2010 |
Delphi Component writing part 4 | Marco Cantù | 10 | 20 | Article | 2010 |
Delphi 2010 Delayed Dynamic Link Libraries | Bob Swart | 10 | 23 | Article | 2010 |
Four Ways to Create a Thread | Primož Gabrijelcic | 10 | 25 | Article | 2010 |
Delphi Advanced Tools: | Michael Rozlog | 10 | 30 | Article | 2010 |
DelphiController | Anton Vogelaar | 10 | 33 | Article | 2010 |
TWebUpdate & UpdateBuilder | Bruno Fierens | 10 | 34 | Article | 2010 |
Debugger Visualizers Part 2 | Jeremy North | 10 | 36 | Article | 2010 |
Synchronisation in a multithreaded environment | Primož Gabrijelcic | 11 | 5 | Article | 2010 |
PacMan in Delphi/Lazarus Algorithm jungle - or how to get caught | Jean Pierre Hoefnagel | 11 | 8 | Article | 2010 |
Moving from the BDE to ADS he End of the BDE? | Bob Swart | 11 | 11 | Article | 2010 |
Putting photographs on the internet A program using Indy Ftp | Henk Schreij | 11 | 16 | Article | 2010 |
Shrinking of JPEGs Not small enough? | Henk Schreij | 11 | 20 | Article | 2010 |
An Introduction to theGoogle Docs API with Delphi Googling magic... | Marco Cantù | 11 | 22 | Article | 2010 |
Using TMS Poly List Controls | Bruno Fierens | 11 | 24 | Article | 2010 |
CodeHealer 2.6: fantastic toolkit or healer? | Rik Smit | 11 | 28 | Article | 2010 |
Drawing regular Polygons Make it look like a star | David Dirkse | 11 | 31 | Article | 2010 |
Database Workbench, productivity tool A fantastic Tool | Miguel van de Laar | 11 | 34 | Article | 2010 |
Programming Frogs | David Dirkse | 11 | 35 | Article | 2010 |
Introducing the Open Tools API | Jeremy North | 11 | 37 | Article | 2010 |
Design patterns Starting a web series of basic patterns | Herman Peeren | 11 | 39 | Article | 2010 |
Electronics Delphi Controller and Delphi Developer board | Anton Vogelaar | 12 | 5 | Article | 2010 |
RAD Studio XE Preview | Detlef Overbeek | 12 | 7 | Article | 2010 |
SQL produced by the ClientDataSet | Henk Schreij | 12 | 12 | Article | 2010 |
Introducing GoogleDocsApi_SecondPart | Marco Cantù | 12 | 16 | Article | 2010 |
The use of kbmMW components in a server application | Benno Evers | 12 | 18 | Article | 2010 |
Inter Process Communication | Primož Gabrijelcic | 12 | 24 | Article | 2010 |
Peg Solitaire - a single player puzzle | David Dirkse | 12 | 27 | Article | 2010 |
Trans-continental travel using the great circle | Peter Bijlsma | 12 | 31 | Article | 2010 |
Creating gateways and other advanced topics | Fikret Hasovic | 12 | 34 | Article | 2010 |
Creating Office 2010 style VCL applications with TMS components | Bruno Fierens | 12 | 37 | Article | 2010 |
Mini Course SQL | Miguel van de Laar | 13 | 10 | Article | 2010 |
Delphi JSON Viewer | Pawel Glowacki | 13 | 13 | Article | 2010 |
Is Lazarus ready for writing commercial applications ? | Zeljan | 13 | 16 | Article | 2010 |
NexusDB exceptionaly good, a real surprise... | Erwin Mouthaan | 13 | 22 | Article | 2010 |
The TMS DataModeler | Bruno Fierens | 13 | 27 | Article | 2010 |
Introduction to Databases Part 1 | Cary Jensen | 13 | 31 | Article | 2010 |
First Look at Advantage Database Server® 10 | Cary Jensen | 13 | 35 | Article | 2010 |
Real-time data collection | Anton Vogelaar | 13 | 39 | Article | 2010 |
About Object oriented databases | Detlef Overbeek | 13 | 45 | Article | 2010 |
Fastreport, whats up? | Detlef Overbeek | 13 | 52 | Article | 2010 |
Using ADS with Delphi Prism and ASP.NET | Bob Swart | 13 | 59 | Article | 2010 |
A datawarehouse example using kbmMW | Kim Madsen | 13 | 66 | Article | 2010 |
Multiplatform Windows CE | Joost van der Sluis | 13 | 73 | Article | 2010 |
Five Considerations for Choosing an Effective SQL Development Tool | Scott Walz | 13 | 81 | Article | 2010 |
Pascal’s triangle by | Detlef Overbeek | 14 | 8 | Article | 2010 |
Create an Index of your (backup) CD's/DVD's by | Peter Bijlsma | 14 | 10 | Article | 2010 |
Debugging multithreaded applications by | Primož Gabrijelcic | 14 | 15 | Article | 2010 |
Using Amazon Simple Storage Service (Amazon S3) from Delphi | Marco Cantù | 14 | 19 | Article | 2010 |
A small spamming machine by | Detlef Overbeek | 14 | 22 | Article | 2010 |
Creating a Webserver in 5 minutes using kbmMW! by | Kim Madsen | 14 | 24 | Article | 2010 |
Gadgets in your Delphi Windows application by | Henk Schreij | 14 | 28 | Article | 2010 |
Open Tools API (OTA) by | Jeremy North | 14 | 30 | Article | 2010 |
Exploring the Async capabilities of TMS IntraWeb grids | Bruno Fierens | 14 | 37 | Article | 2010 |
Delphi XE Starter edition reviewed | Howard Page-Clark | 15 | 7 | Article | 2011 |
Delphi XE Starter try: a littel Mailserver | Jim Duff | 15 | 13 | Article | 2011 |
ClientDataset: what to do if you dont have it? | Detlef Overbeek | 15 | 21 | Article | 2011 |
Introduction to Databases Part 2: Common Database Objects | Cary Jensen | 15 | 28 | Article | 2011 |
Advantage Database Server and Delphi XE Starter Edition | Howard Page-Clark | 15 | 31 | Article | 2011 |
Moving /adding your components into the Delphi Starter Edition | Rik Smit | 15 | 35 | Article | 2011 |
Delphi and Unicode | Peter Prisman | 16 | 8 | Article | 2011 |
Anything to celebrate? | Peter Bijlsma | 16 | 10 | Article | 2011 |
The Xfont project | David Dirkse | 16 | 12 | Article | 2011 |
Learning to use FastReport 4 for VCL, Part 1 | Sergey Lyubeznyy | 16 | 19 | Article | 2011 |
Introduction to 64 bits on Windows | Alexander Alexeev | 16 | 28 | Article | 2011 |
Creating iPhone/iPod/iPad web applications with Delphi & TMS controls | Bruno Fierens | 16 | 33 | Article | 2011 |
Introduction to Databases Part 3: TDataSet | Cary Jensen | 16 | 39 | Article | 2011 |
64-bit Delphi programs are on the way | Primož Gabrijelcic | 16 | 43 | Article | 2011 |
Pascal Programing in the Android OS | Felipe Monteiro de Cavalho | 16 | 46 | Article | 2011 |
FieldByName alternatives | Henk Schreij | 16 | 54 | Article | 2011 |
The FlexCel components in action | Siegfried Zuhr | 16 | 57 | Article | 2011 |
Writing new components in Lazarus | Howard Page-Clark | 17 | 4 | Article | 2011 |
Theme: Using Amazon S3 from Delphi (Part II) | Marco Cantù | 17 | 12 | Article | 2011 |
The calculation of expressions with complex numbers and/or three-dimensional vectors | Anne Meijer | 17 | 17 | Article | 2011 |
Waiting for x64: WOW64 | Alexander Alexeev | 17 | 21 | Article | 2011 |
Learning to use FastReport 4 for VCL, Part 2 | Sergey Lyubeznyy | 17 | 24 | Article | 2011 |
Pascal's Triangle revisited | Paul Gellings | 17 | 29 | Article | 2011 |
Introduction to Test-Driven-Development (TDD) in Delphi | Alexander Alexeev | 17 | 31 | Article | 2011 |
Theme: DataSnap and Android (Part I) | Daniele Teti | 17 | 38 | Article | 2011 |
Financial functions in Delphi | Peter Bijlsma | 17 | 43 | Article | 2011 |
Magic-Button Anti-Pattern | Alexander Alexeev | 17 | 45 | Article | 2011 |
Tiles everywhere: introduction to TAdvSmoothTileList | Bruno Fierens | 17 | 48 | Article | 2011 |
Introduction to Databases part 4: TDataSet | Cary Jensen | 17 | 53 | Article | 2011 |
Writing New Components in Lazarus, Part 2 | Howard Page-Clark | 18 | 6 | Article | 2011 |
Delphi XE2 is the future - theme XE2 | Fikret Hasovic | 18 | 13 | Article | 2011 |
Cylindrical anamorphosis | Peter Bijlsma | 18 | 17 | Article | 2011 |
Web Service Toolkit as a rich type framework for Plug-in development | Inoussa Ouedraogo | 18 | 21 | Article | 2011 |
Polygon colouring and area calculation | David Dirkse | 18 | 24 | Article | 2011 |
Creating a Database program from scratch - 1 | Detlef Overbeek | 18 | 29 | Article | 2011 |
An Android client for a DataSnap Server – Part 2 | Daniele Teti | 18 | 34 | Article | 2011 |
Anti-freeze for the VCL | Alexander Alexeev | 18 | 39 | Article | 2011 |
Delphi XE2 New Features - theme XE2 | Bob Swart | 18 | 46 | Article | 2011 |
Learning to use FastReport 4 for VCL - Part 3 | Sergey Lyubeznyy | 18 | 53 | Article | 2011 |
Creating 64-bit applications with Delphi XE2 - theme XE2 | Jeremy North | 18 | 64 | Article | 2011 |
kbmSQL – Structured Query Language for your memory table | Kim Madsen | 18 | 72 | Article | 2011 |
High Level Multithreading | Primož Gabrijelcic | 18 | 76 | Article | 2011 |
TMS Scripter Studio Pro | Bruno Fierens | 18 | 81 | Article | 2011 |
FreePascal vectorial | Felipe Monteiro de Cavalho | 18 | 90 | Article | 2011 |
The tale of the Nigthtingale | Detlef Overbeek | 18 | 96 | Article | 2011 |
Using the Advantage Database Client Engine | Michael Van Canneyt | 18 | 101 | Article | 2011 |
Introduction to Databases part 5: Configuring OLE DB Providers and ODBC Drivers | Cary Jensen | 18 | 107 | Article | 2011 |
Delphi XE2 LiveBinding - theme XE2 | Bob Swart | 18 | 113 | Article | 2011 |
DataSnap connectivity for iOS using Delphi XE2 and FireMonkey | Anders Ohlsson | 19 | 6 | Article | 2011 |
Creating a simple webserver in Lazarus | Michael Van Canneyt | 19 | 16 | Article | 2011 |
Using Amazon S3 in Delphi XE2 | Marco Cantù | 19 | 21 | Article | 2011 |
Creating a Database program from scratch - 2 | Detlef Overbeek | 19 | 29 | Article | 2011 |
Writing New Components in Lazarus, Part 4 | Howard Page-Clark | 19 | 37 | Article | 2011 |
TMS MultiTouch SDK | Bruno Fierens | 19 | 46 | Article | 2011 |
FireMonkey Containers and Frames | Bob Swart | 19 | 53 | Article | 2011 |
Introduction to Databases Part 6: TClientDataSet | Cary Jensen | 19 | 56 | Article | 2011 |
The new Lazarus for Android | Felipe Monteiro de Carvalho | 20 | 4 | Article | 2011 |
Q&A Special Hint CueBanner | Henk Schreij | 20 | 14 | Article | 2011 |
The XFont Class | David Dirkse | 20 | 15 | Article | 2011 |
3D geometry development | BJ Rao | 20 | 20 | Article | 2011 |
Q&A Determine the latitude and longitude of an address | Henk Schreij | 20 | 23 | Article | 2011 |
Writing New Components in Lazarus, Part 4 | Howard Page-Clark | 20 | 24 | Article | 2011 |
Creating a Database program from scratch - 3 | Detlef Overbeek | 20 | 29 | Article | 2011 |
Introduction to Databases Part 7: dbExpress | Cary Jensen | 20 | 39 | Article | 2011 |
Porting my Amazon S3 client to FireMonkey | Marco Cantù | 20 | 45 | Article | 2011 |
Writing a component for iOS using Delphi XE2 & FireMonkey | Anders Ohlsson | 20 | 49 | Article | 2011 |
TMS Instrumentation Workshop for FireMonkey | Bruno Fierens | 20 | 51 | Article | 2011 |
kbmMW Remote Desktop | Kim Madsen | 20 | 55 | Article | 2011 |
Telnet in Pascal | Felipe Monteiro de Carvalho | 21 | 5 | Article | 2012 |
How to show formatted HTML text? And how to print? | Henk Schreij | 21 | 12 | Article | 2012 |
Developing an Outlook Social Provider | Andrey Terekhov | 21 | 13 | Article | 2012 |
How to use the Format function? | Henk Schreij | 21 | 18 | Article | 2012 |
Creating a Delphi database application for Mac OS X | Tim Opsteeg | 21 | 19 | Article | 2012 |
Working natively with ZIP archives in Windows using zipfldr.dll | Viacheslav Ryabinin | 21 | 23 | Article | 2012 |
Using AnyDac in Delphi | Michael Van Canneyt | 21 | 32 | Article | 2012 |
VCL Custom Styles – Part 1 | Jeremy North | 21 | 37 | Article | 2012 |
Introduction to Databases Part 8: dbExpress (continued) | Cary Jensen | 21 | 43 | Article | 2012 |
Introduction to Debugging | Primož Gabrijelcic | 21 | 50 | Article | 2012 |
Kinect or Xtion? GeNui ! | Simon Stuart | 21 | 56 | Article | 2012 |
DLL Hell, part 1: DLL redirection | Alexander Alexeev | 22 | 6 | Article | 2012 |
Pump some Oxygene into your Android device with Oxygene for Java | Brian Long | 22 | 8 | Article | 2012 |
AnyDAC macros and scripting | Michael Van Canneyt | 22 | 19 | Article | 2012 |
VCL Custom Styles – Part 2 | Jeremy North | 22 | 24 | Article | 2012 |
Creating a Database program from scratch - 4 | Detlef Overbeek | 22 | 30 | Article | 2012 |
One of the hotest topics: XML and lately JSON... | Fikret Hasovic | 22 | 34 | Article | 2012 |
Advanced debugging - Part 2 | Primož Gabrijelcic | 22 | 39 | Article | 2012 |
Object-Relational Mapping with TMS Aurelius | Wagner Landgraf | 22 | 43 | Article | 2012 |
Using Excel functions in Delphi | Nikolay Bilfeld | 22 | 49 | Article | 2012 |
Unit testing and ’TestGrip’ | Marco Geuze | 22 | 50 | Article | 2012 |
How to create a simple About screen? Questions and Answers | Henk Schreij | 22 | 59 | Article | 2012 |
Writing Cross-Platform Code | Alexander Alexeev | 23 | 8 | Article | 2012 |
Solving equations using Regula Falsi | Peter Bijlsma | 23 | 14 | Article | 2012 |
Data-Aware FireMonkey Controls using LiveBindings | Cary Jensen | 23 | 19 | Article | 2012 |
Advanced debugging Tips and Tricks | Primož Gabrijelcic | 23 | 26 | Article | 2012 |
Extending TListBoxItem to customize list box behaviour | Jeremy North | 23 | 29 | Article | 2012 |
Pascal for Internet | Primož Gabrijelcic | 23 | 34 | Article | 2012 |
Pascal scores high notes | Siegfried Zuhr | 23 | 39 | Article | 2012 |
Introducing TTMSFMXGrid | Bruno Fierens | 23 | 43 | Article | 2012 |
Delphi XE2, iOS and SQLite | Bob Swart | 23 | 49 | Article | 2012 |
Meaningful colours or is it all gray? | David Dirkse | 23 | 52 | Article | 2012 |
How to develop n-tier applications for iOS using kbmMW | Roger Nyberg | 23 | 55 | Article | 2012 |
Interview with Kim Madsen | Detlef Overbeek | 24 | 5 | Article | 2012 |
Sending mails using Lazarus | Michael Van Canneyt | 24 | 10 | Article | 2012 |
Interview with Dmitry Arefiev | Detlef Overbeek | 24 | 19 | Article | 2012 |
Smart Mobile Studio | Jørn E. Angeltveit | 24 | 29 | Article | 2012 |
Interview with Marco Cantu | Detlef Overbeek | 24 | 33 | Article | 2012 |
FireMonkey in Delphi XE3: FM2 | Marco Cantù | 24 | 36 | Article | 2012 |
HTML 5 | Bob Swart | 24 | 49 | Article | 2012 |
Interview with Michael Rozlog | Detlef Overbeek | 24 | 52 | Article | 2012 |
XE3 Style Designer | Jeremy North | 24 | 56 | Article | 2012 |
Burgler detection using Lazarus | Michael Van Canneyt | 24 | 62 | Article | 2012 |
Supporting new android features in old android versions with Oxygene | Brian Long | 24 | 68 | Article | 2012 |
Delphi XE3 Helper Types | Bob Swart | 24 | 76 | Article | 2012 |
Interview with Marc Hoffman | Detlef Overbeek | 24 | 79 | Article | 2012 |
Introduction to Databases Part 9: Overview of DataSnap | Cary Jensen | 24 | 83 | Article | 2012 |
Software taxes - storage | Alexander Alexeev | 24 | 87 | Article | 2012 |
Interview with David I. (Intersimone) | Detlef Overbeek | 24 | 96 | Article | 2012 |
The Delphi History | Detlef Overbeek | 24 | 101 | Article | 2012 |
From Delphi to the cloud | Bruno Fierens | 24 | 106 | Article | 2012 |
Interview with Bruno Fierens | Detlef Overbeek | 24 | 111 | Article | 2012 |
kbmMW as a RTMP media server | Kim Madsen | 24 | 114 | Article | 2012 |
Lazarus and dabatases | Michael Van Canneyt | 24 | 117 | Article | 2012 |
Match5, a self learning board game | David Dirkse | 25 | 11 | Article | 2012 |
Programming in Smart: Application Design | Primož Gabrijelcic | 25 | 19 | Article | 2012 |
TMS FlexCel for VCL and FireMonkey | Adrian Gallero | 25 | 26 | Article | 2012 |
Delphi IDE Tips | Brian Long | 25 | 31 | Article | 2012 |
DataSnap: DBX vs. REST | Bob Swart | 25 | 40 | Article | 2012 |
Crowd Funding | BJ.Rao | 25 | 43 | Article | 2012 |
Displaying video files using Free Pascal and Lazarus | Michael Van Canneyt | 25 | 52 | Article | 2012 |
High performance virtual filesystems with kbmMW | Kim Madsen | 25 | 58 | Article | 2012 |
Introduction to Databases Part 10: IP-Based DataSnap | Cary Jensen | 26 | 7 | Article | 2012 |
How to prune a tree | David Dirkse | 26 | 16 | Article | 2012 |
How to display a YouTube clip? | Henk Schreij | 26 | 22 | Article | 2012 |
Programming in Smart: Supporting Multiple Resolutions | Primož Gabrijelcic | 26 | 32 | Article | 2012 |
HTML5 Builder DataSnap Clients | Bob Swart | 26 | 36 | Article | 2012 |
Object serialization using kbmMW | Kim Madsen | 26 | 52 | Article | 2012 |
Introduction to thread programming in Lazarus | Michael Van Canneyt | 27 | 4 | Article | 2013 |
Introduction to Databases Part 11: DataSnap Filters and Security | Cary Jensen | 27 | 13 | Article | 2013 |
Electronic document processing tools for Delphi made by Gnostice | Grish Patil | 27 | 23 | Article | 2013 |
Use of the "absolute" function abs(..) | David Dirkse | 27 | 36 | Article | 2013 |
Development for lazarus | Joost van der Sluis | 27 | 41 | Article | 2013 |
What is polymorphism? | Vsevolod Leonov | 27 | 48 | Article | 2013 |
Creating and using kbmMW as an Ajax back-end server | Benno Evers | 27 | 52 | Article | 2013 |
NEW AND ENHANCED FEATURES IN DELPHI XE4 | Editor | 28 | 4 | Article | 2013 |
Alternative datastreams (ADS) (better inifile) | Jean Pierre Hoefnagel | 28 | 14 | Article | 2013 |
First Look at FireDAC | Cary Jensen | 28 | 16 | Article | 2013 |
Writing UI-Independent Code with Anonymous Methods | Marco Cantù | 28 | 18 | Article | 2013 |
Introduction to Databases Part 12: A DataSnap Client | Cary Jensen | 28 | 23 | Article | 2013 |
Programming in Smart: Refactoring | Primož Gabrijelcic | 28 | 34 | Article | 2013 |
Development for lazarus part two | Joost van der Sluis | 28 | 42 | Article | 2013 |
Delphi XE4 iOS DataSnap Clients | Bob Swart | 28 | 52 | Article | 2013 |
Intro to kbmMW connectivity package 'Spider' | Fikret Hasovic | 28 | 57 | Article | 2013 |
Polymorphism in practice | Alexander Alexeev | 29 | 4 | Article | 2013 |
The Raspberry Pi, Pi Vision and Lazarus/FPC | BJ Rao | 29 | 14 | Article | 2013 |
FPC Hackathon 2013 | Michael Van Canneyt | 29 | 22 | Article | 2013 |
UniDAC — unique components for DB application development for iOS | Boris Matkov | 29 | 25 | Article | 2013 |
1. Pascal Fundamentals: TActionlist, TImagelist and Image editor | Michael Van Canneyt | 29 | 31 | Article | 2013 |
2. Pascal Fundamentals: How long is a pice of string: Short strings and Ansi strings | Howard Page-Clark | 29 | 37 | Article | 2013 |
Smart Mobile Studio - Multiple categories | Primož Gabrijelcic | 29 | 41 | Article | 2013 |
Developing a virtual world | Cary Jensen | 29 | 45 | Article | 2013 |
The TMS iCL | By Bruno Fierens | 29 | 51 | Article | 2013 |
Serialization using kbmMW | Fikret Hasovic | 29 | 55 | Article | 2013 |
Creating an IOS application | Jeremy North | 30 | 4 | Article | 2013 |
Introduction Database Development Part 13: Connecting to Data with FireDac | Cary Jensen | 30 | 18 | Article | 2013 |
Pascal Fundamentals: Understanding components Part 3: The TListBox component | Howard Page-Clark | 30 | 28 | Article | 2013 |
Smart Mobile Studio | Jørn Einar Angeltveit | 30 | 41 | Article | 2013 |
Programming with the leap motion | Michael Van Canneyt | 30 | 47 | Article | 2013 |
Lazarus Closed Source Packages | Mattias Gaertner | 30 | 56 | Article | 2013 |
Support for Word and LibreOffice Text Documents - FPVectorial | Felipe Monteiro de Carvalho | 30 | 60 | Article | 2013 |
Delphi XE5 and FireMonkey for Android | Bob Swart | 30 | 69 | Article | 2013 |
Sciencepalooza Fact: Man causes the earth heating | Yuri Matteman | 30 | 74 | Article | 2013 |
The kbmMW authorization manager | Kim Madsen | 30 | 75 | Article | 2013 |
Designing an API: common mistakes | Alexander Alexeev | 31/32 | 8 | Article | 2013 |
Newest Leap developments | Michael Van Canneyt | 31/32 | 21 | Article | 2013 |
3D Printing | Bj Rao | 31/32 | 26 | Article | 2013 |
Kinect ?! | Michael Van Canneyt | 31/32 | 33 | Article | 2013 |
Smart Mobile Studio 2.0 | Primož Gabrijelcic | 31/32 | 41 | Article | 2013 |
A simple superscript text editor | David Dirkse | 31/32 | 50 | Article | 2013 |
Using GEO services in Delphi applications with TMS components | Bruno Fierens | 31/32 | 57 | Article | 2013 |
Correcting a bad API design: | Alexander Alexeev | 31/32 | 65 | Article | 2013 |
The maXbox Pure Code | Max Kleiner | 31/32 | 77 | Article | 2013 |
Programming Bitmap Rotation | David Dirkse | 31/32 | 98 | Article | 2013 |
Introduction to Model, View and View Model (MVVM) and the Caliburn Micro for Delphi framework | Jeroen Pluimers | 31/32 | 102 | Article | 2013 |
kbmFMX for XE5 (android) | Fikret Hasovic | 31/32 | 113 | Article | 2013 |
maXbox Starter 5 Start with DLL A Library for All | Max Kleiner | 33 | 5 | Article | 2014 |
Suite Rays components | Rik Smit | 33 | 14 | Article | 2014 |
Steps in MindScape AppView Step-By-Step | Jeroen Pluimers | 33 | 26 | Article | 2014 |
Programming Truth Table reduction | David Dirkse | 33 | 46 | Article | 2014 |
Databases for XE6 | Cary Jensen | 33 | 59 | Article | 2014 |
Updating DotTapper | Jeremy North | 33 | 53 | Article | 2014 |
Six new ready to use components! | Fikret Hasovic | 33 | 65 | Article | 2014 |
Ode To the Code | Cary Jensen | 34 | 4 | Article | 2014 |
Animation Lab for Android | Boin Mitov | 34 | 5 | Article | 2014 |
All about understanding the RTTI / Attributes / Functional Programming | Detlef Overbeek | 34 | 7 | Article | 2014 |
Raize Components Version 6PART II Continuation | Rik Smit | 34 | 18 | Article | 2014 |
QR (Quick Response) Codes | Max Kleiner | 34 | 24 | Article | 2014 |
Image Compression | David Dirkse | 34 | 29 | Article | 2014 |
Review : Documentation Insight by DevJet Software | Jeremy North | 34 | 36 | Article | 2014 |
Introduction Local SQL with FireDAC | Cary Jensen | 34 | 41 | Article | 2014 |
Unit testing - test driven development | Michael Van Canneyt | 34 | 46 | Article | 2014 |
Serving an off the shelf Content Management System | Kim Madsen | 34 | 53 | Article | 2014 |
Smart Mobile Studio: Where to use it and with what? Comment | Christan Budde | 35 | 16 | Article | 2014 |
Building simple native applications for Windows | Christan Budde | 35 | 17 | Article | 2014 |
node.js on a Raspberry Pi | Christan Budde | 35 | 19 | Article | 2014 |
Debugging Techniques in Lazarus | Howard Page Clark | 35 | 23 | Article | 2014 |
Start with GEO Maps | Max Kleiner | 35 | 40 | Article | 2014 |
A 3D Tic-Tac-Toe game | David Dirkse | 35 | 45 | Article | 2014 |
Blazing Performance with FireDAC Array DML | Cary Jensen | 35 | 52 | Article | 2014 |
Creating custom edit descendants in XE7 | Jeremy North | 35 | 57 | Article | 2014 |
Leap Motion Version 2 | Michael Van Canneyt | 35 | 62 | Article | 2014 |
KBMMW and messaging (the WIB) | Fikret Hasovic | 35 | 64 | Article | 2014 |
A WYSIWYG math editor | David Dirkse | 36 | 10 | Article | 2014 |
Blue Tooth | Jeremy North | 36 | 16 | Article | 2014 |
3D Printing Lab (maxbox) | Max Kleiner | 36 | 23 | Article | 2014 |
Converting Delphi to Lazarus and Vice Versa | Editor | 36 | 28 | Article | 2014 |
Differences between Delphi and Lazarus | Editor | 36 | 28 | Article | 2014 |
Lazarus for beginners: Starting your first program | Howard Page-Clark | 36 | 35 | Article | 2014 |
TMS XData and TMS Aurelius – Learning Example | Wagner Landgraf | 36 | 40 | Article | 2014 |
UpdateMode and FireDAC DataSets | Cary Jensen | 36 | 50 | Article | 2014 |
AMQP (Advanced Message Queuing Protocol) | Fikret Hasovic | 36 | 54 | Article | 2014 |
Cloning FDMemTable Cursors | Cary Jensen | 37/38 | 13 | Article | 2014 |
Floating points? - | David Dirkse | 37/38 | 20 | Article | 2014 |
The Leap Motion Visualizer | Dmitry Boyarintsev | 37/38 | 23 | Article | 2014 |
Creating Applications for Mobile: How to make money with apps | Mark Wilcox | 37/38 | 31 | Article | 2014 |
Migration to FastReport: How easy can it be ? | Denis Zubov | 37/38 | 37 | Article | 2014 |
TIOBE Index for January 2015 | Editor | 37/38 | 45 | Article | 2014 |
Apple’s Guidelines for Creating and Publishing | Editor | 37/38 | 48 | Article | 2014 |
Google’s Guidelines for Creating and Publishing Android Apps | Editor | 37/38 | 53 | Article | 2014 |
Google’s help for developers for Android | Editor | 37/38 | 55 | Article | 2014 |
Lazarus for beginners 3 | Howard Page-Clark | 37/38 | 62 | Article | 2014 |
WYSIWYG formatted: text & images editor for VCL & FireMonkey | Bruno Fierens | 37/38 | 71 | Article | 2014 |
Regular Expression Report | Max Kleiner | 37/38 | 78 | Article | 2014 |
Regular Expressions Explained / Programming | Editor | 37/38 | 88 | Article | 2014 |
Getting Started with the Raspberry Pi | Siegfried Zuhr | 37/38 | 89 | Article | 2014 |
Using Free Pascal to create Android applications | Michael Van Canneyt | 37/38 | 104 | Article | 2014 |
Having fun with Delphi and AMQP | Kim Madsen | 37/38 | 112 | Article | 2014 |
ClientDataSets and FDMemTables Compared: Part 1 | Cary Jensen | 39 | 11 | Article | 2015 |
Accessing Preferences and Databases in Android (FPC) | Michael Van Canneyt | 39 | 22 | Article | 2015 |
Painting 3D Lissajous figures | David Dirkse | 39 | 31 | Article | 2015 |
Checking your Android device is ready for development | Stephen Ball | 39 | 36 | Article | 2015 |
Interbase Change Views | Stephen Ball | 40 | 5 | Article | 2015 |
From Delphi to Delphi, overview of the Delphi history | Editor | 40 | 8 | Article | 2015 |
Delphi - the city | Editor | 40 | 13 | Article | 2015 |
Why the name Delphi? | Editor | 40 | 14 | Article | 2015 |
Introduction to FlatBox2D for Delphi XE8 | Pawel Glowacki | 40 | 16 | Article | 2015 |
ClientDataSets and FDMemTables Compared: Part 2 | Cary Jensen | 40 | 24 | Article | 2015 |
Start with Function Testing (maXbox) | Max Kleiner | 40 | 31 | Article | 2015 |
Working with Bluetooth beacons in Delphi XE8 | Jeremy North | 40 | 40 | Article | 2015 |
Security And Safety In Applications | Andrea Raimondi | 41 | 5 | Article | 2015 |
Beginning Of Time... The Water Clock | Editor | 41 | 9 | Article | 2015 |
The New Lazarus 1.4 | Editor | 41 | 14 | Article | 2015 |
Security In Applications: Password Handling | Andrea Raimondi | 41 | 18 | Article | 2015 |
In Remembrance Of Alan Turing | Editor | 41 | 23 | Article | 2015 |
Rest Clients: Using The Google Apis In Free Pascal | Michael Van Canneyt | 41 | 25 | Article | 2015 |
Arduino: The Visuino Project - Part 1 | Boian Mitov | 41 | 37 | Article | 2015 |
Working With Google Merchants Rating Data Using kbmmw | Kim Madsen | 41 | 43 | Article | 2015 |
Quantum Computing | Editor | 42 | 5 | Article | 2015 |
Thales Of Miletus - A Pre-Socratic Greek Philosopher | Editor | 42 | 9 | Article | 2015 |
Arduino: The Visuino Project - Part 2 | Boian Mitov | 42 | 14 | Article | 2015 |
Getting Started With The TMS Planner For Firemonkey | Bruno Fierens | 42 | 22 | Article | 2015 |
Mapping Using Delphi | Peter Van Der Sman | 42 | 28 | Article | 2015 |
Log, Debug And Audit The kbmmw Way | Kim Madsen | 42 | 33 | Article | 2015 |
Free Pascal Compiler 3.0 In Preparation | Michael Van Canneyt | 43 | 6 | Article | 2015 |
Overview Of Delphi To Delphi History | Editor | 43 | 8 | Article | 2015 |
Pythagoras Of Samos, Not The Inventor Of The A^2 + B^2 = C^2 | Editor | 43 | 9 | Article | 2015 |
Arduino: The Visuino Project - Part 3 | Boian Mitov | 43 | 12 | Article | 2015 |
Fast Reports VCL In A Nutshell | John Kuiper | 43 | 30 | Article | 2015 |
Brand Your Own Remote Desktop Solution | Kim Madsen | 43 | 40 | Article | 2015 |
Overview Of Delphi To Delphi History | Editor | 44 | 6 | Article | 2015 |
Euclides | Editor | 44 | 7 | Article | 2015 |
An Agep Uzzle | David Dirkse | 44 | 8 | Article | 2015 |
Arduino: The Visuino Project - Part 4 | Boian Mitov | 44 | 12 | Article | 2015 |
Database Workbench 5 | Peter Van Der Sman | 44 | 28 | Article | 2015 |
Tips And Tricks With KBM Memtable | Kim Madsen | 44 | 39 | Article | 2015 |
Overview Of Delphi To Delphi History | Editor | 45/46 | 8 | Article | 2015 |
Plato And Platinic Solids | Editor | 45/46 | 9 | Article | 2015 |
A Simple Editor For In-Line Help | David Dirkse | 45/46 | 12 | Article | 2015 |
FindFirst/FindNext And The Power Of Habits | Heiko Rompel | 45/46 | 18 | Article | 2015 |
Android Programming In Free Pascal Networking External Code And Threads | Michael Van Canneyt | 45/46 | 20 | Article | 2015 |
Rad Studio 10 Seattle Introduction | Editor | 45/46 | 29 | Article | 2015 |
Using Azure Translator Services With Delphi | Stephen Ball | 45/46 | 44 | Article | 2015 |
How To Use Fmx: Tframestand Easily Use Tframe(S) In Fmx Applications | Andrea Magni | 45/46 | 54 | Article | 2015 |
Delphi Building Blocks - Advanced Collections | Boian Mitov | 45/46 | 62 | Article | 2015 |
Maxbox Starter 18 Start With Arduino Programmingv 3.1 | Max Kleiner | 45/46 | 72 | Article | 2015 |
Archimedes | Editor | 47 | 5 | Article | 2015 |
Installing Lazarus On Mac Os X | Michaël Van Canneyt | 47 | 8 | Article | 2015 |
Using “Raspberry Pi” As A Flexible Beacon | Danny Wind | 47 | 12 | Article | 2015 |
The Towers Of Hanoi | David Dirkse | 47 | 19 | Article | 2015 |
Presenting A Fast & Feature-Rich Multicolumn Treeview For Delphi | Bruno Fierens | 47 | 23 | Article | 2015 |
The Enhanced Rtti (Runtime Type Library) | Boian Mitov | 47 | 29 | Article | 2015 |
Getting Things Done At Time, Every Time | Kim Madsen | 47 | 37 | Article | 2015 |
A Microseconds Counter Component In Delphi | David Dirkse | 48 | 5 | Article | 2015 |
Watch Your Calories Using The Nutrition Monitor! | Peter Bijlsma | 48 | 7 | Article | 2015 |
Custom Forms In Delphi | Michaël Van Canneyt | 48 | 19 | Article | 2015 |
Installing Lazarus On Linux Mint | Michaël Van Canneyt | 48 | 26 | Article | 2015 |
Connect Iot With The Cloud Using Tms Lcl Cloud Pack | Bruno Fierens | 48 | 30 | Article | 2015 |
Time Is On My Side - Maxbox | Max Kleiner | 48 | 36 | Article | 2015 |
Enjoy - Semper Fi
Gunny Mike
Sunday, September 20, 2015
What Every Delphi Developer Should Know About Writing for Windows and Mac
I have always wanted a Mac version of my software. So, in September, 2011 when Delphi XE2 was launched I was excited to finally get my chance to create software for both Windows and Mac. Has it happened. Nope not yet.
When it comes to software development I'm a slow starter. Perhaps I think too much. Perhaps I try and anticipate all the possibilities. Perhaps I'm just afraid of making huge mistakes, going down the wrong road and having to rip it all up only to start all over again.
Well, it's happening again, the slow start. However, this time it's not because I'm thinking too much it's because I've been thinking wrong. I have programmed my brain to think in terms of Windows. I need to unlearn how to think like a Windows developer and re-think like a cross-platform developer.
Fortunately there some great resources out there. I received permission from the editor of Blaise Pascal Magazine to reprint the following article in it's entirety.
Semper Fi,
Gunny Mike
Reprinted with permission from the editor of Blaise Pascal Magazine
http://www.blaisepascal.eu/
Issue 23 / March 2012
How can you write Pascal code that is truly crossplatform? This article explains general techniques and best practice in writing code that will run on several platforms natively (that is, without use of a virtual machine or a web-based interface).
A few phrases may need clarification. By FMX I mean the FireMonkey framework in Delphi XE2 or later. The host system means both the hardware platform and operating system which is running your programming environment and compiler. The target system/platform means the hardware platform and operating system for which you are creating an executable program. The simplest case is where the host system and the target platform are identical (but of course that is not crossplatform).
Writing successful cross-platform code requires that you consider three aspects:
An alternative to having your code make direct system is to use a special layer: a framework library or run-time library (RTL).The idea is that you make function calls to this auxiliary library,but do not call the operating system directly. The library translates your requests into calls to specific operating system functions.Then your code will run on any system and hardware platform supported by the compiler and the libraries it uses.
For example:
This means that if you write your code using only the VCL, it will run on Win32 and Win64 (and in the case of Delphi 1 on Win16), but not on Linux, MacOS or iOS.
Also note that these libraries have different capabilities. For example, Qt (on which CLX is based) is a broad framework. That is, it contains functions for many different purposes: the interface, printing, file system, IPC, and so on.
On the other hand, the VCL includes support only for the user interface (not for IPC and so on).
Of course, if you choose a cross-platform library that supports multiple platforms, then you can create a user interface on each platform with the same code.
The only problem here is that the standard controls on different platforms look and behave differently. And if you replace them with generic controls provided by your cross-platform library – then both visually and behaviour-wise they will differ from the standard operating system controls. Your application will look "foreign" and "alien".
A hybrid approach is possible, in which you use a universal crossplatform library, but the library itself creates native controls for each of its platforms, instead of using 'universal' controls.
This has the advantages that your application now has a native look. The disadvantage is in the consequent complexity of development and testing, since you must take into account all the differences between the various target platforms as you develop and test.
The VCL is an example of a library which is wired to its platform. FMX is an example of library with universal controls. Examples of libraries with adaptable controls are CLX (Kylix) and the LCL (Lazarus).
Moreover, differences in the positioning and behaviour of controls between platforms is just the tip of the iceberg. The whole concept of “building an interface” may differ between various platforms. A simple example would be the order of the [OK], [Cancel] and [Help] buttons in dialogs.
Windows and MacOS each uses the reverse of the other's placement order. MacOS programs share a system-wide menu. On Windows this is simply not available. One platform can be focussed on mouse and keyboard interaction, another on gesture and touch. And a UI for a 21-inch monitor is substantially different from one for a PDA with a 640x400 pixel resolution.
The first approach (the so-called WOCA - Write Once, Compile Anywhere approach) gives us:
The second, platform-sensitive approach yields these features:
These are the extremes of the two opposing approaches, with different development costs and different results. Usually, realworld cross-platform applications combine both approaches in varying degrees.
It should be noted that in the not-too-distant past, the cross-platform issue was not so acute as it is today. Windows was the dominant platform for personal computers and x86-32 was the dominant hardware platform. If you wrote a Win32 application you covered 90% of your potential customers (both personal and corporate). And in reality very few people completed versions of their applications for other platforms.
Today we have Win64 and ARM, and tablets and PDAs are playing an increasingly important rôle not only for ordinary home users, but also for corporate customers. Apple's iOS, as well as Google with Android, has captured a significant share of the mobile device market. Although Windows is still in a strong position for desktop applications, the idea of targeting Windows only no longer seems like a lucrative approach for many applications.
If you're writing a desktop application, Windows is still the main option. Of course, versions for MacOS and Linux are also sometimes helpful. Especially when you consider the differences in popularity of different platforms in different countries.
For server applications you should first consider various options for Unix (Linux, FreeBSD, Solaris, etc.), followed immediately by Windows, but you can forget about MacOS.
Mobile applications (for tablets and PDAs) are the most difficult to decide about since there are three major platforms: iOS, Android, and Windows Phone. Other platforms are much less relevant today (Windows Mobile, Blackberry, and Symbian).
Generally you will see various possible combinations in practice: Code is written for one platform using a specialized library, and direct calls to an API (for example: a VCL application for Win32). There is one platform and it's easy. Other platforms are not considered, or used only with an emulator. Often it's easier to use an emulator, and it gives acceptable results. Emulation is acceptable for the corporate customer, and for customised and highly specialized software; but not for boxed products.
Code is written for one platform, using cross-platform tools (for example: an FMX application for Win32). The intention here is either to hold open a future hope ("someday we'll do a version for MacOS"), or to use more powerful features of cross-platform libraries (such as hardware acceleration for 3D/2D). Code is written for two or three platforms, with minor differences, using direct calls to the APIs (for example: a VCL application for both Win32 and Win64). Typically this is used to exploit the limited adaptation needed between closely related platforms.
Code is written for two or three different platforms using a single-purpose solution (for example: a VCL Win32/Win64 application, with an FMX application for MacOS). This is a rare approach because of the high development costs. But it is the approach of choice when you need the maximum number of native features on each platform. The main objective in this case is to maximize use of 'universal' code which can be shared between platforms. As a rule, this approach targets no more than three platforms (selecting only the most popular three).
Code is written for multiple platforms using cross-platform tools (for example: an FMX application for Win32/Win64/MacOS/iOS). Native interface and platform capabilities are sacrificed for universality. This route is chosen when you need to reach the maximum number of customers, even at the risk of compromising your application's ease of use.
You code a web-based application.
Pro: it works wherever there is web-browser.
Con: a simplified web-interface often causes discomfort for users.
After settling on the list of platforms and development tools, you can write code just as you would do for a single platform. But you'll have to check the written code for each selected platform. You cannot just write generic code and expect it to work identically on all platforms. There will always be both small and more significant differences. Even between the highly compatible Win32 and Win64 platforms there are many differences and consequential pitfalls!
To develop and test code it is not necessary have a real machine or device - virtual machines and emulators are often sufficient. You need only one (quite powerful) computer and a few virtual machines - one for each platform (without host system). Of course, a final test on real hardware can be helpful.
As an alternative to virtual machines you can utilize a multi-boot machine with several different operating systems installed on a single physical machine, providing a choice among systems to load at start-up (when the operating system boots). Clearly, this is not the most convenient option (since a restart is required to change platform), and it is not always possible (you cannot dual-boot iOS and Windows), but this is still a working solution for "live" testing on a machine with limited resources.
Conditional compilation and platform-dependent code
In most cases you will not be able to write completely generic code. Often, your code will include some direct API calls. How do you proceed in these cases?
You accomplish this by using conditional compilation directives.
You tell the compiler not to compile certain code when a particular condition is met. Usually the condition is the presence or absence of specific markers (so-called conditional compilation symbols). This is done by using {$IFDEF}/{$IFNDEF}/{$IF} directives. The conditional compilation symbol will either be specified (defined), or not specified (not defined).
That is, it is a binary feature: yes/no, on/off.
Each symbol will be checked for definition (existence) and depending on the test result the selected code is compiled (or not). For example:
Note that this choice is made at compile time (design time). That is , If the symbol DEBUG is defined, then DoSomething will look like this:
If DEBUG is not defined, then the procedure becomes the following:
In other words, the "filtered out code" will not appear at all in the
resulting application.
By the way, conditional compilation symbols are case insensitive.
Conditional compilation symbols can be defined in several ways:
Bad:
Better:
Ideal:
- Do not confuse symbols like WIN32 and MSWINDOWS, MACOS and MACOS32, etc. Symbols with suffixes 32/64 are combinations of a common symbol (like MSWINDOWS, MACOS, etc.) with the symbol for the CPU (like CPU386, CPUX64, etc.).
- Do not use a binding to the compiler version.
Bad:
Good:
However, it is clear that the functionality of loading shared libraries is present on other platforms. Therefore, there must be analogous functions to the LoadLibrary function on other platforms. This means that we must be able to write our own LoadLibrary function, which will call the necessary function(s) of another target platform and, thus, will emulate the behaviour of the original Windows function. Incidentally Delphi does this, and many Windows API functions work on other platforms because they have been implemented in the System and SysUtils units (only for other platforms; under Windows they are just imported).
You can use this approach too. Use a single universal unit (or a unit dedicated to each platform you support, as described above). Then you may not need to change your Windows API coding - because the Windows API will work on another platform through your emulation (at least to the extent that you're implementing it).
A similar technique can be used to compensate for differences between various Delphi versions and other compilers. Simply create a new unit and add there all the features you want that are missing from certain compilers or IDEs.
Almost all programs work with known locations - to load the configuration, store data, etc. Therefore, they should be able to get the path to these folders. In spite of being "known", the locations of these folders are often not fixed but rather set during installation of the system (sometimes – or later when used).
Therefore, systems have special features to obtain paths to such folders. Of course, these functions are specific for each system. Moreover, the full set of possible known folders is different too.
You can get some of these folders using Delphi. For example via TPath.GetHomePath. For all those folders that do not have a ready-to-use implementation in Delphi you will have to write your own code.
Finally, here are some examples of important folders. This is not comprehensive, since exact details depend on the precise version of the operating system.
Further, it should be noted that some Unix systems do not have the concept of "application folder".
On these systems - ParamStr(0) and Application.ExeName sometimes cannot return the folder where the executable program is placed. For this reason applications in Unix often use a fixed installation location to be able to get their folders.
If the path can be changed, then it's better to store the path in a particular place. (This is how it's done in Windows: the program installer writes the program path as a predetermined path like \Software\AppName).
As you can see, despite some similarities, there are numerous differences.
Now the differences: Windows application configuration data is often stored in the registry because it is easier and more efficient. Since there is no registry on other platforms, all other platforms store configuration in files (in differing formats for which there is no standard). This approach is also used in Windows as alternative to the registry.
Therefore, in general, if you eliminate the Windows registry from consideration (except when you cannot work without it), then all the usual recommendations and best practices for programs on Windows are also valid for other platforms - taking into account differences in the distribution of "well-known folders", of course.
For example, a local folder for your program data (read-write) on different systems can be (these are just examples, a specific value may vary):
A global directory:
Resources in executables are specific to Windows and have no direct counterpart on other platforms. You can replace the resources by storing data in files alongside the program. Functionally, this would be equivalent.
MacOS differs from both Unix and Windows in that MacOS applications are distributed in a so-called "application bundle", which contains everything necessary for the application.
Usually you do not need to think about these issues, unless you call the system functions directly. In the latter case, you need to cast the strings to the desired format. In recent Delphi versions coding for these issues is handled automatically. You do not need to manually convert strings - a simple assignment of strings between different formats is enough.
If you exchange text data between platforms, you will need to be prepared to convert one format to another. You can normalize text with the function AdjustLineBreaks.
Another difference is the BOM (Byte Order Mark). Windows follows the Unicode standard by putting a BOM at the beginning of all Unicode files (UTF-8, UTF-16, etc.). But UTF-8 files in Unix often lack a BOM.
As a hint, for a "portable" format you should select the format of your most popular platform which will eliminate the need to convert timestamps there.
Endianness refers to the order in which bytes are stored. The term is taken from a story in Gulliver's Travels by Jonathan Swift about wars fought between those who thought eggs should be cracked on the Big End and those who insisted on the Little End. With chips, as with eggs, it doesn't really matter as long as you know which end is up.
(Lowest address)
The last byte
(Highest address)NoteBigHigh byteLow byte
It is easier to write - the numbers are written in the usual way: the high byte is on the "left" LittleLow byteHigh byte It is more convenient for calculations (carry) – the byte value increases with the address
Each specific hardware processor can be either Little Endian or Big Endian. However, some processors are so-called Bi-Endian (or Dual-Endian) supporting both modes, and allowing a mode switch (set).
Again, you should not worry about the byte order, unless you are operating on a single byte within an integer, or sharing data with other systems (via network or files).
Fortunately Windows always works as a LE (Little Endian) system - even with the BE-processors. But other systems (e.g. MacOS and Linux) can be Big Endian on certain hardware.
Therefore for cross-platform development less efficient universal code is always preferable to faster, processor-specific assembly code.
Use assembler only in the rarest cases, where you cannot accomplish what you need any other way. And when you use assembler, as discussed above, make it platform-specific code via conditional compilation.
MacOS (as well as other Apple products), although based on Unix, promotes exactly the opposite values, putting ease of use and userfriendliness at the heart of the interface. From this follow all the restrictions for developers, including a sandbox, an App Store, packaging applications in bundles, and so on. Rather than sporting many features – a single excellent (perfect) one is better.
Microsoft Windows is somewhere in the middle. The GUI is king, but the developer is not restricted. Microsoft's original mission ("a computer in every home") has already been accomplished.
You must consider all this when developing applications for specific platforms. If you write an application for MacOS following Linux traditions, it is unlikely that it will be warmly received by Mac users, who have very different priorities; and vice-versa.
###
About the Author: Alexander Alexeev
Alexander started working with Delphi while still at university. Following graduation as a Master of Mathematics, he took several jobs in the Delphi world. Today he works for the EurekaLab s.a.s. company, where he develops and supports EurekaLog for Delphi/C++ Builder. Alex lives in the Russian Federation and maintains a personal blog, where he writes about Delphi. Some of his articles are available in English at eurekalog.blogspot.com. His favourite topics are debugging-related.
When it comes to software development I'm a slow starter. Perhaps I think too much. Perhaps I try and anticipate all the possibilities. Perhaps I'm just afraid of making huge mistakes, going down the wrong road and having to rip it all up only to start all over again.
Well, it's happening again, the slow start. However, this time it's not because I'm thinking too much it's because I've been thinking wrong. I have programmed my brain to think in terms of Windows. I need to unlearn how to think like a Windows developer and re-think like a cross-platform developer.
Fortunately there some great resources out there. I received permission from the editor of Blaise Pascal Magazine to reprint the following article in it's entirety.
Semper Fi,
Gunny Mike
Reprinted with permission from the editor of Blaise Pascal Magazine
http://www.blaisepascal.eu/
Issue 23 / March 2012
Writing Cross-Platform Code
By Alexander AlexeevHow can you write Pascal code that is truly crossplatform? This article explains general techniques and best practice in writing code that will run on several platforms natively (that is, without use of a virtual machine or a web-based interface).
A few phrases may need clarification. By FMX I mean the FireMonkey framework in Delphi XE2 or later. The host system means both the hardware platform and operating system which is running your programming environment and compiler. The target system/platform means the hardware platform and operating system for which you are creating an executable program. The simplest case is where the host system and the target platform are identical (but of course that is not crossplatform).
What is cross-platform code?
Cross-platform software is “software that works on more than one hardware platform and / or operating system” - Wikipedia.Writing successful cross-platform code requires that you consider three aspects:
- The Compiler
- The API / framework
- The User Interface
The Compiler
Firstly, to create code that works on several platforms, you'll certainly need a compiler that can generate code for those platforms. There are two possible scenarios in compiling code for a specific target platform: either the compiler can run on the same platform or it can run on a different one. Examples of the first case would be Kylix and Free Pascal – both of these compilers can ru non Linux and compile for Linux. Examples of the second case are Delphi XE2 and (again) Free Pascal – both can run on Windows,while compiling for MacOS.The API and/or framework
You need to consider more than the mere capabilities of your compiler. For cross-platform code it's very important how you rcode will interact with the environment in which it runs. Firstly,does it use the system API? For example, if your program uses the Windows API, it will run on any (hardware) platform, where Windows itself works. You may need to recompile the program for this platform (without needing to change the code). However, it will notwork under Linux, say. Conversely, if you use Linux function calls,then your program will run on any hardware platform running Linux (again, after recompilation), but it will not work under Windows.An alternative to having your code make direct system is to use a special layer: a framework library or run-time library (RTL).The idea is that you make function calls to this auxiliary library,but do not call the operating system directly. The library translates your requests into calls to specific operating system functions.Then your code will run on any system and hardware platform supported by the compiler and the libraries it uses.
For example:
- VCL supports Win16 (Delphi 1 only), Win32, and Win64 (only Delphi XE2+).
- CLX (Kylix 1-3, Delphi 6-7) supports Win32 and Linux.
- FMX (only Delphi XE2+) supports Win32, Win64, MacOS, and iOS.
This means that if you write your code using only the VCL, it will run on Win32 and Win64 (and in the case of Delphi 1 on Win16), but not on Linux, MacOS or iOS.
Also note that these libraries have different capabilities. For example, Qt (on which CLX is based) is a broad framework. That is, it contains functions for many different purposes: the interface, printing, file system, IPC, and so on.
On the other hand, the VCL includes support only for the user interface (not for IPC and so on).
The User Interface
Because Delphi has traditionally been a rapid development environment for graphical user interfaces, the user interface is the focus for most libraries and frameworks in Delphi.Of course, if you choose a cross-platform library that supports multiple platforms, then you can create a user interface on each platform with the same code.
The only problem here is that the standard controls on different platforms look and behave differently. And if you replace them with generic controls provided by your cross-platform library – then both visually and behaviour-wise they will differ from the standard operating system controls. Your application will look "foreign" and "alien".
A hybrid approach is possible, in which you use a universal crossplatform library, but the library itself creates native controls for each of its platforms, instead of using 'universal' controls.
This has the advantages that your application now has a native look. The disadvantage is in the consequent complexity of development and testing, since you must take into account all the differences between the various target platforms as you develop and test.
The VCL is an example of a library which is wired to its platform. FMX is an example of library with universal controls. Examples of libraries with adaptable controls are CLX (Kylix) and the LCL (Lazarus).
Moreover, differences in the positioning and behaviour of controls between platforms is just the tip of the iceberg. The whole concept of “building an interface” may differ between various platforms. A simple example would be the order of the [OK], [Cancel] and [Help] buttons in dialogs.
Windows and MacOS each uses the reverse of the other's placement order. MacOS programs share a system-wide menu. On Windows this is simply not available. One platform can be focussed on mouse and keyboard interaction, another on gesture and touch. And a UI for a 21-inch monitor is substantially different from one for a PDA with a 640x400 pixel resolution.
Conclusions
You can conclude that the term "cross-platform" has, in a sense, two almost opposite meanings. On the one hand, cross-platform code is universal code which once written, will compile on multiple platforms. On the other hand, cross-platform code is code that takes into account the specific peculiarities of each platform.The first approach (the so-called WOCA - Write Once, Compile Anywhere approach) gives us:
- Diminished importance of the individual platform. Once code is written it is suitable for any compiler-supported platform
- Limitation to generic solutions. You cannot use a specific feature (read: a strong point) of a particular platform
- A user interface that appears to be "universal" and "foreign"
- A complex implementation – it is both difficult and expensive to develop; and you are restricted to using only generic methods, and cannot make specialized calls
The second, platform-sensitive approach yields these features:
- You cannot build your executable for the new platform until you have adapted your code for it
- You can make full use of any specific platform's features and advantages
- Your user interface will look native on each platform
- A complex implementation:
– it is both difficult and expensive to develop;
and you need to write individual code for each platform
These are the extremes of the two opposing approaches, with different development costs and different results. Usually, realworld cross-platform applications combine both approaches in varying degrees.
When do you need cross-platform code?
Cross-platform code gives you the opportunity to distribute your application to new markets: to other hardware platforms and operating systems. The answer to how useful or necessary this is depends primarily on your application and its target audience. Cross-platform application development is always difficult. Is there any benefit from working on new platforms? Would it be easier to use emulation or a web-based interface?It should be noted that in the not-too-distant past, the cross-platform issue was not so acute as it is today. Windows was the dominant platform for personal computers and x86-32 was the dominant hardware platform. If you wrote a Win32 application you covered 90% of your potential customers (both personal and corporate). And in reality very few people completed versions of their applications for other platforms.
Today we have Win64 and ARM, and tablets and PDAs are playing an increasingly important rôle not only for ordinary home users, but also for corporate customers. Apple's iOS, as well as Google with Android, has captured a significant share of the mobile device market. Although Windows is still in a strong position for desktop applications, the idea of targeting Windows only no longer seems like a lucrative approach for many applications.
If you're writing a desktop application, Windows is still the main option. Of course, versions for MacOS and Linux are also sometimes helpful. Especially when you consider the differences in popularity of different platforms in different countries.
For server applications you should first consider various options for Unix (Linux, FreeBSD, Solaris, etc.), followed immediately by Windows, but you can forget about MacOS.
Mobile applications (for tablets and PDAs) are the most difficult to decide about since there are three major platforms: iOS, Android, and Windows Phone. Other platforms are much less relevant today (Windows Mobile, Blackberry, and Symbian).
Generally you will see various possible combinations in practice: Code is written for one platform using a specialized library, and direct calls to an API (for example: a VCL application for Win32). There is one platform and it's easy. Other platforms are not considered, or used only with an emulator. Often it's easier to use an emulator, and it gives acceptable results. Emulation is acceptable for the corporate customer, and for customised and highly specialized software; but not for boxed products.
Code is written for one platform, using cross-platform tools (for example: an FMX application for Win32). The intention here is either to hold open a future hope ("someday we'll do a version for MacOS"), or to use more powerful features of cross-platform libraries (such as hardware acceleration for 3D/2D). Code is written for two or three platforms, with minor differences, using direct calls to the APIs (for example: a VCL application for both Win32 and Win64). Typically this is used to exploit the limited adaptation needed between closely related platforms.
Code is written for two or three different platforms using a single-purpose solution (for example: a VCL Win32/Win64 application, with an FMX application for MacOS). This is a rare approach because of the high development costs. But it is the approach of choice when you need the maximum number of native features on each platform. The main objective in this case is to maximize use of 'universal' code which can be shared between platforms. As a rule, this approach targets no more than three platforms (selecting only the most popular three).
Code is written for multiple platforms using cross-platform tools (for example: an FMX application for Win32/Win64/MacOS/iOS). Native interface and platform capabilities are sacrificed for universality. This route is chosen when you need to reach the maximum number of customers, even at the risk of compromising your application's ease of use.
You code a web-based application.
Pro: it works wherever there is web-browser.
Con: a simplified web-interface often causes discomfort for users.
General Principles
First you should select the target platform(s). You can do this on the basis of the recommendations above. Then you need to choose the appropriate development tools for these platforms (compiler, libraries and frameworks).After settling on the list of platforms and development tools, you can write code just as you would do for a single platform. But you'll have to check the written code for each selected platform. You cannot just write generic code and expect it to work identically on all platforms. There will always be both small and more significant differences. Even between the highly compatible Win32 and Win64 platforms there are many differences and consequential pitfalls!
To develop and test code it is not necessary have a real machine or device - virtual machines and emulators are often sufficient. You need only one (quite powerful) computer and a few virtual machines - one for each platform (without host system). Of course, a final test on real hardware can be helpful.
As an alternative to virtual machines you can utilize a multi-boot machine with several different operating systems installed on a single physical machine, providing a choice among systems to load at start-up (when the operating system boots). Clearly, this is not the most convenient option (since a restart is required to change platform), and it is not always possible (you cannot dual-boot iOS and Windows), but this is still a working solution for "live" testing on a machine with limited resources.
Conditional compilation and platform-dependent code
In most cases you will not be able to write completely generic code. Often, your code will include some direct API calls. How do you proceed in these cases?
You accomplish this by using conditional compilation directives.
You tell the compiler not to compile certain code when a particular condition is met. Usually the condition is the presence or absence of specific markers (so-called conditional compilation symbols). This is done by using {$IFDEF}/{$IFNDEF}/{$IF} directives. The conditional compilation symbol will either be specified (defined), or not specified (not defined).
That is, it is a binary feature: yes/no, on/off.
Each symbol will be checked for definition (existence) and depending on the test result the selected code is compiled (or not). For example:
procedure DoSomething; begin {$IFDEF DEBUG} OutputDebugString('DoSomething called'); {$ENDIF} // ... // DoSomething's code // ... end;
Note that this choice is made at compile time (design time). That is , If the symbol DEBUG is defined, then DoSomething will look like this:
procedure DoSomething; begin OutputDebugString('DoSomething called'); // ... // DoSomething's code // ... end;
If DEBUG is not defined, then the procedure becomes the following:
procedure DoSomething; begin // ... // DoSomething's code // ... end;
In other words, the "filtered out code" will not appear at all in the
resulting application.
By the way, conditional compilation symbols are case insensitive.
Conditional compilation symbols can be defined in several ways:
- Predefined – meaning that they are defined by the compiler. For example, WINDOWS, WIN32, DCC, CONSOLE, etc.
- You can specify them in the project options.
For example, DEBUG. - You can define them in your source code using
{$DEFINE Symbol} directive. - You can define them in an include file (.inc file which is linked through the {$IFileName.inc} directive).
Formally, this is identical to the previous method, but is used more often. - You can define them at compile-time by passing a special command line option to the compiler: -D Symbol.
You can use predefined conditional compilation symbols to compile platform-specific code for different platforms. Various IDEs define different sets of conditional compilation symbols. A new version of an IDE may add new symbols. The same symbol can be used by one IDE and not be used in another IDE; or it may have different meanings in the two different IDEs; or (if you're lucky) it might be identical in both IDEs.
For these reasons, real projects rarely use predefined conditional compilation symbols directly. Most often, the project adds an include file (with the .inc extension), which establishes universal symbols for conditional compilation, adapted for each IDE. This file is then included at the beginning of each unit of the project (through the {$IFileName.inc}). After that, your code can use new "beautiful" and universal symbols. If you do not want to write such a file yourself, you can use the file jedi.inc file, which can be downloaded from here:
http://projectjedi.svn.sf.net/viewvc/projectjedi/trunk/shared/include/jedi.inc?view=log
(click "Download" next to the "Links to HEAD")
So, as a result, you have three options for dealing with the platformdependent code:
1. You put platform-specific code directly into the source code and surround it with conditional compilation directives. This option is suitable only for the simplest cases, and is not recommended.
2. You offload all platform-dependent code to a (single) separate unit. Inside the unit you use conditional compilation directives to generate code for the different platforms, and the outer (caller) code can simply call the functions and classes from your unit, without having to use conditional compilation.
Pro: the caller is universal and does not contain any platform-specific parts.
Con: the platform-dependent unit is full of conditional compilation directives.
3. You offload all platform-dependent code to several different units with different names - one unit for each platform. Each unit has the same interface section and different implementation sections. Within each unit you write the code for the specific platform without the use of conditional compilation directives. The recommended format for the unit name is: PlatformName.UnitName.pas. The caller will connect to the correct platform's unit by using conditional compilation directives, and the code will call the functions and classes directly, without using conditional compilation.
Pro: the code is better structured through not being cluttered with conditional compilation directives.
Cons: the calling code still contains conditional compilation directives (inside its uses clause), and it will be harder to add a new platform. In any case, here are a few tips on the use of conditional compilation for platform-specific code:
- Do not assume a fixed set of platforms1. You put platform-specific code directly into the source code and surround it with conditional compilation directives. This option is suitable only for the simplest cases, and is not recommended.
2. You offload all platform-dependent code to a (single) separate unit. Inside the unit you use conditional compilation directives to generate code for the different platforms, and the outer (caller) code can simply call the functions and classes from your unit, without having to use conditional compilation.
Pro: the caller is universal and does not contain any platform-specific parts.
Con: the platform-dependent unit is full of conditional compilation directives.
3. You offload all platform-dependent code to several different units with different names - one unit for each platform. Each unit has the same interface section and different implementation sections. Within each unit you write the code for the specific platform without the use of conditional compilation directives. The recommended format for the unit name is: PlatformName.UnitName.pas. The caller will connect to the correct platform's unit by using conditional compilation directives, and the code will call the functions and classes directly, without using conditional compilation.
Pro: the code is better structured through not being cluttered with conditional compilation directives.
Cons: the calling code still contains conditional compilation directives (inside its uses clause), and it will be harder to add a new platform. In any case, here are a few tips on the use of conditional compilation for platform-specific code:
Bad:
{$IFDEF MSWINDOWS} // Code for Windows {$ELSE} // Code for MacOS {$ENDIF}
Better:
{$IFDEF MSWINDOWS} // Code for Windows {$ENDIF} {$IFDEF MACOS} // Code for MacOS {$ENDIF}
Ideal:
// Code for (very) old Delphi {$DEFINE UNKNOWN_PLATFORM} {$IFDEF MSWINDOWS} {$UNDEF UNKNOWN_PLATFORM} // Code for Windows {$ENDIF} {$IFDEF MACOS} {$UNDEF UNKNOWN_PLATFORM} // Code for MacOS {$ENDIF} {$IFDEF UNKNOWN_PLATFORM} {$MESSAGE FATAL 'Unknown platform'} {$ENDIF} // The modern (compact) version: {$IF DEFINED(MSWINDOWS)} // Code for Windows {$ELSEIF DEFINED(MACOS)} // Code for MacOS {$ELSE} {$MESSAGE FATAL 'Unknown platform'} {$IFEND}
- Do not confuse symbols like WIN32 and MSWINDOWS, MACOS and MACOS32, etc. Symbols with suffixes 32/64 are combinations of a common symbol (like MSWINDOWS, MACOS, etc.) with the symbol for the CPU (like CPU386, CPUX64, etc.).
- Do not use a binding to the compiler version.
Bad:
{$IFDEF VER230} // Some code for a specific version of Delphi {$ENDIF} {$IFDEF COMPILER6_UP}// // Some code for Delphi 6 and above {$ENDIF}
Good:
// In our .inc-file: {$IFDEF VER230} {$DEFINE HAS_FEATURE_X} {$ENDIF} {$IFDEF COMPILER6_UP} {$DEFINE HAS_FEATURE_Y} {$ENDIF} // In real code: {$IFDEF HAS_FEATURE_X} // Code that runs with the X feature {$ENDIF} {$IFDEF HAS_FEATURE_Y} // Code that runs with the Y feature {$ENDIF}
Emulation of source code
I want to stress the importance of source code emulation by devoting a separate section to it. It involves the following process: instead of writing generic functions from scratch, you write versions of existing functions for other platforms. For example the LoadLibrary function is a feature of the Windows API which does not exist on other platforms.However, it is clear that the functionality of loading shared libraries is present on other platforms. Therefore, there must be analogous functions to the LoadLibrary function on other platforms. This means that we must be able to write our own LoadLibrary function, which will call the necessary function(s) of another target platform and, thus, will emulate the behaviour of the original Windows function. Incidentally Delphi does this, and many Windows API functions work on other platforms because they have been implemented in the System and SysUtils units (only for other platforms; under Windows they are just imported).
You can use this approach too. Use a single universal unit (or a unit dedicated to each platform you support, as described above). Then you may not need to change your Windows API coding - because the Windows API will work on another platform through your emulation (at least to the extent that you're implementing it).
A similar technique can be used to compensate for differences between various Delphi versions and other compilers. Simply create a new unit and add there all the features you want that are missing from certain compilers or IDEs.
Platform specifics
When you write cross-platform code (whether universal code for all platforms or specialized code for a particular platform) you should always keep in mind the differences between the platforms.File system
- In some systems, the file names are case sensitive, in others they are not. Use the function SameFileName to compare file names for specific platforms
- Unix-like systems use / (forward slash) to separate file path components, and the Windows-like systems - use \ (backslash) (although almost all file functions understand both characters). Use the constant PathSep and functions like ExtractFilePath, and IncludeTrailingPathDelimiter to manipulate file paths.
- Unix uses - or -- for command-line options, Windows uses /.
- Windows has both alpha-character drive names and UNCnames. Unix does not use drive letters, it uses mounting instead.
- Different platforms allow different sets of acceptable characters in file paths. File naming rules also differ. It makes sense to normalize all names to the canonical form.
- Most popular systems use the same conventions for the current and parent directory (. an .. ) and also for file masks (* and ? ).
- Windows uses file extensions for known file types. Unix mainly relies on the file attributes of executable files, and extensions are used less frequently.
- In Windows, most files have two names - long and short. This may come as a surprise!
- Almost all modern systems have different versions of file links (hard, symbolic, etc.) which have their own characteristics in each system.
- In some cases, directories can be files. Be very careful when checking for the existence of a file with FileExists - make sure (by documentation or experimentally) that the function works as expected. The same is true for file references.
- File access rights and opening modes are implemented in different ways and with different semantics, but it seems with similar functionality.
“Known Folders”
- "Known Folders" is the phrase used by the Windows API, but it can be applied to other systems.
- "Well-known folder" refers to a directory or a virtual path to a predefined folder which has system-wide implications - such as the system folder, your profile folder, the program folder, the settings folder, and so on.
Almost all programs work with known locations - to load the configuration, store data, etc. Therefore, they should be able to get the path to these folders. In spite of being "known", the locations of these folders are often not fixed but rather set during installation of the system (sometimes – or later when used).
Therefore, systems have special features to obtain paths to such folders. Of course, these functions are specific for each system. Moreover, the full set of possible known folders is different too.
You can get some of these folders using Delphi. For example via TPath.GetHomePath. For all those folders that do not have a ready-to-use implementation in Delphi you will have to write your own code.
Finally, here are some examples of important folders. This is not comprehensive, since exact details depend on the precise version of the operating system.
For Windows:
- \Users (Documents and Settings) - root folder for the user profiles.
Each user has their personal folder here, which takes their name. - \Users\UserName\Documents – the user's documents
- \Users\UserName\Application Data - applications' data
- \Users\UserName\Application Data\Local\Temp\ - folder for temporary files
- \Program Files - installed applications
- \Windows - system folder
- \Windows\System32 (SysWOW64/SysNative)
- common components
For Linux:
- \boot, \bin and \sbin - boot software
- \home - root folder for the user's documents.
Each user has their personal folder which takes their name. - \usr - installed programs (also structured in subdirectories)
- \lib - common components
- \etc - program configurations
- \var - temporary and frequently changed files (also structured in subdirectories)
- \mnt - mounted devices
- \proc - virtual file system,
something similar to the Windows Registry
Further, it should be noted that some Unix systems do not have the concept of "application folder".
On these systems - ParamStr(0) and Application.ExeName sometimes cannot return the folder where the executable program is placed. For this reason applications in Unix often use a fixed installation location to be able to get their folders.
If the path can be changed, then it's better to store the path in a particular place. (This is how it's done in Windows: the program installer writes the program path as a predetermined path like \Software\AppName).
As you can see, despite some similarities, there are numerous differences.
Configuration, data and resources
Across platforms there are both similarities and differences in placing data. The general similarities are: there is always a separation between the purely local (current user) and global (all users) data; roughly the same rules always apply (only an administrator can affect all users); user data is always separated from the program; read-only data (programs and master templates) is always separated from the re-writable data (configuration).Now the differences: Windows application configuration data is often stored in the registry because it is easier and more efficient. Since there is no registry on other platforms, all other platforms store configuration in files (in differing formats for which there is no standard). This approach is also used in Windows as alternative to the registry.
Therefore, in general, if you eliminate the Windows registry from consideration (except when you cannot work without it), then all the usual recommendations and best practices for programs on Windows are also valid for other platforms - taking into account differences in the distribution of "well-known folders", of course.
For example, a local folder for your program data (read-write) on different systems can be (these are just examples, a specific value may vary):
- Windows 98: C:\Windows\Local Settings\Application Data\ProjectName\
- Windows XP: C:\Documents and Settings\UserName\Local Settings\Application Data\ProjectName\
- Windows 7: C:\Users\UserName\AppData\Local\ProjectName\
- Linux: /home/UserName/.config/ProjectName/
- MacOS: /Users/UserName/.config/ProjectName/
A global directory:
- Windows 98: C:\Program Files\ProjectName\
- Windows XP: C:\Documents and Settings\All users\Local Settings\Application Data\ProjectName\
- Windows 7: C:\ProgramData\ProjectName\
- Linux: /etc/ProjectName/
- MacOS: /etc/ProjectName/
Resources in executables are specific to Windows and have no direct counterpart on other platforms. You can replace the resources by storing data in files alongside the program. Functionally, this would be equivalent.
MacOS differs from both Unix and Windows in that MacOS applications are distributed in a so-called "application bundle", which contains everything necessary for the application.
Encoding
Modern Windows uses Unicode (UTF-16), and also provides ANSI-versions of most functions for backwards compatibility. Unix often uses UTF-8. Many systems use null-terminated strings (sometimes explicitly specifying their length).Usually you do not need to think about these issues, unless you call the system functions directly. In the latter case, you need to cast the strings to the desired format. In recent Delphi versions coding for these issues is handled automatically. You do not need to manually convert strings - a simple assignment of strings between different formats is enough.
Text files
Different systems use different line separators for text strings. Sometimes it is CR + LF (#13#10), sometimes only LF (#10). Use the constant sLineBreak instead of character literals. You must be particularly careful when analyzing and breaking lines since line breaks have different lengths on different platforms. So it is best always to use ready-made functions.If you exchange text data between platforms, you will need to be prepared to convert one format to another. You can normalize text with the function AdjustLineBreaks.
Another difference is the BOM (Byte Order Mark). Windows follows the Unicode standard by putting a BOM at the beginning of all Unicode files (UTF-8, UTF-16, etc.). But UTF-8 files in Unix often lack a BOM.
Date and time
Different systems store timestamps differently. Usually you do not care about the specifics, unless you are going to exchange data between the platforms (either via a network, or via files/documents). In the latter case, you need to select a date format and convert the date-time in this format to your platform and back.As a hint, for a "portable" format you should select the format of your most popular platform which will eliminate the need to convert timestamps there.
Bitness
You need to take account of bitness all the time – both the system bitness and its data model for integers. In other words, you need to know on your platform how many bytes Integer and Pointer occupy. I will not dwell on this here, as I wrote extensively on this in a previous Win64 article in Blaise Pascal Magazine.Byte order
The byte order (or endianness) depends on the processor. One byte (say, $41 ) is treated uniquely. But if you have two bytes in a row (say, $41 $10), the processor can interpret them either as $4010 or as $1041.Endianness refers to the order in which bytes are stored. The term is taken from a story in Gulliver's Travels by Jonathan Swift about wars fought between those who thought eggs should be cracked on the Big End and those who insisted on the Little End. With chips, as with eggs, it doesn't really matter as long as you know which end is up.
Endianness
The first byte(Lowest address)
The last byte
(Highest address)NoteBigHigh byteLow byte
It is easier to write - the numbers are written in the usual way: the high byte is on the "left" LittleLow byteHigh byte It is more convenient for calculations (carry) – the byte value increases with the address
Each specific hardware processor can be either Little Endian or Big Endian. However, some processors are so-called Bi-Endian (or Dual-Endian) supporting both modes, and allowing a mode switch (set).
Again, you should not worry about the byte order, unless you are operating on a single byte within an integer, or sharing data with other systems (via network or files).
Fortunately Windows always works as a LE (Little Endian) system - even with the BE-processors. But other systems (e.g. MacOS and Linux) can be Big Endian on certain hardware.
Assembler
When developing cross-platform code, you should avoid assembler like the plague because assembler is symbolic text for processor-specific hardware commands. That is, assembly code is always written for a specific processor. It is specific to that processor and is not portable to other platforms.Therefore for cross-platform development less efficient universal code is always preferable to faster, processor-specific assembly code.
Use assembler only in the rarest cases, where you cannot accomplish what you need any other way. And when you use assembler, as discussed above, make it platform-specific code via conditional compilation.
The paradigm behind the platform
The Unix philosophy is “from developers to developers”. Reusable and automated solutions are valued highly. That is why command-line utilities have pride of place, and the user interface is attached on top of them. Open source codebases and diverse OS distributions express the unfettered freedom of Unix.MacOS (as well as other Apple products), although based on Unix, promotes exactly the opposite values, putting ease of use and userfriendliness at the heart of the interface. From this follow all the restrictions for developers, including a sandbox, an App Store, packaging applications in bundles, and so on. Rather than sporting many features – a single excellent (perfect) one is better.
Microsoft Windows is somewhere in the middle. The GUI is king, but the developer is not restricted. Microsoft's original mission ("a computer in every home") has already been accomplished.
You must consider all this when developing applications for specific platforms. If you write an application for MacOS following Linux traditions, it is unlikely that it will be warmly received by Mac users, who have very different priorities; and vice-versa.
###
Alexander started working with Delphi while still at university. Following graduation as a Master of Mathematics, he took several jobs in the Delphi world. Today he works for the EurekaLab s.a.s. company, where he develops and supports EurekaLog for Delphi/C++ Builder. Alex lives in the Russian Federation and maintains a personal blog, where he writes about Delphi. Some of his articles are available in English at eurekalog.blogspot.com. His favourite topics are debugging-related.
Subscribe to:
Posts (Atom)