Tehnički članak

Prepisivanje VBA izvornog koda i ponovna kompresija MS-OVBA u Delphiju

Preimenovanje hardkodirane reference radnog lista u hiljadu obrazaca izveštaja sa makroima isključuje ručno otvaranje svake datoteke u VBA uređivaču. HotXLS rešava taj slučaj tako što izvorni kod VBA modula izlaže kao svojstvo SourceCode koje se može uređivati i svaku izmenu ponovo kompresuje algoritmom MS-OVBA, upisujući rezultat nazad u XLS, VBA projekat ili XLSM radnu svesku

Zašto tok VBA modula nije tekstualna datoteka

VBA modul nije izvorni tekst već mali binarni kontejner sa kešom performansi i izvornim tekstom obrađenim vlasničkom MS-OVBA kompresijom. Ta šema nije zip ni deflate, a ponovna kompresija je deo u kom pogrešan bit može proizvesti datoteku koju Excel odbija da otvori

Šta svojstvo SourceCode zaista menja u HotXLS-u

HotXLS predstavlja svaki VBA modul kao objekat TXLSVBAModule sa jednostavnim svojstvom SourceCode: WideString, a dodela nove vrednosti upravo je tako jednostavna: modul se u memoriji označava kao izmenjen i ništa ne dodiruje osnovni OLE tok dok se projekat ne sačuva. Sam projekat dolazi iz IXLSWorkbook.VBAProject u klasičnom XLS mehanizmu ili iz TXLSXWorkbook.ParsedVBAProject u OOXML mehanizmu sa makroima, pri čemu oba vraćaju TXLSVBAProject čiji se moduli nalaze iza indeksatora Item[] sa početkom od 1 i svojstva Count, pa je grupna izmena svakog modula u radnoj svesci samo petlja kroz celobrojni opseg

var
  Wb: TXLSWorkbook;
  Project: TXLSVBAProject;
  I: Integer;
  Updated: WideString;
begin
  Wb := TXLSWorkbook.Create;
  try
    Wb.Open('MonthlyReport.xls');
    if Wb.HasVBAProject then
    begin
      Project := Wb.VBAProject;
      for I := 1 to Project.Count do
      begin
        Updated := StringReplace(Project[I].SourceCode,
          'ReportSheet2025', 'ReportSheet2026', [rfReplaceAll]);
        if Updated <> Project[I].SourceCode then
          Project[I].SourceCode := Updated;   // marks the module dirty
      end;
      Wb.SaveAs('MonthlyReport.xls');          // recompresses on write
    end;
  finally
    Wb.Free;
  end;
end;

Ta petlja je ujedno oblik revizijskog prolaza za proveru i konverziju radnih svezaka. Pre obrade hiljadu obrazaca, timovi žele da znaju koliko njih sadrži makroe i na šta se oni pozivaju — isti Project.Count ovde pokreće prepisivanje, a tamo postaje zbir makroa po datoteci

Unutar kontejnera za kompresiju MS-OVBA

Format kompresije MS-OVBA pakuje izvorne bajtove u CompressedContainer sa potpisom 0x01 i blokovima CompressedChunk do 4096 bajtova. Zaglavlje bloka nosi potpis, veličinu i CompressedChunkFlag, a tokeni mogu biti doslovni bajtovi ili CopyToken povratne reference; greška za jedan u širini bitova najčešći je problem ručne implementacije

Zašto HotXLS upisuje sirove blokove umesto uparenih tokena

HotXLS potpuno zaobilazi uparivanje tokena. Svaki izmenjeni blok zapisuje sa obrisanom zastavicom CompressedChunkFlag i doslovnim bajtovima, što je dozvoljeno u MS-OVBA. Kompromis se vidi u veličini datoteke, ne u ispravnosti, a Excel rezultat i dalje ispravno otvara

Šta HotXLS ostavlja nepromenjenim pri prepisivanju modula

Ponovna kompresija uvek zamenjuje samo deo toka modula. Svaki tok modula najpre čuva keš performansi, a zatim kompresovani izvorni kod, dok dir tok projekta u zapisu MODULEOFFSET precizno beleži gde se ta podela nalazi za svaki modul; HotXLS čita taj pomeraj, čuva svaki bajt pre njega tačno onako kako ga je pronašao i ponovo gradi samo kompresovani kontejner počev od tog pomeraja

Sam izvorni tekst prolazi kroz kodnu stranicu VBA projekta, a ne kroz UTF-8 — istu zastarelu kodnu stranicu kojom je Office prvobitno zapisao projekat. Izmena SourceCode koja uvede znakove izvan skupa te kodne stranice pri ponovnom kodiranju niske u bajtove u HotXLS-u tiho dobija zamenske znakove po principu najboljeg podudaranja, umesto da bude odbijena, pa je neuobičajen regionalni znak ubačen u komentar ili literal niske najverovatnije mesto na kom ćete primetiti gubitak. Spoljne reference i povezivanja biblioteka unutar istog projekta prate srodan, ali odvojen put očuvanja, opisan u pratećem članku o očuvanju spoljnih veza VBA, koji vredi pročitati pre nego što prolaz za prepisivanje obradi projekat povezan sa drugim radnim sveskama ili bibliotekama tipova

Kako prepisane makroe vraćate u radnu svesku

Nijedan poziv ne pokreće ponovnu kompresiju izričito — ona se automatski izvršava pri čuvanju. TXLSVBAProject.ApplyChanges obrađuje svaki izmenjeni SourceCode, dok TXLSWorkbook.SaveAs, TXLSXWorkbook.SaveAs i SaveVBAProjectToFile interno pokreću istu metodu

var
  Wb: TXLSWorkbook;
begin
  Wb := TXLSWorkbook.Create;
  try
    if Wb.LoadVBAProjectFromFile('LegacyMacros.ole') = 1 then
    begin
      Wb.VBAProject[1].SourceCode :=
        StringReplace(Wb.VBAProject[1].SourceCode, 'OldServer', 'NewServer', [rfReplaceAll]);
      Wb.SaveVBAProjectToFile('LegacyMacros_Patched.ole');  // ApplyChanges runs internally
    end;
  finally
    Wb.Free;
  end;
end;
var
  Xlsx: TXLSXWorkbook;
  Project: TXLSVBAProject;
begin
  Xlsx := TXLSXWorkbook.Create;
  try
    Xlsx.Open('Dashboard.xlsm');
    Project := Xlsx.ParsedVBAProject;
    if Assigned(Project) then
    begin
      Project[1].SourceCode := StringReplace(Project[1].SourceCode,
        'ConnStringV1', 'ConnStringV2', [rfReplaceAll]);
      Xlsx.SaveAs('Dashboard.xlsm');   // SyncParsedVBAProject recompresses before the part is written
    end;
  finally
    Xlsx.Free;
  end;
end;

Sva tri odredišta u osnovi dele istu logiku SourceCode i ApplyChanges; jedina stvarna razlika među njima jeste poziv za čuvanje koji na kraju pokreće ponovnu kompresiju

Gde ovo i dalje može da zakaže

Dva načina otkaza treba predvideti pre rada nad produkcionim datotekama. Promena izvora poništava digitalni potpis VBA projekta i zahteva naknadno potpisivanje. Ručna ponovna implementacija kompresije može zbog jednog pogrešnog bita proizvesti datoteku koju Excel odbija, upravo zato je bezbednije koristiti postojeću strategiju upisa sirovih blokova

Za korišćenje ovoga nije potrebno obrnuto inženjerstvo formata. Delphi i C++Builder programeri dobijaju pristup čitanju i upisu svojstva SourceCode, ponovnu kompresiju usklađenu sa MS-OVBA i sva tri ovde opisana odredišta za upis kao deo standardne HotXLS Component, zajedno sa ostatkom njenog API-ja za klasične XLS i OOXML radne sveske