Considera un trabajo que no hace casi nada: abrir un libro de trabajo mensual, escribir la fecha de hoy en una celda y guardarlo de vuelta. Pásalo por un servicio las veces suficientes y de todos modos llega una queja. Las macros desaparecieron, o los tipos de cambio vinculados ahora muestran #REF!, y el equipo de operaciones está convencido de que tu código los eliminó. No eliminó nada. Lo que suele haber ocurrido es que un libro de trabajo habilitado para macros salió con un nombre .xlsx simple, 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, sin importar que los bytes estén justo ahí. El archivo no se rompió. Se renombró a un estado en el que Excel está obligado a ignorar parte de él
Las macros y los vínculos a libros de trabajo externos son las dos cosas que la automatización pierde con mayor regularidad, por la misma razón de fondo. Ambos viven fuera de la cuadrícula de celdas que el código de edición realmente toca, así que el código que razona en términos de filas y columnas los descartará sin emitir jamás una eliminación. 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 tu ruta de guardado, y dónde terminan las garantías
Por qué estos dos activos se comportan distinto ante 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 formas de perderlo: el escritor nunca lo copia a 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 un blob en absoluto. Es un pequeño grafo de relaciones: una ruta o URL de destino que apunta a otro libro de trabajo, 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 distintos ciclos de vida ante una reescritura, y una biblioteca puede preservar fielmente algunas mientras descarta otras en silencio. Esa asimetría es la parte sobre la que vale 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
Del lado XLSX, TXLSXWorkbook conserva la carga útil de macros de forma literal. La propiedad VbaProject contiene los bytes crudos de vbaProject.bin dentro de un AnsiString, y una cadena vacía es la forma en que el modelo indica 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 de trabajo generados incorporen un proyecto de macros estándar sin arrastrar un archivo de plantilla completo por el 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');
// 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 de trabajo 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. Pásale .xlsx en su lugar y Excel rechaza las macros, aunque los bytes estén físicamente presentes en el paquete y se deserializarían 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 necesitas transportar la carga útil. Cuando necesitas leer dentro de ella, por ejemplo para listar los nombres de módulo en un informe de auditoría, ParsedVBAProject expone un modelo de módulos analizado mientras VbaProject permanece como los bytes originales sin tocar
Reutilizar macros de libros de trabajo 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 lee de vuelta en otro libro de trabajo. El desvío a través de un archivo hace directa una tarea común de modernización: extraer 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 conteo de referencias IXLSWorkbook, así que nunca lo liberas a mano; el TXLSXWorkbook de XLSX es un objeto común que debes envolver en try..finally y liberar. Mezcla las dos convenciones en una sola unidad y siguen los fallos por doble liberación. Un límite más que vale la pena respetar: mantén la extracción y la inyección dentro de un solo formato de archivo. El almacenamiento de proyecto BIFF y el vbaProject.bin de OOXML son primos, no el mismo contenedor, y un pipeline que deba emitir macros en ambos formatos debería mantener una plantilla de macros separada para cada uno
Vínculos externos: el mapa sobrevive, los valores en caché no
Para libros de trabajo 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 de trabajo 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 puedes 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 profundo que la lista de destinos. HotXLS conserva de ida y vuelta el mapa del vínculo, 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 un último número conocido cuando el archivo de origen está fuera de línea, y un libro de trabajo generado se envía sin ella. La consecuencia recae en el destinatario, no en ti. Abre un archivo así donde el destino sea inalcanzable, una laptop fuera de la VPN o un recurso compartido que fue renombrado, y las fórmulas que dependen del vínculo se resuelven a #REF! o se quedan detenidas tras un aviso de actualización. De esto se desprenden dos reglas. No prometas que un libro de trabajo generado mostrará sus valores vinculados externamente sin conexión. Y lee un ExternalLinks.Count distinto de cero como una condición previa de entrega y no como una función: cada destino tiene que ser alcanzable desde donde el archivo realmente se abrirá
Lo que el lector XLS preserva byte por byte
Para las estructuras que no modela, el lado BIFF tiene una respuesta distinta: dejarlas exactamente como las encontró. 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 registro crudos, sin analizar y sin modificar. Las referencias externas en sí hacen el viaje de ida y vuelta a través de los registros subyacentes EXTERNSHEET y SupBook. No hay una API tipada de creación para ellas del lado XLS, pero un vínculo existente sobrevive intacto a la edición
La preservación byte por byte es una garantía genuina con un filo cortante. Como nada lee una estructura preservada, tus ediciones no pueden corromperla. Por la misma razón, nada la actualiza tampoco. Inserta filas a través de una región a la que apunta una caché de tabla dinámica o una tabla de consulta preservadas, y la estructura mantiene sus coordenadas originales mientras los datos debajo se desplazan. El archivo sigue siendo XML o BIFF válido; el significado se ha desalineado silenciosamente, y no se dispara ningún error que te lo diga. El diseño defendible es mantener las ediciones generadas en hojas que no contengan estructuras preservadas, que es la misma disciplina que protege las hojas bloqueadas y configuradas para impresión en nuestro artículo sobre protección de hojas de cálculo y configuración de página
Verificar el archivo que realmente escribiste
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 cubren casi todo. Reabre el archivo y confirma que HasVbaProject siga devolviendo true siempre que se esperaban macros, lo que detecta una carga útil descartada y una extensión incorrecta en una sola prueba. Lee ExternalLinks.Count y compáralo con el conteo previo a la reescritura. Luego abre 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 tus clientes juzgarán el archivo
Nada de eso requiere un análisis completo de entrada. Cuando los libros de trabajo llegan en volumen y solo necesitas clasificar cuáles llevan contenido regulado, el sondeo ligero de nuestro artículo sobre listado de hojas e inspección ligera de libros de trabajo te permite encaminar los archivos con macros y con vínculos a un pipeline más estricto 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 preserva: 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 vale la pena declarar, ya que una macro hostil que pasa por el pipeline permanece inerte hasta que un Excel de escritorio abre el archivo y un usuario habilita el contenido. Convertir un .xlsm a .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 de trabajo sin macros, así que los únicos resultados honestos son permanecer en .xlsm o llamar a ClearVbaProject y enviar un archivo que genuinamente no tiene ninguna. El renombrado 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 faltante 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 garantizas que el destino sea alcanzable, o escribes los valores calculados en las celdas antes de la entrega y eliminas por completo la dependencia
Editar los libros de trabajo de otras personas es, en su mayor parte, el trabajo de preservar cosas que no escribiste y que no entiendes del todo. Las facilidades de ida y vuelta de VBA y vínculos externos descritas aquí se incluyen con el componente HotXLS para Delphi para Delphi y C++Builder, junto con las propiedades de auditoría que te permiten detectar contenido regulado en el momento en que llega un archivo