Skip to content

64-bit in DataFlex

With DataFlex 2021 and higher, programs can be compiled and run in both 32- and 64-bit. The Studio itself is 64-bit only, but you can choose to compile programs as either 32- or 64-bit. A single codebase can therefore generate both 32-bit and 64-bit applications. You can run and debug both 32-bit and 64-bit programs from the same 64-bit Studio.

There is a dropdown selector available to quickly switch between 32- and 64-bit.

This setting can be configured per project and it can also be set through the Project Properties window on the Compiler tab page.

Both the 32- and 64-bit compiled programs end up in the programs folder. They are differentiated by appending a suffix to the executable name. In the screenshot above, the 32-bit version will be named Order.exe, and the 64-bit version will be named Order64.exe. If you do not differentiate the executables with appropriate suffixes, they will get the same name and any compilation will overwrite the one that is already there (this can be the desired situation for webapps).

The compilation platform and executable name suffix are stored in the project cfg file:

Platform=x64
(or
Platform=x86)

64BitSuffix=64
32BitSuffix=32

For simple, well-written programs, switching to 64-bit and compiling may be all that is needed. For more advanced applications, however, significant changes may be required. The following sections document the most important differences and potential changes.


Pointer size and fundamental differences

The main difference between 32-bit and 64-bit DataFlex is that the pointer size increases from 32 to 64 bits. The same is true for Handle. It is important to realize that no pointer in 64-bit mode can safely be moved to the Integer data type because of pointer truncation. Pointer truncation occurs when a 64-bit value (greater than 2^32 − 1) is transferred to a 32-bit data type, which removes the upper 32 bits and results in an incorrect value. Referring to the truncated pointer usually leads to illegal memory access and crashes.

Although Handle is 64-bit in 64-bit mode, its upper half bits are usually empty in many Windows handles, which means moving it to Integer may not always truncate in practice. This is not reliable and is not advised — keep handles as Handle at all times. The exception is HTreeItem, which is actually a pointer and should be treated as such.

The Integer type remains 32-bit in 64-bit DataFlex. For cases where an integer-like value must be able to hold a pointer, DataFlex 2021 introduced the Longptr type.

Longptr data type

Longptr is a memsize type: it is a 32-bit integer in 32-bit compilation and a 64-bit integer in 64-bit compilation. This allows it to hold pointer values without truncation. The single-character identifier for Longptr is P, while Integer is I. (Timespan uses its own identifier.)

Example: defining a constant of type Longptr:

Define SOME_LARGE_VALUE
for |CP$03762874671

Address and Pointer

In earlier versions, Pointer acted like Integer. In DataFlex 2021 and higher, Pointer is a replacement for Address, which is the native pointer type. Address is now considered obsolete but remains available for backward compatibility.

Alias data types and guidance

Over time a number of aliasing rules have changed. The important practical takeaways are:

  • Use the correct data type for the job:
  • Use Handle for Windows handles.
  • Use Pointer for pointer types (e.g., VOID *, LPVOID).
  • Use Integer for 32-bit integers that will never exceed 2^32 − 1.
  • Use Longptr for integer types that may need to hold a pointer value.
  • Longptr is a real data type in DataFlex 2021 and higher (not an alias).
  • In older versions (e.g., 19.1), Longptr existed as an alias for Integer to ease migration; in 2021 it is an independent, pointer-sized type.
  • Handle used to be an alias for Integer; it is now an alias for Longptr. This means arrays of Handle no longer map to arrays of Integer and must be handled accordingly.

DWord

Historically, DWord was an alias for Integer. In DataFlex 2021 it has been changed to an alias for UInteger. To avoid breaking existing code that relied on behaviors such as assigning negative values to a DWord, DataFlex implements value wrapping (similar to C/C++). For example, assigning -1 to a DWord results in the unsigned value 0xFFFFFFFF (4294967295). Likewise, moving a large UInteger to an Integer will wrap to a negative value.

Logical evaluations on UInteger types are now supported; for example:

UInteger uiTest
If (uiTest iand 15) Begin
    // ...
End

32-bit DataFlex — Possible code changes

Even when compiling 32-bit, some 64-bit related changes may be relevant:

DWord

Because DWord is now an alias for UInteger, comparisons that test for negative values will never be true. Review code that relies on signed semantics for DWord.

Handle arrays

Handle is now an alias for Longptr rather than Integer. The following code will raise a runtime error in DataFlex 2021+ (was fine previously):

Property Handle[] phStaticViews
Integer[] iStaticViews
Get phStaticViews to iStaticViews

Fix by using the correct array type:

Integer[] iStaticViews        // only if values are truly integers
// or
Handle[] phStaticViews

64-bit DataFlex — Possible code changes

Whether you need code changes depends on application complexity, third-party DLL usage, and data type correctness. The items below list common issues and remedies.

Compiler switch

Use the compiler switch IS$WIN64 for code that must differ between 32-bit and 64-bit:

#IFDEF IS$WIN64
#Replace LONGPTR_DTSIZE 8
#ELSE
#Replace LONGPTR_DTSIZE 4
#ENDIF

(Use IS$WIN64 consistently in your code.)

Illegal data type conversions

In 64-bit mode, conversions from Pointer or Address to Integer are illegal and will raise runtime illegal conversion errors due to pointer truncation risk. Conversions from Integer to Pointer/Address are allowed but usually invalid in practice. Conversions between Longptr and Integer, Pointer, or Address are allowed.

To avoid problems:

  • Use Pointer for pointer-valued variables and parameters.
  • Use Longptr where a pointer-sized integer is required.

A global search for Pointer, Address, and AddressOf is a good starting point to find potential issues. The compiler will catch straightforward pointer-to-integer conversions, but many problematic conversions manifest only at runtime.

Correct data type usage

  • Use OLE_Handle for OLE handles and Handle for other Windows handles.
  • HTreeItem is actually a pointer to a struct; use Pointer, Longptr, or Handle — not Integer or DWord.
  • Ensure external function signatures and struct fields match the expected Windows/C types exactly.

External functions

When declaring external functions, parameter and return data types must match the Windows API or DLL function signatures. The following table provides recommended DataFlex types for common Windows types and allowed alternatives.

Windows data type (examples) Advised DataFlex type in external function Allowed alternatives
Handle, hWnd, HTreeItem, HItemList, HInstance, hIcon, HGlobal, HDC, etc. Handle Longptr, Pointer
Pointer types (e.g., VOID *lpx), LPCSTR, LPCTSTR, LPVOID, PUINT, LPDWORD Pointer Longptr
OLE_HANDLE / OLE_Handle OLE_Handle Integer, UInteger, DWord
lParam, wParam, lResult Longptr Pointer
DWord DWord Integer, UInteger, OLE_Handle
size_t, UINT_PTR, DWORD_PTR ULongptr Longptr, Pointer
INT_PTR, LONG_PTR Longptr Pointer
INT, INT32, UINT, UINT32, LONG, ULONG Integer / UInteger DWord, OLE_Handle
SHORT, INT16, UINT16, WORD Short / UShort
BYTE UChar
BOOL Integer DWord, OLE_Handle
DWORDLONG UBigInt

In particular, watch for parameters named lParam and wParam typed as Integer — these often need to be changed to Longptr.

Note: Some Windows functions expect structures passed by value (e.g., POINT). DataFlex cannot pass structs by value directly; such parameters usually must be passed by pointer. See the Winuser.pkg system package for handling special cases.

Structs and structure alignment

When exposing Windows structs to external DLLs, ensure field types and alignment match the C compiler expectations. C compilers apply alignment and padding so that a struct gets alignment equal to its widest scalar member. DataFlex does not automatically insert structure padding; you may need to add padding fields conditionally for 64-bit.

Example — CHOOSEFONT structure (tWinChooseFont):

In 32-bit both DWord and Handle are 4 bytes, but in 64-bit Handle becomes 8 bytes. Windows will typically add 4 bytes of padding after a DWord to align the next 8-byte member. To account for this in DataFlex:

Struct tWinChooseFont
    DWord lStructSize
    #IFDEF IS$WIN64
        Integer iStructAlignment
    #ENDIF
    Handle hwndOwner
End_Struct

Be aware this affects calls to SizeOfType() for the struct. Structure alignment is not relevant for COM class interface structs — those are exposed correctly.

More information: - https://msdn.microsoft.com/en-us/library/ms253935.aspx - http://www.catb.org/esr/structure-packing/#_structure_alignment_and_padding

Third-party binaries

One of the biggest hurdles is third-party dependencies: a 64-bit process cannot load 32-bit DLLs, and vice versa. Replace 32-bit dependencies with 64-bit versions or obtain a 64-bit build from the vendor. If neither is possible, alternatives include removing or replacing the 3rd-party component.

You can select which DLL to call using the compiler switch:

#IFDEF IS$WIN64
    External_Function FuncName "FuncName" "xxx64.dll" Integer iLength Returns Handle
#ELSE
    External_Function FuncName "FuncName" "xxx32.dll" Integer iLength Returns Handle
#ENDIF

COM classes (COM Class Generator)

The COM Class Generator can generate DataFlex wrapper classes from a DLL or OCX for both 32- and 64-bit binaries. In many cases the generated PKG files are identical because the generator uses Longptr (or ULongptr) for pointer-sized C types such as INT_PTR. Supported platform-dependent C types that map to Longptr/ULongptr include: INT_PTR, LONG_PTR, LPARAM, HMODULE, ULONG_PTR, UINT_PTR, WPARAM.

If the COM signatures differ between 32-bit and 64-bit (for example a type is LONG in 32-bit and __int64 in 64-bit), the generated classes will differ. You can:

  • Edit and add IS$WIN64 switches in a single PKG to handle small differences, or
  • Maintain two PKG files and use IS$WIN64 in the USE statement to include the correct one.

Use the define OLE_VT_INT_PTR when you need OLE_VT_I4 in 32-bit and OLE_VT_I8 in 64-bit.

Windows functions such as GetWindowLong, SetWindowLong, GetClassLong, and SetClassLong exist on 64-bit Windows, but using them with pointer-sized values causes pointer truncation. Replace them with the corresponding *Ptr versions (e.g., GetWindowLongPtr, SetWindowLongPtr). The *Ptr variants work on both 32- and 64-bit, so you typically do not need compiler switches; simply call the Ptr variant.


See also