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.