Luați o sarcină care nu face aproape nimic: deschide un registru lunar, scrie data de azi într-o celulă și îl salvează înapoi. Treceți asta printr-un serviciu de destule ori și tot sosește o reclamație. Macrocomenzile au dispărut sau cursurile valutare legate arată acum #REF!, iar echipa de operațiuni este convinsă că le-a șters codul dumneavoastră. Nu a șters nimic. Ce s-a întâmplat de obicei este că un registru cu macrocomenzi a ieșit sub un nume simplu .xlsx, iar Excel a respectat regulile de tip de conținut din ECMA-376: un pachet al cărui tip de conținut nu declară VBA nu poate încărca un proiect VBA, indiferent dacă octeții stau chiar acolo. Fișierul nu s-a stricat. A fost redenumit într-o stare în care Excel este obligat să ignore o parte din el
Macrocomenzile și legăturile către registre externe sunt cele două lucruri pe care automatizarea le pierde cel mai constant, din același motiv de fond. Amândouă trăiesc în afara grilei de celule pe care o atinge de fapt codul de editare, deci un cod care raționează în termeni de rânduri și coloane le va pierde fără să emită vreodată o ștergere. HotXLS este o bibliotecă nativă pentru Delphi și C++Builder care citește și scrie XLS și XLSX fără Excel instalat, și tratează ambele active ca pe niște încărcături pe care le poartă deliberat, nu ca pe date pe care se întâmplă să le copieze. Urmează ce are nevoie fiecare dintre ele de la traseul dumneavoastră de salvare și unde se opresc garanțiile
De ce se comportă diferit aceste două active la o rescriere
Un proiect VBA este un singur binar opac. Într-un pachet OOXML este fișierul vbaProject.bin; într-un fișier BIFF clasic este o stocare OLE. Există exact două feluri de a-l pierde: scriitorul nu îl copiază niciodată în ieșire, sau ieșirea primește un tip de fișier care îl interzice. Oricare dintre eșecuri este total și silențios. Proiectul este prezent sau nu este
O legătură externă nu este deloc un bloc binar. Este un mic graf de relații: o cale sau un URL țintă care indică spre alt registru de lucru, lista numelor de foi pe care le expune acea țintă și un cache opțional al valorilor văzute ultima dată în acele foi, ca Excel să poată arăta ceva atunci când ținta este offline. Acele trei părți au durate de viață diferite la o rescriere, iar o bibliotecă poate păstra fidel unele, aruncându-le în tăcere pe altele. Acea asimetrie este partea despre care merită să fim preciși, pentru că nimic din codul de editare a celulelor nu o va scoate la suprafață
Purtarea unui proiect VBA printr-o rescriere XLSX
Pe partea XLSX, TXLSXWorkbook păstrează încărcătura de macrocomenzi cuvânt cu cuvânt. Proprietatea VbaProject ține octeții bruți ai lui vbaProject.bin într-un AnsiString, iar un șir gol este felul în care modelul spune că nu există macrocomenzi. În jurul ei stau trei operații: HasVbaProject răspunde dacă este prezent un proiect, ClearVbaProject îl elimină intenționat, iar LoadVbaProjectFromFile injectează unul extras dintr-un șablon. Acel ultim apel valorează mai mult decât pare. El lasă registrele generate să preia un proiect de macrocomenzi standard fără să târască un fișier-șablon întreg prin lanțul de prelucrare
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');
// Extensia .xlsm nu este cosmetică: ea selectează
// tipul de conținut cu macrocomenzi din interiorul pachetului.
Book.SaveAs('monthly-report.xlsm');
finally
Book.Free;
end;
end;
Linia de salvare este locul în care se răstoarnă toată problema. Un registru care ține un proiect VBA trebuie scris cu semantică de macrocomenzi activate, iar HotXLS o aplică atunci când numele țintă se termină în .xlsm. Dați-i în schimb .xlsx și Excel refuză macrocomenzile, chiar dacă octeții sunt fizic prezenți în pachet și s-ar deserializa fără probleme. Extensia nu este decorativă; ea selectează tipul de conținut care îi spune Excelului că un proiect VBA are voie să existe. De cele mai multe ori trebuie doar să duceți încărcătura mai departe. Când trebuie să citiți în interiorul ei, să zicem ca să listați numele modulelor pentru un raport de audit, ParsedVBAProject expune un model de module analizat, în timp ce VbaProject rămâne octeții originali neatinși
Refolosirea macrocomenzilor din registre XLS clasice
Fațada BIFF oglindește acel set de unelte, cu un pas în plus. HasVBAProject sondează un fișier încărcat, SaveVBAProjectToFile scrie stocarea proiectului pe disc, iar LoadVBAProjectFromFile o citește înapoi în alt registru. Ocolul prin fișier face simplă o corvoadă frecventă de modernizare: scoateți macrocomenzile dintr-un model din era 2003 și plantați-le în ieșire XLS proaspăt generată, fără niciun șablon original la execuție
var
Src, Dst: IXLSWorkbook; // referințe de interfață: fără Free manual
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;
Modelul de memorie este capcana aici și merge invers față de clasa XLSX. TXLSWorkbook este ținut prin interfața numărată pe referințe IXLSWorkbook, deci nu îl eliberați niciodată manual; TXLSXWorkbook din XLSX este un obiect obișnuit, pe care trebuie să îl înveliți în try..finally și să îl eliberați. Amestecați cele două convenții într-o singură unitate și urmează căderi prin dublă eliberare. Încă o limită de respectat: țineți extragerea și injectarea în interiorul aceluiași format de fișier. Stocarea de proiect BIFF și vbaProject.bin din OOXML sunt veri, nu același container, iar un lanț care trebuie să emită macrocomenzi în ambele formate ar trebui să țină câte un șablon de macrocomenzi separat pentru fiecare
Legături externe: harta supraviețuiește, valorile din cache nu
Pentru registrele XLSX, HotXLS expune legăturile externe prin colecția ExternalLinks. Fiecare TXLSXExternalLink poartă un Target, calea sau URL-ul registrului la distanță, plus o listă SheetNames care numește foile referite. Amândouă supraviețuiesc intacte unui ciclu de deschidere și salvare, iar puteți construi și o legătură de la zero:
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;
Limita stă cu un nivel mai adânc decât lista de ținte. HotXLS duce dus-întors harta legăturii, adică ținta și numele de foi, dar nu analizează și nu rescrie valorile din cache pe care OOXML le ține în elementul sheetDataSet al legăturii. Acel cache este ce îi permite Excelului să arate un ultim număr cunoscut atunci când fișierul-sursă este offline, iar un registru generat pleacă fără el. Consecința aterizează la destinatar, nu la dumneavoastră. Deschideți un asemenea fișier acolo unde ținta este inaccesibilă, un laptop scos de pe VPN sau o partajare care a fost redenumită, iar formulele care depind de legătură se rezolvă la #REF! sau se blochează în spatele unei solicitări de actualizare. Deci de aici decurg două reguli. Nu promiteți că un registru generat își va afișa offline valorile legate extern. Și citiți un ExternalLinks.Count diferit de zero ca pe o precondiție de livrare, nu ca pe o funcționalitate: fiecare țintă trebuie să fie accesibilă din locul de unde va fi chiar deschis fișierul
Ce păstrează cititorul XLS octet cu octet
Pentru structurile pe care nu le modelează, partea BIFF are alt răspuns: lasă-le exact cum le-ai găsit. Cache-urile și vizualizările de tabel pivot (familia de înregistrări SX*), definițiile QueryTable, conexiunile la date externe, vizualizările personalizate, imaginile de antet și înregistrările de temă trec toate printr-un ciclu de deschidere și salvare ca blocuri brute de înregistrări, neanalizate și nemodificate. Referințele externe însele fac drumul dus-întors prin înregistrările subiacente EXTERNSHEET și SupBook. Nu există un API tipizat de creare pentru ele pe partea XLS, dar o legătură existentă supraviețuiește neatinsă editării
Păstrarea octet cu octet este o garanție autentică, cu un tăiș ascuțit. Fiindcă nimic nu citește o structură păstrată, editările dumneavoastră nu o pot corupe. Din același motiv, nici nu o actualizează nimic. Inserați rânduri printr-o regiune spre care indică un cache de tabel pivot păstrat sau un query table, iar structura își ține coordonatele originale în timp ce datele de dedesubt se deplasează. Fișierul rămâne XML sau BIFF valid; sensul s-a abătut discret din aliniere, și nicio eroare nu vă anunță. Aranjamentul apărabil este să țineți editările generate pe foi care nu conțin structuri păstrate, aceeași disciplină care protejează foile blocate și configurate pentru tipărire în articolul nostru despre protecția foilor și configurarea paginii
Verificarea fișierului pe care l-ați scris de fapt
Ambele moduri de eșec sunt silențioase la scriere, deci afirmația care contează se face redeschizând ieșirea, nu încrezându-vă în codul care a produs-o. Trei verificări acoperă aproape tot. Redeschideți fișierul și confirmați că HasVbaProject tot returnează true ori de câte ori se așteptau macrocomenzi, ceea ce prinde într-un singur test și o încărcătură pierdută, și o extensie greșită. Citiți ExternalLinks.Count și comparați-l cu numărul de dinaintea rescrierii. Apoi deschideți fișierul o dată în Excel, cu macrocomenzile dezactivate, pentru că validarea tipului de conținut din Excel este mai strictă decât a oricărei biblioteci, iar Excel este programul după care vă vor judeca clienții fișierul
Nimic din toate acestea nu cere o analiză completă la intrare. Când registrele sosesc în volum și trebuie doar să triați care poartă conținut supus guvernanței, sondarea ușoară din articolul nostru despre listarea foilor și inspecția ușoară a registrelor vă lasă să dirijați fișierele cu macrocomenzi și cu legături într-un lanț mai strict, înainte ca prima rescriere să ruleze vreodată
Câteva întrebări apar destul de des ca să merite un răspuns direct. HotXLS nu execută niciodată macrocomenzile pe care le păstrează: nu există niciun mediu de rulare VBA în bibliotecă, doar mașinăria de a stoca, copia, extrage și injecta proiectul ca date. Pe un server, aceasta este o proprietate de securitate care merită enunțată, fiindcă o macrocomandă ostilă care trece prin lanț rămâne inertă până când un Excel de birou deschide fișierul, iar un utilizator activează conținutul. Convertirea unui .xlsm în .xlsx cu păstrarea macrocomenzilor nu este posibilă, iar aceasta este regula formatului, nu o limitare a bibliotecii: tipul de conținut .xlsx declară un registru fără macrocomenzi, deci singurele rezultate oneste sunt să rămâneți la .xlsm sau să apelați ClearVbaProject și să livrați un fișier care chiar nu are niciuna. Redenumirea tăcută este singura alegere care nu mulțumește pe nimeni. Iar când celulele legate arată #REF! după o rescriere, cauza este cache-ul de valori lipsă discutat mai sus: fișierul nou poartă ținta, dar nu și numerele din cache, deci Excel trebuie să rezolve sursa la deschidere, iar o cale inaccesibilă sau relativă la mediu îl învinge. Fie garantați că ținta este accesibilă, fie scrieți valorile calculate în celule înainte de livrare și eliminați dependența cu totul
Editarea registrelor altora înseamnă în mare parte munca de a păstra lucruri pe care nu le-ați scris și nu le înțelegeți pe deplin. Facilitățile de drum dus-întors pentru VBA și legături externe descrise aici sunt livrate cu HotXLS Delphi Component pentru Delphi și C++Builder, împreună cu proprietățile de audit care vă lasă să detectați conținutul supus guvernanței din clipa în care sosește un fișier