Artículo técnico

HotXLS: comments, hyperlinks, and review workflows in Delphi

Renombre una hoja de "Summary" a "Overview" en un libro de trabajo generado, y todo hipervínculo interno que apuntaba a Summary!A1 deja de ir a ningún sitio. Ninguna excepción al guardar, ninguna al abrir. El enlace se sigue renderizando, sigue pareciendo clicable, y silenciosamente resuelve a nada. El mismo tipo de rotura aparece tras una conversión de guardar-como o un viaje de ida y vuelta .xls/.xlsx, cuando un comentario aterriza una columna descolocado o un enlace relativo pierde su objetivo. Ambas funciones llevan estado de revisión sobre el que actúan personas reales, así que cuando se rompen el fallo es invisible hasta que un revisor hace clic y no pasa nada

Esa es la razón práctica por la que los comentarios y los hipervínculos merecen más cuidado del que sugiere su apariencia cosmética. HotXLS da al código Delphi y C++Builder acceso de escritura directo a ambos, en XLS y XLSX, sin ninguna automatización de Excel de por medio. La otra cara de ese control es la responsabilidad: la biblioteca escribe exactamente los objetivos que usted le entrega y no valida ninguno de ellos, así que mantener intacto un flujo de revisión es trabajo de su código, no de Excel

Los comentarios de celda como registros de revisión escritos por máquina

En el modelo de clases XLSX un comentario es un objeto a nivel de hoja de cálculo: conoce su fila, su columna, un autor, y un cuerpo de texto. El campo de autor se gana su lugar. Cuando un libro de trabajo que generó su código viaja a través de una cadena de revisión, la primera pregunta que hace un auditor es quién escribió una nota dada, y una nota dejada sin autor responde a esa pregunta con un vacío. Marque los comentarios generados con una identidad de servicio para que la procedencia nunca sea ambigua

Diagrama de un reintento de comentario de HotXLS en Delphi donde una sonda FindAt actualiza la nota de celda existente mientras un reintento ciego de AddComment apila un duplicado
Un reintento que llama a AddComment a ciegas apila una segunda nota en la misma celda, mientras que la sonda FindAt edita la nota que ya está ahí
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Note: TXLSXComment;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('reconciliation.xlsx');
    Sheet := Book.Sheets[0];

    // Nota firmada sobre la cifra ajustada
    Sheet.AddComment(14, 4, 'Manual adjustment: late FX rate, see ticket FIN-2214',
      'recon-service');

    // Actualiza una nota existente en lugar de apilar una segunda
    Note := Sheet.Comments.FindAt(14, 4);
    if Note <> nil then
      Note.Text := Note.Text + ' [verified 2026-06-11]';

    Book.SaveAs('reconciliation-reviewed.xlsx');
  finally
    Book.Free;
  end;
end;

La comprobación con FindAt lleva más peso del que parece. Un job por lotes que reintenta tras un fallo transitorio llamará alegremente a AddComment una segunda vez sobre una celda que ya anotó, y la celda termina con dos notas apiladas que nadie pidió. Compruebe primero con FindAt, y actualice el objeto que devuelve. La colección Comments también expone DeleteAt y DeleteInRange. Esa variante de rango es la que hay que usar cuando se depura un libro de trabajo antes de que salga del edificio: borrar anotaciones internas de QA de toda una región es una sola llamada en lugar de un bucle escrito a mano sobre las celdas

Las URL externas y los saltos dentro del libro son API distintas

OOXML mantiene los dos tipos de enlace en lugares distintos. Una URL externa se convierte en una entrada de relación en la parte .rels de la hoja, con la celda apuntando a la relación por id. Un salto interno nunca toca en absoluto la capa de relaciones; es una simple cadena de ubicación como Summary!A1 almacenada directamente en el enlace. HotXLS mantiene esa distinción visible en la API en lugar de sobrecargar un único método, lo que significa que usted elige la llamada correcta sabiendo dónde vive el objetivo:

Diagrama que contrasta cómo HotXLS almacena una URL externa como relationship en la parte rels y un salto interno como cadena de location simple en libros generados en Delphi
Una URL externa viaja por la capa de relaciones mientras que un salto interno es texto simple, de modo que cada tipo falla a su manera y necesita su propia regla de auditoría
Sheet.Cells[2, 1].Value := 'Source record';
Sheet.AddHyperlink(2, 1, 'https://intranet.example.com/records/2214',
  'Open record 2214', 'ERP source entry');

Sheet.Cells[3, 1].Value := 'Totals';
Sheet.AddHyperlinkToCell(3, 1, 'Overview!B12', 'Jump to totals');

En el objeto TXLSXHyperlink resultante, Url y Location son mutuamente excluyentes, y IsInternal le indica cuál de los dos está poblado. Ese indicador es lo que comprueba cuando inventaría los enlaces de un libro de trabajo abierto y necesita tratar "sale del archivo" y "se queda en el archivo" bajo reglas distintas: un host externo podría enfrentarse a una lista de permitidos mientras que un objetivo interno solo tiene que nombrar una hoja que exista. Los enlaces internos no llevan partes de relación detrás, lo que también los hace más baratos de reescribir en bloque

La rotura del principio vive enteramente en el lado interno, y se deriva de un hecho: una cadena de ubicación no es una referencia analizada. HotXLS escribe exactamente el texto que usted le entrega, y nada vuelve a apuntar ese texto cuando una hoja se renombra más tarde. Dos defensas se sostienen en la práctica. La primera es disciplina sobre el orden: renombre todas las hojas antes de generar un solo enlace, y luego trate los nombres de hoja como identificadores congelados. La segunda es más robusta y sobrevive a renombrados hechos después del hecho. Apunte el enlace a un nombre definido a nivel de libro de trabajo en lugar de una dirección Sheet!Cell en bruto, porque Excel reescribe la definición de un nombre cuando la hoja subyacente cambia, así que el enlace viaja con él automáticamente. Ese segundo enfoque combina de forma natural con las técnicas de nombres definidos y fórmulas entre hojas en HotXLS

El lado XLS: los mismos conceptos, fontanería más antigua

La fachada BIFF8 cuelga los comentarios de los rangos en lugar de una colección a nivel de hoja de cálculo. Se llama a AddComment sobre un IXLSRange y se recibe de vuelta un TXLSComment; la propiedad Comment del rango lee una nota existente, y ClearComments las borra. El punto delicado aquí es posicional. Un TXLSComment no expone públicamente su propia fila y columna, así que el bucle natural, "recorrer cada comentario e informar de dónde está", va a contracorriente de la API. Hay que partir de las celdas. O bien dirija la auditoría a partir de la lista de direcciones que anotó, o lleve su propio registro de posiciones mientras escribe, porque el objeto de comentario no le dirá después dónde vive

var
  Book: IXLSWorkbook;
  Sheet: IXLSWorksheet;
  Remark: TXLSComment;
begin
  Book := TXLSWorkbook.Create;
  Sheet := Book.Sheets.Add;
  Sheet.Name := 'Review';
  Sheet.Cells.Item[5, 2].Value := 4821.50;

  Remark := Sheet.Cells.Item[5, 2].AddComment('Awaiting sign-off from controller');
  Remark.Visible := True;   // abre la nota al primer vistazo

  Sheet.AddHyperlink(7, 2, 'https://intranet.example.com/signoff/4821',
    'Sign-off form', 'Opens the controller queue');
  Book.SaveAs('review.xls');
end;

Fijar Visible a True es la forma heredada de hacer que una nota sea imposible de pasar por alto: el cuadro amarillo permanece abierto en la hoja en lugar de esperar a que alguien pase el ratón por encima. TXLSComment va un paso más allá que su homólogo XLSX al exponer TextRuns, así que una sola nota puede llevar una advertencia en negrita junto a una explicación simple, un formato que la API de comentarios de XLSX no expone de la misma manera. Los hipervínculos en este lado llegan a través de tres sobrecargas progresivas (solo dirección, luego con texto de visualización, luego con información en pantalla) y se leen de vuelta a través de la colección HyperLinks de la hoja, donde cada enlace expone Address, SubAddress, DisplayText, y ScreenTip

Una hoja índice de revisión gana a las notas dispersas

Pasada una docena de anotaciones más o menos, leer al pasar el ratón deja silenciosamente de escalar. Las notas se acumulan en hojas que un revisor nunca abre, y las que más importan son exactamente las más fáciles de pasar por alto. La estructura que mejor ha aguantado es una hoja índice generada: una fila por ubicación anotada, listando su nombre de hoja, dirección de celda, autor, y un breve extracto de la nota. La última columna lleva un hipervínculo interno construido con AddHyperlinkToCell que salta directamente a la celda anotada. Ahora el revisor lee una lista de arriba abajo en lugar de cazar por toda una cuadrícula, y el número de filas de ese índice sirve también como su inventario de comentarios para el paso de auditoría de más abajo

El índice es barato de construir porque su generador ya conoce cada posición que tocó. Añada una tupla (hoja, fila, columna, autor, resumen) a una lista mientras escribe cada comentario, y luego emita la hoja índice al final para que su número de filas sea definitivo antes de guardar. Dos refinamientos merecen la pena: ordene el índice por severidad o por hoja en lugar de por secuencia de inserción, y ponga un enlace de retorno en la cabecera del índice para que un revisor pueda volver arriba tras cada elemento. Como los enlaces internos son simples cadenas de ubicación sin nada en la capa de relaciones detrás, incluso un índice de mil filas añade casi nada al tamaño del archivo ni al tiempo de guardado

Esa misma hoja da beneficio otra vez en el viaje de vuelta. Cuando el libro de trabajo revisado regresa, su código lee valores de estado escritos en celdas junto a las filas del índice en lugar de volver a escanear cada hoja en busca de comentarios que puedan haber cambiado. Una columna de celdas de estado estructuradas se analiza limpiamente; una dispersión de notas de texto libre no

Un paso de auditoría previo a la entrega que realmente detecta la rotura

Ninguna de estas API valida un objetivo. Un enlace a una hoja que eliminó, un host de intranet mal escrito, un recurso compartido de archivos dado de baja el trimestre pasado: todos ellos se guardan sin protesta. ECMA-376 especifica cómo se almacena un enlace, no que resuelva a algo. Un libro de trabajo que lleva metadatos de revisión merece por tanto una breve etapa de auditoría propia, ejecutada justo antes de SaveAs:

Diagrama de la pasada de auditoría previa a la entrega de HotXLS que comprueba destinos internos, listas de permitidos de URL, recuentos de comentarios y limpieza de destinatarios antes de SaveAs en Delphi
Cuatro comprobaciones corren justo antes de SaveAs y cada una atrapa un fallo que la propia biblioteca nunca lanzará
  • Recopile cada ubicación interna escrita durante la generación y confirme que el nombre de hoja anterior a la exclamación todavía existe en la colección de hojas del libro de trabajo
  • Compruebe las URL externas contra una lista de permitidos de esquemas y hosts. Las rutas file:// y UNC desnudas filtran detalles del entorno y se rompen en el momento en que el archivo sale de su red
  • Cuente los comentarios por hoja y compárelos con lo que su generador pretendía escribir. Un reintento que duplicó las notas aflora aquí en lugar de en la bandeja de entrada del revisor
  • Elimine las anotaciones exclusivamente internas con DeleteInRange siempre que el destinatario esté fuera de la organización

Los equipos que construyen sus libros de trabajo a partir de una capa de datos pueden plegar esta etapa dentro del mismo paso del pipeline que ya valida los datos, así que la comprobación de metadatos viaja gratis. Los mecanismos son los descritos en exportar resultados de consultas de base de datos a informes de Excel, orientados hacia enlaces y comentarios en lugar de filas

Un detalle de comillado hace tropezar a la gente cuando construye cadenas de ubicación a mano. Una hoja cuyo nombre contiene un espacio tiene que ir entre comillas dentro de la ubicación, exactamente como la barra de fórmulas la comilla: 'Quarterly Totals'!A1, no Quarterly Totals!A1. HotXLS aplica las mismas reglas que usa el motor de fórmulas para las referencias entre hojas, así que si un enlace funciona en una fórmula de hoja de cálculo su comillado también funcionará aquí. Entréguele un nombre sin comillas con un espacio y obtendrá el mismo enlace muerto silencioso que advertía el principio

Los comentarios y los hipervínculos son las partes de un libro de trabajo generado sobre las que los revisores actúan sin pensarlo dos veces, razón exacta por la que un objetivo que apunta a nada hace daño real antes de que nadie lo note. Construya el paso de validación una vez, ejecútelo en cada libro de trabajo antes de que se entregue, y el flujo de revisión permanece intacto a través de renombrados y conversiones. La superficie completa de la API tanto para la fachada XLS como para la XLSX está documentada en la página de producto de HotXLS Delphi Component