HotXLS bygges under Free Pascal og Lazarus på Windows, og porteringen hang på fire beslutninger som ikke har noe med Object Pascal-syntaks å gjøre: holde kjernen i DELPHIUNICODE-modus, deklarere OLE structured storage-grensesnittene som CORBA-grensesnitt med håndstyrt referansetelling, erstatte Win32 AES-objektfilene med en Pascal-implementasjon, og fikse en inflate-løkke som kunne godta en avkortet ZIP som fullstendig
Alle som har portert et modent Delphi-bibliotek, kjenner formen på dette arbeidet. Kompilatoren godtar nesten alt i første gjennomløp. Det som følger, er en lang hale av atferdsforskjeller som kompilerer rent og gir feil resultater, og en regnearkmotor er uvanlig eksponert for dem fordi den berører tekstkoding, COM structured storage, kompresjon og kryptografi i én kodevei
Hvorfor insisterer kjernen på DELPHIUNICODE snarere enn ren DELPHI?
Fordi formelmotoren avhenger av at String og Char bærer UTF-16-semantikk, og ANSI-alternativet mister tegn før noe som helst når filen. Det er fristende å bygge kjernen i FPC DELPHI-modus, siden det er kompatibilitetsbryteren de fleste porteringer griper etter, og koden kompilerer. Så runder en arbeidsbok med kinesiske arkenavn eller kyrilliske merkelapper turen gjennom beregningsveien, og tegnene er borte i det skriveren ser dem, uten noen feil noe sted
Modusen er ikke ensartet på tvers av biblioteket, og det er bevisst snarere enn uordnet. PNG-byte-dekoderen og LCL-override-ene trenger genuint ANSI-signaturer, fordi de omgås byte og det widgetsettet gir dem. De enhetene slår på en separat LX_FPC_ANSI-bryter. To moduser i ett bibliotek høres ut som en code smell helt til du legger merke til at alternativet er en byte-dekoder som behandler inndataene sine som tekst
Det finnes en tilhørende detalj som fanger folk senere. DELPHIUNICODE gjør ikke TFormatSettings.DecimalSeparator til en WideChar i FPC-kjøretiden. Inndata med et Unicode-desimaltegn må normaliseres til et ASCII-tegn inne i Unicode-strengen først, og alle inndata hvis skilletegn ikke matcher det forventede, må avvises i stedet for å bli stille avkortet ved tegnet parseren ikke gjenkjente
program ExportReport;
{$MODE DELPHI}
uses
Interfaces, // må komme først: initialiserer LCL-widgetsettet
SysUtils, lxHandle; // og UTF-8-konverteringslaget
var
Book: TXLSWorkbook;
begin
Book := TXLSWorkbook.Create(nil);
try
Book.LoadFromFile('input.xls');
Book.Sheets[0].AsString[1, 1] := 'Quarterly summary';
Book.SaveToFile('output.xls');
finally
Book.Free;
end;
end.
Enheten Interfaces er ikke valgfri, og den må komme først. Det er den som initialiserer LCL-widgetsettet og UTF-8-konverteringslaget, og HotXLS er avhengig av begge så snart skrifter, filstier eller tekst krysser RTL- og LCL-grensen. Et konsollprogram som hopper over den, vil kompileres og vil oppføre seg galt på enhver ikke-ASCII-sti. Dette er også grunnen til at en vellykket kompilering beviser så lite her: porteringen var bare demonstrabelt fungerende da ekte dokumenter med ekte skriftnavn og ekte stier gjorde en full rundtur
En klasse-VMT er ikke en COM-vtable
Free Pascal lar deg ikke gi en klasse-VMT til Windows som en COM-grensesnitt-vtable, selv når deklarasjonen ser identisk ut med den Delphi aksepterer. Oppsettene skiller seg på måter som gir et kall inn i feil slot, noe som manifesterer seg som et krasj et sted urelatert til kallstedet. Structured storage betyr noe her fordi det klassiske binære arbeidsbokformatet er en OLE compound-fil, og å lese eller skrive én betyr å implementere ILockBytes som Windows storage API vil kalle tilbake til
Ordningen som fungerer, er et CORBA-grensesnitt med COM-slots deklarert eksplisitt og AddRef og Release håndtert for hånd. Det betyr å gi opp automatisk referansetelling for disse typene og ta ansvar for levetiden, noe som er en rimelig avveining for en neve grensesnitt som lever inne i én enhet. Den spesifikke fellen inne i det arbeidet er QueryInterface: den må returnere en grensesnittpeker, ikke objektpekeren. Begge kompilerer. En av dem gir Windows en adresse hvis første maskinord ikke er en vtable
De FPC-spesifikke deklarasjonene ligger i lxOleInterfaces.inc, ved siden av lxAESBackend.inc og lxZlibBackend.inc i FPC-kildekatalogen, så de kompilatorspesifikke valgene ligger ett sted i stedet for å være strødd gjennom motoren. Formatet selv og hvordan biblioteket navigerer det, er beskrevet i lesing av OLE2 compound-filer i Pascal
Enda en typedetalj hører til samme familie. LargeInt må løses til Int64 i FPC-grenen, og kompilatorens klassifisering av Comp skiller nok mellom de to verktøykjedene til at overload-oppløsning kan velge en annen kandidat. Test stor-offset-atferd med en filstrøm snarere enn en HGLOBAL-strøm: Windows global-minne-strømmen wrappes rundt på søk forbi 4 GiB av seg selv, så en bestående test der beviser ingenting om aritmetikken din
Hva en selvkonsistent AES-implementasjon skjuler
Win32 AES-objektfilene som Delphi-bygget kobler inn, er OMF, og Free Pascal-linkeren kan ikke konsumere dem, så FPC-grenen bruker en Pascal AES-implementasjon i stedet. Delphi fortsetter å koble inn objektfilene den alltid har gjort, noe som holder den utgitte binærfilen uendret for eksisterende kunder
Verifiseringskravet er delen verdt å ta med inn i ethvert prosjekt. Å kryptere data og dekryptere dem igjen med samme implementasjon beviser ingenting i det hele tatt: en symmetrisk algoritme med feil nøkkelplan, feil blokkrekkefølge eller feil kjeding er perfekt selvkonsistent og runder turen med sitt eget utdata hver gang. Bare kjente-svar-vektorer fanger det, ved å sjekke nøkkelekspansjonen, blokkrekkefølgen og CBC-kjedingen mot publiserte verdier. Send ut en selvkonsistent gal implementasjon, og symptomet dukker opp første gang en kunde åpner filen i Excel
Kompresjonen hadde en defekt av en annen karakter. En Pascal inflate-backend kan fortsatt ha utdata hengende etter at den har konsumert alt sitt komprimerte inndata, så kalleren må fortsette å kalle til strømmen rapporterer sin slutt. Å behandle utmattet inndata som slutt på strømmen avkorter siste blokk. Verre, det gjør et skadet arkiv til et stille akseptert et, noe som er nøyaktig feilmoden herdingen i validering av ZIP end-of-central-directory-posten finnes for å forhindre. Regelen er at ingen fremgang pluss ikke ferdig er en avkortingsfeil, aldri en EOF
To byggssystemfeller som koster ekte timer
LCL-søkestiene må komme foran FPC-pakkens jokertegnstier, ellers skygger Free Vision-enheten Menus LCL-enheten med samme navn, og du får en PPU-sjekksum-mismatch som ikke sier noe om noen av dem. En Lazarus-installasjon som ble flyttet etter installasjon, kan også etterlate foreldede stier i fpc.cfg, så bygg-inngangspunktene spesifiserer enhets- og binærstier eksplisitt i stedet for å arve hva miljøet tilbyr
Den andre fellen har ingenting med Pascal å gjøre. En .cmd-batchfil skrevet med LF-linjeslutt fungerer til filen vokser forbi størrelsen på tolkens lesebuffer, og da feiler call :label med påstanden om at batch-etiketten ikke finnes, og feilen viser seg ved det programmet som tilfeldigvis ligger forbi grensen. Ethvert verktøy som omskriver et batchskript, må skrive CRLF tilbake. Og lazbuild --build-all tømmer pakkens enhets-utdatakatalog før kompilering, så en alternativfil parkert i den katalogen slettes før den kan leses: hold den utenfor, og husk at @-stien løses relativt til pakkekatalogen fordi lazbuild kaller kompilatoren derfra
// Lazarus grid-eksport: TGridToXLS følger med Lazarus-pakken, så den
// samme DB-grid-eksportkoden fungerer i en LCL-applikasjon
var
Exporter: TGridToXLS;
begin
Exporter := TGridToXLS.Create(nil);
try
Exporter.DBGrid := GridOrders;
Exporter.WorksheetName := 'Orders';
Exporter.ExportHeader := True;
Exporter.SetColumnsWidth := True;
Exporter.ExportDBGrid;
Exporter.SaveAs('orders.xls');
finally
Exporter.Free;
end;
end;
Hva en kompilatoradvarsel er verdt
Free Pascal rapporterer uinitialiserte lokale variabler som Delphi ikke gjør, og å kjøre FPC-bygget gjorde den forskjellen om til to reelle defekter i beregningsenheten. Én funksjon leste en tellevariabel som aldri var tilordnet før bruk, og en annen brukte to koordinater i én gren før koden som beregnet dem, kjørte i en annen gren. Under Delphi oppførte begge seg etter hva stabelen tilfeldigvis holdt, noe som er definisjonen på en feil som reproduserer på én maskin og ikke en annen
Den praktiske konklusjonen er at den andre kompilatoren er verdt å beholde i loopen selv for et produkt som hovedsakelig leveres på den første. Å skanne FPC-advarselklassene med jevne mellomrom er en billig statisk analyse-runde over en Delphi-kodebase, og den finner en kategori av defekter som ingen test rekkevidde pålitelig når. Den bredere versjonsmatrise-disiplinen dette sitter inne i, er beskrevet i tverrkompilator-byggmatrisen
Free Pascal- og Lazarus-støtte for Windows følger med HotXLS Delphi regnearkkomponent som en Lazarus-pakke ved siden av Delphi- og C++Builder-pakkene, bygget fra samme kildetre snarere enn en fork. Det er poenget med øvelsen: én motor, fire verktøykjeder, og de kompilatorspesifikke beslutningene isolert i include-filer der de kan leses i én sitting