Kun ylläpidät Delphillä kirjoitettua VCL-komponenttia, jota käyttävät sekä Delphi- että C++Builder-käyttäjät, huomaat nopeasti, että nämä kaksi koontijärjestelmää käsittelevät riippuvuuksia hyvin eri tavoin. Yksikkö, joka kääntyy täydellisesti Delphissä, saattaa aiheuttaa katastrofaalisia linkitysvirheitä C++Builderissa. Tämä ero on yleinen ansa komponenttien tekijöille, erityisesti silloin, kun olemassa olevaan kirjastoon lisätään uusia sisäisiä yksiköitä
Tässä on yksityiskohtainen erittely siitä, miksi näin tapahtuu käyttäen todellisia esimerkkejä HotXLS-komponentista, ja kuinka voit tehdä ristiinkäännösprosessistasi pomminvarman käyttämällä eksplisiittisiä sisällytyksiä, {$HPPEMIT}-direktiiviä ja pragma-linkitystä
Ansa: Implisiittinen käännös vs. eksplisiittinen sisällyttäminen
Oletetaan, että luot uuden Delphi-yksikön, lxXlsSummary.pas, käsittelemään asiakirjan metatietoja, ja lisäät sen uses-lauseeseen pääjäsentämisyksikössäsi, lxRead.pas. Painat kääntämispainiketta Delphi IDE:ssä, koontiversio muuttuu vihreäksi, ja julkaiset päivityksen
Seuraavana päivänä C++Builder-käyttäjäsi ilmoittavat virheestä linkitysvaiheen aikana: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
Delphi-tapa (dcc32)
Kun Delphi-kääntäjä käsittelee paketin (.dpk), se tarkastelee contains-lausekkeessa nimenomaisesti lueteltuja yksiköitä. Jos jokin näistä yksiköistä käyttää (uses) ulkoista yksikköä (kuten lxXlsSummary.pas), jota ei ole contains-luettelossa, Delphi-kääntäjä suorittaa implisiittisen staattisen linkityksen. Se yksinkertaisesti löytää .pas-tiedoston hakupolusta, kääntää sen .dcu-tiedostoksi ja leipoo sen sisään tuloksena olevaan .bpl-tiedostoon. Käännös onnistuu piilottaen puutteen täysin
C++Builder-tapa (MSBuild / .cbproj)
C++Builderin koontijärjestelmä on paljon tiukempi. Se tuottaa C++-objektitiedostoja (.obj) ja otsikkotiedostoja (.hpp) vain niille Delphi-yksiköille, jotka on eksplisiittisesti lueteltu .cbproj-tiedoston <DelphiCompile>-kohderyhmässä. Koska lxXlsSummary.pas-tiedostoa ei koskaan rekisteröity eksplisiittisesti projektitiedostoon, lxXlsSummary.obj-tiedostoa ei luoda. Kun linkittäjä yrittää ratkaista lxRead.obj-tiedoston tekemiä kutsuja, symbolit puuttuvat, mikä johtaa ratkaisemattomaan ulkoiseen virheeseen (unresolved external error)
Ulkoisten viitteiden ratkaiseminen Pragma Link -direktiivillä ja HPPEMIT-määrityksellä
Jos haluat varmistaa, että yksikkö on linkitetty oikein C++:ssa pakottamatta käyttäjää lisäämään .obj-tiedostoa manuaalisesti projektiinsa, voit käyttää Delphin {$HPPEMIT}-direktiiviä. Tämä käskee Delphi-kääntäjää syöttämään tietyn C++-ohjeen (#pragma link) luotuun .hpp-tiedostoon
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.
Kun C++Builder sisällyttää lxXlsSummary.hpp-tiedoston, kääntäjä kohtaa #pragma link -direktiivin ja käskee linkittäjää (ILINK32/ILINK64) automaattisesti ratkaisemaan symbolit tiedostosta lxXlsSummary.obj
Kultainen sääntö komponenttien ylläpitoon
Välttääksesi C++Builder-koontiversioiden täydellisen rikkoutumisen, sinun on otettava käyttöön tiukka rekisteröintikäytäntö. Aina kun kirjastoosi lisätään uusi Pascal-yksikkö, se on rekisteröitävä eksplisiittisesti kaikissa kolmessa projektitiedostotyypissä samanaikaisesti
1. Päivitä C++Builder-projekti (.cbproj / .bpk)
Avaa .cbproj-tiedosto tekstieditorissa ja lisää uusi yksikkö käännösluetteloon varmistaen, että annat sille yksilöllisen käännösjärjestyksen. Jos käytät vanhempia C++Builder-versioita .bpk-tiedostojen kanssa, varmista, että tunniste <file containerid="PascalCompiler" designclass="" filename="lxXlsSummary.pas" formname="" localcommand="" unitname="lxXlsSummary"></file> on lisätty
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Päivitä Delphi-paketti (.dpk)
Lisää yksikkö eksplisiittiseen contains-lausekkeeseen. Tämä varmistaa, ettei Delphi-kääntäjän tarvitse luottaa implisiittiseen linkitykseen, jota pidetään yleisesti huonona käytäntönä
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Jatkuvan integroinnin validointi
Lopullinen puolustuskeino tätä ansaa vastaan on CI/CD-validointi. Älä koskaan luota pelkästään onnistuneeseen Delphi-käännökseen ennen kaksikielisen komponentin julkaisemista. Koontikomentosarjojesi on kutsuttava MSBuild- tai bcc32c-komentorivityökaluja C++Builder-projekteille (esim. build-Win32-Lib-CB.cmd) ja suoritettava C++-kokeiluversion sekä täysien demojen täydellinen linkitys. Vasta kun C++-linkittäjä onnistuu, voit olla varma, että kaikki Delphi-yksiköt on rekisteröity oikein ja ne paljastavat symbolinsa C++-ajonajalle
Huomautus: Monialustainen kääntäjien yhteensopivuus ylläpidetään tiukasti HotXLS VCL -komponentin Delphi- ja C++Builder-versioiden välillä