Net-Base Revista

19.04.2026

Migração para Unicode de antigos projetos Delphi: armadilhas, estratégia e implementação limpa

Muitas aplicações legadas Delphi ainda trabalham com ANSI-Strings. Uma migração para Unicode é, portanto, mais do que uma simples opção do compilador: afeta o acesso a dados, interfaces, relatórios, bibliotecas de terceiros e testes. Este artigo mostra um caminho de migração prático, incluindo

19.04.2026

A migração para Unicode de projetos antigos Delphi é, em muitas empresas, um passo necessário, porque aplicações legadas esbarram cada vez mais em limites ao lidar com dados internacionais, sistemas operativos modernos, integrações e novas interfaces. Na prática raramente se trata de um “recompilar e pronto”. Delphi fez, desde as versões Unicode (a partir de Delphi 2009), mudanças fundamentais nos tipos de string padrão. Isso altera pressupostos sobre codificação de caracteres, layout de memória e assinaturas de API. Quem subestima isso produz erros de dados silenciosos, exports corrompidos, casos de suporte pouco claros e riscos de segurança.

Este artigo fornece uma abordagem com fundamento técnico: como analisar o legado, definir o escopo de forma sensata, reduzir riscos em pontos críticos (bancos de dados, ficheiros, Windows-APIs, COM, REST-services) e proteger a migração de modo que operação e evolução possam ocorrer em paralelo. O foco está nas armadilhas típicas de Delphi em aplicações VCL, services e interfaces — com vista para caminhos de modernização que mais tarde podem incluir temas como BDE-substituição com ligação nativa, REST-servers ou multiplataforma.

Por que a transição para Unicode em Delphi é frequentemente “maior do que se imagina”

Nas versões clássicas de Delphi o string era um ANSI-String (dependendo da codepage do sistema). Desde Delphi 2009 o string é, por padrão, um UnicodeString (UTF-16). Ao mesmo tempo muitas bibliotecas e classes VCL foram adaptadas para Wide-APIs. Isso é, em princípio, positivo porque oferece suporte robusto a caracteres internacionais. Mas: código legado cresceu ao longo dos anos em torno das suposições “1 carácter = 1 byte”, “PChar é PAnsiChar” ou “Length() corresponde ao número de bytes”.

As causas típicas de porque as migrações se tornam mais complexas:

  • Conversões implícitas podem até funcionar, mas alteram dados (especialmente em ficheiros, interfaces ou campos BLOB/texto de base de dados).
  • Código orientado a bytes (streams, buffers, hashing, encriptação) torna‑se incorreto sem que isso seja notado quando conteúdos de String são interpretados como bytes.
  • Componentes de terceiros são por vezes apenas ANSI, ou usam tipos de string e callbacks próprios.
  • Ambiente externo (Windows-APIs, COM, impressão/reporting, EDI, CSV, XML/JSON) espera determinadas codificações.

O objetivo, portanto, não deve ser “alterar o mínimo possível”, mas sim modificar de forma direcionada onde é necessário definir fluxos de dados e codificações. Uma migração limpa para Unicode é também uma oportunidade para finalmente documentar e testar limites de codificação pouco claros.

Fundamentos técnicos: tipos de string em Delphi, encodings e seus efeitos colaterais

string, UnicodeString, AnsiString, WideString – o que realmente importa no projeto

Para a migração é decisivo quais tipos são usados em interfaces e funções centrais:

  • string: desde Delphi 2009 um UnicodeString (UTF-16, com contagem de referências, semântica imutável via Copy-on-Write).
  • AnsiString: string de bytes com codepage associada (dependendo da versão de Delphi pode transportar uma codepage). Adequado quando uma interface externa exige explicitamente uma codificação 8‑bit específica.
  • UTF8String: em versões mais recentes de Delphi frequentemente um alias/AnsiString com codepage UTF-8; prático para REST/JSON e muitos protocolos.
  • WideString: BSTR (COM), gerido por SysAllocString; hoje geralmente necessário apenas para certos interops COM.
  • PChar: desde as versões Unicode de Delphi PWideChar. Este é um dos pontos de quebra mais frequentes em chamadas de Windows-APIs.

Quando esses tipos são misturados surgem conversões. Algumas são corretas, outras surpreendentes: uma conversão só é “certa” se souber qual codepage existe na origem e qual é esperada no destino.

UTF-16 internamente, UTF-8 externamente: um princípio prático

Em aplicações VCL de Delphi costuma fazer sentido trabalhar internamente de forma consistente com string (UTF-16). Externamente (REST, ficheiros, messaging) domina, na prática, o UTF-8. Uma linha robusta é, portanto:

  • Internamente: string/UnicodeString como padrão.
  • Fronteiras: ao ler/escrever converter explicitamente via TEncoding.UTF8 (ou codepages ANSI definidas).
  • Processamento orientado a bytes: TBytes em vez de Strings.

Isto reduz conversões implícitas e torna responsabilidades auditáveis: “Onde se transforma bytes em texto, e com que encoding?”

Inventário: onde o Unicode costuma quebrar em projetos antigos de Delphi

Antes de tocar no código vale uma inventariação estruturada. Na migração para Unicode de projetos antigos de Delphi as fontes de erro normalmente não estão distribuídas uniformemente, mas concentradas em alguns hotspots.

1) Acesso a bases de dados e tipos de campo (BDE, ADO, FireDAC)

Muitos projetos legados ainda usam BDE ou camadas de acesso mais antigas. Os problemas comuns aqui são:

  • Mapeamento de charsets de base de dados para strings de Delphi (ANSI vs. tipos de campo Unicode).
  • “Texto” em BLOBs ou campos Memo sem codificação definida.
  • SQL statements como Strings, que com Umlauts/caracteres Unicode são interpretados de forma diferente.

Se já existe um plano de modernização, uma migração para Unicode pode ser bem combinada com uma limpeza do acesso a dados, por exemplo rumo a BDE-Ablosung mit nativer Anbindung e configuração clara de charset (como em PostgreSQL ou MariaDB). Importante: uma migração não deve forçar automaticamente uma migração de base de dados, mas a interface entre BD e Delphi tem de ser inequívoca.

2) I/O de ficheiros e streams: CSV, INI, formatos proprietários, import/export

Um clássico: ficheiros eram lidos/escritos com AssignFile/ReadLn, TFileStream ou TStringList.LoadFromFile sem definir encoding. Em Delphi Unicode o runtime decide heurísticamente (BOM) ou usa encodings por defeito. Isso leva a:

  • interpretações erradas de Umlauts (ä, ö) em CSV/logs,
  • erros em contagens de comprimento em formatos proprietários,
  • incompatibilidades com parceiros externos que esperam ISO-8859-1 ou Windows-1252.

Uma solução limpa é definir um encoding fixo por formato de ficheiro e ancorá‑lo no código e na documentação. Para CSV/JSON UTF-8 é na maioria dos casos a escolha correta; para interfaces antigas por vezes Windows-1252. O decisivo é a explicitude.

3) Windows-API, PChar, tamanhos de buffer e tratamento de mensagens

Muitas aplicações Delphi chamam funções da WinAPI ou trabalham com buffers. Pontos de quebra frequentes:

  • Uso de PChar em conjunto com funções que têm variantes ANSI ou Wide (…A/…W).
  • Tamanhos de buffer calculados em bytes, mas Char em UTF-16 são 2 bytes.
  • Aritmética de ponteiros e layouts de records que assumem chars de 1 byte.

Aqui é preciso refatoração precisa: ou usar consistentemente Wide-APIs, ou chamar deliberadamente a variante ANSI e trabalhar com AnsiString/codepage. “Compila de alguma forma” não é critério de qualidade.

4) COM, ActiveX, Office-Automation e bibliotecas de terceiros

Interfaces COM frequentemente trabalham com BSTR (WideString). Em versões antigas de Delphi havia outros defaults de string, de modo que o código “casava por sorte”. Em Delphi Unicode surgem frequentemente conversões duplicadas ou pressupostos de tipo errados em wrappers. Bibliotecas de terceiros também são críticas: umas passam callbacks como PAnsiChar, outras esperam byte-strings terminados em null.

Vale classificar dependências: qual biblioteca está pronta para Unicode, qual não está, e qual pode ser substituída ou encapsulada? Encapsular é muitas vezes o caminho mais rápido para isolar legados Unicode numa área bem definida.

Estratégia: tratar a migração para Unicode de projetos antigos de Delphi como um programa de modernização controlado

A abordagem que minimiza riscos é um programa em várias etapas que torna problemas visíveis cedo e mantém a aplicação operacional.

Passo 1: definir o escopo e priorizar hotspots de código

Nem todo o código-fonte precisa de alteração imediata. Priorize pelo fluxo de dados e risco:

  • Interfaces externas (REST-API, TCP/IP, ficheiros, e-mail, impressão/reporting).
  • Acesso a dados (SQL, ORM/Datamodule, BDE/FireDAC-camadas).
  • Funções utilitárias próximas de strings (parser, formatter, encoder/decoder).
  • Integrações (COM, DLL-Imports, ligações a hardware).

O resultado deve ser uma lista de locais onde “encoding é uma especificação”. Esses pontos serão tornados testáveis mais tarde.

Passo 2: ativar opções do compilador e warnings de forma consciente

Em muitos projetos os warnings foram desligados ao longo dos anos. Para uma migração Unicode isso é contraproducente. É sensato reativar warnings e levar a sério avisos de conversão. Ajuda também definir regras a nível de projeto, por exemplo: sem conversões implícitas para AnsiString em fronteiras de I/O, uso de TEncoding nas operações de ficheiro, sem “truques com PChar” sem contexto claro.

Passo 3: introduzir “fronteiras de encoding” como camada técnica

Um recurso arquitetural prático é introduzir pequenos adapters/helpers que definam explicitamente como dados externos entram e saem. Exemplos:

  • CSV-Reader/-Writer: sempre com TEncoding.UTF8 (ou codepage definida) e regras claras de separador.
  • REST-Client/Server: JSON sempre como bytes UTF-8, cabeçalhos corretamente definidos, body não transmitido como „baseado em string“.
  • Windows-API-Wrapper: funções centrais que encapsulam Wide/Ansi de forma limpa.

Assim evita-se que decisões de encoding se espalhem desordenadamente pela base de código.

Armadi|lhas de código típicas e como corrigi-las de forma limpa

Length, SizeOf, ByteLength: quando comprimento de caracteres e tamanho em bytes divergem

Em tempos ANSI Length(s) era frequentemente usado como número de bytes. Em UTF-16 isso é errado. Quando precisar de arrays de bytes converta explicitamente:

  • Para UTF-8: TEncoding.UTF8.GetBytes(s)
  • Para codepage ANSI definida: TEncoding.GetEncoding(1252).GetBytes(s) (apenas se for correto do ponto de vista funcional)

Para tamanhos de buffer em chamadas de API: verifique se a função espera unidades em caracteres ou em bytes. Muitas Wide-APIs esperam número de caracteres, não bytes. A documentação e a assinatura decidem, não a intuição.

PAnsiChar vs. PWideChar: DLL-Imports e protocolos externos

Em imports de DLL existe grande risco de assinaturas no código Delphi deixarem de corresponder. Defina o que a DLL espera:

  • A DLL espera UTF-8? Então a passagem como PAnsiChar(UTF8String) é comum, mas é preciso controlar a vida útil e a terminação nula.
  • Espera UTF-16? Então use PWideChar e wide-strings.

Em todo caso, encapsule os imports numa unit separada para que a política de strings não se espalhe pelo projeto.

Formatação, mudança de caixa, comparação: locale e normalização

Unicode traz também questões semânticas: maiúsculas/minúsculas não são triviais em todas as línguas, e caracteres podem ter várias formas de normalização. Em aplicações empresariais típicas isso é menos crítico do que em editores de texto para consumidores, mas afeta:

  • Ordenação e filtragem (por exemplo em grids ou funções de pesquisa),
  • Comparações case-insensitive para valores chave,
  • Geração de nomes de ficheiros ou identificadores.

É importante ter uma regra clara: o que são “chaves” (por exemplo números de artigo, códigos de cliente) que devem permanecer próximos do ASCII, e o que são “textos” que precisam de suporte Unicode total? Essa separação reduz erros subsequentes.

GUI/Reporting: fontes, impressão, PDF e comportamento de componentes

VCL é, desde as versões Unicode, capaz de Unicode, mas na prática depende de componentes e caminhos de saída. Riscos surgem com:

  • motores de reporting ou geradores de PDF antigos que assumem ANSI,
  • impressão de códigos de barras/etiquetas que requer codepages específicas,
  • fonts ou conjuntos de caracteres hardcoded.

Planeie testes cedo com dados de exemplo reais (nomes, localidades, caracteres especiais, alfabetos não latinos, quando relevantes). O valor está menos em “pode fazer Unicode” e mais em comprovar: “Esta saída está correta no nosso contexto.”

Dados e persistência: Unicode não termina no código

Definir corretamente charsets e collations nas bases de dados

Uma migração para Unicode é estável apenas se bases de dados e drivers estiverem configurados corretamente. Exemplos:

  • Em PostgreSQL UTF-8 é normalmente o padrão; ainda assim é necessário verificar client_encoding e o comportamento do driver.
  • Em SQL Server a distinção entre VARCHAR e NVARCHAR é relevante; escolha de colunas incorreta pode levar à perda de caracteres.
  • Em MariaDB/MySQL charset/collation (por exemplo utf8mb4) é decisivo para que caracteres de 4 bytes não sejam truncados.

No código Delphi parâmetros e tipos de campo devem ser usados de forma a evitar que o Unicode seja “reconvertido de volta” no caminho. FireDAC normalmente oferece melhor controlo do que camadas de acesso muito antigas.

Formados de ficheiro legados: regras de migração em vez de conversão silenciosa

Se a sua aplicação gerou ficheiros ao longo de anos (formatos de exportação, ficheiros de arquivo, estruturas proprietárias) precisa definir:

  • Que ficheiros existentes permanecem “como estão” e serão lidos corretamente?
  • Que formatos serão promovidos para UTF-8?
  • Existem campos de versão/headers para distinguir claramente ficheiros novos e antigos?

Conversão silenciosa sem identificação é arriscada, porque erros aparecem tardiamente. Melhor: versionar, identificar e migrar de forma explícita.

Garantia de qualidade: testes que realmente encontram problemas Unicode

Erros Unicode dependem frequentemente dos dados. Por isso testes do “caminho feliz” não são suficientes. É sensato ter um conjunto de testes que cubra os pontos problemáticos:

  • Roundtrip-Tests: Import → Processamento → Export, seguido de comparação byte-a-byte (para formatos definidos).
  • DB-Roundtrip: escrita/leitura de textos com Umlauts, acentos e, se aplicável, caracteres não latinos; verificação de igualdade.
  • Testes de interface: requests REST como UTF-8, cabeçalhos, JSON-escaping, logging.
  • Regressão: reproduzir dados legados e casos de utilizador típicos, sobretudo em pesquisa, filtragem e ordenação.

Para sistemas B2B é também relevante que erros sejam observáveis: logging não deve destruir encodings. Quem escreve logs em ANSI perde, em caso de falha, exatamente a informação necessária.

Planeamento e esforço: o que realmente aumenta a complexidade

O esforço de migrar para Unicode em projetos antigos de Delphi depende menos de “linhas de código” e mais de acoplamentos e dependências externas:

  • Muitas integrações (DLLs, COM, dispositivos, ERP/DMS/CRM) aumentam o esforço de verificação, porque encoding é relevante em cada fronteira.
  • Formatos históricos (exports antigos, CSVs específicos de cliente) exigem regras de migração e estratégias de compatibilidade.
  • Versões mistas de Delphi ou vários produtos derivados de um mesmo código aumentam a necessidade de coordenação.
  • Camadas antigas de acesso a dados (por exemplo BDE) podem bloquear Unicode indiretamente e justificar modernização.

Na prática tem-se mostrado eficaz estabilizar primeiro o Unicode no núcleo e nos fluxos de dados mais críticos. Depois podem‑se ir atualizando módulos de forma faseada. Isso reduz risco e evita longos períodos de “big bang” sem releases.

Inserção em caminhos de modernização: REST, services, multiplataforma

Unicode é frequentemente um pilar quando software legado é modernizado. Perguntas seguintes típicas são:

  • REST-Servers ou REST-APIs a montar (tratar JSON/UTF-8 de forma correcta).
  • Operar Windows-services ou Linux-services de forma estável (logging, ficheiros de configuração, protocolos).
  • Modernização gradual da UI em VCL, mais tarde eventualmente clientes multiplataforma.

A ordem importa: ao construir novas interfaces os regras de encoding devem estar definidas previamente. Uma migração Unicode “em paralelo” com o desenvolvimento de interfaces novas tende a produzir erros difíceis de testar, porque causa e efeito ficam misturados.

Para ligação interna no magazine é adequado colocar temas adjacentes como modernização de Delphi, acesso a dados FireDAC ou arquitetura de REST-servers como artigos de aprofundamento, para que o leitor possa seguir o próximo passo técnico.

Conclusão: a migração para Unicode é um tema de risco — com o método certo torna-se previsível

A migração para Unicode de projetos antigos de Delphi não é uma atualização cosmética, mas uma correção de pressupostos fundamentais sobre texto, bytes e interfaces. Quem agir de forma estruturada ganha, porém, mais do que “os Umlauts voltam a funcionar”: fluxos de dados tornam-se inequívocos, integrações mais robustas e modernizações posteriores (por exemplo REST-servers, services, limpeza de bases de dados) ficam mais simples porque encodings deixam de acontecer implicitamente “em algum lugar”.

Se precisa de um plano de migração concreto para a sua aplicação Delphi, de uma análise de risco dos hotspots ou de apoio na execução, o passo seguinte mais rápido é uma conversa técnica inicial sobre os seus requisitos e dependências: entre em contacto.

No âmbito técnico, também a Delphi Unicode Migration e Delphi Ansi Zu Unicode desempenham um papel importante quando integrações, fluxos de dados e evolução têm de operar de forma coerente.

Discutir projeto ou iniciativa de modernização com Net-Base.

Partilhar publicação

Compartilhar esta publicação diretamente

LinkedIn, X, XING, Facebook, WhatsApp e e‑mail estão imediatamente disponíveis. Para o Instagram, preparamos o link e um texto curto de imediato.

E-mail

O Instagram abre numa nova aba. O link e o texto curto são copiados previamente para a área de transferência.