Denk aan een taak die vrijwel niets doet: open een maandelijkse werkmap, schrijf de datum van vandaag in één cel, sla hem weer op. Laat dat vaak genoeg door een service lopen en er komt toch een klacht. De macro's zijn weg, of de gekoppelde wisselkoersen tonen nu #REF!, en de operationele afdeling is ervan overtuigd dat uw code ze heeft verwijderd. Er is niets verwijderd. Wat er meestal gebeurde, is dat een werkmap met macro's onder een gewone .xlsx-naam naar buiten ging, en Excel gehoorzaamde de regels voor inhoudstypen uit ECMA-376: een pakket waarvan het inhoudstype geen VBA verklaart, kan geen VBA-project laden, ongeacht of de bytes er gewoon in staan. Het bestand ging niet stuk. Het is hernoemd naar een toestand waarin Excel verplicht is een deel ervan te negeren
Macro's en koppelingen naar externe werkmappen zijn de twee zaken die automatisering het betrouwbaarst kwijtraakt, om dezelfde onderliggende reden. Beide leven buiten het celraster dat bewerkende code daadwerkelijk aanraakt, dus code die in rijen en kolommen redeneert, laat ze vallen zonder ooit een verwijdering uit te voeren. HotXLS is een native Delphi- en C++Builder-bibliotheek die XLS en XLSX leest en schrijft zonder dat Excel is geïnstalleerd, en behandelt beide activa als lading die hij bewust meedraagt in plaats van gegevens die hij toevallig kopieert. Wat volgt, is wat elk van beide van uw opslagpad nodig heeft, en waar de garanties ophouden
Waarom deze twee activa zich anders gedragen bij een herschrijving
Een VBA-project is één ondoorzichtig binair blok. In een OOXML-pakket is het het bestand vbaProject.bin; in een klassiek BIFF-bestand is het een OLE-opslag. Er zijn precies twee manieren om het kwijt te raken: de schrijver kopieert het nooit naar de uitvoer, of de uitvoer krijgt een bestandstype dat het verbiedt. Beide storingen zijn totaal en stil. Het project is aanwezig of het is dat niet
Een externe koppeling is helemaal geen blob. Het is een kleine graaf van relaties: een doelpad of URL dat naar een andere werkmap wijst, de lijst met bladnamen die dat doel blootstelt, en een optionele cache van de waarden die het laatst in die bladen zijn gezien, zodat Excel iets kan tonen wanneer het doel offline is. Die drie delen hebben bij een herschrijving verschillende levensduren, en een bibliotheek kan sommige getrouw bewaren terwijl ze andere stilletjes laat vallen. Die asymmetrie is het deel waarover het loont precies te zijn, want niets in de celbewerkende code brengt haar aan het licht
Een VBA-project door een XLSX-herschrijving meedragen
Aan de XLSX-kant houdt TXLSXWorkbook de macrolading letterlijk vast. De eigenschap VbaProject bevat de ruwe bytes van vbaProject.bin binnen een AnsiString, en met een lege tekenreeks zegt het model dat er geen macro's zijn. Daaromheen liggen drie bewerkingen: HasVbaProject beantwoordt of er een project aanwezig is, ClearVbaProject verwijdert het met opzet, en LoadVbaProjectFromFile injecteert er een die uit een sjabloon is gehaald. Die laatste aanroep is meer waard dan hij lijkt. Hij laat gegenereerde werkmappen een standaard macroproject oppikken zonder een volledig sjabloonbestand door de pijplijn te slepen
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');
// De extensie .xlsm is niet cosmetisch: zij selecteert het
// inhoudstype met macro-ondersteuning binnen het pakket.
Book.SaveAs('monthly-report.xlsm');
finally
Book.Free;
end;
end;
De opslagregel is waar het hele probleem kantelt. Een werkmap die een VBA-project bevat, moet met macro-ondersteunende semantiek worden weggeschreven, en HotXLS past die toe wanneer de doelnaam op .xlsm eindigt. Geef hem in plaats daarvan .xlsx en Excel weigert de macro's, ook al zitten de bytes fysiek in het pakket en zouden ze prima deserialiseren. De extensie is geen versiering; zij selecteert het inhoudstype dat Excel vertelt dat een VBA-project mag bestaan. Meestal hoeft u de lading alleen mee te dragen. Moet u erin lezen, bijvoorbeeld om modulenamen voor een auditrapport op te sommen, dan legt ParsedVBAProject een geparseerd modulemodel bloot terwijl VbaProject de oorspronkelijke, onaangeroerde bytes blijft
Macro's hergebruiken uit klassieke XLS-werkmappen
De BIFF-facade spiegelt die gereedschapsset met één extra stap. HasVBAProject peilt een geladen bestand, SaveVBAProjectToFile schrijft de projectopslag naar schijf, en LoadVBAProjectFromFile leest er een terug in een andere werkmap. De omweg via een bestand maakt een veelvoorkomende moderniseringsklus overzichtelijk: til de macro's uit een model uit het 2003-tijdperk en plant ze in vers gegenereerde XLS-uitvoer, zonder dat er tijdens de uitvoering een oorspronkelijk sjabloon nodig is
var
Src, Dst: IXLSWorkbook; // interfacereferenties: geen handmatige 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;
Het geheugenmodel is hier de valstrik, en het loopt tegengesteld aan de XLSX-klasse. TXLSWorkbook wordt vastgehouden via de referentiegetelde interface IXLSWorkbook, dus u geeft hem nooit met de hand vrij; de XLSX-klasse TXLSXWorkbook is een gewoon object dat u in try..finally moet wikkelen en vrijgeven. Meng de twee conventies in één unit en crashes door dubbele vrijgave volgen. Nog een grens die respect verdient: houd extractie en injectie binnen één bestandsformaat. De BIFF-projectopslag en het OOXML-bestand vbaProject.bin zijn neven, niet dezelfde container, en een pijplijn die macro's in beide formaten moet uitleveren, houdt beter voor elk een apart macrosjabloon aan
Externe koppelingen: de kaart overleeft, de gecachte waarden niet
Voor XLSX-werkmappen stelt HotXLS externe koppelingen bloot via de verzameling ExternalLinks. Elke TXLSXExternalLink draagt een Target, het pad of de URL van de externe werkmap, plus een lijst SheetNames die de bladen benoemt waarnaar hij verwijst. Beide overleven een cyclus van openen en opslaan intact, en u kunt een koppeling ook vanaf nul opbouwen:
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;
De grens ligt één niveau dieper dan de doellijst. HotXLS laat de koppelingskaart een rondgang maken, dus het doel en de bladnamen, maar hij parseert of herschrijft de gecachte celwaarden niet die OOXML in het element sheetDataSet van de koppeling bewaart. Die cache is wat Excel in staat stelt een laatst bekend getal te tonen wanneer het bronbestand offline is, en een gegenereerde werkmap gaat er zonder de deur uit. Het gevolg landt bij de ontvanger, niet bij u. Open zo'n bestand waar het doel onbereikbaar is, een laptop buiten de VPN of een share die is hernoemd, en de formules die van de koppeling afhangen, lossen op naar #REF! of blijven achter een bijwerkprompt hangen. Daaruit vallen twee regels. Beloof niet dat een gegenereerde werkmap haar extern gekoppelde waarden offline zal tonen. En lees een ExternalLinks.Count die niet nul is als een leveringsvoorwaarde en niet als een functie: elk doel moet bereikbaar zijn vanaf de plek waar het bestand daadwerkelijk wordt geopend
Wat de XLS-lezer byte voor byte bewaart
Voor structuren die hij niet modelleert, heeft de BIFF-kant een ander antwoord: laat ze precies zoals ze zijn aangetroffen. Draaitabelcaches en draaitabelweergaven (de recordfamilie SX*), definities van QueryTable, externe gegevensverbindingen, aangepaste weergaven, koptekstafbeeldingen en themarecords passeren een cyclus van openen en opslaan alle als ruwe recordblokken, ongeparseerd en ongewijzigd. Externe verwijzingen zelf maken hun rondgang via de onderliggende records EXTERNSHEET en SupBook. Er is aan de XLS-kant geen getypeerde API om ze aan te maken, maar een bestaande koppeling overleeft het bewerken onaangeroerd
Bewaring byte voor byte is een echte garantie met een scherpe rand. Omdat niets een bewaarde structuur leest, kunnen uw bewerkingen haar niet beschadigen. Om precies dezelfde reden werkt niets haar ook bij. Voeg rijen in door een gebied waar een bewaarde draaitabelcache of querytabel naar wijst, en de structuur houdt haar oorspronkelijke coördinaten vast terwijl de gegevens eronder verschuiven. Het bestand is nog steeds geldige XML of BIFF; de betekenis is stilletjes uit het lood gelopen, en geen enkele fout gaat af om het u te vertellen. De verdedigbare indeling is om gegenereerde bewerkingen te houden op bladen die geen bewaarde structuren bevatten, dezelfde discipline die vergrendelde en voor afdrukken geconfigureerde bladen beschermt in ons artikel over werkbladbeveiliging en pagina-instellingen
Het bestand verifiëren dat u werkelijk hebt geschreven
Beide faalwijzen zijn stil op het moment van schrijven, dus de bewering die telt, wordt gedaan door de uitvoer opnieuw te openen in plaats van de code te vertrouwen die haar produceerde. Drie controles dekken vrijwel alles. Heropen het bestand en bevestig dat HasVbaProject nog steeds waar teruggeeft wanneer er macro's werden verwacht, wat een gedropte lading en een verkeerde extensie in één test opvangt. Lees ExternalLinks.Count en vergelijk die met het aantal van vóór de herschrijving. Open het bestand daarna eenmaal in Excel met macro's uitgeschakeld, want de validatie van inhoudstypen in Excel is strenger dan die van welke bibliotheek dan ook, en Excel is het programma waarmee uw klanten het bestand zullen beoordelen
Niets daarvan vereist een volledige parse aan de invoerkant. Wanneer werkmappen in volume binnenkomen en u alleen hoeft te sorteren welke beheerde inhoud dragen, laat het lichtgewicht peilen in ons artikel over bladlijsten en lichtgewicht werkmapinspectie u bestanden met macro's en koppelingen naar een strengere pijplijn leiden nog voordat de eerste herschrijving draait
Een paar vragen komen vaak genoeg terug om ze direct te beantwoorden. HotXLS voert de macro's die hij bewaart nooit uit: er zit geen VBA-runtime in de bibliotheek, alleen het machinewerk om het project als gegevens op te slaan, te kopiëren, te extraheren en te injecteren. Op een server is dat een beveiligingseigenschap die het benoemen waard is, want een vijandige macro die door de pijplijn gaat, blijft inert tot een Excel op de desktop het bestand opent en een gebruiker inhoud inschakelt. Een .xlsm naar .xlsx converteren en de macro's behouden kan niet, en dat is de regel van het formaat en geen beperking van de bibliotheek: het inhoudstype .xlsx verklaart een werkmap zonder macro's, dus de enige eerlijke uitkomsten zijn .xlsm blijven of ClearVbaProject aanroepen en een bestand leveren dat er werkelijk geen heeft. De stille hernoeming is de ene keuze die niemand tevredenstelt. En wanneer gekoppelde cellen na een herschrijving #REF! tonen, ligt de oorzaak bij de ontbrekende waardecache die hierboven is besproken: het nieuwe bestand draagt het doel maar niet de gecachte getallen, dus Excel moet de bron bij het openen oplossen, en een onbereikbaar of omgevingsafhankelijk pad maakt dat onmogelijk. Garandeer ofwel dat het doel bereikbaar is, ofwel schrijf berekende waarden vóór levering in de cellen en laat de afhankelijkheid volledig vallen
Werkmappen van anderen bewerken is grotendeels het werk van dingen bewaren die u niet hebt geschreven en niet volledig begrijpt. De hier beschreven voorzieningen voor de rondgang van VBA en externe koppelingen worden geleverd met de HotXLS Delphi-component voor Delphi en C++Builder, samen met de auditeigenschappen waarmee u beheerde inhoud kunt detecteren zodra een bestand binnenkomt