Piense en un trabajo que no hace casi nada: abrir un libro mensual, escribir la fecha de hoy en una celda y volver a guardarlo. Páselo por un servicio con la suficiente frecuencia y aun así llegará una queja. Las macros han desaparecido, o los tipos de cambio vinculados ahora muestran #REF!, y el equipo de operaciones está convencido de que su código los borró. No borró nada. Lo que suele haber ocurrido es que un libro habilitado para macros salió con un nombre .xlsx corriente, y Excel obedeció las reglas de tipo de contenido de ECMA-376: un paquete cuyo tipo de contenido no declara VBA no puede cargar un proyecto VBA, con independencia de que los bytes estén ahí mismo. El archivo no se rompió. Se le cambió el nombre a un estado en el que Excel está obligado a ignorar parte de él
Las macros y los vínculos a libros externos son las dos cosas que la automatización pierde con más fiabilidad, por la misma razón de fondo. Ambos viven fuera de la cuadrícula de celdas que el código de edición toca realmente, así que un código que razona en términos de filas y columnas los descartará sin emitir nunca un borrado. HotXLS es una biblioteca nativa para Delphi y C++Builder que lee y escribe XLS y XLSX sin Excel instalado, y trata ambos activos como cargas útiles que transporta deliberadamente y no como datos que copia por casualidad. Lo que sigue es lo que cada uno necesita de su ruta de guardado, y dónde terminan las garantías
Por qué estos dos activos se comportan de forma distinta en una reescritura
Un proyecto VBA es un único binario opaco. En un paquete OOXML es el archivo vbaProject.bin; en un archivo BIFF heredado es un almacenamiento OLE. Hay exactamente dos maneras de perderlo: el escritor nunca lo copia en la salida, o la salida recibe un tipo de archivo que lo prohíbe. Cualquiera de los dos fallos es total y silencioso. El proyecto está presente o no lo está
Un vínculo externo no es en absoluto un blob. Es un pequeño grafo de relaciones: una ruta o URL de destino que apunta a otro libro, la lista de nombres de hoja que ese destino expone, y una caché opcional de los valores vistos por última vez en esas hojas para que Excel pueda mostrar algo cuando el destino está fuera de línea. Esas tres partes tienen vidas distintas en una reescritura, y una biblioteca puede conservar fielmente algunas mientras descarta otras en silencio. Esa asimetría es la parte sobre la que merece la pena ser preciso, porque nada en el código de edición de celdas la hará visible
Transportar un proyecto VBA a través de una reescritura XLSX
En el lado XLSX, TXLSXWorkbook conserva la carga de macros literalmente. La propiedad VbaProject guarda los bytes crudos de vbaProject.bin dentro de un AnsiString, y una cadena vacía es la forma en que el modelo dice que no hay macros. A su alrededor hay tres operaciones: HasVbaProject responde si hay un proyecto presente, ClearVbaProject lo elimina a propósito, y LoadVbaProjectFromFile inyecta uno extraído de una plantilla. Esa última llamada vale más de lo que parece. Permite que los libros generados adopten un proyecto de macros estándar sin arrastrar un archivo de plantilla completo por la canalización
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');
// La extensión .xlsm no es cosmética: selecciona el
// tipo de contenido habilitado para macros dentro del paquete.
Book.SaveAs('monthly-report.xlsm');
finally
Book.Free;
end;
end;
La línea de guardado es donde gira todo el problema. Un libro que contiene un proyecto VBA tiene que escribirse con semántica habilitada para macros, y HotXLS la aplica cuando el nombre de destino termina en .xlsm. Entréguele .xlsx en su lugar y Excel rechazará las macros, aunque los bytes estén físicamente presentes en el paquete y se deserializaran sin problema. La extensión no es decoración; selecciona el tipo de contenido que le dice a Excel que un proyecto VBA tiene permitido existir. La mayoría de las veces solo necesita transportar la carga útil. Cuando necesite leer dentro de ella, por ejemplo para listar los nombres de los módulos en un informe de auditoría, ParsedVBAProject expone un modelo de módulos analizado mientras VbaProject sigue siendo los bytes originales intactos
Reutilizar macros de libros XLS heredados
La fachada BIFF refleja ese conjunto de herramientas con un paso adicional. HasVBAProject sondea un archivo cargado, SaveVBAProjectToFile escribe el almacenamiento del proyecto en disco, y LoadVBAProjectFromFile lo vuelve a leer en otro libro. El desvío a través de un archivo hace sencilla una tarea de modernización habitual: sacar las macros de un modelo de la era 2003 y plantarlas en una salida XLS recién generada, sin necesidad de la plantilla original en tiempo de ejecución
var
Src, Dst: IXLSWorkbook; // referencias de interfaz: sin 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;
El modelo de memoria es la trampa aquí, y funciona al revés que la clase XLSX. TXLSWorkbook se sostiene a través de la interfaz con recuento de referencias IXLSWorkbook, así que nunca lo libera a mano; el TXLSXWorkbook de XLSX es un objeto corriente que debe envolver en try..finally y liberar. Mezcle las dos convenciones en una unidad y le seguirán cierres por doble liberación. Un límite más que conviene respetar: mantenga la extracción y la inyección dentro de un único formato de archivo. El almacenamiento del proyecto BIFF y el vbaProject.bin de OOXML son primos, no el mismo contenedor, y una canalización que tenga que emitir macros en ambos formatos debería conservar una plantilla de macros separada para cada uno
Vínculos externos: el mapa sobrevive, los valores en caché no
Para los libros XLSX, HotXLS expone los vínculos externos a través de la colección ExternalLinks. Cada TXLSXExternalLink lleva un Target, la ruta o URL del libro remoto, más una lista SheetNames que nombra las hojas a las que hace referencia. Ambos sobreviven intactos a un ciclo de apertura y guardado, y también puede construir un vínculo desde cero:
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;
El límite está un nivel más abajo que la lista de destinos. HotXLS conserva el mapa del vínculo en el ciclo de ida y vuelta, es decir, el destino y los nombres de hoja, pero no analiza ni reescribe los valores de celda en caché que OOXML guarda en el elemento sheetDataSet del vínculo. Esa caché es lo que permite a Excel mostrar el último número conocido cuando el archivo de origen está fuera de línea, y un libro generado se entrega sin ella. La consecuencia recae en el destinatario, no en usted. Abra un archivo así donde el destino sea inalcanzable, un portátil fuera de la VPN o un recurso compartido al que cambiaron el nombre, y las fórmulas que dependen del vínculo se resuelven en #REF! o se quedan atascadas tras un aviso de actualización. De ahí se derivan dos reglas. No prometa que un libro generado mostrará sus valores vinculados externamente fuera de línea. Y lea un ExternalLinks.Count distinto de cero como una condición previa de entrega y no como una función: todos los destinos tienen que ser alcanzables desde donde el archivo se abra realmente
Lo que el lector XLS conserva byte a byte
Para las estructuras que no modela, el lado BIFF tiene una respuesta distinta: dejarlas exactamente como se encontraron. Las cachés y vistas de tablas dinámicas (la familia de registros SX*), las definiciones de QueryTable, las conexiones de datos externas, las vistas personalizadas, las imágenes de encabezado y los registros de tema pasan todos por un ciclo de apertura y guardado como bloques de registros crudos, sin analizar y sin modificar. Las propias referencias externas hacen el ciclo de ida y vuelta a través de los registros subyacentes EXTERNSHEET y SupBook. No hay ninguna API tipada para crearlas en el lado XLS, pero un vínculo existente sobrevive intacto a la edición
La conservación byte a byte es una garantía genuina con una arista afilada. Como nada lee una estructura conservada, sus ediciones no pueden corromperla. Por la misma razón, nada la actualiza tampoco. Inserte filas a través de una región a la que apunta una caché dinámica o una tabla de consulta conservadas, y la estructura mantiene sus coordenadas originales mientras los datos de debajo se desplazan. El archivo sigue siendo XML o BIFF válido; el significado se ha desalineado en silencio, y no salta ningún error que se lo diga. La disposición defendible es mantener las ediciones generadas en hojas que no contengan estructuras conservadas, que es la misma disciplina que protege las hojas bloqueadas y configuradas para impresión en nuestro artículo sobre protección de hojas y configuración de página
Verificar el archivo que realmente escribió
Ambos modos de fallo son silenciosos en el momento de escribir, así que la aserción que importa se hace reabriendo la salida en lugar de confiar en el código que la produjo. Tres comprobaciones lo cubren casi todo. Reabra el archivo y confirme que HasVbaProject sigue devolviendo true siempre que se esperaban macros, lo que detecta una carga perdida y una extensión equivocada en una sola prueba. Lea ExternalLinks.Count y compárelo con el recuento anterior a la reescritura. Luego abra el archivo una vez en Excel con las macros deshabilitadas, porque la validación de tipo de contenido de Excel es más estricta que la de cualquier biblioteca, y Excel es el programa por el que sus clientes juzgarán el archivo
Nada de eso requiere un análisis completo al entrar. Cuando los libros llegan en volumen y solo necesita clasificar cuáles contienen contenido gobernado, el sondeo ligero de nuestro artículo sobre listado de hojas e inspección ligera de libros le permite encaminar los archivos con macros y vínculos hacia una canalización más estricta antes de que se ejecute la primera reescritura
Algunas preguntas surgen con la frecuencia suficiente como para responderlas directamente. HotXLS nunca ejecuta las macros que conserva: no hay ningún runtime de VBA en la biblioteca, solo la maquinaria para almacenar, copiar, extraer e inyectar el proyecto como datos. En un servidor, esa es una propiedad de seguridad que merece la pena declarar, ya que una macro hostil que pase por la canalización permanece inerte hasta que un Excel de escritorio abre el archivo y un usuario habilita el contenido. Convertir un .xlsm en .xlsx y conservar las macros no es posible, y esa es la regla del formato y no una limitación de la biblioteca: el tipo de contenido .xlsx declara un libro sin macros, así que los únicos resultados honestos son seguir siendo .xlsm o llamar a ClearVbaProject y entregar un archivo que de verdad no tenga ninguna. El cambio de nombre silencioso es la única opción que no satisface a nadie. Y cuando las celdas vinculadas muestran #REF! tras una reescritura, la causa es la caché de valores ausente comentada arriba: el archivo nuevo lleva el destino pero no los números en caché, así que Excel debe resolver el origen al abrir, y una ruta inalcanzable o relativa al entorno lo impide. O bien garantice que el destino es alcanzable, o bien escriba los valores calculados en las celdas antes de la entrega y elimine la dependencia por completo
Editar los libros de otras personas es, en su mayor parte, el trabajo de conservar cosas que usted no escribió y no entiende del todo. Las facilidades de ida y vuelta de VBA y vínculos externos descritas aquí se incluyen con el HotXLS Delphi Component para Delphi y C++Builder, junto con las propiedades de auditoría que le permiten detectar contenido gobernado en el momento en que llega un archivo