Představte si úlohu, která nedělá téměř nic: otevře měsíční workbook, zapíše do jedné buňky dnešní datum, uloží ho zpět. Pusťte to přes službu dostatečně často a stejně dorazí stížnost. Makra zmizela, nebo propojené směnné kurzy teď ukazují #REF!, a provozní tým je přesvědčen, že je váš kód smazal. Nic nesmazal. Obvykle se stalo to, že workbook s podporou maker odešel pod obyčejným názvem s příponou .xlsx, a Excel se řídil pravidly typu obsahu podle ECMA-376: balíček, jehož typ obsahu neuvádí VBA, nemůže načíst projekt VBA, bez ohledu na to, jestli tam bajty fyzicky leží. Soubor se nerozbil. Byl přejmenován do stavu, ve kterém Excel musí část jeho obsahu ignorovat
Makra a externí odkazy mezi workbooky jsou dvě věci, které automatizace ztrácí nejspolehlivěji, a to ze stejného základního důvodu. Obě žijí mimo mřížku buněk, které se editační kód skutečně dotýká, takže kód uvažující v pojmech řádků a sloupců je vyřadí, aniž by kdy vydal příkaz ke smazání. HotXLS je nativní knihovna pro Delphi a C++Builder, která čte a zapisuje XLS a XLSX bez nainstalovaného Excelu, a k oběma aktivům přistupuje jako k payloadu, který nese záměrně, ne jako k datům, která náhodou zkopírovala. Dále se podíváme na to, co každé z nich potřebuje od vaší ukládací cesty a kde končí záruky
Proč se tato dvě aktiva chovají při přepisu odlišně
Projekt VBA je jeden neprůhledný binární blok. V balíčku OOXML je to soubor vbaProject.bin; ve starším souboru BIFF je to úložiště OLE. Existují přesně dva způsoby, jak o něj přijít: writer ho nikdy nezkopíruje do výstupu, nebo výstup dostane typ souboru, který ho zakazuje. Obojí selhání je úplné a tiché. Projekt buď je přítomný, nebo není
Externí odkaz naopak žádný blob není. Je to malý graf vztahů: cílová cesta nebo URL ukazující na jiný workbook, seznam názvů listů, které tento cíl vystavuje, a volitelná mezipaměť hodnot naposledy viděných v těchto listech, aby Excel mohl něco zobrazit, když je cíl nedostupný. Tyto tři části mají při přepisu různou životnost, a knihovna může některé věrně zachovat, zatímco jiné potichu vypustí. Právě tato asymetrie stojí za to mít přesně ujasněnou, protože ji kód pro editaci buněk nikdy sám neodhalí
Přenesení projektu VBA přes přepis XLSX
Na straně XLSX drží TXLSXWorkbook payload maker doslova beze změny. Vlastnost VbaProject obsahuje syrové bajty vbaProject.bin uvnitř AnsiString, a prázdný řetězec je způsob, jakým model říká, že žádná makra nejsou. Kolem toho sedí tři operace: HasVbaProject odpoví, zda je projekt přítomný, ClearVbaProject ho záměrně odstraní a LoadVbaProjectFromFile vloží ten, který byl extrahován ze šablony. Poslední z těchto volání je cennější, než na první pohled vypadá. Umožňuje vygenerovaným workbookům převzít standardní projekt maker, aniž by bylo nutné celou šablonu tahat celým pipeline
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');
// Přípona .xlsm není kosmetická: vybírá tím
// typ obsahu s podporou maker uvnitř balíčku.
Book.SaveAs('monthly-report.xlsm');
finally
Book.Free;
end;
end;
Řádek s uložením je místo, kde se celý problém zlomí. Workbook obsahující projekt VBA musí být zapsán se sémantikou pro podporu maker, a HotXLS ji použije, když cílový název končí na .xlsm. Dáte-li mu místo toho .xlsx, Excel makra odmítne, přestože jsou bajty v balíčku fyzicky přítomné a v pořádku by se deserializovaly. Přípona není dekorace; vybírá typ obsahu, který Excelu říká, že projekt VBA smí existovat. Většinu času potřebujete jen payload přenést. Když do něj potřebujete nahlédnout, řekněme kvůli výpisu názvů modulů pro auditní report, ParsedVBAProject vystaví rozparsovaný model modulů, zatímco VbaProject zůstává původními nedotčenými bajty
Opakované použití maker ze starších workbooků XLS
Fasáda BIFF odráží stejnou sadu nástrojů s jedním krokem navíc. HasVBAProject zjistí u načteného souboru, zda projekt existuje, SaveVBAProjectToFile zapíše úložiště projektu na disk a LoadVBAProjectFromFile ho znovu načte zpět do jiného workbooku. Tato oklika přes soubor zjednodušuje běžnou modernizační úlohu: přenést makra ze starého modelu z roku 2003 a vsadit je do čerstvě vygenerovaného výstupu XLS, aniž by byla za běhu potřeba původní šablona
var
Src, Dst: IXLSWorkbook; // reference přes rozhraní: bez ručního 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;
Past je tady v paměťovém modelu, a jde přesně opačným směrem než u třídy XLSX. TXLSWorkbook se drží přes referenčně počítané rozhraní IXLSWorkbook, takže ho nikdy neuvolňujete ručně; XLSX TXLSXWorkbook je naopak obyčejný objekt, který musíte obalit do try..finally a uvolnit. Smíchejte obě konvence v jedné jednotce a následují pády kvůli dvojitému uvolnění. Ještě jedna hranice, kterou stojí za to respektovat: udržujte extrakci a vkládání uvnitř jednoho formátu souboru. Úložiště projektu BIFF a OOXML vbaProject.bin jsou bratranci, ne stejný kontejner, a pipeline, která musí emitovat makra v obou formátech, by měla mít pro každý z nich vlastní šablonu maker
Externí odkazy: mapa přežije, uložené hodnoty ne
U workbooků XLSX vystavuje HotXLS externí odkazy přes kolekci ExternalLinks. Každý TXLSXExternalLink nese Target, tedy cestu nebo URL vzdáleného workbooku, a k tomu seznam SheetNames pojmenovávající listy, na které odkazuje. Obojí přežije cyklus otevření a uložení neporušené, a odkaz můžete sestavit i 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;
Hranice leží o úroveň hlouběji než seznam cílů. HotXLS provede round-trip mapy odkazů, tedy cíle a názvů listů, ale neparsuje ani nepřepisuje uložené hodnoty buněk, které OOXML drží v elementu sheetDataSet odkazu. Právě tato mezipaměť umožňuje Excelu zobrazit naposledy známé číslo, když je zdrojový soubor nedostupný, a vygenerovaný workbook se posílá bez ní. Důsledek dopadne na příjemce, ne na vás. Otevřete-li takový soubor tam, kde je cíl nedosažitelný, notebook mimo VPN nebo přejmenované sdílení, vzorce závislé na odkazu se vyhodnotí na #REF! nebo se zaseknou za výzvou k aktualizaci. Z toho plynou dvě pravidla. Neslibujte, že vygenerovaný workbook zobrazí své externě propojené hodnoty offline. A nenulovou hodnotu ExternalLinks.Count berte jako podmínku doručení, ne jako funkci: každý cíl musí být dosažitelný odkudkoli, kde bude soubor skutečně otevřen
Co čtečka XLS zachovává bajt po bajtu
U struktur, které nemodeluje, má strana BIFF jinou odpověď: nechat je přesně tak, jak byly nalezeny. Mezipaměti a zobrazení kontingenčních tabulek (rodina záznamů SX*), definice QueryTable, externí datová připojení, vlastní zobrazení, obrázky v záhlaví a záznamy motivů - to vše projde cyklem otevření a uložení jako syrové bloky záznamů, nerozparsované a neupravené. Samotné externí reference proběhnou round-tripem skrz podkladové záznamy EXTERNSHEET a SupBook. Na straně XLS pro ně neexistuje typované API pro vytváření, ale existující odkaz přežije editaci nedotčený
Zachování bajt po bajtu je skutečná záruka, ale s ostrou hranou. Protože nic zachovanou strukturu nečte, vaše úpravy ji nemohou poškodit. Ze stejného důvodu ji ale ani nic neaktualizuje. Vložte řádky přes oblast, na kterou ukazuje zachovaná mezipaměť kontingenční tabulky nebo dotazová tabulka, a struktura si podrží své původní souřadnice, zatímco data pod ní se posunou. Soubor je stále platné XML nebo BIFF; význam se ale potichu vychýlil z rovnováhy a žádná chyba vás na to neupozorní. Obhajitelné řešení je držet vygenerované úpravy na listech, které neobsahují žádné zachované struktury, což je stejná disciplína, jaká chrání zamčené a tiskově nastavené listy v našem článku o ochraně listů a nastavení stránky
Ověření souboru, který jste skutečně zapsali
Oba způsoby selhání jsou při zápisu tiché, takže na tvrzení, na kterém záleží, se přijde až opětovným otevřením výstupu, ne důvěrou v kód, který ho vyprodukoval. Téměř vše pokryjí tři kontroly. Znovu otevřete soubor a ověřte, že HasVbaProject stále vrací true všude tam, kde se makra očekávala, což jedním testem odhalí zahozený payload i špatnou příponu. Přečtěte ExternalLinks.Count a porovnejte ho s počtem před přepisem. Pak soubor jednou otevřete v Excelu s vypnutými makry, protože ověřování typu obsahu v Excelu je přísnější než u kterékoli knihovny, a právě podle Excelu budou soubor posuzovat vaši zákazníci
Nic z toho nevyžaduje na vstupu plný parsing. Když workbooky přicházejí v objemu a vy jen potřebujete roztřídit, které z nich nesou regulovaný obsah, lehké sondování popsané v našem článku o výpisu listů a lehké inspekci workbooků vám umožní nasměrovat soubory s makry a odkazy do přísnějšího pipeline dřív, než vůbec proběhne první přepis
Pár otázek se objevuje dost často na to, aby stálo za to je zodpovědět přímo. HotXLS makra, která zachovává, nikdy nespouští: v knihovně není žádný runtime VBA, jen mechanismus pro ukládání, kopírování, extrakci a vkládání projektu jako dat. Na serveru je to bezpečnostní vlastnost, kterou stojí za to zmínit, protože nepřátelské makro procházející pipeline zůstává neaktivní, dokud soubor neotevře desktopový Excel a uživatel v něm nepovolí obsah. Převést .xlsm na .xlsx a přitom si ponechat makra není možné, a je to pravidlo formátu, ne omezení knihovny: typ obsahu .xlsx deklaruje workbook bez maker, takže jedinými poctivými výsledky jsou zůstat u .xlsm, nebo zavolat ClearVbaProject a doručit soubor, který makra skutečně nemá. Tiché přejmenování je jediná volba, která neuspokojí nikoho. A když propojené buňky po přepisu ukážou #REF!, příčinou je chybějící mezipaměť hodnot popsaná výše: nový soubor nese cíl, ale ne uložená čísla, takže Excel musí zdroj vyřešit až při otevření, a nedosažitelná nebo na prostředí závislá cesta ho porazí. Buď zaručte, že cíl bude dosažitelný, nebo před doručením zapište do buněk vypočítané hodnoty a závislost zcela odstraňte
Editace cizích workbooků je většinou práce spočívající v zachování věcí, které jste nenapsali a plně jim nerozumíte. Zde popsané mechanismy round-tripu pro VBA a externí odkazy jsou součástí HotXLS Delphi Component pro Delphi a C++Builder, spolu s auditními vlastnostmi, které umožňují odhalit regulovaný obsah ve chvíli, kdy soubor dorazí