Net-Base Журнал

19.04.2026

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

Багато Delphi-наявних застосунків досі працюють з ANSI-рядками. Міграція на Unicode у цьому випадку — це більше, ніж перемикач компілятора: вона зачіпає доступ до даних, інтерфейси, звіти, сторонні бібліотеки та тести. У цьому дописі показано практичний шлях міграції — вкл...

19.04.2026

Міграція в Unicode старих Delphi-проектів в багатьох компаніях — необхідний крок, інакше існуючі застосунки з часом досягають обмежень при роботі з міжнародними даними, сучасними ОС, інтеграціями й новими інтерфейсами. На практиці це рідко буває «Recompile und fertig». Delphi з часів версій Unicode (починаючи з Delphi 2009) вніс фундаментальні зміни у стандартні типи рядків. Це зміщує припущення про кодування символів, макет пам’яті та сигнатури API. Хто це недооцінює, ризикує отримати поступово накопичувані помилки даних, зламані експортні файли, незрозумілі інциденти підтримки і проблеми безпеки.

У цьому матеріалі запропоновано технічно обґрунтований підхід: як провести аналіз коду, розумно обмежити scope, зменшити ризики в «гарячих точках» (бази даних, файли, Windows-API, COM, REST-сервіси) і зафіксувати міграцію так, щоб експлуатація та подальша розробка могли йти паралельно. Фокус — на типових підводних каменях Delphi у VCL-застосунках, сервісах і інтерфейсах — з поглядом на шляхи модернізації, в які пізніше можна вбудувати теми на кшталт BDE-вилучення з нативним підключенням, REST-серверів або мультиплатформених рішень.

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

У класичних версіях Delphi string був ANSI-рядком (залежно від системної codepage). З Delphi 2009 string за замовчуванням — UnicodeString (UTF-16). Водночас багато бібліотек і VCL-класів було переведено на Wide-API. Це в цілому позитивно, оскільки забезпечує надійну підтримку міжнародних символів. Але: у спадщині часто накопичувався код, що базується на припущеннях «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 часто як alias/AnsiString з UTF-8 codepage; практичний варіант для REST/JSON і багатьох протоколів.
  • WideString: BSTR (COM), керується через SysAllocString; сьогодні потрібен переважно для специфічних COM-interop сценаріїв.
  • PChar: з часу Unicode-Delphi це здебільшого PWideChar. Це одна з найпоширеніших точок руйнування при викликах Windows-API.

Якщо ці типи змішуються, виникають конвертації. Деякі з них коректні, інші — несподівані: конвертація є «правильною» лише тоді, коли ви знаєте, яка codepage використовується в джерелі й яка очікується на приймаючому боці.

UTF-16 внутрішньо, UTF-8 зовні: практичний принцип

У VCL-застосунках Delphi часто доцільно послідовно працювати внутрішньо з string (UTF-16). Зовні (REST, файли, messaging) у реальності домінує UTF-8. Робоча політика може бути такою:

  • Внутрішньо: string/UnicodeString як стандарт.
  • На межах: при вводі/виводі конвертація явно через TEncoding.UTF8 (або визначені ANSI-codepage).
  • Байтові операції: TBytes замість рядків.

Це зменшує неявні перетворення і робить відповідальності тестованими: «Де байти стають текстом, і в якому кодуванні?»

Інвентаризація: де Unicode у старих Delphi-проектах зазвичай дає збої

Перш ніж торкатися коду, варто провести структуровану інвентаризацію. У міграції Unicode старих Delphi-проєктів джерела помилок зазвичай не розподілені рівномірно, а концентруються в кількох «гарячих точках».

1) Доступ до баз даних і типи полів (BDE, ADO, FireDAC)

Багато старих проектів досі використовують BDE або застарілі рівні доступу до даних. Тут проблеми найчастіше такі:

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

Якщо все одно планується модернізація, міграцію в Unicode доцільно поєднати з очищенням доступу до даних, наприклад у бік BDE-Ablosung mit nativer Anbindung і чіткої конфігурації Charset (наприклад, для PostgreSQL або MariaDB). Важливо: міграція не повинна автоматично означати примусову зміну СУБД, але інтерфейс між БД і Delphi має бути однозначним.

2) Файловий та Stream-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, яка ні, і яку можна замінити або інкапсулювати? Інкапсуляція часто є найшвидшим шляхом, щоб перемістити спадщину ANSI у чітко окреслену область.

Стратегія: міграція Unicode старих Delphi-проєктів як контрольована програма модернізації

Найбільш безпечний підхід — багатоступенева програма, яка робить ризики помітними на ранніх етапах і при цьому зберігає застосунок працездатним.

Крок 1: визначити scope і пріоритизувати «гарячі точки» коду

Не кожен файл коду потребує негайних змін. Пріоритезуйте за потоками даних і ризиком:

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

Результат має бути списком місць, де «кодування — це специфікація». Ці точки пізніше роблять тестованими.

Крок 2: свідомо загострити попередження компілятора/проекту

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

Крок 3: ввести «границі кодування» як технічний шар

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

  • CSV-Reader/-Writer: завжди з 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-імпорти і зовнішні протоколи

У DLL-імпортах велика ймовірність того, що сигнатури в коді Delphi більше не підходять. Визначте, чого очікує DLL:

  • Якщо DLL очікує UTF-8, тоді звичайна практика — передавати PAnsiChar(UTF8String), але потрібно контролювати час життя рядка і нуль-термінування.
  • Якщо вона очікує UTF-16, використовуйте PWideChar і Wide-рядки.

У будь-якому випадку імпорти краще інкапсулювати в окремій unit, щоб політика роботи з рядками не поширювалася по всьому проєкту.

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

Unicode також вносить семантичні питання: upper/lower не тривіальні у всіх мовах, і символи можуть мати різні форми нормалізації. У типовому корпоративному ПЗ це рідше критично, ніж у текстових редакторах, але це стосується:

  • сортування і фільтрації (наприклад у грідах або пошукових функціях),
  • регістронезалежних порівнянь для ключових значень,
  • генерації імен файлів або ідентифікаторів.

Важливо мати чітке правило: що є «ключами» (наприклад, артикульні номери, коди клієнтів), які повинні лишатися 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 зазвичай дає кращий контроль, ніж дуже старі рівні доступу.

Старі формати файлів: правила міграції замість тихої конвертації

Якщо ваш застосунок роками створював файли (експортні формати, архіви, пропрієтарні структури), потрібно визначити:

  • Які наявні файли залишаються «як є» і як їх правильно інтерпретувати при читанні?
  • Які формати піднімаються до UTF-8?
  • Чи є в заголовках/версіях поля, що однозначно відрізняють нові й старі файли?

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

Забезпечення якості: тести, які дійсно знаходять Unicode-проблеми

Unicode-помилки часто залежать від даних. Тому «Happy Path»-тестів недостатньо. Доцільний набір тестів має покривати проблемні місця:

  • Roundtrip-тести: імпорт → обробка → експорт, потім побайтове порівняння (для визначених форматів).
  • DB-Roundtrip: запис/читання текстів з умляутами, акцентами і за потреби нелатинськими символами; перевірка на тотожність.
  • Тести інтерфейсів: REST-запити як UTF-8, перевірка заголовків, JSON-escape, логування.
  • Регресія: відтворення старих даних і типових користувацьких сценаріїв, особливо у пошуку, фільтрах, сортуванні.

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

Планування і обсяг робіт: що справді визначає складність

Обсяг робіт з міграції Unicode у старих Delphi-проектах залежить менше від «кількості рядків» і більше від зчеплень і зовнішніх залежностей:

  • Багато інтеграцій (DLL, COM, пристрої, ERP/DMS/CRM) збільшують обсяг перевірок, оскільки кодування важливе на кожній межі.
  • Історичні формати (старі експорти, кастомні CSV) потребують правил міграції і стратегій сумісності.
  • Змішані версії Delphi або декілька продуктів з одного code‑base підвищують потребу в координації.
  • Старі рівні доступу до даних (наприклад BDE) можуть опосередковано блокувати Unicode і вимагати модернізації.

На практиці працює підхід: спочатку стабілізувати Unicode у «серці» і в найкритичніших потоках даних. Далі поетапно переносити модулі. Це зменшує ризик і запобігає тривалим «big bang» фазам без релізів.

Вбудування в шляхи модернізації: REST, сервіси, мультиплатформа

Unicode часто є базовим елементом, коли потрібно модернізувати існуюче ПЗ. Типові питання далі:

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

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

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

Висновок: міграція Unicode — це питання ризику, яке з правильною методикою стає планованим

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

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

Поділитися дописом

Поділитися цим дописом безпосередньо

LinkedIn, X, XING, Facebook, WhatsApp та електронна пошта доступні негайно. Для Instagram ми готуємо посилання та короткий текст безпосередньо.

Електронна пошта

Instagram відкривається в новій вкладці. Посилання та короткий текст попередньо копіюються у буфер обміну.