Wednesday, October 14, 2015

Create Professional Installs for Mac OS X

I'm just getting started with learning cross-application development. One of the many issues that's crossed my mind has been how do I get the Mac OS X version of my software installed on a customers machine.

Well that question was answered yesterday during the "Prototyping an Object Pascal Code Editor with FireMonkey in Delphi for Windows and OSX" object pascal CodeRageX session.




Eli M. from Peacekeeper Enterprises, LLC presented a slide for a product that lets you create installs for Mac OS X. The product is called Packages and can be found at the below url:
http://s.sudre.free.fr/Software/Packages/about.html

I have not tried Packages yet. However, it appears similar to the Inno Setup tool for Windows.
http://www.jrsoftware.org/isinfo.php

Enjoy - Semper Fi,
Gunny Mike

Monday, September 28, 2015

FireMonkey Under the Hood: How FireMonkey Does What It Does

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

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

ArticleAuthorIssuePageTypeYear
Delphi 2007 HandbookFrans Doove14Book Review2008
Delphi Win 32 / .NET Component DevelopmentFrans Doove14Book Review2008
Representing graphics for math functionsPeter Bijlsma16Article2008
Client Dataset ToolkitDetlef Overbeek18Article2008
Website name checkingHenk Schreij19Article2008
Database normalisationHerman Peeren111Article2008
Delphi 2007 and VCL Component BuildingBob Swart113Article2008
WallpaperDetlef Overbeek116Article2008
Coding for two or more typesHenk Schreij117Article2008
Recipe for creating a cookbookTim Opsteeg121Article2008
Exploring Rave ReportsRik Smit125Article2008
Drawing on a changing backgroundDavid Dirkse133Article2008
Mini application: Opening filesDetlef Overbeek135Article2008
Rotating imagesDetlef Overbeek136Article2008
Delphi for PHP .2 new versionHerman Peeren23Article2008
Delphi 7 for Win 32 Development EssentialsFrans Doove26Book Review2008
Essential PascalFrans Doove26Book Review2008
Applied Mathematics for Database ProfessionalsFrans Doove27Book Review2008
Illustrated C# 2008Frans Doove27Book Review2008
Efficient Pascal – For-loopsHallvard Vassbotn29Article2008
Hack #1: Write access to a read-only propertyHallvard Vassbotn211Article2008
Resizing BitmapsDavid Dirkse212Article2008
Making right-justified EditsHenk Schreij214Article2008
Delphi 2007 VCL Component Building - IIBob Swart216Article2008
ClientDataset filteringMartin De Bont220Article2008
Binary HeapJulian Bucknall221Article2008
Intraweb and AJAXBob Swart225Article2008
Solving unknown variablesPeter Bijlsma228Article2008
Obfuscator?Rob van den Bogert230Article2008
Restricting Edits to numbersHenk Schreij232Article2008
Monitor aspectsJeremy North233Article2008
Euro symbol generation with the Alt keyHenk Schreij238Article2008
VCL Ribbon controlsJeremy North33Article2008
The Tomes of Delphi: Algorithms and Data StructuresFrans Doove35Book Review2008
Advantage Database Server, A Developer's GuideFrans Doove35Book Review2008
Combining visual components in an objectWim Zandt36Article2008
A simple component for color selectionDavid Dirkse39Article2008
Introduction to EnumeratorsPrimož Gabrijelcic310Article2008
Images in a DbGridHenk Schreij312Article2008
Change indication in a multiuser systemHenk Schreij315Article2008
Curve Fitting made easyPeter Bijlsma316Article2008
Advantage Database Server 9Rob van den Bogert319Article2008
VCL Property EditorsBob Swart321Article2008
Hack#2: Access to protected methodsHallvard Vassbotn326Article2008
Hack#1 updateHallvard Vassbotn327Article2008
Delphi Stream ClassesMarco Cantù328Article2008
TExpandButtonJeremy North330Article2008
Extending streamsJulian Bucknall332Article2008
Service Oriented ArchitectureFikret Hasovic336Article2008
Advanced EnumeratorsPrimož Gabrijelcic44Article2008
Text encryptionDavid Dirkse47Article2008
First In First OutFrank Dubbeld48Article2008
Delphi and the KeyboardDavid Dirkse49Article2008
RichEdit reportsHenk Schreij411Article2008
BookmarkStrHenk Schreij415Article2008
Continuation Curve Fitting made easyPeter Bijlsma417Article2008
Delphi 2009, Development EssentialsFrans Doove420Book Review2008
Delphi 2009 Language EnhancementsBob Swart421Article2008
Hacks3-Access to private fieldsHallvard Vassbotn426Article2008
Creating TView objectJeremy North427Article2008
Delphi Memory StreamsMarco Cantù430Article2008
SOA part twoFikret Hasovic433Article2008
Delphi 2009 HandbookFrans Doove54Book Review2009
Delphi 2009 Development EssentialsFrans Doove54Book Review2009
Specifying more than one shortcut keyHenk Schreij56Article2009
Solution for slow scrolling in DbGridsHenk Schreij57Article2009
The Xbitmap componentDavid Dirkse59Article2009
Painting framesDavid Dirkse512Article2009
Creating Stereographic ImagesPeter Bijlsma513Article2009
Dialogue boxesAdrian Kummerlaender515Article2009
The “Oracle” of DelphiThomas Pfister516Article2009
Advanced EnumeratorsPrimož Gabrijelcic519Article2009
Using TReader en TWriterMarco Cantù522Article2009
ASP.NET Web Services with Delphi Prism 2009Bob Swart524Article2009
Ceating a progress barJeremy North530Article2009
SOA part threeFikret Hasovic533Article2009
An Advantage for Delphi DevelopersCary Jensen65Article2009
Delphi RoadmapMarco Cantù68Article2009
A Twitter REST Client in DelphiMarco Cantù611Article2009
Resolving in “kbmMW”Fikret Hasovic622Article2009
JSDialog PackJeremy North631Article2009
Visual FormsJeremy North633Article2009
RemObjects Hydra: Win32 and .NET PluginsBob Swart636Article2009
Controlling the parallel portThiago Batista Limeira651Article2009
The Xbitmap class (part 2) Lines and EllipsesDavid Dirkse696Article2009
The Xbitmap class (part 3) PolygonsDavid Dirkse713Article2009
FastReport, A report generator for Delphi and .NETMarco Roessen718Article2009
When as var, const, out: or not to declareHenk Schreij724Article2009
Using Colours in DBGridHenk Schreij725Article2009
Writing Delphi Components: an IntroductionMarco Cantù727Article2009
Windows Communication Foundation with Delphi PrismBob Swart730Article2009
Datastore metadataFikret Hasovic734Article2009
using the Alt KeyFrans Dubbeid738Article2009
Delphi 2010 – what a feeling! GesturesBob Swart87Article2009
CountersDavid Dirkse811Article2009
Virus in Delphi?Nick Hodges814Article2009
Dezign for databasesMarco Roessen816Article2009
Customizing the T-Field data displayHenk Schreij818Article2009
Using Free Pascal and Lazarus to create applications for OSXJeremy North820Article2009
Writing Delphi Components IIMarco Cantù822Article2009
My Top Five Delphi 2010 New FeaturesPawel Glowacki824Article2009
Fast Graphic deformation by using ScanlinesPeter Bijlsma828Article2009
Wide Information Bus (Introduction)Fikret Hasovic833Article2009
Freehand Drawing (Introduction)David Dirkse836Article2009
The 3D spheres generatorDavid Dirkse95Article2009
What is kbmMW?Benno Evers97Article2009
Delphi 2010 Feature Highlight - Debugger VisualizersJeremy North912Article2009
Introduction to multithreadingPrimož Gabrijelcic918Article2009
Writing Delphi Components IIIMarco Cantù921Article2009
Talking DelphiHenk Schreij925Article2009
LCD Interfacing: Driving a HD44780 LCD in DelphiThiago Batista Limeira928Article2009
Exploring the inplace editing capabilities of TAdvStringGridBruno Fierens934Article2009
Remembering inputHenk Schreij106Article2010
Rotating photo cubePeter Bijlsma107Article2010
Non overlapping rectanglesDavid Dirkse1011Article2010
The Wide Information Bus part 2Fikret Hasovic1012Article2010
Library UnitHenk Schreij1016Article2010
Delphi Component writing part 4Marco Cantù1020Article2010
Delphi 2010 Delayed Dynamic Link LibrariesBob Swart1023Article2010
Four Ways to Create a ThreadPrimož Gabrijelcic1025Article2010
Delphi Advanced Tools:Michael Rozlog1030Article2010
DelphiControllerAnton Vogelaar1033Article2010
TWebUpdate & UpdateBuilderBruno Fierens1034Article2010
Debugger Visualizers Part 2Jeremy North1036Article2010
Synchronisation in a multithreaded environmentPrimož Gabrijelcic115Article2010
PacMan in Delphi/Lazarus Algorithm jungle - or how to get caughtJean Pierre Hoefnagel118Article2010
Moving from the BDE to ADS he End of the BDE?Bob Swart1111Article2010
Putting photographs on the internet A program using Indy FtpHenk Schreij1116Article2010
Shrinking of JPEGs Not small enough?Henk Schreij1120Article2010
An Introduction to theGoogle Docs API with Delphi Googling magic...Marco Cantù1122Article2010
Using TMS Poly List ControlsBruno Fierens1124Article2010
CodeHealer 2.6: fantastic toolkit or healer?Rik Smit1128Article2010
Drawing regular Polygons Make it look like a starDavid Dirkse1131Article2010
Database Workbench, productivity tool A fantastic ToolMiguel van de Laar1134Article2010
Programming FrogsDavid Dirkse1135Article2010
Introducing the Open Tools APIJeremy North1137Article2010
Design patterns Starting a web series of basic patternsHerman Peeren1139Article2010
Electronics Delphi Controller and Delphi Developer boardAnton Vogelaar125Article2010
RAD Studio XE PreviewDetlef Overbeek127Article2010
SQL produced by the ClientDataSetHenk Schreij1212Article2010
Introducing GoogleDocsApi_SecondPartMarco Cantù1216Article2010
The use of kbmMW components in a server applicationBenno Evers1218Article2010
Inter Process CommunicationPrimož Gabrijelcic1224Article2010
Peg Solitaire - a single player puzzleDavid Dirkse1227Article2010
Trans-continental travel using the great circlePeter Bijlsma1231Article2010
Creating gateways and other advanced topicsFikret Hasovic1234Article2010
Creating Office 2010 style VCL applications with TMS componentsBruno Fierens1237Article2010
Mini Course SQLMiguel van de Laar1310Article2010
Delphi JSON ViewerPawel Glowacki1313Article2010
Is Lazarus ready for writing commercial applications ?Zeljan1316Article2010
NexusDB exceptionaly good, a real surprise...Erwin Mouthaan1322Article2010
The TMS DataModelerBruno Fierens1327Article2010
Introduction to Databases Part 1Cary Jensen1331Article2010
First Look at Advantage Database Server® 10Cary Jensen1335Article2010
Real-time data collectionAnton Vogelaar1339Article2010
About Object oriented databasesDetlef Overbeek1345Article2010
Fastreport, whats up?Detlef Overbeek1352Article2010
Using ADS with Delphi Prism and ASP.NETBob Swart1359Article2010
A datawarehouse example using kbmMWKim Madsen1366Article2010
Multiplatform Windows CEJoost van der Sluis1373Article2010
Five Considerations for Choosing an Effective SQL Development ToolScott Walz1381Article2010
Pascal’s triangle byDetlef Overbeek148Article2010
Create an Index of your (backup) CD's/DVD's byPeter Bijlsma1410Article2010
Debugging multithreaded applications byPrimož Gabrijelcic1415Article2010
Using Amazon Simple Storage Service (Amazon S3) from DelphiMarco Cantù1419Article2010
A small spamming machine byDetlef Overbeek1422Article2010
Creating a Webserver in 5 minutes using kbmMW! byKim Madsen1424Article2010
Gadgets in your Delphi Windows application byHenk Schreij1428Article2010
Open Tools API (OTA) byJeremy North1430Article2010
Exploring the Async capabilities of TMS IntraWeb gridsBruno Fierens1437Article2010
Delphi XE Starter edition reviewedHoward Page-Clark157Article2011
Delphi XE Starter try: a littel MailserverJim Duff1513Article2011
ClientDataset: what to do if you dont have it?Detlef Overbeek1521Article2011
Introduction to Databases Part 2: Common Database ObjectsCary Jensen1528Article2011
Advantage Database Server and Delphi XE Starter EditionHoward Page-Clark1531Article2011
Moving /adding your components into the Delphi Starter EditionRik Smit1535Article2011
Delphi and UnicodePeter Prisman168Article2011
Anything to celebrate?Peter Bijlsma1610Article2011
The Xfont projectDavid Dirkse1612Article2011
Learning to use FastReport 4 for VCL, Part 1Sergey Lyubeznyy1619Article2011
Introduction to 64 bits on WindowsAlexander Alexeev1628Article2011
Creating iPhone/iPod/iPad web applications with Delphi & TMS controlsBruno Fierens1633Article2011
Introduction to Databases Part 3: TDataSetCary Jensen1639Article2011
64-bit Delphi programs are on the wayPrimož Gabrijelcic1643Article2011
Pascal Programing in the Android OSFelipe Monteiro de Cavalho1646Article2011
FieldByName alternativesHenk Schreij1654Article2011
The FlexCel components in actionSiegfried Zuhr1657Article2011
Writing new components in LazarusHoward Page-Clark174Article2011
Theme: Using Amazon S3 from Delphi (Part II)Marco Cantù1712Article2011
The calculation of expressions with complex numbers and/or three-dimensional vectorsAnne Meijer1717Article2011
Waiting for x64: WOW64Alexander Alexeev1721Article2011
Learning to use FastReport 4 for VCL, Part 2Sergey Lyubeznyy1724Article2011
Pascal's Triangle revisitedPaul Gellings1729Article2011
Introduction to Test-Driven-Development (TDD) in DelphiAlexander Alexeev1731Article2011
Theme: DataSnap and Android (Part I)Daniele Teti1738Article2011
Financial functions in DelphiPeter Bijlsma1743Article2011
Magic-Button Anti-PatternAlexander Alexeev1745Article2011
Tiles everywhere: introduction to TAdvSmoothTileListBruno Fierens1748Article2011
Introduction to Databases part 4: TDataSetCary Jensen1753Article2011
Writing New Components in Lazarus, Part 2Howard Page-Clark186Article2011
Delphi XE2 is the future - theme XE2Fikret Hasovic1813Article2011
Cylindrical anamorphosisPeter Bijlsma1817Article2011
Web Service Toolkit as a rich type framework for Plug-in developmentInoussa Ouedraogo1821Article2011
Polygon colouring and area calculationDavid Dirkse1824Article2011
Creating a Database program from scratch - 1Detlef Overbeek1829Article2011
An Android client for a DataSnap Server – Part 2Daniele Teti1834Article2011
Anti-freeze for the VCLAlexander Alexeev1839Article2011
Delphi XE2 New Features - theme XE2Bob Swart1846Article2011
Learning to use FastReport 4 for VCL - Part 3Sergey Lyubeznyy1853Article2011
Creating 64-bit applications with Delphi XE2 - theme XE2Jeremy North1864Article2011
kbmSQL – Structured Query Language for your memory tableKim Madsen1872Article2011
High Level MultithreadingPrimož Gabrijelcic1876Article2011
TMS Scripter Studio ProBruno Fierens1881Article2011
FreePascal vectorialFelipe Monteiro de Cavalho1890Article2011
The tale of the NigthtingaleDetlef Overbeek1896Article2011
Using the Advantage Database Client EngineMichael Van Canneyt18101Article2011
Introduction to Databases part 5: Configuring OLE DB Providers and ODBC DriversCary Jensen18107Article2011
Delphi XE2 LiveBinding - theme XE2Bob Swart18113Article2011
DataSnap connectivity for iOS using Delphi XE2 and FireMonkeyAnders Ohlsson196Article2011
Creating a simple webserver in LazarusMichael Van Canneyt1916Article2011
Using Amazon S3 in Delphi XE2Marco Cantù1921Article2011
Creating a Database program from scratch - 2Detlef Overbeek1929Article2011
Writing New Components in Lazarus, Part 4Howard Page-Clark1937Article2011
TMS MultiTouch SDKBruno Fierens1946Article2011
FireMonkey Containers and FramesBob Swart1953Article2011
Introduction to Databases Part 6: TClientDataSetCary Jensen1956Article2011
The new Lazarus for AndroidFelipe Monteiro de Carvalho204Article2011
Q&A Special Hint CueBannerHenk Schreij2014Article2011
The XFont ClassDavid Dirkse2015Article2011
3D geometry developmentBJ Rao2020Article2011
Q&A Determine the latitude and longitude of an addressHenk Schreij2023Article2011
Writing New Components in Lazarus, Part 4Howard Page-Clark2024Article2011
Creating a Database program from scratch - 3Detlef Overbeek2029Article2011
Introduction to Databases Part 7: dbExpressCary Jensen2039Article2011
Porting my Amazon S3 client to FireMonkeyMarco Cantù2045Article2011
Writing a component for iOS using Delphi XE2 & FireMonkeyAnders Ohlsson2049Article2011
TMS Instrumentation Workshop for FireMonkeyBruno Fierens2051Article2011
kbmMW Remote DesktopKim Madsen2055Article2011
Telnet in PascalFelipe Monteiro de Carvalho215Article2012
How to show formatted HTML text? And how to print?Henk Schreij2112Article2012
Developing an Outlook Social ProviderAndrey Terekhov2113Article2012
How to use the Format function?Henk Schreij2118Article2012
Creating a Delphi database application for Mac OS XTim Opsteeg2119Article2012
Working natively with ZIP archives in Windows using zipfldr.dllViacheslav Ryabinin2123Article2012
Using AnyDac in DelphiMichael Van Canneyt2132Article2012
VCL Custom Styles – Part 1Jeremy North2137Article2012
Introduction to Databases Part 8: dbExpress (continued)Cary Jensen2143Article2012
Introduction to DebuggingPrimož Gabrijelcic2150Article2012
Kinect or Xtion? GeNui !Simon Stuart2156Article2012
DLL Hell, part 1: DLL redirectionAlexander Alexeev226Article2012
Pump some Oxygene into your Android device with Oxygene for JavaBrian Long228Article2012
AnyDAC macros and scriptingMichael Van Canneyt2219Article2012
VCL Custom Styles – Part 2Jeremy North2224Article2012
Creating a Database program from scratch - 4Detlef Overbeek2230Article2012
One of the hotest topics: XML and lately JSON...Fikret Hasovic2234Article2012
Advanced debugging - Part 2Primož Gabrijelcic2239Article2012
Object-Relational Mapping with TMS AureliusWagner Landgraf2243Article2012
Using Excel functions in DelphiNikolay Bilfeld2249Article2012
Unit testing and ’TestGrip’Marco Geuze2250Article2012
How to create a simple About screen? Questions and AnswersHenk Schreij2259Article2012
Writing Cross-Platform CodeAlexander Alexeev238Article2012
Solving equations using Regula FalsiPeter Bijlsma2314Article2012
Data-Aware FireMonkey Controls using LiveBindingsCary Jensen2319Article2012
Advanced debugging Tips and TricksPrimož Gabrijelcic2326Article2012
Extending TListBoxItem to customize list box behaviourJeremy North2329Article2012
Pascal for InternetPrimož Gabrijelcic2334Article2012
Pascal scores high notesSiegfried Zuhr2339Article2012
Introducing TTMSFMXGridBruno Fierens2343Article2012
Delphi XE2, iOS and SQLiteBob Swart2349Article2012
Meaningful colours or is it all gray?David Dirkse2352Article2012
How to develop n-tier applications for iOS using kbmMWRoger Nyberg2355Article2012
Interview with Kim MadsenDetlef Overbeek245Article2012
Sending mails using LazarusMichael Van Canneyt2410Article2012
Interview with Dmitry ArefievDetlef Overbeek2419Article2012
Smart Mobile StudioJørn E. Angeltveit2429Article2012
Interview with Marco CantuDetlef Overbeek2433Article2012
FireMonkey in Delphi XE3: FM2Marco Cantù2436Article2012
HTML 5Bob Swart2449Article2012
Interview with Michael RozlogDetlef Overbeek2452Article2012
XE3 Style DesignerJeremy North2456Article2012
Burgler detection using LazarusMichael Van Canneyt2462Article2012
Supporting new android features in old android versions with OxygeneBrian Long2468Article2012
Delphi XE3 Helper TypesBob Swart2476Article2012
Interview with Marc HoffmanDetlef Overbeek2479Article2012
Introduction to Databases Part 9: Overview of DataSnapCary Jensen2483Article2012
Software taxes - storageAlexander Alexeev2487Article2012
Interview with David I. (Intersimone)Detlef Overbeek2496Article2012
The Delphi HistoryDetlef Overbeek24101Article2012
From Delphi to the cloudBruno Fierens24106Article2012
Interview with Bruno FierensDetlef Overbeek24111Article2012
kbmMW as a RTMP media serverKim Madsen24114Article2012
Lazarus and dabatasesMichael Van Canneyt24117Article2012
Match5, a self learning board gameDavid Dirkse2511Article2012
Programming in Smart: Application DesignPrimož Gabrijelcic2519Article2012
TMS FlexCel for VCL and FireMonkeyAdrian Gallero2526Article2012
Delphi IDE TipsBrian Long2531Article2012
DataSnap: DBX vs. RESTBob Swart2540Article2012
Crowd FundingBJ.Rao2543Article2012
Displaying video files using Free Pascal and LazarusMichael Van Canneyt2552Article2012
High performance virtual filesystems with kbmMWKim Madsen2558Article2012
Introduction to Databases Part 10: IP-Based DataSnapCary Jensen267Article2012
How to prune a treeDavid Dirkse2616Article2012
How to display a YouTube clip?Henk Schreij2622Article2012
Programming in Smart: Supporting Multiple ResolutionsPrimož Gabrijelcic2632Article2012
HTML5 Builder DataSnap ClientsBob Swart2636Article2012
Object serialization using kbmMWKim Madsen2652Article2012
Introduction to thread programming in LazarusMichael Van Canneyt274Article2013
Introduction to Databases Part 11: DataSnap Filters and SecurityCary Jensen2713Article2013
Electronic document processing tools for Delphi made by GnosticeGrish Patil2723Article2013
Use of the "absolute" function abs(..)David Dirkse2736Article2013
Development for lazarusJoost van der Sluis2741Article2013
What is polymorphism?Vsevolod Leonov2748Article2013
Creating and using kbmMW as an Ajax back-end serverBenno Evers2752Article2013
NEW AND ENHANCED FEATURES IN DELPHI XE4Editor284Article2013
Alternative datastreams (ADS) (better inifile)Jean Pierre Hoefnagel2814Article2013
First Look at FireDACCary Jensen2816Article2013
Writing UI-Independent Code with Anonymous MethodsMarco Cantù2818Article2013
Introduction to Databases Part 12: A DataSnap ClientCary Jensen2823Article2013
Programming in Smart: RefactoringPrimož Gabrijelcic2834Article2013
Development for lazarus part twoJoost van der Sluis2842Article2013
Delphi XE4 iOS DataSnap ClientsBob Swart2852Article2013
Intro to kbmMW connectivity package 'Spider'Fikret Hasovic2857Article2013
Polymorphism in practiceAlexander Alexeev294Article2013
The Raspberry Pi, Pi Vision and Lazarus/FPCBJ Rao2914Article2013
FPC Hackathon 2013Michael Van Canneyt2922Article2013
UniDAC — unique components for DB application development for iOSBoris Matkov2925Article2013
1. Pascal Fundamentals: TActionlist, TImagelist and Image editorMichael Van Canneyt2931Article2013
2. Pascal Fundamentals: How long is a pice of string: Short strings and Ansi stringsHoward Page-Clark2937Article2013
Smart Mobile Studio - Multiple categoriesPrimož Gabrijelcic2941Article2013
Developing a virtual worldCary Jensen2945Article2013
The TMS iCLBy Bruno Fierens2951Article2013
Serialization using kbmMWFikret Hasovic2955Article2013
Creating an IOS applicationJeremy North304Article2013
Introduction Database Development Part 13: Connecting to Data with FireDacCary Jensen3018Article2013
Pascal Fundamentals: Understanding components Part 3: The TListBox componentHoward Page-Clark3028Article2013
Smart Mobile StudioJørn Einar Angeltveit3041Article2013
Programming with the leap motionMichael Van Canneyt3047Article2013
Lazarus Closed Source PackagesMattias Gaertner3056Article2013
Support for Word and LibreOffice Text Documents - FPVectorialFelipe Monteiro de Carvalho3060Article2013
Delphi XE5 and FireMonkey for AndroidBob Swart3069Article2013
Sciencepalooza Fact: Man causes the earth heatingYuri Matteman3074Article2013
The kbmMW authorization managerKim Madsen3075Article2013
Designing an API: common mistakesAlexander Alexeev31/328Article2013
Newest Leap developmentsMichael Van Canneyt31/3221Article2013
3D PrintingBj Rao31/3226Article2013
Kinect ?!Michael Van Canneyt31/3233Article2013
Smart Mobile Studio 2.0Primož Gabrijelcic31/3241Article2013
A simple superscript text editorDavid Dirkse31/3250Article2013
Using GEO services in Delphi applications with TMS componentsBruno Fierens31/3257Article2013
Correcting a bad API design:Alexander Alexeev31/3265Article2013
The maXbox Pure CodeMax Kleiner31/3277Article2013
Programming Bitmap RotationDavid Dirkse31/3298Article2013
Introduction to Model, View and View Model (MVVM) and the Caliburn Micro for Delphi frameworkJeroen Pluimers31/32102Article2013
kbmFMX for XE5 (android)Fikret Hasovic31/32113Article2013
maXbox Starter 5 Start with DLL A Library for AllMax Kleiner335Article2014
Suite Rays componentsRik Smit3314Article2014
Steps in MindScape AppView Step-By-StepJeroen Pluimers3326Article2014
Programming Truth Table reductionDavid Dirkse3346Article2014
Databases for XE6Cary Jensen3359Article2014
Updating DotTapperJeremy North3353Article2014
Six new ready to use components!Fikret Hasovic3365Article2014
Ode To the CodeCary Jensen344Article2014
Animation Lab for AndroidBoin Mitov345Article2014
All about understanding the RTTI / Attributes / Functional ProgrammingDetlef Overbeek347Article2014
Raize Components Version 6PART II ContinuationRik Smit3418Article2014
QR (Quick Response) CodesMax Kleiner3424Article2014
Image CompressionDavid Dirkse3429Article2014
Review : Documentation Insight by DevJet SoftwareJeremy North3436Article2014
Introduction Local SQL with FireDACCary Jensen3441Article2014
Unit testing - test driven developmentMichael Van Canneyt3446Article2014
Serving an off the shelf Content Management SystemKim Madsen3453Article2014
Smart Mobile Studio: Where to use it and with what? CommentChristan Budde3516Article2014
Building simple native applications for WindowsChristan Budde3517Article2014
node.js on a Raspberry PiChristan Budde3519Article2014
Debugging Techniques in LazarusHoward Page Clark3523Article2014
Start with GEO MapsMax Kleiner3540Article2014
A 3D Tic-Tac-Toe gameDavid Dirkse3545Article2014
Blazing Performance with FireDAC Array DMLCary Jensen3552Article2014
Creating custom edit descendants in XE7Jeremy North3557Article2014
Leap Motion Version 2Michael Van Canneyt3562Article2014
KBMMW and messaging (the WIB)Fikret Hasovic3564Article2014
A WYSIWYG math editorDavid Dirkse3610Article2014
Blue ToothJeremy North3616Article2014
3D Printing Lab (maxbox)Max Kleiner3623Article2014
Converting Delphi to Lazarus and Vice VersaEditor3628Article2014
Differences between Delphi and LazarusEditor3628Article2014
Lazarus for beginners: Starting your first programHoward Page-Clark3635Article2014
TMS XData and TMS Aurelius – Learning ExampleWagner Landgraf3640Article2014
UpdateMode and FireDAC DataSetsCary Jensen3650Article2014
AMQP (Advanced Message Queuing Protocol)Fikret Hasovic3654Article2014
Cloning FDMemTable CursorsCary Jensen37/3813Article2014
Floating points? -David Dirkse37/3820Article2014
The Leap Motion VisualizerDmitry Boyarintsev37/3823Article2014
Creating Applications for Mobile: How to make money with appsMark Wilcox37/3831Article2014
Migration to FastReport: How easy can it be ?Denis Zubov37/3837Article2014
TIOBE Index for January 2015Editor37/3845Article2014
Apple’s Guidelines for Creating and PublishingEditor37/3848Article2014
Google’s Guidelines for Creating and Publishing Android AppsEditor37/3853Article2014
Google’s help for developers for AndroidEditor37/3855Article2014
Lazarus for beginners 3Howard Page-Clark37/3862Article2014
WYSIWYG formatted: text & images editor for VCL & FireMonkeyBruno Fierens37/3871Article2014
Regular Expression ReportMax Kleiner37/3878Article2014
Regular Expressions Explained / ProgrammingEditor37/3888Article2014
Getting Started with the Raspberry PiSiegfried Zuhr37/3889Article2014
Using Free Pascal to create Android applicationsMichael Van Canneyt37/38104Article2014
Having fun with Delphi and AMQPKim Madsen37/38112Article2014
ClientDataSets and FDMemTables Compared: Part 1Cary Jensen3911Article2015
Accessing Preferences and Databases in Android (FPC)Michael Van Canneyt3922Article2015
Painting 3D Lissajous figuresDavid Dirkse3931Article2015
Checking your Android device is ready for developmentStephen Ball3936Article2015
Interbase Change ViewsStephen Ball405Article2015
From Delphi to Delphi, overview of the Delphi historyEditor408Article2015
Delphi - the cityEditor4013Article2015
Why the name Delphi?Editor4014Article2015
Introduction to FlatBox2D for Delphi XE8Pawel Glowacki4016Article2015
ClientDataSets and FDMemTables Compared: Part 2Cary Jensen4024Article2015
Start with Function Testing (maXbox)Max Kleiner4031Article2015
Working with Bluetooth beacons in Delphi XE8Jeremy North4040Article2015
Security And Safety In ApplicationsAndrea Raimondi415Article2015
Beginning Of Time... The Water ClockEditor419Article2015
The New Lazarus 1.4Editor4114Article2015
Security In Applications: Password HandlingAndrea Raimondi4118Article2015
In Remembrance Of Alan TuringEditor4123Article2015
Rest Clients: Using The Google Apis In Free PascalMichael Van Canneyt4125Article2015
Arduino: The Visuino Project - Part 1Boian Mitov4137Article2015
Working With Google Merchants Rating Data Using kbmmwKim Madsen4143Article2015
Quantum ComputingEditor425Article2015
Thales Of Miletus - A Pre-Socratic Greek PhilosopherEditor429Article2015
Arduino: The Visuino Project - Part 2Boian Mitov4214Article2015
Getting Started With The TMS Planner For FiremonkeyBruno Fierens4222Article2015
Mapping Using DelphiPeter Van Der Sman4228Article2015
Log, Debug And Audit The kbmmw WayKim Madsen4233Article2015
Free Pascal Compiler 3.0 In PreparationMichael Van Canneyt436Article2015
Overview Of Delphi To Delphi HistoryEditor438Article2015
Pythagoras Of Samos, Not The Inventor Of The A^2 + B^2 = C^2Editor439Article2015
Arduino: The Visuino Project - Part 3Boian Mitov4312Article2015
Fast Reports VCL In A NutshellJohn Kuiper4330Article2015
Brand Your Own Remote Desktop SolutionKim Madsen4340Article2015
Overview Of Delphi To Delphi HistoryEditor446Article2015
EuclidesEditor447Article2015
An Agep UzzleDavid Dirkse448Article2015
Arduino: The Visuino Project - Part 4Boian Mitov4412Article2015
Database Workbench 5Peter Van Der Sman4428Article2015
Tips And Tricks With KBM MemtableKim Madsen4439Article2015
Overview Of Delphi To Delphi HistoryEditor45/468Article2015
Plato And Platinic SolidsEditor45/469Article2015
A Simple Editor For In-Line HelpDavid Dirkse45/4612Article2015
FindFirst/FindNext And The Power Of HabitsHeiko Rompel45/4618Article2015
Android Programming In Free Pascal Networking External Code And ThreadsMichael Van Canneyt45/4620Article2015
Rad Studio 10 Seattle IntroductionEditor45/4629Article2015
Using Azure Translator Services With DelphiStephen Ball45/4644Article2015
How To Use Fmx: Tframestand Easily Use Tframe(S) In Fmx ApplicationsAndrea Magni45/4654Article2015
Delphi Building Blocks - Advanced CollectionsBoian Mitov45/4662Article2015
Maxbox Starter 18 Start With Arduino Programmingv 3.1Max Kleiner45/4672Article2015
Archimedes Editor475Article2015
Installing Lazarus On Mac Os XMichaël Van Canneyt478Article2015
Using “Raspberry Pi” As A Flexible BeaconDanny Wind4712Article2015
The Towers Of HanoiDavid Dirkse4719Article2015
Presenting A Fast & Feature-Rich Multicolumn Treeview For DelphiBruno Fierens4723Article2015
The Enhanced Rtti (Runtime Type Library)Boian Mitov4729Article2015
Getting Things Done At Time, Every TimeKim Madsen4737Article2015
A Microseconds Counter Component In DelphiDavid Dirkse485Article2015
Watch Your Calories Using The Nutrition Monitor!Peter Bijlsma487Article2015
Custom Forms In DelphiMichaël Van Canneyt4819Article2015
Installing Lazarus On Linux MintMichaël Van Canneyt4826Article2015
Connect Iot With The Cloud Using Tms Lcl Cloud PackBruno Fierens4830Article2015
Time Is On My Side - MaxboxMax Kleiner4836Article2015

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

Writing Cross-Platform Code 

By Alexander Alexeev

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

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 platforms
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/
The main thing here is to have at hand a universal function to get paths to known folders.

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.

###

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.