Predstavte si úlohu, ktorá nerobí takmer nič: otvorí mesačný zošit, zapíše dnešný dátum do jednej bunky a uloží ho späť. Nechajte to bežať v službe dostatočne často a sťažnosť aj tak príde. Makrá sú preč alebo prepojené výmenné kurzy zrazu ukazujú #REF! a prevádzkový tím je presvedčený, že ich váš kód vymazal. Nič nevymazal. Zvyčajne sa stalo to, že zošit s podporou makier odišiel pod obyčajným názvom .xlsx a Excel poslúchol pravidlá typov obsahu podľa ECMA-376: balík, ktorého typ obsahu žiadne VBA nedeklaruje, nesmie projekt VBA načítať bez ohľadu na to, či tam bajty ležia alebo nie. Súbor sa nepokazil. Bol premenovaný do stavu, v ktorom je Excel povinný časť z neho ignorovať
Makrá a externé odkazy na zošity sú dve veci, ktoré automatizácia stráca najspoľahlivejšie, a to z rovnakého základného dôvodu. Obe žijú mimo mriežky buniek, ktorej sa upravujúci kód skutočne dotýka, takže kód uvažujúci v riadkoch a stĺpcoch ich zahodí bez toho, aby vôbec vydal príkaz na vymazanie. HotXLS je natívna knižnica pre Delphi a C++Builder, ktorá číta a zapisuje XLS a XLSX bez nainštalovaného Excelu, a s oboma týmito prostriedkami zaobchádza ako s nákladom, ktorý nesie zámerne, nie ako s dátami, ktoré náhodou skopíruje. Nasleduje to, čo každý z nich od vašej cesty ukladania potrebuje, a kde záruky končia
Prečo sa tieto dva prostriedky pri prepise správajú odlišne
Projekt VBA je jeden nepriehľadný binárny blok. 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 oň prísť: zapisovač ho do výstupu nikdy neskopíruje, alebo výstup dostane typ súboru, ktorý ho zakazuje. Oba spôsoby zlyhania sú úplné a tiché. Projekt tam buď je, alebo nie je
Externý odkaz nie je binárny blok vôbec. Je to malý graf vzťahov: cieľová cesta alebo URL ukazujúca 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ť, kým iné potichu zahodí. Práve túto asymetriu sa oplatí spresniť, pretože nič v kóde upravujúcom bunky ju na povrch nevynesie
Prenesenie projektu VBA cez prepis XLSX
Na strane XLSX si trieda TXLSXWorkbook uchováva náklad s makrami doslovne. Vlastnosť VbaProject drží surové bajty súboru vbaProject.bin vnútri typu AnsiString a prázdny reťazec je spôsob, akým model hovorí, že žiadne makrá nie sú. Okolo nej stoja tri operácie: HasVbaProject odpovie, či je projekt prítomný, ClearVbaProject ho zámerne odstráni a LoadVbaProjectFromFile vloží projekt vytiahnutý zo šablóny. Práve posledné volanie je cennejšie, než sa zdá. Umožňuje generovaným zošitom prevziať štandardný projekt makier bez toho, aby sa celý súbor šablóny musel vliecť celou linkou
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');
// Prípona .xlsm nie je kozmetická: vyberá typ obsahu
// s povolenými makrami vnútri balíka.
Book.SaveAs('monthly-report.xlsm');
finally
Book.Free;
end;
end;
Riadok s uložením je miesto, kde sa celý problém láme. Zošit obsahujúci projekt VBA musí byť zapísaný so sémantikou povolených makier a HotXLS ju uplatní vtedy, keď cieľový názov končí na .xlsm. Podajte mu namiesto toho .xlsx a Excel makrá odmietne, hoci sú bajty v balíku fyzicky prítomné a deserializovali by sa bez problémov. Prípona nie je ozdoba; vyberá typ obsahu, ktorý Excelu hovorí, že projekt VBA smie existovať. Väčšinu času potrebujete náklad iba prepraviť. Keď doň potrebujete nazrieť, napríklad vypísať názvy modulov do auditnej zostavy, vlastnosť ParsedVBAProject sprístupní rozanalyzovaný model modulov, kým VbaProject zostáva pôvodnými nedotknutými bajtmi
Opätovné použitie makier zo starších zošitov XLS
Fasáda BIFF túto sadu nástrojov zrkadlí s jedným krokom navyše. HasVBAProject preskúma načítaný súbor, SaveVBAProjectToFile zapíše úložisko projektu na disk a LoadVBAProjectFromFile ho načíta späť do iného zošita. Obchádzka cez súbor robí bežnú modernizačnú robotu priamočiarou: vyzdvihnete makrá z modelu z čias roku 2003 a zasadíte ich do čerstvo generovaného výstupu XLS, bez potreby pôvodnej šablóny za behu
var
Src, Dst: IXLSWorkbook; // referencie na rozhranie: žiadne ručné 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;
Pascou je tu pamäťový model a ide presne proti triede pre XLSX. TXLSWorkbook sa drží cez rozhranie IXLSWorkbook s počítaním referencií, takže ho nikdy neuvoľňujete ručne; XLSX trieda TXLSXWorkbook je obyčajný objekt, ktorý musíte zabaliť do try..finally a uvoľniť. Zmiešajte obe konvencie v jednej jednotke a pády z dvojitého uvoľnenia sú na svete. Ešte jedna hranica si zaslúži rešpekt: držte extrakciu aj vloženie vnútri jedného formátu súboru. Úložisko projektu v BIFF a súbor vbaProject.bin v OOXML sú príbuzní, nie ten istý kontajner, a linka, ktorá musí makrá vydávať v oboch formátoch, by si mala držať samostatnú šablónu makier pre každý z nich
Externé odkazy: mapa prežije, uložené hodnoty nie
Pri zošitoch XLSX sprístupňuje HotXLS externé odkazy cez kolekciu ExternalLinks. Každý TXLSXExternalLink nesie vlastnosť Target, teda cestu alebo URL vzdialeného zošita, plus zoznam SheetNames pomenúvajúci hárky, na ktoré sa odkazuje. Oboje prežije cyklus otvorenia a uloženia neporušené a odkaz môžete zostaviť 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 než zoznam cieľov. HotXLS prenesie mapu odkazu, teda cieľ a názvy hárkov, ale neanalyzuje ani neprepisuje uložené hodnoty buniek, ktoré OOXML drží v prvku sheetDataSet daného odkazu. Práve táto vyrovnávacia pamäť umožňuje Excelu ukázať posledné známe číslo vtedy, keď je zdrojový súbor offline, a generovaný zošit odchádza bez nej. Dôsledok dopadne na príjemcu, nie na vás. Otvorte taký súbor tam, kde je cieľ nedosiahnuteľný, na notebooku mimo VPN alebo na zdieľaní, ktoré niekto premenoval, a vzorce závislé od odkazu sa vyriešia na #REF! alebo uviaznu za výzvou na aktualizáciu. Vyplývajú z toho dve pravidlá. Nesľubujte, že generovaný zošit zobrazí svoje externe prepojené hodnoty offline. A nenulovú hodnotu ExternalLinks.Count čítajte skôr ako podmienku doručenia než ako funkciu: každý cieľ musí byť dosiahnuteľný odtiaľ, kde sa súbor bude naozaj otvárať
Čo čítačka XLS zachová bajt po bajte
Pri štruktúrach, ktoré nemodeluje, má strana BIFF inú odpoveď: nechať ich presne tak, ako ich našla. Vyrovnávacie pamäte a pohľady kontingenčných tabuliek (rodina záznamov SX*), definície QueryTable, externé dátové pripojenia, vlastné pohľady, obrázky v hlavičke aj záznamy motívov prejdú cyklom otvorenia a uloženia ako surové bloky záznamov, nerozanalyzované a nezmenené. Samotné externé odkazy prežijú cyklus cez podkladové záznamy EXTERNSHEET a SupBook. Typové rozhranie na ich vytváranie na strane XLS neexistuje, ale existujúci odkaz úpravy prežije nedotknutý
Zachovanie bajt po bajte je skutočná záruka s ostrou hranou. Keďže zachovanú štruktúru nikto nečíta, vaše úpravy ju nemôžu poškodiť. Z toho istého dôvodu ju však nikto ani neaktualizuje. Vložte riadky do oblasti, na ktorú ukazuje zachovaná vyrovnávacia pamäť kontingenčnej tabuľky alebo tabuľka dotazu, a štruktúra si podrží pôvodné súradnice, kým sa dáta pod ňou posunú. Súbor je stále platné XML alebo BIFF; význam sa potichu vychýlil a žiadna chyba vás na to neupozorní. Obhájiteľné usporiadanie je držať generované úpravy na hárkoch, ktoré žiadne zachované štruktúry neobsahujú, čo je tá istá disciplína, ktorá chráni uzamknuté a na tlač nakonfigurované hárky v našom článku o ochrane hárkov a nastavení strany
Overenie súboru, ktorý ste skutočne zapísali
Oba režimy zlyhania sú pri zápise tiché, takže tvrdenie, na ktorom záleží, vzniká opätovným otvorením výstupu, nie dôverou v kód, ktorý ho vyprodukoval. Tri kontroly pokryjú takmer všetko. Znovu otvorte súbor a potvrďte, že HasVbaProject stále vracia true vždy, keď sa makrá očakávali, čo jediným testom odhalí zahodený náklad aj nesprávnu príponu. Prečítajte ExternalLinks.Count a porovnajte ho s počtom spred prepisu. Potom súbor raz otvorte v Exceli so zakázanými makrami, pretože kontrola typov obsahu v Exceli je prísnejšia než v ktorejkoľvek knižnici a Excel je programom, podľa ktorého budú súbor posudzovať vaši zákazníci
Nič z toho nevyžaduje úplnú analýzu na vstupe. Keď zošity prichádzajú vo veľkých objemoch a vy potrebujete iba roztriediť, ktoré nesú riadený obsah, ľahké preskúmanie opísané v našom článku o výpise hárkov a nenáročnej kontrole zošitov vám umožní nasmerovať súbory s makrami a odkazmi do prísnejšej linky ešte pred prvým prepisom
Niekoľko otázok sa opakuje dosť často na to, aby si zaslúžili priamu odpoveď. HotXLS makrá, ktoré zachováva, nikdy nespúšťa: v knižnici nie je žiadny runtime VBA, len mechanika na uloženie, kopírovanie, vytiahnutie a vloženie projektu ako dát. Na serveri je to bezpečnostná vlastnosť, ktorú sa oplatí povedať nahlas, keďže nepriateľské makro prechádzajúce linkou zostáva nečinné dovtedy, kým súbor neotvorí desktopový Excel a používateľ nepovolí obsah. Konverzia .xlsm na .xlsx so zachovaním makier možná nie je a je to pravidlo formátu, nie obmedzenie knižnice: typ obsahu .xlsx deklaruje zošit bez makier, takže jediné čestné východiská sú zostať pri .xlsm alebo zavolať ClearVbaProject a dodať súbor, ktorý ich naozaj nemá. Tiché premenovanie je jediná voľba, ktorá neuspokojí nikoho. A keď prepojené bunky po prepise ukazujú #REF!, príčinou je chýbajúca vyrovnávacia pamäť hodnôt opísaná vyššie: nový súbor nesie cieľ, ale nie uložené čísla, takže Excel musí zdroj vyriešiť pri otvorení a nedosiahnuteľná alebo od prostredia závislá cesta ho porazí. Buď zaručte, že cieľ bude dosiahnuteľný, alebo pred doručením zapíšte vypočítané hodnoty do buniek a závislosť úplne zrušte
Úprava cudzích zošitov je väčšinou prácou na zachovaní vecí, ktoré ste nenapísali a úplne im nerozumiete. Prostriedky na bezstratový prenos VBA a externých odkazov opísané tu sa dodávajú s komponentom HotXLS Delphi Component pre Delphi a C++Builder, spolu s auditnými vlastnosťami, ktoré vám umožnia odhaliť riadený obsah hneď, ako súbor dorazí