La migración a Unicode de proyectos antiguos de Delphi es en muchas empresas un paso necesario, porque las aplicaciones existentes, de lo contrario, se topan cada vez más con límites ante datos internacionales, sistemas operativos modernos, integraciones y nuevas interfaces. En la práctica rara vez es un «Recompile y listo». Delphi introdujo desde las versiones Unicode (a partir de Delphi 2009) cambios fundamentales en los tipos de cadena estándar. Eso desplaza supuestos sobre la codificación de caracteres, el layout de memoria y las firmas de API. Quienes lo subestiman generan errores de datos progresivos, exportaciones defectuosas, casos de soporte poco claros y riesgos de seguridad.
Este artículo ofrece un procedimiento técnicamente sólido: cómo analizar el legado, acotar el scope de forma sensata, reducir riesgos en puntos críticos (bases de datos, ficheros, APIs Windows, COM, servicios REST) y asegurar la migración de modo que la operación y el desarrollo puedan seguir en paralelo. El enfoque se centra en las trampas típicas de Delphi en aplicaciones VCL, servicios e interfaces —con vista a rutas de modernización en las que posteriormente encajen temas como la sustitución de BDE con enlace nativo, servidores REST o multiplataforma.
Por qué el cambio a Unicode en Delphi suele ser «más grande de lo esperado»
En las versiones clásicas de Delphi string era un ANSI-String (según la codepage del sistema). Desde Delphi 2009 string es por defecto un UnicodeString (UTF-16). Al mismo tiempo muchas bibliotecas y clases VCL se adaptaron a APIs Wide. Eso es en principio positivo, porque soporta de forma robusta caracteres internacionales. Pero: el código legado con frecuencia llevaba años asumiendo «1 carácter = 1 byte», «PChar es PAnsiChar» o «Length() corresponde al número de bytes».
Las causas típicas por las que las migraciones se complican:
- Conversiones implícitas ocurren pero modifican datos (especialmente en ficheros, interfaces o campos BLOB/TEXT de bases de datos).
- Código orientado a bytes (Streams, buffers, hashing, cifrado) falla inadvertidamente cuando el contenido de cadenas se interpreta como bytes.
- Componentes de terceros son en parte sólo ANSI, o usan tipos de cadena y callbacks propios.
- Entorno externo (APIs Windows, COM, impresión/reporting, EDI, CSV, XML/JSON) espera ciertas codificaciones.
Por tanto, el objetivo no debería ser «cambiar lo menos posible», sino cambiar deliberadamente donde deben definirse los flujos de datos y las codificaciones. Una migración a Unicode bien hecha es también una oportunidad para documentar y testear finalmente los límites de codificación que estaban poco claros.
Fundamentos técnicos: tipos de cadena en Delphi, codificaciones y sus efectos secundarios
string, UnicodeString, AnsiString, WideString — qué cuenta realmente en el proyecto
Para la migración es decisivo qué tipos se usan en interfaces y funciones centrales:
- string: desde Delphi 2009 un UnicodeString (UTF-16, gestionado por conteo de referencias, semántica inmutable mediante Copy-on-Write).
- AnsiString: cadena de bytes con codepage asignada (según la versión de Delphi puede llevar la codepage). Adecuado cuando una interfaz externa exige explícitamente una codificación de 8 bits.
- UTF8String: en versiones más recientes de Delphi con frecuencia alias/AnsiString con codepage UTF-8; práctico para REST/JSON y muchos protocolos.
- WideString: BSTR (COM), gestionado en memoria vía SysAllocString; hoy generalmente necesario solo para interoperaciones COM específicas.
- PChar: desde la versión Unicode de Delphi PWideChar. Este es uno de los puntos de ruptura más frecuentes en llamadas a APIs Windows.
Si se mezclan estos tipos, se generan conversiones. Algunas son correctas, otras sorprendentes: una conversión solo es «correcta» si se conoce qué codepage está en la fuente y qué se espera en el destino.
UTF-16 internamente, UTF-8 externamente: una guía práctica
En aplicaciones VCL de Delphi suele ser sensato trabajar internamente de forma consistente con string (UTF-16). Externamente (REST, ficheros, mensajería) domina en la práctica UTF-8. Una línea robusta sería:
- Interno: string/UnicodeString como estándar.
- Límites: en entrada/salida convertir explícitamente mediante TEncoding.UTF8 (o codepages ANSI definidas).
- Procesamiento basado en bytes: TBytes en lugar de Strings.
Esto reduce conversiones implícitas y hace verificables las responsabilidades: «¿Dónde se convierte de bytes a texto y con qué encoding?»
Inventario: dónde suele fallar Unicode en proyectos antiguos de Delphi
Antes de tocar el código conviene un inventario estructurado. En la migración a Unicode de proyectos antiguos de Delphi las fuentes de error no suelen estar repartidas uniformemente, sino concentradas en ciertos hotspots.
1) Acceso a base de datos y tipos de campo (BDE, ADO, FireDAC)
Muchos proyectos legacy siguen usando BDE u otras capas de acceso antiguas. Aquí los problemas habituales son:
- Mapeo de charsets de la base de datos a las cadenas de Delphi (tipos de campo ANSI vs. Unicode).
- «Texto» en BLOBs o campos Memo sin codificación definida.
- Sentencias SQL como Strings que se interpretan de forma distinta ante diéresis/caracteres Unicode.
Si ya hay previsto modernizar, una migración a Unicode se puede combinar bien con una limpieza del acceso a datos, por ejemplo hacia BDE-Ablosung mit nativer Anbindung y una configuración clara de charset (p. ej. en PostgreSQL o MariaDB). Importante: una migración no debe forzar automáticamente una migración de base de datos, pero la interfaz entre la BD y Delphi debe ser inequívoca.
2) E/S de ficheros y streams: CSV, INI, formatos propietarios, import/export
Un clásico: los ficheros se leían/escribían antes con AssignFile/ReadLn, TFileStream o TStringList.LoadFromFile sin establecer el encoding. En las versiones Unicode de Delphi el framework decide heurísticamente (BOM) o usa encodings por defecto. Esto provoca:
- umlauts interpretados incorrectamente (ä, ö) en CSV/logfiles,
- longitudes erróneas en formatos propietarios,
- incompatibilidades con partners externos que esperan ISO-8859-1 o Windows-1252.
Una solución limpia es definir un encoding fijo por formato de fichero y dejarlo reflejado en el código y la documentación. Para CSV/JSON UTF-8 suele ser el estándar adecuado; para interfaces antiguas a veces Windows-1252. Lo decisivo es la explícitez.
3) API Windows, PChar, tamaños de buffer y manejo de mensajes
Muchas aplicaciones Delphi llaman funciones WinAPI o trabajan con buffers. Puntos de ruptura frecuentes:
- Uso de PChar con funciones que tienen variantes ANSI o Wide (…A/…W).
- Los tamaños de buffer se calculan en bytes, pero Char en UTF-16 ocupa 2 bytes.
- Aritmética de punteros y layouts de registros basados en Char de 1 byte.
Aquí se requiere un refactor preciso: o bien usar consistentemente APIs Wide o bien invocar conscientemente la variante ANSI y trabajar con AnsiString/codepage. «Que compile de alguna manera» no es criterio de calidad.
4) COM, ActiveX, automatización Office y bibliotecas de terceros
Las interfaces COM suelen trabajar con BSTR (WideString). Las versiones antiguas de Delphi tenían otros strings por defecto, de modo que el código «encajaba por casualidad». En la versión Unicode de Delphi a menudo surgen conversiones dobles o suposiciones de tipo erróneas en wrappers. Las bibliotecas de terceros también son críticas: algunas entregan callbacks como PAnsiChar, otras esperan strings de bytes terminados en nulo.
Conviene clasificar dependencias: ¿qué biblioteca está preparada para Unicode, cuál no, y cuál se puede reemplazar o encapsular? Encapsular suele ser la vía más rápida para trasladar pasivos Unicode a una zona claramente delimitada.
Estrategia: la migración a Unicode de proyectos antiguos de Delphi como un programa de modernización controlado
El enfoque más seguro para la migración es un programa por fases que haga visibles los riesgos pronto y mantenga la aplicación operativa.
Paso 1: definir el scope y priorizar hotspots de código
No todo el código fuente necesita ajustes inmediatos. Priorice según flujo de datos y riesgo:
- Interfaces hacia el exterior (REST-API, TCP/IP, ficheros, e-mail, impresión/reporting).
- Acceso a datos (SQL, ORM/Datamodule, BDE/FireDAC-capas).
- Funciones utilitarias próximas a strings (parsers, formatters, codificadores/decodificadores).
- Integraciones (COM, imports de DLL, conexiones a hardware).
Como resultado debería surgir una lista de puntos donde «el encoding es una especificación». Esos puntos serán más tarde convertidos en elementos testables.
Paso 2: ajustar deliberadamente opciones de compilador/proyecto y activar advertencias
En muchos proyectos las advertencias se han silenciado durante años. Para una migración a Unicode eso resulta contraproducente. Es recomendable reactivar las advertencias y tomarse en serio las de conversión. Además ayuda definir reglas a nivel de proyecto, por ejemplo: no permitir conversiones implícitas a AnsiString en límites de I/O, usar TEncoding en operaciones de fichero, no hacer “trucos” con PChar sin contexto claro.
Paso 3: introducir „límites de encoding“ como capa técnica
Un patrón práctico de arquitectura es introducir pequeños adaptadores/helpers que definan exactamente cómo entran y salen los datos externos. Ejemplos:
- CSV-Reader/-Writer: siempre con TEncoding.UTF8 (o codepage definida) y reglas claras de separador.
- Cliente/Servidor REST: JSON siempre como bytes UTF-8, cabeceras correctamente establecidas, cuerpo no transmitido como „string“ simple sino como stream de bytes.
- Wrapper de API Windows: funciones centrales que encapsulan Wide/Ansi de forma limpia.
Así evita que las «decisiones de encoding» se distribuyan de forma dispersa por la base de código.
Trampas de código típicas y cómo solucionarlas de forma limpia
Length, SizeOf, ByteLength: cuando longitud de caracteres y tamaño en bytes divergen
En la época ANSI Length(s) se usaba a menudo como número de bytes. En UTF-16 eso es incorrecto. Si necesita arrays de bytes, convierta explícitamente:
- Para UTF-8: TEncoding.UTF8.GetBytes(s)
- Para una codepage ANSI definida: TEncoding.GetEncoding(1252).GetBytes(s) (solo cuando sea correcto desde el punto de vista funcional)
Para tamaños de buffer en llamadas a API: compruebe si la función espera unidades de carácter o de byte. Muchas Wide-APIs esperan número de caracteres, no bytes. Deciden la documentación y la firma, no la intuición.
PAnsiChar vs. PWideChar: imports de DLL y protocolos externos
En imports de DLL existe un alto riesgo de que las firmas en el código Delphi ya no encajen. Defina qué espera la DLL:
- ¿La DLL espera UTF-8? Entonces la entrega como PAnsiChar(UTF8String) es habitual, pero debe controlar la vida útil y la terminación nula.
- ¿Espera UTF-16? Entonces use PWideChar y cadenas Wide.
En todo caso los imports deberían encapsularse en una unidad separada para que la política de strings no se disperse por todo el proyecto.
Formateo, cambios de mayúsculas/minúsculas, comparación: locale y normalización
Unicode también introduce aspectos semánticos: la conversión entre mayúsculas y minúsculas no es trivial en todos los idiomas, y los caracteres pueden tener diferentes formas de normalización. En aplicaciones empresariales típicas es menos crítico que en editores de texto para consumidores, pero afecta a:
- Ordenación y filtrado (p. ej. en grids o búsquedas),
- Comparaciones case-insensitive para claves,
- Generación de nombres de fichero o identificadores.
Es importante una regla clara: qué son „claves“ (p. ej. números de artículo, códigos de cliente) que deberían permanecer cercanos al ASCII, y qué son „textos“ que deben ser totalmente Unicode. Esta separación reduce errores subsecuentes.
GUI/Reporting: fuentes, impresión, PDF y comportamiento de componentes
VCL es desde las versiones Unicode capaz de manejar Unicode, pero en la práctica depende de componentes y rutas de salida. Los riesgos aparecen con:
- motores de reporting o generadores PDF antiguos que esperan ANSI,
- impresión de códigos de barras/etiquetas que requiere codepages específicas,
- fuentes o juegos de caracteres codificados en el código.
Planifique pruebas tempranas con datos de ejemplo reales (nombres, localidades, caracteres especiales, escrituras no latinas si proceden). El valor no está en „puede Unicode“, sino en la evidencia: «Esta salida es correcta en nuestro contexto.»
Datos y persistencia: Unicode no termina en el código
Definir limpiamente charsets y collations en la base de datos
Una migración a Unicode solo es estable si las bases de datos y los drivers están configurados correctamente. Ejemplos:
- En PostgreSQL UTF-8 suele ser el estándar; aun así hay que comprobar client_encoding y el comportamiento del driver.
- En SQL Server la distinción entre VARCHAR y NVARCHAR es relevante; elegir mal la columna puede hacer perder caracteres.
- En MariaDB/MySQL charset/collation (p. ej. utf8mb4) son cruciales para que no se trunquen caracteres de 4 bytes.
En el código Delphi debe emplearse parámetros y tipos de campo de forma que Unicode no se „reconvierta“ de vuelta en el trayecto. FireDAC suele ofrecer mejor control que capas de acceso muy antiguas.
Formatos de fichero legacy: reglas de migración en lugar de conversiones silenciosas
Si su aplicación ha generado ficheros durante años (formatos de exportación, archivos de archivo, estructuras propietarias), debe definir:
- Qué ficheros antiguos se mantienen „tal cual“ y cómo se interpretan correctamente al leerlos.
- Qué formatos se elevan a UTF-8.
- Si existen campos de versión/headers para distinguir claramente ficheros nuevos y antiguos.
La conversión silenciosa sin marcado es arriesgada, porque los errores suelen aparecer tarde. Mejor: versionar, detectar claramente y migrar de forma dirigida.
Garantía de calidad: tests que realmente detectan problemas Unicode
Los errores Unicode suelen depender de los datos. Por eso no bastan tests de „Happy Path“. Conviene un conjunto de pruebas que cubra los puntos problemáticos:
- Tests de roundtrip: import → procesamiento → export, seguidos de una comparación byte a byte (en formatos definidos).
- Roundtrip BD: escribir/leer textos con diéresis, acentos y, si procede, escrituras no latinas; verificar igualdad.
- Tests de interfaz: peticiones REST como UTF-8, cabeceras, escape JSON, logging.
- Regresión: reproducir datos antiguos y casos de usuario típicos, especialmente en búsqueda, filtrado y ordenación.
Para sistemas B2B también es relevante que los errores sean observables: el logging no debe destruir encodings. Quien escribe logs en ANSI pierde en la incidencia justo la información que necesita.
Planificación y esfuerzo: qué realmente impulsa la complejidad
El esfuerzo de la migración a Unicode de proyectos antiguos de Delphi depende menos de «líneas de código» y más de acoplamientos y dependencias externas:
- Muchas integraciones (DLLs, COM, dispositivos, ERP/DMS/CRM) aumentan el esfuerzo de verificación porque en cada frontera importa la codificación.
- Formatos históricos (exportaciones antiguas, CSVs específicos de clientes) requieren reglas de migración y estrategias de compatibilidad.
- Versiones mezcladas de Delphi o múltiples productos derivados de una misma base de código incrementan la necesidad de coordinación.
- Capas de acceso a datos antiguas (p. ej. BDE) pueden bloquear indirectamente Unicode y recomendar una modernización.
En la práctica ha resultado efectivo estabilizar primero Unicode en el núcleo y en los flujos de datos más críticos. A continuación se actualizan módulos de forma incremental. Eso reduce el riesgo y evita fases largas tipo „Big Bang“ sin release.
Ubicación en rutas de modernización: REST, servicios, multiplataforma
Unicode suele ser un pilar cuando se pretende modernizar software legado. Preguntas conexas típicas son:
- Implementar servidores REST o APIs REST (tratar JSON/UTF-8 correctamente).
- Operar de forma estable servicios Windows o servicios Linux (logging, ficheros de configuración, protocolos).
- Modernizar la UI de forma gradual en VCL y, más adelante, contemplar clientes multiplataforma.
Importa el orden: si va a construir nuevas interfaces, las reglas de encoding deberían establecerse con anterioridad. Una migración a Unicode «en paralelo» con el desarrollo de interfaces conduce a cuadros de error difíciles de probar, porque causa mezcla de causas y efectos.
Para la vinculación interna en el Magazin resulta conveniente situar artículos adyacentes sobre modernización Delphi, acceso a datos FireDAC o arquitectura de servidores REST como lecturas de profundización, de modo que el lector pueda avanzar al siguiente paso técnico de forma dirigida.
Conclusión: la migración a Unicode es un asunto de riesgo — con el método adecuado se hace planificable
La migración a Unicode de proyectos antiguos de Delphi no es una actualización cosmética, sino la corrección de supuestos fundamentales sobre texto, bytes e interfaces. Quien actúe de forma estructurada gana, sin embargo, más que «los umlauts funcionan de nuevo»: los flujos de datos quedan más claros, las integraciones más robustas y la posterior modernización (p. ej. servidores REST, servicios, limpieza de bases de datos) se facilita porque las codificaciones ya no ocurren implícita y „en algún lugar“.
Si necesita un plan de migración concreto para su aplicación Delphi, un análisis de riesgos de los hotspots o apoyo en la realización, el siguiente paso más rápido es una primera conversación técnica sobre sus condiciones marco y dependencias: ponerse en contacto.
En el entorno profesional Delphi Unicode Migration y Delphi Ansi Zu Unicode también desempeñan un papel importante cuando integraciones, flujos de datos y evolución deben encajar de forma limpia.
Discutir proyecto o iniciativa de modernización con Net-Base.