Tehnički članak

Delphi Excel komponente kompatibilne s C++Builderom

Prilikom održavanja VCL komponente napisane u Delphiju koju koriste i korisnici Delphija i C++Buildera, brzo shvatite da ta dva sustava za izgradnju vrlo različito tretiraju ovisnosti. Jedinica (unit) koja se savršeno prevodi u Delphiju može uzrokovati katastrofalne greške povezivača (linker errors) u C++Builderu. Ovo neslaganje česta je zamka za autore komponenti, posebno pri dodavanju novih internih jedinica u postojeću biblioteku

Ovdje je detaljna raščlamba zašto se to događa, koristeći primjere iz stvarnog svijeta iz komponente HotXLS, i kako osigurati vaš proces unakrsnog prevođenja pomoću eksplicitnih uključivanja, {$HPPEMIT} i pragma povezivanja

Zamka: Implicitno prevođenje naspram eksplicitnog uključivanja

Pretpostavimo da kreirate novu Delphi jedinicu, lxXlsSummary.pas, za rukovanje metapodacima dokumenta i dodate je u uses u vašoj glavnoj jedinici za parsiranje, lxRead.pas. Pritisnete compile (prevedi) u Delphi IDE-u, izrada (build) pozeleni i isporučite ažuriranje

Sljedeći dan vaši korisnici C++Buildera prijave grešku tijekom faze povezivanja: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj

Delphi način (dcc32)

Kada Delphi prevoditelj obrađuje paket (.dpk), on gleda jedinice koje su eksplicitno navedene u klauzuli contains. Ako jedna od tih jedinica koristi (uses) vanjsku jedinicu (poput lxXlsSummary.pas) koja nije na popisu contains, Delphi prevoditelj izvodi implicitno statičko povezivanje. On jednostavno pronalazi .pas datoteku u putanji pretraživanja, prevodi je u .dcu i ugrađuje je u rezultirajući .bpl. Izrada uspijeva, potpuno maskirajući propust

C++Builder način (MSBuild / .cbproj)

Sustav izgradnje C++Buildera mnogo je stroži. On generira samo C++ datoteke objekata (.obj) i zaglavlja (.hpp) za Delphi jedinice eksplicitno navedene u grupi stavki <DelphiCompile> unutar datoteke .cbproj. Budući da lxXlsSummary.pas nikada nije bio eksplicitno registriran u datoteci projekta, ne stvara se lxXlsSummary.obj. Kada povezivač (linker) pokuša razriješiti pozive koje vrši lxRead.obj, simboli nedostaju, što dovodi do greške neriješene vanjske reference

Rješavanje vanjskih referenci pomoću Pragma Link i HPPEMIT

Ako želite osigurati da je jedinica pravilno povezana u C++ bez prisiljavanja korisnika da ručno doda .obj datoteku u svoj projekt, možete koristiti Delphi direktivu {$HPPEMIT}. Ovo govori Delphi prevoditelju da ubaci specifičnu C++ #pragma link direktivu u generiranu .hpp datoteku

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.

Kada C++Builder uključi lxXlsSummary.hpp, prevoditelj nailazi na #pragma link i automatski naređuje povezivaču (ILINK32/ILINK64) da razriješi simbole iz lxXlsSummary.obj

Zlatno pravilo za održavanje komponenti

Kako biste u potpunosti izbjegli uništavanje izrade u C++Builderu, morate usvojiti strogu politiku registracije. Kad god se u vašu biblioteku doda nova Pascal jedinica, ona mora biti eksplicitno registrirana u sve tri vrste projektnih datoteka istovremeno

1. Ažurirajte C++Builder projekt (.cbproj / .bpk)

Otvorite .cbproj datoteku u uređivaču teksta i dodajte novu jedinicu na popis za prevođenje, osiguravajući da pružite jedinstveni redoslijed izrade (build order). Ako koristite starije verzije C++Buildera s .bpk datotekama, osigurajte da je dodana oznaka <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file>

<DelphiCompile Include="lxXlsSummary.pas">
  <BuildOrder>101</BuildOrder>
</DelphiCompile>

2. Ažurirajte Delphi paket (.dpk)

Dodajte jedinicu eksplicitnoj klauzuli contains. Ovo osigurava da se Delphi prevoditelj ne mora oslanjati na implicitno povezivanje, što se ionako općenito smatra lošom praksom

package HotXLS;

{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}

requires
  rtl,
  vcl;

contains
  lxRead in 'lxRead.pas',
  lxXlsSummary in 'lxXlsSummary.pas';

end.

Validacija kontinuirane integracije

Krajnja obrana od ove zamke je CI/CD validacija. Nikada se ne oslanjajte isključivo na uspješnu izradu Delphija prije isporuke dvojezične komponente. Vaše skripte za izradu moraju pozvati MSBuild ili alate naredbenog retka bcc32c na projektima C++Buildera (npr. build-Win32-Lib-CB.cmd) i pokrenuti potpuno povezivanje C++ probnih verzija i cjelovitih demonstracija. Tek kada C++ povezivač uspije, možete biti sigurni da su sve Delphi jedinice ispravno registrirane i da izlažu svoje simbole C++ okruženju za izvođenje (runtime)

Napomena: Kompatibilnost prevoditelja na više platformi strogo se održava u Delphi i C++Builder izdanjima HotXLS VCL Component