Predstavte si úlohu, ktorá nerobí takmer nič: otvorí mesačný zošit, zapíše dnešný dátum do jednej bunky a zošit uloží. Spustite ju cez službu dostatočne často a skôr či neskôr príde sťažnosť. Makrá zmizli, prípadne prepojené výmenné kurzy teraz zobrazujú chybu #REF! a prevádzkový tím je presvedčený, že ich váš kód vymazal. Váš kód nič nevymazal. Zvyčajne sa stalo to, že zošit s povolenými makrami bol uložený s bežnou príponou .xlsx a Excel splnil pravidlá pre typy obsahu špecifikácie ECMA-376: balík, ktorého typ obsahu nedeklaruje prítomnosť VBA, nemôže načítať projekt VBA, bez ohľadu na to, že bajty sú v ňom priamo zapísané. Súbor sa nepoškodil. Bol iba premenovaný do stavu, v ktorom je Excel povinný ignorovať časť jeho obsahu.
Makrá a externé odkazy zošita sú dve veci, ktoré automatizácia stráca najčastejšie, a to z rovnakého dôvodu. Obe žijú mimo mriežky buniek, ktorej sa kód úprav reálne dotýka, takže kód uvažujúci v rovinách riadkov a stĺpcov ich zahodí bez toho, aby kedykoľvek zavolal príkaz na vymazanie. HotXLS, natívna knižnica pre Delphi a C++Builder, číta a zapisuje formáty XLS a XLSX bez nutnosti inštalácie Excelu a s oboma týmito prvkami zaobchádza ako s dôležitým nákladom, ktorý prenáša zámerne, a nie ako s dátami, ktoré iba náhodne kopíruje. V nasledujúcom texte si popíšeme, čo každý z nich vyžaduje od vašej cesty ukladania a kde záruky končia.
Prečo sa tieto dva prvky správajú pri prepise odlišne
Projekt VBA je jeden uzavretý binárny súbor. V balíku OOXML je to súbor vbaProject.bin; v staršom súbore BIFF je to úložisko OLE. Existujú presne dva spôsoby, ako ho stratiť: zapisovač ho neskopíruje do výstupu, alebo výstup získa typ súboru, ktorý ho zakazuje. Obe zlyhania sú úplné a tiché. Projekt je buď prítomný, alebo nie.
Externý odkaz nie je binárny objekt. Je to malá sieť vzťahov: cieľová cesta alebo URL smerujúce na iný zošit, zoznam názvov hárkov, ktoré tento cieľ sprístupňuje, a voliteľná vyrovnávacia pamäť hodnôt naposledy videných v týchto hárkoch, aby Excel mohol niečo zobraziť, keď je cieľ offline. Tieto tri časti majú pri prepise rôznu životnosť a knižnica môže niektoré verne zachovať, zatiaľ čo iné ticho zahodí. Táto nesymetria je časť, ktorú si treba upresniť, pretože kód na úpravu buniek ju nijako neprejaví.
Prenos projektu VBA cez prepis XLSX
Na strane XLSX uchováva trieda TXLSXWorkbook obsah makier doslovne. Vlastnosť VbaProject obsahuje surové bajty súboru vbaProject.bin v reťazci typu AnsiString a prázdny reťazec vyjadruje absenciu makier v modeli. Okolo nej existujú tri operácie: metóda HasVbaProject overuje prítomnosť projektu, ClearVbaProject ho zámerne odstraňuje a LoadVbaProjectFromFile ho importuje zo súboru šablóny. Posledné volanie má väčší význam, než sa zdá. Umožňuje generovaným zošitom prevziať štandardný projekt makier bez nutnosti ťahať celú šablónu cez proces spracovania.
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;
Ukladanie je moment, kedy sa rozhoduje o celom probléme. Zošit obsahujúci projekt VBA musí byť zapísaný s podporou makier a HotXLS ju aplikuje, keď cieľový názov končí príponou .xlsm. Ak namiesto toho zadáte .xlsx, Excel makrá odmietne, hoci bajty sú fyzicky prítomné v balíku a dešifrovali by sa v poriadku. Prípona nie je len okrasná; vyberá typ obsahu, ktorý Excelu hovorí, že projekt VBA má právo existovať. Väčšinu času potrebujete iba preniesť obsah. Keď doň potrebujete vidieť, napríklad na výpis názvov modulov pre audit, vlastnosť ParsedVBAProject sprístupňuje model modulov, zatiaľ čo VbaProject zostáva ako pôvodné nedotknuté bajty.
Opätovné použitie makier zo starších zošitov XLS
Rozhranie BIFF zrkadlí túto sadu nástrojov s jedným krokom navyše. Metóda HasVBAProject preveruje načítaný súbor, SaveVBAProjectToFile zapisuje úložisko projektu na disk a LoadVBAProjectFromFile ho načíta späť do iného zošita. Odbočka cez súbor zjednodušuje bežnú modernizačnú úlohu: vyberie makrá z modelu z roku 2003 a vloží ich do novo vygenerovaného výstupu XLS bez potreby pôvodnej šablóny za behu programu.
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;
Pamäťový model je tu pascou a beží opačne k triede XLSX. Knižnica TXLSWorkbook sa drží cez počítané rozhranie IXLSWorkbook, takže ju nikdy neuvoľňujete ručne; naopak, XLSX trieda TXLSXWorkbook je bežný objekt, ktorý musíte zabaliť do try..finally a uvoľniť. Zmiešajte obe konvencie v jednej jednotke a nasleduje pád pre dvojité uvoľnenie (double-free). Ešte jedno obmedzenie stojí za rešpekt: udržiavajte extrakciu a injekciu v rámci jedného formátu súborov. Úložisko projektov BIFF a súbor vbaProject.bin pre OOXML sú príbuzné, no nie identické kontajnery, a proces generujúci makrá v oboch formátoch by si mal udržiavať samostatnú makro šablónu pre každý z nich.
Externé odkazy: mapa prežije, uložené hodnoty nie
Pre zošity XLSX sprístupňuje HotXLS externé odkazy cez kolekciu ExternalLinks. Každý objekt TXLSXExternalLink nesie vlastnosť Target (cestu alebo URL k vzdialenému zošitu) a zoznam SheetNames s názvami hárkov, na ktoré odkazuje. Obe prežívajú cyklus otvorenia a uloženia bez zmeny, a odkaz môžete vytvoriť aj od nuly:
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;
Hranica leží o úroveň hlbšie ako zoznam cieľov. HotXLS prenáša mapu odkazov (teda cieľ a názvy hárkov), ale neanalyzuje ani neprepisuje vyrovnávaciu pamäť buniek, ktorú OOXML uchováva v elemente sheetDataSet odkazu. Táto pamäť je tým, čo Excelu umožňuje zobraziť poslednú známu hodnotu, keď je zdrojový súbor nedostupný, a vygenerovaný zošit ju neobsahuje. Dôsledok padá na príjemcu, nie na vás. Otvorte takýto súbor v prostredí, kde je cieľ nedostupný (napríklad notebook mimo VPN alebo premenovaný zdieľaný priečinok), a vzorce závislé od odkazu vrátia chybu #REF! alebo sa zaseknú na výzve na aktualizáciu. Z toho vyplývajú dve pravidlá. Nesľubujte, že vygenerovaný zošit bude zobrazovať externe prepojené hodnoty offline. A nenulovú hodnotu ExternalLinks.Count berte ako podmienku doručenia než ako vlastnosť: každý cieľ musí byť dosiahnuteľný z miesta, kde sa súbor reálne otvorí.
Čo čítačka XLS zachováva bajt po bajte
Pre štruktúry, ktoré nemodeluje, má strana BIFF inej odpoveď: ponechá ich presne v stave, v akom ich našla. Vyrovnávacie pamäte a zobrazenia kontingenčných tabuliek (rodina záznamov SX*), definície QueryTable, externé dátové pripojenia, vlastné zobrazenia, obrázky v hlavičkách a záznamy tém – to všetko prechádza cyklom otvorenia a uloženia ako surové bloky záznamov, neanalyzované a nezmenené. Samotné externé odkazy prechádzajú cez podkladové záznamy EXTERNSHEET a SupBook. Na strane XLS neexistuje typované API na ich vytváranie, no existujúci odkaz prežije úpravy bez zásahu.
Zachovanie bajt po bajte je spoľahlivou zárukou s ostrou hranou. Keďže žiadny mechanizmus preskakovanú štruktúru nečíta, vaše úpravy ju nemôžu poškodiť. Z rovnakého dôvodu ju však nič ani neaktualizuje. Vložte riadky cez oblasť, na ktorú ukazuje zachovaná kontingenčná tabuľka alebo dopyt, a štruktúra si ponechá svoje pôvodné súradnice, kým dáta pod ňou sa posúdu. Súbor je stále platným XML alebo BIFF; jeho význam sa však ticho posunul mimo súlad a žiadna chyba sa neohlási. Bezpečné rozloženie spočíva v udržiavaní generovaných úprav na hárkoch, ktoré neobsahujú žiadne zachovávané štruktúry, čo je rovnaká disciplína, aká chráni uzamknuté hárky a nastavenia tlače popísané v článku o ochrane hárkov a nastavení stránky.
Overenie súboru, ktorý ste reálne zapísali
Oba chybové stavy sú pri zápise tiché, takže overenie, na ktorom záleží, spočíva v opätovnom otvorení výstupu a nie v dôvere kódu, ktorý ho vyprodukoval. Tri kontroly pokrývajú takmer všetko. Otvorte súbor znova a overte, že HasVbaProject stále vracia true všade, kde sa makrá očakávali, čo zachytí stratený obsah aj nesprávnu príponu v jednom teste. Prečítajte vlastnosť ExternalLinks.Count a porovnajte ju s počtom pred prepisom. Potom otvorte súbor v Exceli so zakázanými makrami, pretože validácia typov obsahu v Exceli je prísnejšia ako v akejkoľvek knižnici a Excel je program, podľa ktorého budú zákazníci súbor hodnotiť.
Nič z toho nevyžaduje plnú analýzu pri vstupe. Keď zošity prichádzajú vo veľkých objemoch a potrebujete iba roztriediť tie, ktoré nesú riadený obsah, ľahká kontrola popísaná v článku o výpise hárkov a kontrole zošitov vám umožní nasmerovať súbory s makrami a odkazmi na prísnejšiu cestu ešte pred spustením prvého prepisu.
Niekoľko otázok sa objavuje dostatočne často na to, aby sme na ne odpovedali priamo. HotXLS nikdy nespúšťa makrá, ktoré zachováva: v knižnici nie je žiadne runtime prostredie VBA, iba mechanizmus na uloženie, kopírovanie, extrakciu a vloženie projektu ako dát. Na serveri ide o dôležitá bezpečnostnú vlastnosť, keďže škodlivé makro prechádzajúce systémom zostáva neaktívne, kým používateľ neotvorí súbor v desktopovom Exceli a nepovolí obsah. Prevod .xlsm na .xlsx so zachovaním makier nie je možný a ide o pravidlo formátu, nie o obmedzenie knižnice: typ obsahu .xlsx deklaruje zošit bez makier, takže jediné správne riešenia sú ponechať formát .xlsm alebo zavolať ClearVbaProject a odoslať súbor, ktorý žiadne nemá. Tiché premenovanie je voľba, ktorá neuspokojí nikoho. A keď prepojené bunky po prepise zobrazujú chybu #REF!, príčinou je chýbajúca vyrovnávacia pamäť hodnôt popísaná vyššie: nový súbor nesie cieľ, ale nie uložené čísla, takže Excel musí zdroj vyhodnotiť pri otvorení a nedostupná alebo relatívna cesta ho porazí. Buď zaručte, že cieľ je dostupný, alebo zapíšte vypočítané hodnoty priamo do buniek pred doručením a závislosť úplne zrušte.
Úprava zošitov iných ľudí je z veľkej časti o zachovaní vecí, ktoré ste nenapísali a ktorým plne nerozumiete. Možnosti prenosu makier VBA a externých odkazov popísané v tomto článku sa dodávajú s komponentom HotXLS Component pre Delphi a C++Builder spolu s auditnými vlastnosťami, ktoré vám umožnia detegovať riadený obsah v momente, keď súbor dorazí.