Wenn Sie eine VCL-Komponente warten, die in Delphi geschrieben wurde, aber sowohl von Delphi- als auch von C++Builder-Benutzern verwendet wird, werden Sie schnell feststellen, dass die beiden Build-Systeme Abhängigkeiten sehr unterschiedlich behandeln. Eine Unit, die in Delphi perfekt kompiliert wird, kann in C++Builder katastrophale Linker-Fehler verursachen. Diese Diskrepanz ist eine häufige Falle für Komponentenautoren, insbesondere wenn einer vorhandenen Bibliothek neue interne Units hinzugefügt werden
Hier ist eine detaillierte Aufschlüsselung, warum dies geschieht – anhand von Beispielen aus der Praxis der HotXLS-Komponente – und wie Sie Ihren Cross-Kompilierungsprozess durch explizite Includes, {$HPPEMIT} und Pragma-Linking kugelsicher machen können
Die Falle: Implizite Kompilierung vs. Explizite Einbindung
Angenommen, Sie erstellen eine neue Delphi-Unit, lxXlsSummary.pas, um Dokumentmetadaten zu verarbeiten, und Sie binden sie über uses in Ihre Haupt-Parsing-Unit lxRead.pas ein. Sie klicken in der Delphi-IDE auf „Kompilieren“, der Build wird grün und Sie liefern das Update aus
Am nächsten Tag melden Ihre C++Builder-Benutzer einen Fehler während der Verknüpfungsphase: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
Der Delphi-Weg (dcc32)
Wenn der Delphi-Compiler ein Paket (.dpk) verarbeitet, betrachtet er die Units, die explizit in der contains-Klausel aufgeführt sind. Wenn eine dieser Units eine externe Unit (wie lxXlsSummary.pas) verwendet (uses), die nicht in der contains-Liste enthalten ist, führt der Delphi-Compiler ein implizites statisches Linken durch. Er findet einfach die .pas-Datei im Suchpfad, kompiliert sie in eine .dcu und brennt sie in die resultierende .bpl ein. Der Build ist erfolgreich und maskiert die Auslassung vollständig
Der C++Builder-Weg (MSBuild / .cbproj)
Das Build-System des C++Builders ist viel strenger. Es generiert C++-Objektdateien (.obj) und Header (.hpp) nur für die Delphi-Units, die explizit in der <DelphiCompile>-Elementgruppe der .cbproj-Datei aufgeführt sind. Da lxXlsSummary.pas nie explizit in der Projektdatei registriert wurde, wird keine lxXlsSummary.obj erstellt. Wenn der Linker versucht, die von lxRead.obj getätigten Aufrufe aufzulösen, fehlen die Symbole, was zu einem Fehler aufgrund einer ungelösten externen Referenz führt
Auflösen externer Referenzen mit Pragma Link und HPPEMIT
Wenn Sie sicherstellen möchten, dass eine Unit in C++ ordnungsgemäß verknüpft wird, ohne den Benutzer zu zwingen, die .obj-Datei manuell zu seinem Projekt hinzuzufügen, können Sie die {$HPPEMIT}-Direktive von Delphi verwenden. Dies weist den Delphi-Compiler an, eine bestimmte C++-#pragma link-Direktive in die generierte .hpp-Datei einzufügen
unit lxXlsSummary;
interface
{$IFDEF WINDOWS}
// Inject a pragma link into the generated C++ header file
// This forces the C++ linker to include the corresponding .obj file
{$HPPEMIT '#pragma link "lxXlsSummary.obj"'}
{$ENDIF}
uses
SysUtils, Classes;
type
TXlsSummaryInfo = class(TObject)
public
Title: string;
Author: string;
CreateTime: TDateTime;
end;
function XlsReadSummaryInformation(const FileName: string): TXlsSummaryInfo;
implementation
function XlsReadSummaryInformation(const FileName: string): TXlsSummaryInfo;
begin
Result := TXlsSummaryInfo.Create;
// Metadata extraction logic here
end;
end.
Wenn C++Builder lxXlsSummary.hpp einbindet, stößt der Compiler auf den #pragma link und weist den Linker (ILINK32/ILINK64) automatisch an, Symbole aus lxXlsSummary.obj aufzulösen
Die goldene Regel für die Wartung von Komponenten
Um zu vermeiden, dass C++Builder-Builds vollständig fehlschlagen, müssen Sie eine strenge Registrierungsrichtlinie einführen. Wann immer Ihrer Bibliothek eine neue Pascal-Unit hinzugefügt wird, muss diese explizit in allen drei Projektdateitypen gleichzeitig registriert werden
1. Aktualisieren des C++Builder-Projekts (.cbproj / .bpk)
Öffnen Sie die .cbproj-Datei in einem Texteditor und fügen Sie die neue Unit zur Kompilierungsliste hinzu, wobei Sie sicherstellen müssen, dass eine eindeutige Build-Reihenfolge (BuildOrder) angegeben wird. Wenn Sie ältere C++Builder-Versionen mit `.bpk`-Dateien verwenden, stellen Sie sicher, dass der Tag <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> hinzugefügt wird
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Aktualisieren des Delphi-Pakets (.dpk)
Fügen Sie die Unit zur expliziten contains-Klausel hinzu. Dadurch wird sichergestellt, dass sich der Delphi-Compiler nicht auf implizites Linken verlassen muss, was ohnehin allgemein als schlechte Praxis angesehen wird
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Validierung durch Continuous Integration
Die ultimative Verteidigung gegen diese Falle ist die CI/CD-Validierung. Verlassen Sie sich niemals ausschließlich auf einen erfolgreichen Delphi-Build, bevor Sie eine zweisprachige Komponente ausliefern. Ihre Build-Skripte müssen MSBuild oder die bcc32c-Befehlszeilentools für die C++Builder-Projekte aufrufen (z. B. build-Win32-Lib-CB.cmd) und einen vollständigen Link der C++-Test- und vollständigen Demos ausführen. Nur wenn der C++-Linker erfolgreich ist, können Sie sicher sein, dass alle Delphi-Units korrekt registriert sind und ihre Symbole für die C++-Laufzeitumgebung verfügbar machen
Hinweis: Die plattformübergreifende Compiler-Kompatibilität wird zwischen den Delphi- und C++Builder-Editionen der HotXLS VCL-Komponente strikt aufrechterhalten