Techninis straipsnis

HotXLS Free Pascal aplinkoje: Unicode, COM lizdai ir zlib

HotXLS statoosi su Free Pascal ir Lazarus Windows sistemoje, ir pernešimas priklausė nuo keturių sprendimų, neturinčių nieko bendra su Object Pascal sintakse: laikyti šerdį DELPHIUNICODE režime, deklaruoti OLE struktūrinės saugyklos sąsajas kaip CORBA sąsajas su rankomis valdomu nuorodų skaičiavimu, pakeisti Win32 AES objektinius failus Pascal realizacija, ir sutvarkyti inflate ciklą, kuris galėjo priimti nukirptą ZIP kaip pilną

Kas bet kada pernešė brandžią Delphi biblioteką, tas žino šio darbo formą. Kompiliatorius priima beveik viską pirmu perėjimu. Po to seka ilga elgesio skirtumų uodega, kurie kompiliuojasi švariai ir duoda neteisingus rezultatus, o skaičiuoklės variklis prie jų neįprastai atviras, nes jis liečia teksto kodavimą, COM struktūrinę saugyklą, kompresiją ir kriptografiją viename kodo kelyje

Kodėl šerdis reikalauja DELPHIUNICODE, o ne paprasto DELPHI?

Nes formulės variklis priklauso nuo String ir Char, nešančių UTF-16 semantiką, o ANSI alternatyva praranda simbolius dar prieš ką nors pasiekiant failą. Pagunda statyti šerdį FPC DELPHI režime yra didelė, nes tai suderinamumo jungiklis, kurį dauguma pernešimų ir renkasi, ir kodas kompiliuojasi. Tada darbo knyga su kinietiškais lapų pavadinimais ar kirilicos etiketėmis praeina pro skaičiavimo kelią atgal ir pirmyn, ir simbolių nebėra, kai rašytojas juos mato, be jokios klaidos

Režimas nėra vienodas visoje bibliotekoje, ir tai sąmoninga, o ne netvarkinga. PNG baitų dekoduotojui ir LCL perrašymams tikrai reikia ANSI parašų, nes jie elgiasi baitais ir tuo, ką widgetset jiems atiduoda. Tie moduliai įjungia atskirą LX_FPC_ANSI jungiklį. Du režimai vienoje bibliotekoje skamba kaip code smell, kol nepastebi, kad alternatyva yra baitų dekoduotojas, traktuojantis savo įvestį kaip tekstą

Yra lydraštė detalė, kuri pagauna žmones vėliau. DELPHIUNICODE nepadaro TFormatSettings.DecimalSeparator WideChar FPC runtime viduje. Įvestis, nešanti Unicode dešimtainį skyriklį, turi būti normalizuota į ASCII skyriklį Unicode eilutės viduje pirmiausia, o bet kokia įvestis, kurios skyriklis nesutampa su laukiamuoju, turi būti atmetama, o ne tyliai nukerpama ties simboliu, kurio analizatorius neatpažino

program ExportReport;
{$MODE DELPHI}
uses
  Interfaces,          // turi ateiti pirmas: inicializuoja LCL widgetset
  SysUtils, lxHandle;  // ir UTF-8 konversijos sluoksnį

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.

Interfaces modulis nėra pasirinktinas ir jis turi ateiti pirmas. Jis inicializuoja LCL widgetset ir UTF-8 konversijos sluoksnį, o HotXLS remiasi abiem, kai tik šriftai, failų keliai ar tekstas kerta RTL ir LCL ribą. Konsolė programa, praleidusi jį, kompiliuosis ir elgsis blogai bet kuriame ne ASCII kelyje. Tai ir priežastis, kodėl sėkminga kompiliacija čia įrodo tiek mažai: pernešimas buvo įrodytai veikiantis tik tada, kai tikri dokumentai su tikrais šriftų vardais ir tikrais keliais padarė pilną ratą atgal ir pirmyn

Klasės VMT nėra COM vtable

Free Pascal neleis jums atiduoti klasės VMT Windows kaip COM sąsajos vtable, net kai deklaracija atrodo identiška tai, kurią priima Delphi. Išdėstymai skiriasi taip, kad atsiranda iškvietimas į neteisingą lizdą, kas pasireiškia kaip užstrigimas kažkur nesusijusiame su iškvietimo vieta. Struktūrinė saugykla čia svarbi, nes klasikinis dvinarinis darbo knygos formatas yra OLE jungtinis failas, ir tokio skaitymas ar rašymas reiškia ILockBytes realizavimą, į kurį Windows saugyklos API atšauks

Darbinis sutvarkymas yra CORBA sąsaja su aiškiai deklaruotais COM lizdais ir rankomis valdomais AddRef ir Release. Tai reiškia atsisakyti automatinio nuorodų skaičiavimo šiems tipams ir prisiimti atsakomybę už gyvavimo laiką, kas yra sąžiningas mainas saujai sąsajų, gyvenančių viename modulyje. Konkretūs šio darbo spąstai yra QueryInterface: jis turi grąžinti sąsajos rodyklę, o ne objekto rodyklę. Abu kompiliuojasi. Vienas iš jų atiduoda Windows adresą, kurio pirmasis mašininis žodis nėra vtable

Diagrama, lyginanti Free Pascal klasės VMT su COM sąsajos vtable, kurį HotXLS turi pateikti Windows struktūrinės saugyklos API: skirtingi lizdų eiliškumai tai pačiai Pascal deklaracijai, plius QueryInterface spąstai, kur grąžinus objekto rodyklę vietoj sąsajos rodyklės ILockBytes iškvietimas patenka į klasės lizdą ir užstringa toli nuo iškvietimo vietos
Free Pascal atsisako patiekti klasės VMT kaip COM vtable, tad HotXLS deklaruoja CORBA sąsajas su aiškiais COM lizdais ir rankomis valdomais AddRef ir Release, o QueryInterface grąžina sąsajos rodyklę, kurią Windows gali dereferencuoti

FPC specifinės deklaracijos gyvena lxOleInterfaces.inc, greta lxAESBackend.inc ir lxZlibBackend.inc FPC šaltinių kataloge, tad kompiliatoriui specifiniai pasirinkimai sėdi vienoje vietoje, o ne išmėtyti per variklį. Pats formatas ir kaip biblioteka juo naviguoja aprašyti straipsnyje OLE2 jungtinių failų skaitymas Pascal

Dar viena tipo detalė priklauso tai pačiai šeimai. LargeInt turi išsiskirti į Int64 FPC šakoje, ir kompiliatoriaus Comp klasifikacija skiriasi pakankamai tarp dviejų įrankių grandinių, kad perkrovų atranka gali pasirinkti kitą kandidatą. Didelių poslinkių elgesį tikrinkite failų srautu, o ne HGLOBAL srautu: Windows globalios atminties srautas pats apsiverčia pozicionuojant už 4 GiB, tad praeinantis testas ten nieko neįrodo apie jūsų pačių aritmetiką

Ką slepia savaime suderinama AES realizacija

Win32 AES objektiniai failai, kuriuos susieja Delphi versija, yra OMF, ir Free Pascal linkeris jų suvartoti negali, tad FPC šaka naudoja Pascal AES realizaciją vietoj. Delphi toliau susieja objektinius failus, kuriuos visada susiedavo, kas laiko išleistą dvinarinį failą nepakitusį esamiems klientams

Patikros reikalavimas yra dalis, verta nešti į bet kurį projektą. Duomenų užšifravimas ir pakartotinis iššifravimas ta pačia realizacija neįrodo nieko: simetrinis algoritmas su neteisingu rakto grafiku, neteisinga blokų tvarka ar neteisinga grandine yra tobulai savaime suderinamas ir kas kartą praleis savo paties išvestį pro ratą. Tik žinomų atsakymų vektoriai tai pagauna, tikrindami rakto išplėtimą, blokų tvarką ir CBC grandinę prieš paskelbtas reikšmes. Išleiskite savaime suderinamą neteisingą realizaciją, ir simptomas pasirodys pirmą kartą, kai klientas atvers failą Excel

Kompresija turėjo kitokio charakterio defektą. Pascal inflate posistemė gali vis dar turėti išvesties, laukiančios išdavimo, sunaudojusi visą savo suspaustą įvestį, tad kvietėjas turi kviesdamas likti, kol srautas praneša savo galą. Išsekusios įvesties traktavimas kaip srauto galas nukerpa paskutinį bloką. Blogiau – tai pavercia pažeistą archyvą į tyliai priimtą, kas yra tiksliai tas nesėkmės režimas, kurio užkirsti kelią egzistuoja sukietėjimas straipsnyje ZIP end-of-central-directory įrašo patikrinimas. Taisyklė tokia: jokios pažangos plius nebaigta yra nukirpimo klaida, niekada ne EOF

Du statymo sistemos spąstai, kainavę tikrų valandų

LCL paieškos keliai turi eiti priekyje FPC paketų pakaitos kelių, kitaip Free Vision Menus modulis uždengia LCL modulį to paties vardo, ir jūs gaunate PPU kontrolinės sumos nesutapimą, apie kurį nesako nieko nė vienas. Lazarus diegimas, perkeltas po įdiegimo, taip pat gali palikti pasenęs kelius fpc.cfg, tad statymo įėjimo taškai nurodo modulių ir dvinarinius kelius aiškiai, vietoj to, kad paveldėtų ką bepasiūlo aplinka

Antrasis spąstai neturi nieko bendra su Pascal. .cmd paketinis failas, parašytas su LF eilučių pabaigomis, veikia, kol failas išauga už interpretatoriaus skaitymo buferio dydžio, kur call :label nepajėgia teigdamas, kad paketinės etikės nėra, ir nesėkmė pasirodo ties ta programa, kuri atsitiktinai sėdi už ribos. Bet koks įrankis, perrašantis paketinį scenarijų, turi rašyti CRLF atgal. Ir lazbuild --build-all išvalo paketo modulių išvesties katalogą prieš kompiliuodamas, tad parinkčių failas, pastatytas tame kataloge, ištrinamas, kol spėja būti perskaitytas: laikykite jį užapus, ir prisiminkite, kad @ kelias išsiskiria santykinai paketo katalogui, nes lazbuild iškviečia kompiliatorių iš ten

Dviejų pavojų sluoksnių, slypinčių už švarios HotXLS Free Pascal kompiliacijos, žemėlapis: DELPHIUNICODE režimas, laikantis String ir Char UTF-16, LX_FPC_ANSI išeitis PNG baitų dekoduotojui ir LCL perrašymams, ir statymo spąstai nuo Free Vision Menus uždengimo, pasenusių fpc.cfg kelių, tik LF paketinių failų ir lazbuild išvesties nuplovimo
Pirmoji kompiliacija įrodo mažai: režimų žemėlapis nusprendžia, kurie simboliai išgyvena iki rašytojo, o statymo sistemos spąstai iškyla kaip kontrolinių sumų nesutapimai, fantominės dingusios etikės ir parinkčių failai, ištrinti prieš jiems būnant perskaitytiems
// Lazarus tinklelio eksportas: TGridToXLS keliauja su Lazarus paketu, tad
// tas pats DB-tinklelio eksporto kodas veikia LCL programoje
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;

Ką verta kompiliatoriaus įspėjimas

Free Pascal praneša neinicializuotus vietinius kintamuosius, kurių Delphi nepraneša, ir FPC versijos paleidimas pavertė tą skirtumą dviem tikrais defektais skaičiavimo modulyje. Viena funkcija skaitė skaičiaus kintamąjį, niekada nepriskirtą prieš naudojimą, o kita naudojo dvi koordinates vienoje šakoje prieš kodą, kuris jas perskaičiavo, bėgantį kitoje šakoje. Po Delphi abu elgėsi pagal tai, ką atsitiktinai laikė stekas, kas yra apibrėžimas klaidai, atsikeliančiai viename kompiuteryje ir ne kitoje

Praktinė išvada ta, kad antrasis kompiliatorius vertas likti cikle net produktui, išleidžiamam pirmiausia pirmuoju. FPC įspėjimų klasių skenavimas periodiškai yra pigus statinės analizės perėjimas per Delphi kodų bazę, ir jis randa defektų kategoriją, kurios joks testų rinkinys patikimai nepasiekia. Platesnė versijų matricos drausmė, kurioje tai sėdi, aprašyta straipsnyje kryžminių kompiliatorių statymo matrica

Free Pascal ir Lazarus palaikymas Windows keliauja su HotXLS Delphi skaičiuoklės komponentu kaip Lazarus paketas greta Delphi ir C++Builder paketų, kuriamas iš to paties šaltinių medžio, o ne atšakos. Tam ir buvo ši pratyba: vienas variklis, keturios įrankių grandinės, ir kompiliatoriui specifiniai sprendimai izoliuoti include failuose, kuriuos galima perskaityti vienu sėdimu