How to also prepare for 64-bits when migrating to Delphi 2010 and Unicode

Solution 1:

There's another similar question, but I'll repeat my reply here too, to make sure as many people see this info:

First up, a disclaimer: although I work for Embarcadero. I can't speak for my employer. What I'm about to write is based on my own opinion of how a hypothetical 64-bit Delphi should work, but there may or may not be competing opinions and other foreseen or unforeseen incompatibilities and events that cause alternative design decisions to be made.

That said:

  • There are two integer types, NativeInt and NativeUInt, whose size will float between 32-bit and 64-bit depending on platform. They've been around for quite a few releases. No other integer types will change size depending on bitness of the target.

  • Make sure that any place that relies on casting a pointer value to an integer or vice versa is using NativeInt or NativeUInt for the integer type. TComponent.Tag should be NativeInt in later versions of Delphi.

  • I'd suggest don't use NativeInt or NativeUInt for non-pointer-based values. Try to keep your code semantically the same between 32-bit and 64-bit. If you need 32 bits of range, use Integer; if you need 64 bits, use Int64. That way your code should run the same on both bitnesses. Only if you're casting to and from a Pointer value of some kind, such as a reference or a THandle, should you use NativeInt.

  • Pointer-like things should follow similar rules to pointers: object references (obviously), but also things like HWND, THandle, etc.

  • Don't rely on internal details of strings and dynamic arrays, like their header data.

  • Our general policy on API changes for 64-bit should be to keep the same API between 32-bit and 64-bit where possible, even if it means that the 64-bit API does not necessarily take advantage of the machine. For example, TList will probably only handle MaxInt div SizeOf(Pointer) elements, in order to keep Count, indexes etc. as Integer. Because the Integer type won't float (i.e. change size depending on bitness), we don't want to have ripple effects on customer code: any indexes that round-tripped through an Integer-typed variable, or for-loop index, would be truncated and potentially cause subtle bugs.

  • Where APIs are extended for 64-bit, they will most likely be done with an extra function / method / property to access the extra data, and this API will also be supported in 32-bit. For example, the Length() standard routine will probably return values of type Integer for arguments of type string or dynamic array; if one wants to deal with very large dynamic arrays, there may be a LongLength() routine as well, whose implementation in 32-bit is the same as Length(). Length() would throw an exception in 64-bit if applied to a dynamic array with more than 232 elements.

  • Related to this, there will probably be improved error checking for narrowing operations in the language, especially narrowing 64-bit values to 32-bit locations. This would hit the usability of assigning the return value of Length to locations of type Integer if Length(), returned Int64. On the other hand, specifically for compiler-magic functions like Length(), there may be some advantage of the magic taken, to e.g. switch the return type based on context. But advantage can't be similarly taken in non-magic APIs.

  • Dynamic arrays will probably support 64-bit indexing. Note that Java arrays are limited to 32-bit indexing, even on 64-bit platforms.

  • Strings probably will be limited to 32-bit indexing. We have a hard time coming up with realistic reasons for people wanting 4GB+ strings that really are strings, and not just managed blobs of data, for which dynamic arrays may serve just as well.

  • Perhaps a built-in assembler, but with restrictions, like not being able to freely mix with Delphi code; there are also rules around exceptions and stack frame layout that need to be followed on x64.

Solution 2:

First, look at the places where you interact with non-delphi libraries and api-calls, they might differ. On Win32, libraries with the stdcall calling convenstion are named like _SomeFunction@4 (@4 indicating the size of the parameters, etc). On Win64, there is only one calling convention, and the functions in a dll are no more decorated. If you import functions from dll files, you might need to adjust them.

Keep in mind, in a 64 bit exe you cannot load a 32-bit dll, so, if you depend on 3rd party dll files, you should check for a 64-bit version of those files as well.

Also, look at Integers, if you depend on their max value, for example when you let them overflow and wait for the moment that happens, it will cause trouble if the size of an integer is changed.

Also, when working with streams, and you want to serialize different data, with includes an integer, it will cause trouble, since the size of the integer changed, and your stream will be out of sync.

So, on places where you depend on the size of an integer or pointer, you will need to make adjustments. When serializing sush data, you need to keep in mind this size issue as well, as it might cause data incompatibilities between 32 and 64 bit versions.

Also, the FreePascal compiler with the Lazarus IDE already supports 64-bit. This alternative Object Pascal compiler is not 100% compatible with the Borland/Codegear/Embarcadero dialect of Pascal, so just recompiling with it for 64-bit might not be that simple, but it might help point out problems with 64-bit.