Net-Base Журнал

19.04.2026

Миграция на Unicode старых проектов Delphi: подводные камни, стратегия и аккуратная реализация

Многие Delphi-существующие приложения по-прежнему работают с ANSI-строками. Миграция на Unicode — это больше, чем переключатель компилятора: она затрагивает доступ к данным, интерфейсы, отчёты, сторонние библиотеки и тесты. В этой статье показан практический путь миграции — включая

19.04.2026

Миграция на Unicode старых Delphi-проектов в большинстве компаний является необходимым шагом, поскольку старые приложения иначе начинают сталкиваться с ограничениями при работе с международными данными, современными операционными системами, интеграциями и новыми интерфейсами. На практике это редко сводится к «перекомпиляции и готово». Delphi с момента внедрения Unicode (начиная с Delphi 2009) внесли фундаментальные изменения в стандартные строковые типы. Это сместило предположения о кодировке символов, layout памяти и сигнатурах API. Если это недооценивать, появляются постепенные ошибки данных, сломанные экспорты, неясные обращения в поддержку и риски безопасности.

В этой статье изложен технически обоснованный подход: как проанализировать существующий код, разумно ограничить объём работ, снизить риски в горячих точках (базы данных, файлы, Windows-API, COM, REST-сервисы) и защитить миграцию так, чтобы эксплуатация и дальнейшая разработка могли идти параллельно. Фокус на типичных подводных камнях Delphi в VCL-приложениях, службах и интерфейсах — с прицелом на пути модернизации, в которые позже можно встроить такие темы, как BDE-замещение с нативным подключением, REST-сервера или мультиплатформенность.

Почему переход на Unicode в Delphi часто оказывается «больше, чем ожидалось»

В классических версиях Delphi тип string был ANSI-строкой (в зависимости от системной codepage). Начиная с Delphi 2009 string по умолчанию — это UnicodeString (UTF-16). Одновременно многие библиотеки и VCL-классы были переведены на Wide-APIs. Это в общем случае положительно, потому что надёжно поддерживает международные символы. Но унаследованный код зачастую годами строился вокруг допущений «1 символ = 1 байт», «PChar — это PAnsiChar» или «Length() равен количеству байт».

Типичные причины, почему миграции становятся более трудоёмкими:

  • Неявные конверсии проходят, но меняют данные (особенно при работе с файлами, интерфейсами или BLOB/Text-полями в БД).
  • Код, ориентированный на байты (потоки, буферы, хеширование, шифрование) становится незаметно неверным, если содержимое строк интерпретируется как байты.
  • Сторонние компоненты частично поддерживают только ANSI или используют собственные строковые типы и callback’и.
  • Внешняя среда (Windows-API, COM, печать/отчёты, EDI, CSV, XML/JSON) ожидает определённые кодировки.

Цель не должна сводиться к «как можно меньше менять», а к целенаправленному изменению там, где нужно однозначно определить потоки данных и кодировки. Корректная миграция на Unicode — это также возможность документировать и протестировать неочевидные границы кодирования.

Технические основы: строковые типы Delphi, кодировки и их побочные эффекты

string, UnicodeString, AnsiString, WideString — что действительно важно в проекте

Для миграции критично, какие типы используются на интерфейсах и в ключевых функциях:

  • string: с Delphi 2009 это UnicodeString (UTF-16, reference-counted, семантика immutable через Copy-on-Write).
  • AnsiString: байтовая строка с привязкой к codepage (в зависимости от версии Delphi codepage может храниться). Подходит, если внешняя интерфейс явно требует конкретной 8-битной кодировки.
  • UTF8String: в новых версиях Delphi часто встречается как алиас/AnsiString с UTF-8 codepage; практичен для REST/JSON и многих протоколов.
  • WideString: BSTR (COM), управление памятью через SysAllocString; сейчас чаще требуется только для отдельных COM-interop случаев.
  • PChar: с Unicode-Delphi это PWideChar. Это одна из самых частых точек разрыва при Windows-вызовах.

При смешении этих типов происходят конверсии. Некоторые корректны, некоторые неожиданны: конверсия «правильна» только если вы знаете, какая codepage применялась у источника и какая ожидается у приёмника.

Внутри UTF-16, снаружи UTF-8: практическое руководство

В VCL-приложениях на Delphi часто выгодно внутри системы последовательно работать со string (UTF-16). Снаружи (в REST, файлах, messaging) в реальности доминирует UTF-8. Надёжная установка правил может выглядеть так:

  • Внутри: string/UnicodeString как стандарт.
  • Границы: при вводе/выводе явно конвертировать через TEncoding.UTF8 (или через определённые ANSI-codepages).
  • Байтовая обработка: использовать TBytes вместо строк.

Это снижает количество неявных конверсий и делает ответственность проверяемой: «Где байты превращаются в текст и в какой кодировке?»

Инвентаризация: где Unicode в старых Delphi-проектах типично ломается

Прежде чем менять код, имеет смысл выполнить структурированный аудит. В миграциях на Unicode источники ошибок обычно сосредоточены не равномерно, а в нескольких горячих точках.

1) Доступ к базе данных и типы полей (BDE, ADO, FireDAC)

Многие старые проекты до сих пор используют BDE или более старые уровни доступа к данным. Здесь типичные проблемы:

  • Соответствие charset базы данных и строк Delphi (ANSI против Unicode-полей).
  • «Текст» в BLOB-ах или Memo-полях без определённой кодировки.
  • SQL-операторы как строки, которые при наличии умляутов/Unicode-символов интерпретируются по-разному.

Если уже планируется модернизация, миграцию на Unicode удобно связать с ревизией доступа к данным, например в сторону BDE-Ablosung mit nativer Anbindung и явной настройки charset (как в PostgreSQL или MariaDB). Важно: миграция не обязательно должна автоматически влечь перенос БД, но интерфейс между БД и Delphi должен быть однозначным.

2) Файловый и потоковый I/O: CSV, INI, проприетарные форматы, импорт/экспорт

Классический случай: файлы ранее читались/писались через AssignFile/ReadLn, TFileStream или TStringList.LoadFromFile без явного указания кодировки. В Unicode-Delphi поведение определяется heuristisch (BOM) или используются кодировки по умолчанию. Это приводит к:

  • неправильно интерпретированным умляутам (ä, ö) в CSV/логах,
  • ошибочным значениям длины в проприетарных форматах,
  • несовместимостям с внешними партнёрами, ожидающими ISO-8859-1 или Windows-1252.

Чистое решение — для каждого формата файла задать фиксированную кодировку и зафиксировать это в коде и документации. Для CSV/JSON обычно правильным стандартом является UTF-8, для старых интерфейсов иногда — Windows-1252. Ключевое — явность.

3) Windows-API, PChar, размеры буферов и обработка сообщений

Многие Delphi-приложения вызывают WinAPI-функции или работают с буферами. Частые точки разрыва:

  • Использование PChar вместе с функциями, имеющими ANSI- и Wide-варианты (…A/…W).
  • Размеры буферов считаются в байтах, но Char в UTF-16 занимает 2 байта.
  • Указательная арифметика и layout Record-ов, рассчитанные на 1-байтовые Char.

Требуется точное рефакторинг: либо последовательно использовать Wide-API, либо сознательно вызывать ANSI-вариант и работать через AnsiString/соответствующую codepage. «Просто компилируется» — не критерий качества.

4) COM, ActiveX, Office-Automation и сторонние библиотеки

COM-интерфейсы часто работают через BSTR (WideString). В старых версиях Delphi поведение по умолчанию было иным, поэтому код «случайно» работал. В Unicode-версиях часто возникают двойные конверсии или неверные предположения о типах в обёртках. Сторонние библиотеки тоже критичны: некоторые возвращают callback’и как PAnsiChar, другие ожидают нуль-терминированные байтовые строки.

Здесь имеет смысл классифицировать зависимости: какая библиотека готова к Unicode, какая нет, и какие можно заменить или инкапсулировать. Инкапсуляция часто оказывается самым быстрым способом локализовать Unicode-унаследованность в чётко ограниченной области.

Стратегия: миграция старых Delphi-проектов как контролируемая программа модернизации

Наиболее безопасный путь — многоэтапная программа, которая делает риски видимыми на ранних стадиях и при этом сохраняет работоспособность приложения.

Шаг 1: определить объём работ и приоритизировать горячие участки кода

Не весь исходный код требует немедленных изменений. Приоритизируйте по потокам данных и риску:

  • Интерфейсы наружу (REST-API, TCP/IP, файлы, e‑mail, печать/отчёты).
  • Доступ к данным (SQL, ORM/Datamodule, BDE/FireDAC-слои).
  • Утилиты, тесно работающие со строками (парсеры, форматтеры, encoder/decoder).
  • Интеграции (COM, DLL-imports, подключение оборудования).

Результатом должна стать перечень мест, где «кодировка — это спецификация». Эти места затем делаются тестируемыми.

Шаг 2: жёстко включить опции компилятора и предупреждения

Во многих проектах предупреждения годами отключались. Для миграции на Unicode это контрпродуктивно. Рекомендуется вновь включить предупреждения и серьёзно относиться к предупреждениям о конверсиях. Дополнительно полезно ввести проектные правила, например: никаких неявных AnsiString-конверсий на границах I/O, использовать TEncoding при файловых операциях, никаких «PChar‑трюков» без ясного контекста.

Шаг 3: ввести «границы кодировок» как технический слой

Практичный архитектурный приём — добавить небольшие адаптеры/хелперы, которые чётко определяют, как внешние данные входят и выходят. Примеры:

  • CSV-ридер/-райтер: всегда с TEncoding.UTF8 (или определённой codepage) и ясными правилами разделителей.
  • REST-клиент/сервер: JSON всегда как UTF-8-байты, заголовки корректно выставлены, тело не стримится «как строка».
  • Windows-API-обёртки: центральные функции, которые чисто инкапсулируют Wide/Ansi.

Так вы не допустите, чтобы «решения о кодировке» распределялись по всей кодовой базе.

Типичные кодовые ловушки и как их корректно исправлять

Length, SizeOf, ByteLength: когда длина в символах и в байтах расходятся

Во времена ANSI Length(s) часто использовали как число байт. В UTF-16 это неверно. Если нужны массивы байт, конвертируйте явно:

  • Для UTF-8: TEncoding.UTF8.GetBytes(s)
  • Для определённой ANSI-codepage: TEncoding.GetEncoding(1252).GetBytes(s) (только если это семантически верно)

Для размеров буферов при вызовах API проверьте, ожидает ли функция единицы измерения в символах или в байтах. Многие Wide-API ожидают количество символов, а не байт. Руководящими являются документация и сигнатура, а не интуиция.

PAnsiChar vs. PWideChar: DLL-imports и внешние протоколы

При DLL-imports высок риск, что сигнатуры в коде Delphi перестанут соответствовать. Установите, чего именно ожидает DLL:

  • Ожидает ли DLL UTF-8? Тогда распространённая практика — передача PAnsiChar(UTF8String), но при этом нужно контролировать время жизни и нуль-терминирование.
  • Ожидает ли она UTF-16? Тогда используйте PWideChar и Wide-строки.

Во всех случаях полезно инкапсулировать импорты в отдельной Unit, чтобы политика обращения со строками не размывалась по проекту.

Форматирование, изменение регистра, сравнение: локаль и нормализация

Unicode добавляет и семантические вопросы: приведение к верхнему/нижнему регистру не тривиально во всех языках, символы могут существовать в разных формах нормализации. Для корпоративных приложений это обычно менее критично, чем для текстовых редакторов, но затрагивает:

  • сортировки и фильтрацию (например, в гридах или поиске),
  • регистронезависимые сравнения ключевых значений,
  • генерацию имён файлов или идентификаторов.

Важна ясная политика: какие поля являются «ключами» (например, артикулы, коды клиентов), которые должны оставаться ASCII-близкими, и какие — «тексты», требующие полной поддержки Unicode. Такое разделение снижает последующие ошибки.

GUI/Reporting: шрифты, печать, PDF и поведение компонентов

VCL с Unicode-версиями в целом поддерживает Unicode, но на практике всё сводится к компонентам и путям вывода. Риски возникают при:

  • старых генераторах отчётов или PDF-генераторах, предполагающих ANSI,
  • печати штрихкодов/этикеток, требующей конкретных codepage,
  • жёстко заданных шрифтах или наборах символов.

Планируйте ранние тесты на реальных примерах (имена, места, спецсимволы, нелатинские алфавиты, если они релевантны). Ценность не в том, что компонент «умеет Unicode», а в подтверждении: «Этот вывод в нашем контексте корректен.»

Данные и персистентность: Unicode не заканчивается в коде

Чёткое задание Charset и Collation в БД

Миграция на Unicode стабильна только если базы данных и драйверы настроены правильно. Примеры:

  • В PostgreSQL UTF-8 обычно является стандартом; тем не менее нужно проверить client-encoding и поведение драйвера.
  • В SQL Server важно различие между VARCHAR и NVARCHAR; неверный выбор столбца может привести к потере символов.
  • В MariaDB/MySQL charset/collation (например, utf8mb4) критичны, чтобы 4-байтовые символы не обрезались.

В Delphi-коде параметры и типы полей должны использоваться так, чтобы Unicode не «конвертировался обратно» по пути. FireDAC обычно даёт лучшую контрольность по сравнению с очень старыми слоями доступа.

Legacy-файловые форматы: правила миграции вместо тихой конверсии

Если ваше приложение годами генерировало файлы (экспортные форматы, архивы, проприетарные структуры), нужно определить:

  • Какие существующие файлы остаются «как есть» и будут корректно интерпретироваться при чтении?
  • Какие форматы будут переведены на UTF-8?
  • Есть ли поля версии/заголовки, чтобы однозначно отличать новые и старые файлы?

Тихая конверсия без маркировки рискованна, потому что ошибки часто проявляются поздно. Лучше: версионировать, распознавать явно и мигрировать целенаправленно.

Контроль качества: тесты, которые действительно находят Unicode-проблемы

Ошибки Unicode часто зависят от данных. Поэтому «happy path» тесты недостаточны. Полезен тестовый набор, покрывающий проблемные зоны:

  • Roundtrip-тесты: импорт → обработка → экспорт с последующим пословным/байтовым сравнением (для определённых форматов).
  • DB-Roundtrip: запись/чтение текстов с умляутами, акцентами и, при необходимости, нелатинскими символами; проверка эквивалентности.
  • Интерфейсные тесты: REST-запросы как UTF-8, корректные заголовки, JSON-эскейпинг, логирование.
  • Регрессии: воспроизведение старых данных и типовых пользовательских сценариев, особенно поиск, фильтрация, сортировка.

Для B2B-систем также важно, чтобы ошибки были наблюдаемы: логирование не должно разрушать кодировки. Если логи пишутся в ANSI, в случае ошибки вы теряете именно ту информацию, которая нужна.

Планирование и объём работ: что действительно увеличивает сложность

Объём работ по миграции старых Delphi-проектов зависит не столько от количества строк, сколько от связей и внешних зависимостей:

  • Много интеграций (DLL, COM, устройства, ERP/DMS/CRM) увеличивают объём проверок, потому что кодировки важны на каждой границе.
  • Исторические форматы (старые экспорты, кастомные CSV) требуют правил миграции и стратегий совместимости.
  • Смешанные версии Delphi или несколько продуктов из одного кодового ствола увеличивают координационные усилия.
  • Старые слои доступа к данным (например, BDE) могут косвенно блокировать Unicode и накладывать необходимость модернизации.

На практике эффективна стратегия: сначала стабилизировать Unicode в ядре и в самых критичных потоках данных. Затем постепенно приводить остальные модули. Это снижает риски и предотвращает долгие «Big Bang» этапы без релиза.

Встраивание в пути модернизации: REST, сервисы, мультиплатформа

Unicode часто является фундаментом, когда речь идёт о модернизации существующего ПО. Типичные сопутствующие вопросы:

  • REST-сервера или REST-API — добавить (обрабатывать JSON/UTF-8 корректно).
  • Стабильно эксплуатировать Windows-сервисы или Linux-сервисы (логирование, конфиги, протоколы).
  • Пошаговая модернизация UI в VCL, позднее — возможные мультиплатформенные клиенты.

Порядок важен: если вы создаёте новые интерфейсы, правила по кодировкам должны быть утверждены заранее. Миграция на Unicode «в процессе» разработки интерфейсов приведёт к трудноотслеживаемым ошибкам, где причина и следствие смешиваются.

Для внутренних ссылок в журнале имеет смысл разместить смежные темы, такие как модернизация Delphi, доступ к данным через FireDAC или архитектура REST-серверов как углубляющие статьи, чтобы читатели могли перейти к следующему техническому шагу.

Вывод: миграция на Unicode — это риск, но с правильным методом он станови­тся планируемым

Миграция на Unicode старых Delphi-проектов — не косметическое обновление, а исправление базовых допущений о тексте, байтах и интерфейсах. При структурированном подходе вы получите больше, чем «умляуты снова работают»: потоки данных станут однозначными, интеграции — более надёжными, а последующая модернизация (например, REST-сервера, сервисы, очистка БД) упростится, поскольку кодировки перестанут происходить неявно «где‑то там».

Если вам нужен конкретный план миграции для вашей Delphi-системы, анализ рисков по горячим точкам или помощь в реализации — самым быстрым следующим шагом будет техническая предварительная встреча по вашим рамкам и зависимостям: свяжитесь с нами.

В профессиональном контексте также важны темы Delphi Unicode Migration и Delphi Ansi Zu Unicode, когда интеграции, потоки данных и дальнейшая разработка должны работать согласованно.

Проект или задача модернизации обсудить с Net-Base.

Поделиться записью

Поделиться этой записью напрямую

LinkedIn, X, XING, Facebook, WhatsApp и E-Mail доступны сразу. Для Instagram мы сразу подготовим ссылку и краткий текст.

Электронная почта

Instagram открывается в новой вкладке. Ссылка и короткий текст предварительно копируются в буфер обмена.