Tehnički članak

Očuvanje VBA makroa i eksternih veza kada Delphi kod ponovo upisuje radnu svesku

Zamislite posao koji skoro ništa ne radi: otvori mesečnu radnu svesku, upiše današnji datum u jednu ćeliju i sačuva je. Pokrenite to dovoljno puta kroz neku uslugu i sigurno će stići žalba. Makroi su nestali ili povezani kursevi valuta sada prikazuju grešku #REF!, a operativni tim je uveren da ih je vaš kod obrisao. Kod nije ništa obrisao. Ono što se obično desilo jeste da je radna sveska sa makroima sačuvana pod običnim .xlsx nazivom, a Excel je poštovao ECMA-376 pravila o tipu sadržaja: paket čiji tip sadržaja ne deklariše VBA ne može da učita VBA projekat, bez obzira na to što se bajtovi zapravo nalaze unutra. Datoteka se nije pokvarila. Preimenovana je u stanje u kojem je Excel obavezan da ignoriše njen deo

Makroi i eksterne veze radne sveske su dve stvari koje automatizacija najčešće gubi iz istog razloga. Obe žive izvan mreže ćelija koju kod za uređivanje zapravo dodiruje, pa će ih kod koji se oslanja samo na redove i kolone odbaciti, a da nikada ne pokrene brisanje. HotXLS is a native Delphi and C++Builder library that reads and writes XLS and XLSX without Excel installed, and it treats both assets as payloads it carries deliberately rather than data it happens to copy. U nastavku je opisano šta je svakom od njih potrebno prilikom čuvanja i gde prestaju garancije

Zašto se ova dva elementa ponašaju drugačije prilikom ponovnog upisivanja

VBA projekat je jedna neprozirna binarna datoteka. U OOXML paketu to je datoteka vbaProject.bin; u starijem BIFF formatu to je OLE skladište. Postoje tačno dva načina da ga izgubite: ako ga program za pisanje nikada ne kopira u izlaz, ili ako izlaz dobije tip datoteke koji to zabranjuje. Bilo koji od ovih neuspeha je potpun i tih. Projekat je ili prisutan ili nije

Eksterna veza uopšte nije binarni objekat (blob). To je mali grafikon odnosa: ciljna putanja ili URL koji ukazuje na drugu radnu svesku, lista naziva listova koje taj cilj izlaže i opcioni keš vrednosti koje su poslednji put viđene na tim listovima, kako bi Excel mogao da prikaže nešto kada cilj nije na mreži. Ova tri dela imaju različite cikluse trajanja tokom ponovnog upisivanja, pa biblioteka može verno sačuvati neke delove dok druge tiho odbacuje. Ta asimetrija je deo o kome treba biti precizan, jer je ništa u kodu za uređivanje ćelija neće otkriti

Prenošenje VBA projekta kroz XLSX ponovno upisivanje

Na XLSX strani, TXLSXWorkbook čuva sadržaj makroa doslovno. Svojstvo VbaProject sadrži sirove bajtove vbaProject.bin unutar AnsiString tipa, a prazan tekstualni niz je način na koji model kaže da nema makroa. Oko toga se nalaze tri operacije: HasVbaProject odgovara na pitanje da li je projekat prisutan, ClearVbaProject ga namerno uklanja, a LoadVbaProjectFromFile ubacuje projekat izvučen iz šablona. Taj poslednji poziv vredi više nego što se čini. On omogućava generisanim radnim sveskama da preuzmu standardni makro projekat bez provlačenja celokupne datoteke šablona kroz tok obrade

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Data');
    Sheet.Cells[1, 1].Value := 'Refreshed ' + DateTimeToStr(Now);

    Book.LoadVbaProjectFromFile('macros\vbaProject.bin');
    if not Book.HasVbaProject then
      raise Exception.Create('VBA payload failed to load');

    // The .xlsm extension is not cosmetic: it selects the
    // macro-enabled content type inside the package.
    Book.SaveAs('monthly-report.xlsm');
  finally
    Book.Free;
  end;
end;

Linija za čuvanje je tačka u kojoj nastaje ceo problem. Radna sveska koja sadrži VBA projekat mora biti upisana sa semantikom koja omogućava makroe, a HotXLS je primenjuje kada se ciljno ime završava sa .xlsm. Ako joj umesto toga prosledite .xlsx, Excel odbija makroe, iako su bajtovi fizički prisutni u paketu i ispravno bi se deserializovali. Ekstenzija nije ukras; ona bira tip sadržaja koji govori Excel-u da je VBA projektu dozvoljeno da postoji. Većinu vremena potrebno je samo da prenesete sadržaj. Kada treba da čitate iz njega, na primer da biste izlistali nazive modula za izveštaj o reviziji, svojstvo ParsedVBAProject izlaže parsirani model modula, dok VbaProject ostaje u obliku originalnih, neizmenjenih bajtova

Ponovno korišćenje makroa iz starijih XLS radnih svezaka

BIFF fasada oslikava taj skup alata uz jedan dodatni korak. Funkcija HasVBAProject ispituje učitanu datoteku, SaveVBAProjectToFile upisuje skladište projekta na disk, a LoadVBAProjectFromFile ga učitava nazad u drugu radnu svesku. Obilazak preko datoteke čini uobičajeni zadatak modernizacije jednostavnim: izvucite makroe iz modela iz 2003. godine i umetnite ih u sveže generisani XLS izlaz, bez potrebe za originalnim šablonom tokom izvršavanja

var
  Src, Dst: IXLSWorkbook;   // interface references: no manual Free
begin
  Src := TXLSWorkbook.Create;
  if Src.Open('legacy-model.xls') <= 0 then
    raise Exception.Create('Cannot open legacy model');
  if Src.HasVBAProject then
    Src.SaveVBAProjectToFile('extracted-vba.bin');

  Dst := TXLSWorkbook.Create;
  Dst.Sheets.Add.Name := 'Report2026';
  Dst.LoadVBAProjectFromFile('extracted-vba.bin');
  Dst.SaveAs('report-with-macros.xls');
end;

Memorijski model je ovde zamka i radi suprotno od XLSX klase. TXLSWorkbook se drži preko interfejsa IXLSWorkbook koji koristi brojanje referenci, tako da ga nikada ne oslobađate ručno; XLSX klasa TXLSXWorkbook je običan objekat koji morate obmotati blokom try..finally i osloboditi. Pomešajte ove dve konvencije u jednoj jedinici i uslediće rušenje programa zbog dvostrukog oslobađanja memorije. Još jedna granica koju vredi poštovati: držite ekstrakciju i ubacivanje unutar jednog istog formata datoteke. BIFF skladište projekta i OOXML vbaProject.bin su srodni formati, ali nisu isti kontejner, pa bi tok obrade koji mora da isporuči makroe u oba formata trebalo da koristi poseban makro šablon za svaki od njih

Eksterne veze: mapa preživljava, keširane vrednosti ne

Za XLSX radne sveske, HotXLS izlaže eksterne veze kroz kolekciju ExternalLinks. Svaki TXLSXExternalLink sadrži Target, putanju ili URL udaljene radne sveske, kao i listu SheetNames koja imenuje listove na koje se odnosi. Obe komponente preživljavaju ciklus otvaranja i čuvanja netaknute, a vezu možete izgraditi i od nule:

var
  Link: TXLSXExternalLink;
begin
  Link := Book.ExternalLinks.Add('\\fileserver\finance\fx-rates-2026.xlsx');
  Link.SheetNames.Add('FX');

  if Book.ExternalLinks.Count > 0 then
    Writeln(Format('%d external link(s): delivery requires reachable targets',
      [Book.ExternalLinks.Count]));
end;

Granica se nalazi jedan nivo dublje od same liste ciljeva. HotXLS prenosi mapu veza, što znači ciljnu putanju i nazive listova, ali ne parsira niti ponovo upisuje keširane vrednosti ćelija koje OOXML čuva u elementu veza sheetDataSet. Taj keš je ono što omogućava Excel-u da prikaže poslednji poznati broj kada izvorna datoteka nije na mreži, a generisana radna sveska se isporučuje bez njega. Posledica pogađa primaoca, a ne vas. Ako otvorite takvu datoteku gde je cilj nedostižan, na primer na laptopu koji nije na VPN mreži ili na deljenom direktorijumu koji je preimenovan, formule koje zavise od veze će se razrešiti kao greška #REF! ili će se zaustaviti iza upita za ažuriranje. Iz ovoga proizilaze dva pravila. Nemojte obećavati da će generisana radna sveska prikazivati eksterne povezane vrednosti van mreže. I tumačite vrednost ExternalLinks.Count koja je veća od nule kao preduslov isporuke pre nego kao funkciju: svaki cilj mora biti dostupan sa mesta na kome će se datoteka zapravo otvarati

Šta XLS čitač čuva bajt-za-bajt

Za strukture koje ne modelira, BIFF strana ima drugačiji odgovor: ostavlja ih tačno onakvima kakve su pronađene. Pivot keševi i pivot prikazi (familija zapisa SX*), definicije QueryTable tabela, eksterne veze sa podacima, prilagođeni prikazi, slike zaglavlja i zapisi o temama prolaze kroz ciklus otvaranja i čuvanja kao sirovi blokovi zapisa, neparsirani i neizmenjeni. Same eksterne reference se prenose kroz osnovne zapise EXTERNSHEET i SupBook. Ne postoji tipizirani API za njihovo kreiranje na XLS strani, ali postojeća veza preživljava uređivanje netaknuta

Očuvanje bajt-za-bajt je stvarna garancija sa oštrom ivicom

Očuvanje bajt-za-bajt je stvarna garancija sa oštrom ivicom. Pošto ništa ne čita očuvanu strukturu, vaša uređivanja je ne mogu pokvariti. Iz istog razloga, ništa je i ne ažurira. Ako umetnete redove kroz oblast na koju ukazuje očuvani pivot keš ili upitna tabela, struktura zadržava svoje originalne koordinate dok se podaci ispod nje pomeraju. Datoteka je i dalje validan XML ili BIFF; značenje je tiho odstupilo od poravnanja, a greška se ne pokreće da vas obavesti. Bezbedan raspored je da generisana uređivanja držite na listovima koji ne sadrže očuvane strukture, što je ista disciplina koja štiti zaključane listove i listove sa podešenom štampom u našem članku o zaštiti radnih listova i podešavanju stranice

Verifikacija datoteke koju ste zapravo napisali

Oba načina otkazivanja su tiha u trenutku pisanja, tako da se prava potvrda vrši ponovnim otvaranjem izlaza umesto verovanja kodu koji ga je proizveo. Tri provere pokrivaju skoro sve. Ponovo otvorite datoteku i potvrdite da HasVbaProject i dalje vraća true kada god su makroi očekivani, što u jednom testu hvata i izgubljeni sadržaj i pogrešnu ekstenziju. Pročitajte ExternalLinks.Count i uporedite to sa brojem pre ponovnog upisivanja. Zatim jednom otvorite datoteku u Excel-u sa onemogućenim makroima, jer je Excel-ova validacija tipa sadržaja stroža od bilo koje biblioteke, a Excel je program na osnovu kojeg će vaši klijenti ocenjivati datoteku

Ništa od toga ne zahteva kompletno parsiranje na samom početku. Kada radne sveske pristižu u velikom broju, a vi samo treba da izvršite trijažu koje od njih nose regulisani sadržaj, lagano ispitivanje u našem članku o izlistavanju listova i laganoj inspekciji radnih svezaka omogućava vam da usmerite datoteke sa makroima i vezama u stroži tok obrade pre nego što se pokrene prvo ponovno upisivanje

Nekoliko pitanja se javlja dovoljno često da na njih direktno odgovorimo. HotXLS nikada ne izvršava makroe koje čuva: u biblioteci ne postoji VBA runtime okruženje, već samo mehanizam za skladištenje, kopiranje, ekstrakciju i ubacivanje projekta kao podataka. Na serveru je to bezbednosna osobina vredna pomena, jer neprijateljski makro koji prolazi kroz tok obrade ostaje neaktivan sve dok desktop verzija Excel-a ne otvori datoteku i korisnik ne omogući sadržaj. Pretvaranje .xlsm u .xlsx uz zadržavanje makroa nije moguće, i to je pravilo samog formata, a ne ograničenje biblioteke: tip sadržaja .xlsx deklariše radnu svesku bez makroa, pa su jedini ispravni ishodi da datoteka ostane .xlsm ili da pozovete ClearVbaProject i isporučite datoteku koja ih zaista nema. Tiho preimenovanje je jedini izbor koji nikoga ne zadovoljava. A kada povezane ćelije prikažu grešku #REF! nakon ponovnog upisivanja, uzrok je nedostatak keša vrednosti o kojem je bilo reči iznad: nova datoteka nosi cilj, ali ne i keširane brojeve, pa Excel mora da razreši izvor u trenutku otvaranja, a nedostupna ili relativna putanja u okruženju to onemogućava. Ili garantujte da je cilj dostupan, ili upišite izračunate vrednosti u ćelije pre isporuke i potpuno odbacite zavisnost

Uređivanje tuđih radnih svezaka je uglavnom posao očuvanja stvari koje niste sami napisali i koje ne razumete u potpunosti. Mogućnosti za prenos VBA i eksternih veza koje su ovde opisane dolaze uz HotXLS komponentu za Delphi i C++Builder, zajedno sa revizorskim svojstvima koja vam omogućavaju da otkrijete regulisani sadržaj u trenutku kada datoteka stigne