Updating 32bit code to Delphi 64bit

Ever since Delphi XE2, it has been possible to generate Delphi 64bit applications from the same code base as your traditional Windows 32bit Delphi code. The business case for 64-bit for business is covered in this tech paper The Impact of 64-bit Applications to your Company’s Bottom Line.

On the whole moving to 64bit (on iOS or Windows) is beautifully simple to achieve! It can be just as simple as adding the Delphi 64bit Windows target platform in the project manager and rebuilding the project.

My experience from talking to many developers who have moved up is that normally there are a few things to check in your code but typically its not a massive task to get compiling and ready to test.

A lot has been recorded on moving from Windows 32bit to Windows 64bit Delphi and this should be a useful summary if your just planning now moving up from older versions of Delphi to Delphi 10. If you are building iOS applications, then you will need to use the 64bit build now to get into the AppStore. Thankfully, Delphi has made the task of using 64bit very simple across all platforms and protected us from the headaches non Delphi Developers have had on the whole.

Lets start with this short video from David I who covers some the foundations in 7 minutes!

Whats the same in 32bit and 64bit Delphi!

As they don’t need to sit in bigger memory spaces, Integer, LongInt, Cardinal – still 32bit on both platforms. Int64 and UInt64 are 64bit (8bytes) on both platforms.

Unicode String, AnsiString, WideString etc are all the same. Work in the same way and use the same RTL calls.

Exception handling is the same! You still set break points and they work in the same way when debugging, the exceptions that get raised are still handled in the same way.

The units you use are still the same e.g. SysUtils, Classes etc. The RTL (Run Time Library) is still the same.

Signed Types Delphi 32bit and Delphi 64bit ShortInt 1 byte SmallInt 2 byte LongInt 4 byte Integer 4 byte Int64 8 byte Unsigned Types Delphi 32bit and Delphi 64bit Byte 1 byte Word 2 byte LongWord 4 byte Cardinal 4 byte UInt64 8 byte

Differences in 32bit and 64bit Delphi code

There are a few types that are different depending on which platform you compile for.

NativeInt and NativeUInt are 32bit or 64bit / 4 or 8 bytes, depending on the target platform.

Dynamic Arrays use 64bit Indexing on a 64bit compilation.

64bit processors don’t have the same extended type that exists on 32bit meaning that the Extended type reverts to DOUBLE on 64bit. If you need to use Extended use TExtended80Rec.

But the key point is that Pointers are all 64bit on 64bit versions. This means a few things. Firstly, all objects have a bigger pointer than an Integer. So if you have been typecasting your pointers into Integers….. be warned!!!

Pointer Types that are 4 bytes in Delphi 32bit code and 8 bytes in 64bit code include

Pointer

String

Class Instance

Class reference

Interface

AnsiString

WideString

UnicodeString

Procedure Pointer

Dynamic Array

PAnsiChar

PWideChar

PChar

Code that uses SHL/SHR can also act differently over 32bit and 64bit values if you use NativeInt – using Integer will work the same.

Been using TAG property to store pointers?

Luckily – while its not best practice, your friendly Delphi component team has updated the Tag property on TComponent to be a NativeInt so it will be 4 / 8 bytes depending on the platform so will not break your code!

Calling Conventions

In Windows 32bit code you needed to use specific calling conventions to share and use specific API’s. In Windows 64bit, there is only one convention so the following are not required

Register

pascal

cdecl

stdcall

HOWEVER – still leave them in the code for the Win 32bit code to compile. The 64bit compiler is smart enough to just ignore them.

Safecall (used in COM development) is still “Special” however.

Using Assembler (ASM) blocks in your code on Windows?

One change in 64bit Delphi v 32bit Delphi code is that inline Assembler is not allowed. What this means is that you can’t have a method that contains both Pascal and Assembler between the same begin and end statement. This is easily fixed however and you have two options

Create a new function / procedure to wrap up the ASM code and call that function Replace the ASM code with pure Pascal code that will work on 32bit and 64bit Delphi targets (not only on Windows, but beyond)

If you do extract the method into a new function (so you can keep your assembler for Win32) you will still need to add in code to allow it to work on 64bit compilations and need to tell the compiler how to differentiate between the two blocks of code… That is where Pre-defined conditions come in.

Pre-defined conditions

The Delphi 64bit compiler introduced a number of new pre-defined conditions. While you don’t need to use these regularly, they can be used if you want to keep assembler code that you have optimised for Win32 and then only use pure pascal code on other platforms.

Pre-defined conditions allow specific blocks of code to be compiled in depending on the platform e.g. The following block will show Win32 as a show message on Windows 32bit versions only.

procedure DoSomething; var S : string; begin {$IFDEF WIN32} S := 'Win32' ; {$else} S := 'Not on Win32' ; {$ENDIF} ShowMessage(S); end;

Here is a list of the conditions and the platforms they are defined for. You can get a full list of Delphi pre-defined conditions from DocWiki

Delphi and the Windows API

The core windows windows RT code will work correctly, if you are doing any special low level messaging, there are differences in the WPARAM and LPARAM. You can use explicit cast as appropriate: e.g if you are passing messages through SendMessage:

SendMessage(hWnd, WM_SETTEXT, 0, LPARAM(@MyCharArray));

Use LRESULT to case message results

Message.Result := LRESULT(Self);

If you are doing any TWMxxx message cracking, then you will need to re-align changes and field sizes changes to allow for the difference between Win32 and Win64.

Further reading / viewing