Tehnički članak

Delphi Excel komponente kompatibilne sa C++Builder

Kada održavate VCL komponentu napisanu u Delphiju koju koriste i korisnici Delphija i korisnici C++Buildera, brzo ćete shvatiti da ova dva sistema za izgradnju tretiraju zavisnosti veoma različito. Jedinica koja se savršeno kompajlira u Delphiju može izazvati katastrofalne greške linkera u C++Builderu. Ovo nepodudaranje je česta zamka za autore komponenti, posebno kada se dodaju nove interne jedinice u postojeću biblioteku

Evo detaljne analize zašto se to dešava, koristeći primere iz stvarnog sveta sa HotXLS Delphi komponentom, i kako da obezbedite otpornost vašeg procesa unakrsnog kompajliranja korišćenjem eksplicitnih uključivanja, {$HPPEMIT} i pragma povezivanja

Zamka: Implicitno kompajliranje naspram eksplicitnog uključivanja

Pretpostavimo da kreirate novu Delphi jedinicu, lxXlsSummary.pas, za obradu metapodataka dokumenta, i uključite je pomoću uses u vašu glavnu jedinicu za parsiranje, lxRead.pas. Pritisnete dugme za kompajliranje u Delphi IDE-u, proces se uspešno završi, i vi objavite ažuriranje

Sledećeg dana, vaši C++Builder korisnici prijavljuju grešku tokom faze povezivanja: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj

Delphi način (dcc32)

Kada Delphi kompajler obrađuje paket (.dpk), on posmatra jedinice eksplicitno navedene u contains klauzuli. Ako jedna od tih jedinica koristi (uses) eksternu jedinicu (kao što je lxXlsSummary.pas) koja nije na contains listi, Delphi kompajler vrši implicitno statičko povezivanje. On jednostavno pronalazi .pas datoteku u putanji za pretragu, kompajlira je u .dcu, i ugrađuje je u rezultujući .bpl. Izrada uspeva, potpuno maskirajući propust

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

Sistem za izgradnju u C++Builderu je mnogo stroži. On generiše C++ objektne datoteke (.obj) i zaglavlja (.hpp) samo za one Delphi jedinice koje su eksplicitno navedene u <DelphiCompile> grupi stavki u .cbproj datoteci. Pošto lxXlsSummary.pas nikada nije eksplicitno registrovan u projektnoj datoteci, nijedan lxXlsSummary.obj se ne kreira. Kada linker pokuša da razreši pozive koje pravi lxRead.obj, simboli nedostaju, što dovodi do greške nerešene eksterne reference

Poređenje build procesa za HotXLS Delphi komponentu: Delphi dcc32 implicitno kompajlira neizlistanu jedinicu u paket i gradi zeleno, dok C++Builder MSBuild nikad ne generiše njenu object datoteku pa ILINK32 odustaje sa unresolved external
Ista neizlistana jedinica prelazi dve razjedinjene putanje: dcc32 maskira izostavljanje kroz implicitno statičko povezivanje dok ga strogiji C++Builder povezivač izlaže

Rešavanje eksternih referenci sa Pragma Link i HPPEMIT

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

unit lxXlsSummary;

interface

{$IFDEF WINDOWS}
  // Ubacuje pragma link u generisanu C++ zaglavlje datoteku
  // Ovo prisiljava C++ linker da uključi odgovarajuću .obj datoteku
  {$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;
  // Logika ekstrakcije metapodataka ovde
end;

end.

Kada C++Builder uključi lxXlsSummary.hpp, kompajler nailazi na #pragma link i automatski nalaže linkeru (ILINK32/ILINK64) da razreši simbole iz lxXlsSummary.obj

Četvorokoloni tok HPPEMIT pragma link mehanizma u Delphi-ju: deklarisana direktiva podstiče dcc32 da ubaci linker redosled u lxXlsSummary.hpp, C++Builder ga troši pri include, a ILINK32 automatski razrešava XlsReadSummaryInformation
Redosled povezivača piše se jednom u Pascal izvoru, putuje unutar generisanog zaglavlja, i stiže u svaku buduću C++ izgradnju bez ručnih izmena projekta

Zlatno pravilo za održavanje komponenti

Da biste izbegli potpuno kvarenje C++Builder build-ova, morate usvojiti strogu politiku registracije. Kad god se nova Pascal jedinica doda u vašu biblioteku, ona mora biti eksplicitno registrovana u svim tipovima projektnih datoteka istovremeno

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

Otvorite .cbproj datoteku u uređivaču teksta i dodajte novu jedinicu na listu za kompajliranje, pazeći da obezbedite jedinstveni redosled izgradnje. Ako koristite starije verzije C++Buildera sa .bpk datotekama, osigurajte da je dodata 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 u eksplicitnu contains klauzulu. Ovo osigurava da Delphi kompajler ne mora da se oslanja na implicitno povezivanje, što se ionako generalno 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 odbrana od ove zamke je CI/CD validacija. Nikada se nemojte oslanjati isključivo na uspešan Delphi build pre objavljivanja komponente za dva jezika. Vaše skripte za izgradnju moraju pozvati MSBuild ili alatke komandne linije bcc32c na C++Builder projektima (npr. build-Win32-Lib-CB.cmd) i pokrenuti kompletno povezivanje C++ probne verzije i punih demo primera. Tek kada C++ linker uspe, možete biti sigurni da su sve Delphi jedinice pravilno registrovane i da izlažu svoje simbole C++ okruženju

Napomena: Kompatibilnost unakrsnog kompajliranja je strogo održavana u Delphi i C++Builder izdanjima HotXLS Delphi VCL komponente