Upgrading Existing C++ Projects to 64-bit Windows

From RAD Studio
Jump to: navigation, search

Go Up to C++Builder 64-bit Windows Application Development


Existing BCC32-based projects must be updated to compile correctly with BCC64, or to use the same codebase with both platforms simultaneously. In addition to these project- and tool-related differences, note that BCC64 is also a stricter compiler.

Object and Library File Format

  • BCC32 and its associated tools use OMF in .obj and .lib files.
  • BCC64 uses ELF in .o and .a files.
  • BCC64X uses COFF for .o and .lib files.
  • BCC64X uses PDB for debugging information.

Where possible, you should remove object and library file extensions so that each tool can use the appropriate extension. When necessary, change the extension to make it conditional with version detection, as in custom scripts.

#pragma link

If the files named in #pragma link statements contain a file extension, those extensions must be removed. Each compiler will append the appropriate extension.

For example, Control Panel apps that use this statement:

#pragma link "Ctlpanel.obj"

must be updated to read:

#pragma link "Ctlpanel"

See #pragma link for more information.

#pragma comment

For libraries, it is best to use #pragma comment(lib ..), as in:

#pragma comment(lib, "library-name") // Looks for library-name.lib in WIN32 and library-name.a in WIN64

#include <windows.h>

Applications that use the Windows API must explicitly contain:

 #include <windows.h>

With BCC32, including windows.h is not required, but BCC64 requires windows.h and is more strict about #includes.

For more information, see #include.

NO_STRICT Macro

The NO_STRICT type checking scheme is not supported in BCC64. If you have existing projects that use it, it should be removed.

For more information, see C++ Applications Use STRICT Type Checking.

Updating WebBroker Projects

  • Change #pragma link as described above.

Upgrading to the bcc64x (Windows 64-bit (Modern))

There are key differences between the classic and previous Clang compilers and new compilers.

The Windows 64-bit (Modern) toolchain has a few notable differences from previous Clang-based toolchains.

Legacy RTL methods

C RTL

The new toolchain provides a C runtime library by using the inbuilt Windows system component, the Universal C Runtime. This means all C RTL methods and the optional Microsoft-style extensions are standardized.

POSIX-style functions, e.g. itoa vs _itoa

Our previous Windows toolchains included POSIX-style methods, such as itoa. These methods and naming were non-standard; itoa is not part of the standard, and the correct way to add non-standard methods is to prefix them with an underscore. In this platform, the conversion functions, such as itoa, are provided with the `_` names like `_itoa`. This is the correct convention for functions that live in the RTL/standard headers like `stdlib.h`.

Turbo C

Our previous Windows toolchains included some non-standard RTL methods, which originated in Turbo C or Turbo C++’s runtime libraries and date from before C++ was standardized or before C99. These are no longer included.

Each one should be straightforward to replace because the C and C++ standards add methods that directly provide or can be used to provide equivalent functionality. For example, the old random() method, which dates from Turbo C, can be replaced with code using.

  • rand(), for C.
  • std::rand(), for C++,

This depends on your use case possibly, using the modulo operator % to keep within a range.

Static Package Linking

In RAD Studio 12.1, packages (BPL files) are not loaded dynamically by C++ applications using the modern toolchain.

Instead, packages are statically linked in, following the Delphi default convention. This results in larger EXE sizes, but fewer files to distribute.

  • Dynamic package consumption is planned to be added in a future release soon.
  • Dynamic loading of a package, using LoadPackage(), is fully supported.
  • Dynamic loading, in general, such as using a DLL, is fully supported.

_import and _export methods

The __import and __export keywords are not present.

Please use __declspec(dllimport) and __declspec(dllexport) instead, or define a macro mapping the old keywords to these declspecs.

Unit initialization

Unit initialization is the order in which globals are created/initialized in different translation units on application startup (and the order they are destroyed on program exit), or code that depends on globals or other initialization done in other units before it can be executed on startup/shutdown in this translation unit.

Previous C++ toolchains had subtle issues calculating dependencies in order to get the right unit initialization order, especially when linking in Delphi-origin units.

The new linker and RTL of the Windows 64-bit (Modern) platform use init records for initializing units on Windows, mirroring the approach once used for mobile.

For third-party component authors: some units are referenced with {$HPPEMIT LINKUNIT}, which generates an auto-link pragma, as seen in Vcl.Styles. Without this method, initialization would fail, which would render the dynamic loading of VCL *.vsf styles inoperable.

Fix Warnings to Avoid Crashes

Newer versions of Clang and LLVM try to apply more compiler optimizations, which often involve being stricter about undefined behavior.

One common undefined behavior is leaving uninitialized variables, which in past releases would often ‘work’ even if it was by accident. In release mode, Clang will now leave a trap instruction, causing a runtime error when an uninitialized variable exists. Read more on the Troubleshooting page.

We recommend that when you upgrade, build with -Wall (turn on all useful warnings) and resolve them.

Note: that -Wall is ‘all useful warnings’; every warning is -Weverything, which can include less likely useful warnings. Our recommendation is to build with -Wall.)

Different Memory Manager

In past toolchains, C++Builder provided a version of FastMM as the memory manager.

In the new toolchain, C++Builder uses the Windows system component, the Universal C Runtime (UCRT). This provides a memory manager, and it cannot be hooked to replace the MM with another. This means all C++ apps built with the new toolchain, including when linking in the Delphi runtime as is done for VCL and FMX apps, use this memory manager.

Freed memory is also filled with 0x80 bytes, unlike FastMM. This may make use-after-free issues more obvious. See the Troubleshooting page for more details.

make_shared<delphi-class>

The std::make_shared<>() method is not available for Delphi-style classes (ones defined in Delphi, or defined in C++ but inheriting from a Delphi class.) This is due to Delphi-style classes not supporting placement new.

You can use shared_ptr-s with these types, creating via new().

std::make_unique<> is available for all expected types.

Symbols emitted from binaries

When building either an EXE or DLL, you will see that the binary exports RTTI data or global variables marked with the Package attribute.

Exporting symbols from DLLs

When building a DLL, all symbols will be exported if no symbols are marked __declspec(export). This may be unexpected behavior.

To resolve this, ensure at least one symbol (you can create a dummy method) is marked __declspec(export). When one or more symbols are marked with that declspec, only symbols with that declspec are exported (noting the RTTI and globals, per above).

You can also use the linker flag, -Xlinker -exclude-all-symbols.

See Also