Upgrade von vorhandenen C++-Projekten nach 64-Bit-Windows

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu C++Builder-Anwendungsentwicklung für 64-Bit-Windows


Vorhandene BCC32-basierte Projekte müssen aktualisiert werden, damit sie mit BCC64 ordnungsgemäß compiliert werden können oder damit dieselbe Codebasis auf beiden Plattformen verwendet werden kann. BCC64 ist zusätzlich zu diesen projekt- und tool-bezogenen Unterschieden auch ein strikterer Compiler.

Format für Objekt- und Bibliotheksdateien

  • BCC32 und die zugehörigen Tools verwenden in .obj und .lib-Dateien OMF.
  • BCC64 verwendet in .o und .a-Dateien ELF.
  • BCC64X verwendet für .o- und .lib-Dateien COFF.
  • BCC64X verwendet für Debug-Informationen PDB.

Entfernen Sie, wo immer möglich, die Erweiterungen von Objekt- und Bibliotheksdateien, damit jedes Tool die geeignete Erweiterung verwenden kann. Ändern Sie bei Bedarf die Erweiterung, um sie von der Versionserkennung abhängig zu machen, wie in benutzerdefinierten Skripten.

In der Dokumentation zu Automatisches Linken finden Sie Empfehlungen zur neuen Win64-Modern-Toolkette.

#pragma link

Wenn die in #pragma link Anweisungen aufgeführten Dateien eine Dateierweiterung enthalten, müssen diese Erweiterungen entfernt werden. Jeder Compiler hängt die geeignete Erweiterung an.

Beispielsweise müssen Systemsteuerungsanwendungen, in denen diese Anweisung:

#pragma link "Ctlpanel.obj"

verwendet wird, folgendermaßen aktualisiert werden:

#pragma link "Ctlpanel"

Weitere Informationen finden Sie unter #pragma link.

#pragma comment

Für Bibliotheken verwenden Sie am besten #pragma comment(lib ..) wie in:

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

#include <windows.h>

Anwendungen, die die Windows-API verwenden, müssen Folgendes enthalten:

 #include <windows.h>

Für BCC32 ist das Einbeziehen von windows.h nicht erforderlich, aber BCC64 erfordert windows.h und ist in Bezug auf #includes strikter.

Weitere Informationen finden Sie unter #include.

Makro NO_STRICT

Das Typprüfungsschema NO_STRICT wird in BCC64 nicht unterstützt. Wenn in vorhandenen Projekten dieses Typprüfungsschema verwendet wird, sollten Sie es entfernen.

Weitere Informationen finden Sie unter C++-Anwendungen verwenden eine strikte Typprüfung.

Aktualisieren von WebBroker-Projekten

  • Ändern Sie #pragma link wie oben beschrieben.

Upgrade von bcc64x (64-Bit-Windows (Modern))

Es gibt wesentliche Unterschiede zwischen den klassischen und den früheren Clang-Compilern sowie den neuen Compilern.

Die Toolkette von 64-Bit-Windows (Modern) weist einige wichtige Unterschiede zu den früheren Clang-basierten Toolketten auf.

Alte RTL-Methoden

C-RTL

Die neue Toolkette stellt unter Verwendung der integrierten Windows-Systemkomponente, der UCRT (universelle C-Laufzeit), eine C-Laufzeitbibliothek bereit. Das bedeutet, dass alle C-RTL-Methoden und die optionalen Erweiterungen im Microsoft-Stil standardisiert sind.

Funktionen im POSIX-Stil, z. B. itoa im Vergleich zu _itoa

Unsere früheren Windows-Toolketten enthielten Methoden im POSIX-Stil, wie itoa. Diese Methoden und die Namensgebung waren nicht standardisiert. itoa ist nicht Teil des Standards (EN) und der korrekte Weg, nicht standardisierte Methoden hinzuzufügen, ist, ihnen einen Unterstrich voranzustellen. Auf dieser Plattform werden die Konvertierungsfunktionen, wie itoa, mit den _-Namen wie z. B. _itoa bereitgestellt. Dies ist die korrekte Konvention für Funktionen in den RTL-/Standard-Headern wie stdlib.h.

Turbo C

Unsere früheren Windows-Toolketten enthielten einige RTL-Methoden, die nicht dem Standard entsprachen. Diese Methoden stammten aus den Laufzeitbibliotheken von Turbo C oder Turbo C++ aus der Zeit vor der Standardisierung von C++ oder vor C99. Diese Methoden sind nicht mehr enthalten.

Jede dieser Methoden sollte einfach zu ersetzen sein, da die C- und C++-Standards Methoden hinzufügen, die eine gleichwertige Funktionalität bieten oder direkt verwendet werden können. Beispielsweise kann die alte random()-Methode, die aus Turbo C stammt, durch den folgenden Code ersetzt werden.

  • rand(), für C
  • std::rand(), für C++

Dies hängt von Ihrem Anwendungsfall ab, wobei möglicherweise der Modulo-Operator % verwendet wird, um in einem Bereich zu bleiben.

Schlüsselwörter _import und _export

Die Schlüsselwörter _import und _export sind nicht mehr vorhanden.

Verwenden Sie stattdessen __declspec(dllimport) und __declspec(dllexport) oder definieren Sie ein Makro, das die alten Schlüsselwörter diesen declspecs zuordnet.

Unit-Initialisierung

Die Unit-Initialisierung bezeichnet die Reihenfolge in der globale Variablen in unterschiedlichen Übersetzungs-Units beim Anwendungsstart erstellt/initialisiert werden (und die Reihenfolge, in der sie bei Programmende freigegeben werden) oder Code, der von globalen Variablen oder anderen Initialisierungen abhängt, die in anderen Units durchgeführt wurden, bevor er beim Starten/Herunterfahren in dieser Übersetzungs-Unit ausgeführt werden kann.

Frühere C++-Toolketten wiesen subtile Probleme bei der Berechnung von Abhängigkeiten auf, um die richtige Reihenfolge der Unit-Initialisierung zu erhalten, insbesondere beim Einbinden von aus Delphi stammenden Units.

Der neue Linker und die neue RTL von 64-Bit-Windows-Plattform (Modern) verwenden Init-Records für die Initialisierung von Units unter Windows analog zur Vorgehensweise bei mobilen Anwendungen.

Für Ersteller von Drittanbieterkomponenten: Einige Units werden mit {$HPPEMIT LINKUNIT} referenziert. Dadurch wird ein Pragma für das automatische Linken wie in Vcl.Styles generiert. Ohne diese Methode würde die Initialisierung fehlschlagen, wodurch das dynamische Laden von VCL-*.vsf-Stilen nicht funktionieren würde.


Informationen zu BCC64X finden Sie unter Unit-Initialisierung und -Finalisierung für die neue C++-Toolkette.

Warnungen zur Vermeidung von Abstürzen beheben

Neuere Versionen von Clang und LLVM versuchen, mehr Compiler-Optimierungen anzuwenden, was oft bedeutet, dass undefiniertes Verhalten strenger gehandhabt wird.

Ein häufiges undefiniertes Verhalten ist das Belassen nicht initialisierter Variablen. Dies "funktionierte" in früheren Versionen oft, auch wenn es nur ein Versehen war. Im Release-Modus hinterlässt Clang nun eine Trap-Anweisung, die einen Laufzeitfehler verursacht, wenn eine nicht initialisierte Variable vorhanden ist. Weitere Informationen finden Sie auf der Seite Fehlerbehebung.

Wir empfehlen, beim Upgrade den Build der Anwendung mit -Wall (alle nützlichen Warnungen aktivieren) zu erzeugen und alle Warnungen zu beheben.

Hinweis: -Wall bezeichnet "alle nützlichen Warnungen", alle Warnungen werden mit -Weverything angegeben, was auch weniger nützliche Warnungen einschließt. Erstellen Sie den Build daher mit -Wall.)

Anderer Speichermanager

In früheren Toolketten enthielt C++Builder eine Version von FastMM als Speichermanager (MM).

In der neuen Toolkette verwendet C++Builder die Windows-Systemkomponente UCRT (universelle C-Laufzeit). Dadurch wird ein Speichermanager bereitgestellt, der nicht durch einen anderen MM ersetzt werden kann. Das bedeutet, dass alle C++-Anwendungen, die mit der neuen Toolkette erstellt werden, auch beim Linken in die Delphi-Laufzeit, wie es bei VCL- und FMX-Anwendungen der Fall ist, diesen Speichermanager verwenden.

Freigegebener Speicher wird im Gegensatz zu FastMM auch mit 0x80 Byte gefüllt. Dadurch werden Probleme, die sich aus der Verwendung nach der Freigabe ergeben, möglicherweise deutlicher. Weitere Einzelheiten finden Sie auf der Seite Fehlerbehebung.

make_shared<delphi-class>

Die Methode std::make_shared<>() ist für Klassen im Delphi-Stile nicht verfügbar (Klassen, die in Delphi definiert sind oder in C++ definiert sind, aber von einer Delphi-Klasse geerbt sind). Dies ist darauf zurückzuführen, dass Delphi-Klassen die neue Platzierung nicht unterstützen.

Sie können mit diesen Typen "shared_ptr-s" verwenden, die über new() erstellt werden.

std::make_unique<> ist für alle erwarteten Typen verfügbar.

System::make_shared_dc<>

Moderne Programmierstile nutzen die intelligenten Zeiger (Smart Pointer) unique_ptr und shared_ptr. </br> Gehen Sie wie folgt vor, um diese zu erstellen:

std::make_unique<typename>(param1, param2) 
std::make_shared<typename>(param1, param2).

std::make_shared verwendet jedoch die neue Platzierung, was nicht mit Klassen im Delphi-Stil kompatibel ist. Klassen im Delphi-Stil können als gemeinsame Zeiger (Shared Pointer) verwendet werden. Nur die Methode der Standardbibliothek macht sie inkompatibel. Verwenden Sie die neue Methode System:make_shared_dc<>(), die mit Klassen im Delphi-Stil funktioniert. </br> Sehen Sie sich dazu das folgende Beispiel an:

auto my_shared { System::make_shared_dc<TButton>(nullptr) };
Hinweis: std::make_unique<>() ist bereits kompatibel mit Klassen im Delphi-Stil.
Hinweis: Weitere Informationen zu unique_ptr und shared_ptr finden Sie unter:

Von Binärdateien ausgegebene Symbole

Bei Erzeugen eines Builds einer EXE oder DLL exportiert die Binärdatei RTTI-Daten oder globale Variablen, die mit dem Attribut "Package" gekennzeichnet sind.

Exportieren von Symbolen aus DLLs

Bei Erzeugen eines Builds einer DLL werden alle Symbole exportiert, wenn kein Symbol als __declspec(export) gekennzeichnet ist. Dies könnte unerwartetes Verhalten sein.

Um dies zu beheben, stellen Sie sicher, dass mindestens ein Symbol (Sie können eine Dummy-Methode erstellen) als __declspec(export) gekennzeichnet ist. Wenn ein oder mehrere Symbole mit diesem declspec gekennzeichnet sind, werden nur Symbole mit diesem declspec exportiert (unter Beachtung der RTTI und globalen Variablen, siehe oben).

Sie können auch das Linker-Flag -Xlinker -exclude-all-symbols verwenden.

Siehe auch