Teknik Makale

Free Pascal Altında HotXLS: Unicode, COM Slotları ve zlib

HotXLS, Windows'ta Free Pascal ve Lazarus altında derlenir ve portaj, Object Pascal sözdizimiyle hiç ilgisi olmayan dört karara dayanır: çekirdeği DELPHIUNICODE modunda tutmak, OLE structured-storage interfacelerini elle referans sayılan CORBA interfaceler olarak bildirmek, Win32 AES nesne dosyalarını bir Pascal uygulamasıyla değiştirmek ve kesilmiş bir ZIP'i tam kabul edebilen bir inflate döngüsünü düzeltmek

Olgun bir Delphi kütüphanesini portajlamış herkes bu işin şeklini bilir. Derleyici ilk geçişte neredeyse her şeyi kabul eder. Ardından, temiz derlenen ama yanlış sonuç üreten davranış farklarının uzun kuyruğu gelir ve bir spreadsheet motoru bunlara olağandışı açıktır; çünkü metin kodlamasına, COM structured storage'a, sıkıştırmaya ve kriptografiye tek bir kod yolunda dokunur

Çekirdek neden yalın DELPHI yerine DELPHIUNICODE'de ısrar eder?

Çünkü formül motoru, String ile Char'ın UTF-16 semantiği taşımasına yaslanır ve ANSI alternatifi, dosyaya bir şey ulaşmadan karakterleri kaybettirir. Çekirdeği FPC DELPHI modunda kurmak caziptir; çoğu portajın uzandığı uyumluluk anahtarı odur ve kod derlenir de. Sonra Çince sayfa adlı ya da Kiril etiketli bir çalışma kitabı hesaplama yolundan gidiş-geliş yapar ve yazıcı onları görene dek karakterler çoktan gitmiştir; ortada hiçbir hata yoktur

Mod kütüphane genelinde tekdüze değildir ve bu dağınıklık değil kasıtlılıktır. PNG bayt çözücüsü ile LCL override'ları gerçekten ANSI imzalar ister; çünkü bayt ve widgetset'in kendilerine verdiğiyle iş yaparlar. O unit'ler ayrı bir LX_FPC_ANSI anahtarı açar. Tek kütüphanede iki mod, alternatifin girdisini metin sanan bir bayt çözücüsü olduğunu fark edene kadar kod kokusu gibi duyulur

Sonradan yakalayan bir refik ayrıntı daha var. FPC runtime'ında DELPHIUNICODE, TFormatSettings.DecimalSeparator'ı bir WideChar yapmaz. Unicode ondalık ayracı taşıyan girdi, önce Unicode dizgenin içinde bir ASCII ayracına normalize edilmelidir ve ayracı bekleneni tutmayan her girdi, çözümleyicinin tanımadığı karakterde sessizce kırpılmak yerine reddedilmelidir

program ExportReport;
{$MODE DELPHI}
uses
  Interfaces,          // önce gelmeli: LCL widgetset'ini ilklendirir
  SysUtils, lxHandle;  // ve UTF-8 dönüşüm katmanını

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 unit'i isteğe bağlı değildir ve önce gelmek zorundadır. LCL widgetset'ini ve UTF-8 dönüşüm katmanını ilklendiren odur ve HotXLS, fontlar, dosya yolları ya da metin RTL ile LCL sınırını geçtiği anda ikisine de yaslanır. Onu atlayan bir konsol programı derlenir ama her non-ASCII yolda yanlış davranır. Başarılı bir derlemenin burada az şey kanıtlamasının nedeni de budur: portaj, gerçek font adları ve gerçek yollar taşıyan gerçek belgeler tam bir gidiş-dönüş yaptığında ancak kanıtlanabilir biçimde çalışıyordu

Bir sınıf VMT'si bir COM vtable değildir

Free Pascal, bir sınıf VMT'sini bildirim Delphi'nin kabul ettiğine tıpatıp benzese bile Windows'a bir COM interface vtable olarak vermenize izin vermez. Yerleşimler, yanlış slota çağrı üreten şekillerde farklıdır; bu da çağrı noktasıyla ilgisiz bir yerde çökme olarak görünür. Structured storage burada önemlidir; çünkü klasik ikili çalışma kitabı formatı bir OLE compound dosyasıdır ve birini okumak ya da yazmak, Windows storage API'sinin geri çağıracağı ILockBytes'i uygulamak demektir

İşleyen düzen, COM slotları açıkça bildirilmiş ve AddRef ile Release elle yönetilen bir CORBA interface'tir. Bu, bu türler için otomatik referans sayımından vazgeçip yaşam süresinin sorumluluğunu almak demektir; tek bir unit içinde yaşayan bir avuç interface için adil bir takastır. O işin içindeki özgül tuzak QueryInterface'tir: interface pointer döndürmek zorundadır, nesne pointer'ı değil. İkisi de derlenir. Birinin verdiği adresin ilk makine kelimesi bir vtable değildir

Free Pascal sınıf VMTsi ile HotXLSin Windows structured storage API'sine sunmak zorunda olduğu COM interface vtable karşılaştırma şeması: aynı Pascal bildirimi için farklı slot sıraları ve QueryInterface tuzağı; interface pointer yerine nesne pointerını döndürmek bir ILockBytes çağrısını sınıf slotuna yollar ve çağrı noktasından çok uzakta çökertir
Free Pascal bir sınıf VMT'sini COM vtable olarak sunmayı reddeder; bu yüzden HotXLS, açık COM slotlu ve AddRef ile Release elle yönetilen CORBA interfaceler bildirir ve QueryInterface, Windows'un dereference edebileceği interface pointer'ı döndürür

FPC'ye özgü bildirimler lxOleInterfaces.inc içinde, FPC kaynak dizininde lxAESBackend.inc ile lxZlibBackend.inc'in yanı başında durur; böylece derleyiciye özgü seçimler motor içinde dağılmak yerine tek bir yerde oturur. Formatın kendisi ve kütüphanenin onda nasıl gezindiği, Pascal'da OLE2 compound dosyaları okuma makalesinde anlatılır

Aynı aileden bir tür ayrıntısı daha var. LargeInt, FPC dalında Int64'e çözümlenmelidir ve Comp'un derleyici sınıflandırması iki araç zinciri arasında o kadar farklıdır ki overload çözümlemesi başka bir adayı seçebilir. Büyük-offset davranışını HGLOBAL akışıyla değil dosya akışıyla sınayın: Windows global bellek akışı 4 GiB ötesindeki seek'lerde kendiliğinden dolanır; oradaki geçen bir test, sizin aritmetiğiniz hakkında hiçbir şey kanıtlamaz

Kendisiyle tutarlı bir AES uygulamasının sakladıkları

Delphi derlemesinin bağladığı Win32 AES nesne dosyaları OMF'dir ve Free Pascal bağlayıcısı onları tüketemez; bu yüzden FPC dalı bunun yerine bir Pascal AES uygulaması kullanır. Delphi, hep bağladığı nesne dosyalarını bağlamaya devam eder; böylece yayımlanmış binary, mevcut müşteriler için değişmeden kalır

Doğrulama zorunluluğu, herhangi bir projeye taşınmaya değer kısımdır. Veriyi şifreleyip aynı uygulamayla yeniden çözmek hiçbir şey kanıtlamaz: anahtar takvimi yanlış, blok sırası yanlış ya da zincirlemesi yanlış bir simetrik algoritma kusursuz biçimde kendisiyle tutarlıdır ve kendi çıktısını her seferinde gidiş-dönüş yapar. Yakalayan tek şey known-answer vektörleridir: anahtar genişletmesini, blok sırasını ve CBC zincirlemesini yayımlanmış değerlerle karşılaştırır. Kendisiyle tutarlı yanlış bir uygulamayı sevk edin ve belirti, bir müşteri dosyayı Excel'de ilk açtığında görünür

Sıkıştırmada ise farklı karakterde bir kusur vardı. Bir Pascal inflate backend'i, sıkıştırılmış girdisinin tamamını tükettikten sonra hâlâ çıktı bekletiyor olabilir; çağıranın, akış sonunu bildirene kadar çağırmaya devam etmesi gerekir. Tükenmiş girdiyi akış sonu saymak son bloğu kırpar. Daha kötüsü, hasarlı bir arşivi sessizce kabul edilen bir arşive çevirir; ZIP end-of-central-directory kaydının doğrulanması makalesindeki sertleştirmenin önlemek için var olduğu hata modu tam olarak budur. Kural şudur: ilerleme yok artı bitmedi, bir kesilme hatasıdır; asla EOF değil

Gerçek saatler yutan iki derleme sistemi tuzağı

LCL arama yolları, FPC paket joker yollarından önce gelmek zorundadır; yoksa Free Vision'ın Menus unit'i, aynı adlı LCL unit'ini gölgeler ve ikisi hakkında da hiçbir şey söylemeyen bir PPU checksum uyuşmazlığı alırsınız. Kurulduktan sonra taşınmış bir Lazarus kurulumu ayrıca fpc.cfg içinde bayat yollar bırakabilir; bu yüzden derleme giriş noktaları, ortamın sunacağını devralmak yerine unit ve binary yollarını açıkça belirtir

İkinci tuzak Pascal'la ilgisizdir. LF satır sonlarıyla yazılmış bir .cmd toplu dosyası, dosya yorumlayıcının okuma tamponu büyüklüğünü aşana kadar çalışır; o noktada call :label, toplu etiketinin var olmadığı iddiasıyla kalır ve hata, sınırın ötesinde duran hangi program varsa onda belirir. Toplu betiği yeniden yazan her aracın CRLF'i geri yazması gerekir. Ve lazbuild --build-all, derlemeden önce paket unit çıktı dizinini temizler; o dizinde park edilmiş bir options dosyası, okunabilmeden silinir: onu dışarıda tutun ve @ yolunun paket dizinine göre çözümlendiğini hatırlayın, çünkü lazbuild derleyiciyi oradan çağırır

Temiz bir HotXLS Free Pascal derlemesinin ardındaki iki tehlike katmanının haritası: String ve Charı UTF-16da tutan DELPHIUNICODE modu, PNG bayt çözücüsü ve LCL override'ları için LX_FPC_ANSI kaçışı ve Free Vision Menus gölgesi, bayat fpc.cfg yolları, yalnız LF toplu dosyaları ile lazbuild çıktı silmesinden gelen derleme tuzakları
İlk derleme az şey kanıtlar: mod haritası hangi karakterlerin yazıcıya kadar hayatta kaldığına karar verirken, derleme sistemi tuzakları checksum uyuşmazlıkları, hayalet kayıp etiketler ve okunmadan önce silinen options dosyaları olarak ortaya çıkar
// Lazarus grid dışa aktarımı: TGridToXLS Lazarus paketiyle gelir; aynı
// DB-grid dışa aktarım kodu bir LCL uygulamasında da çalışır
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;

Bir derleyici uyarısının değeri

Free Pascal, Delphi'nin bildirmediği ilklendirilmemiş yerel değişkenleri bildirir ve FPC derlemesini çalıştırmak o farkı, hesaplama unit'inde iki gerçek kusura dönüştürdü. Bir işlev, kullanılmadan önce hiç atanmamış bir sayaç değişkenini okudu; öteki, kendilerini hesaplayan kod başka bir dalda çalışmadan önce bir dalda iki koordinat kullandı. Delphi altında ikisi de stack'te tesadüfen ne varsa ona göre davrandı; ki bu, bir makinede tekrarlanıp öbüründe tekrarlanmayan böceğin tanımıdır

Pratik sonuç şu: esas olarak ilk derleyicide sevk edilen bir ürün için bile ikinci derleyiciyi döngüde tutmaya değer. FPC uyarı sınıflarını düzenli aralıklarla taramak, bir Delphi kod tabanına uygulanan ucuz bir statik analiz geçişidir ve hiçbir test süitinin güvenilir biçimde ulaşamadığı bir kusur kategorisini bulur. Bunun içinde yer aldığı daha geniş sürüm matrisi disiplini, çapraz derleyici derleme matrisi makalesinde anlatılır

Windows için Free Pascal ve Lazarus desteği, Delphi ve C++Builder paketlerinin yanında bir Lazarus paketi olarak HotXLS Delphi spreadsheet bileşeni ile birlikte gelir; çatallanma değil aynı kaynak ağacından derlenir. İşin amacı da budur: tek motor, dört araç zinciri ve derleyiciye özgü kararların, tek oturuşta okunabilecek include dosyalarında izole edilmesi