Converting 32-bit Delphi Applications to 64-bit Windows

From RAD Studio
Jump to: navigation, search

Go Up to 64-bit Windows Application Development


If you have a code base of 32-bit Windows Delphi applications that you want to convert to 64-bit Windows, you should

  • Open your 32-bit application in the IDE, add and activate the 64-bit Windows target platform, and compile your application as a 64-bit Windows application. (For details, see Steps in Creating Multi-Device Applications.)
  • Review and handle the following issues (mostly related to pointer operations, NativeInt size, and Assembly code).

Pointer operations

You should review pointer operations in your code base.

  • The size of all pointers has changed, as follows:
    • On the 32-bit Windows platform, a pointer is 4 bytes.
    • On the 64-bit Windows platform, a pointer is 8 bytes.
  • You cannot assume that:
    SizeOf(Pointer)=SizeOf(Integer/Cardinal/Longint)
  • If you increment pointers, be aware that the behavior of a 64-bit pointer might not be the same as the behavior of a 32-bit pointer. You might need to make adjustments in your code.
    • Correct:
    MyPtr := PByte(P) + 10;
    • Backward-compatible:
    MyPtr := PAnsiChar(P) + 10;
    • Wrong:
    MyPtr := Pointer(Integer(P) + 10);
  • You cannot assume that:
    SizeOf(Pointer) == 4
    • Correct:
    Move(FSelection[Index + 1], FSelection[Index], (FSelectionCount - Index - 1) * SizeOf(Pointer));
    • Wrong:
    Move(FSelection[Index + 1], FSelection[Index], (FSelectionCount - Index - 1) * 4);

For more information about using pointers in a 64-bit Windows applications, see the MSDN article, Rules for Using Pointers.

Integer Types

  • On the 64-bit Windows platform, integer remains 4 bytes.

Size of NativeInt Changes

  • On the 64-bit Windows platform, the NativeInt size is now 8 bytes.
  • On the 32-bit Windows platform, the NativeInt size is still 4 bytes.


Inline Assembly code

If your application contains inline assembly (ASM) code, you need to examine the ASM code and make the following changes:

  • Mixing of assembly statements with Pascal code is not supported in 64-bit applications. Replace assembly statements with either Pascal code or functions written completely in assembly.
  • Porting assembly code from IA-32 to Intel 64 cannot be done by simply copying the code. Consider the architecture specifics, such as the size of pointers and aligning. You may also want to consult the processor manual for new instructions. If you want to compile the same code for different architectures, use conditional defines. See Using Conditional Defines for Cross-Platform Code in "Using Inline Assembly Code."

RAD Studio supports Intel x86 through SSE4.2 and AMD 3dNow, and for x64, Intel/AMD through SSE4.2.

For more information, see:

Winapi Issues

  • If you pass pointers to SendMessage/PostMessage/TControl.Perform, the wParam and lParam parameters should be type-casted to the WPARAM/LPARAM type and not to Integer/Longint.
    • Correct:
      SendMessage(hWnd, WM_SETTEXT, 0, LPARAM(@MyCharArray));
    • Wrong:
      SendMessage(hWnd, WM_SETTEXT, 0, Integer(@MyCharArray));
  • Replace SetWindowLong/GetWindowLog with SetWindowLongPtr/GetWindowLongPtr for GWLP_HINSTANCE, GWLP_ID, GWLP_USERDATA, GWLP_HWNDPARENT and GWLP_WNDPROC as they return pointers and handles. Pointers that are passed to SetWindowLongPtr should be type-casted to LONG_PTR and not to Integer/Longint.
    • Correct:
      SetWindowLongPtr(hWnd, GWLP_WNDPROC, LONG_PTR(@MyWindowProc));
    • Wrong:
      SetWindowLong(hWnd, GWL_WNDPROC, Longint(@MyWindowProc));
  • Pointers that are assigned to the TMessage.Result field should use a type-cast to LRESULT instead of Integer/Longint.
    • Correct:
      Message.Result := LRESULT(Self);
    • Wrong:
      Message.Result := Integer(Self);
  • All TWM...-records for the windows message handlers must use the correct Windows types for the fields:
    Msg: UINT; wParam: WPARAM; lParam: LPARAM; Result: LRESULT)

Record Type Data Field Alignment

In previous versions of Delphi, packed records were used to ensure proper data alignment when calling external libraries and notably certain external Windows API functions that require record or struct parameters. Unused data fields were introduced to "pad things out" and make the needed data fields line up as needed. The current compiler implementation eliminates the need for using packed records this way, in most cases. Revising your existing source code to eliminate needless packed records and "padding" makes the source more clear and easier to maintain.

If you require that your existing data files maintain compatibility with older versions of Delphi, apply the compiler directive {$OLDTYPELAYOUT ON} to source code where the older record organization is still needed.

Other Minor Issues to Resolve

  • The DataEvent procedure in the VCL-RTL is affected by the change in size of NativeInt.
You might see a compiler override error on virtual methods (E2170 Cannot override a non-virtual method). Code changes are required in this case.
  • Run-time function patching, like injecting an unconditional jump instruction to redirect a function, has to be rewritten as 64-bit instructions.
  • 64-bit External .obj Files: If you expect your 64-bit Windows application to link to an external .obj file, keep in mind that you will need a 64-bit version of the .obj file to link with a 64-bit Windows application.

See Also