Unicode-migracija starih Delphi-projektov je v mnogih podjetjih potreben korak, saj obstoječe aplikacije sicer pri mednarodnih podatkih, sodobnih operacijskih sistemih, integracijah in novih vmesnikih vse bolj naletijo na omejitve. V praksi to redko pomeni »ponovno prevajanje in to je vse«. Delphi je od Unicode-izvedb (od Delphi 2009) uvedel temeljne spremembe standardnih tipov nizov. S tem se spremenijo predpostavke o kodiranju znakov, razporeditvi v pomnilniku in podpisih API-jev. Kdor to podcenjuje, povzroča prikrite napake v podatkih, pokvarjene izvoze, nejasne support-primere in varnostna tveganja.
Ta prispevek poda tehnično utemeljeno postopnost: kako analizirati obstoječe stanje, smiselno omejiti obseg, zmanjšati tveganja na vročih točkah (baze podatkov, datoteke, Windows-APIs, COM, REST-storitve) in migracijo zavarovati tako, da ostaneta obratovanje in nadaljnji razvoj možna vzporedno. Fokus je na Delphi-tipičnih pasti v VCL-aplikacijah, storitvah in vmesnikih – z vidika modernizacijskih poti, v katere se kasneje lahko uvrstijo tudi teme, kot so BDE-zamenjava z nativno povezavo, REST-strežniki ali večplatformnost.
Zakaj je prehod na Unicode v Delphi pogosto »večji, kot se zdi«
V klasičnih različicah Delphi je bil string ANSI-niz (odvisno od sistemske kodene strani). Od Delphi 2009 je string privzeto UnicodeString (UTF-16). Hkrati so bile mnoge knjižnice in VCL-klasse preurejene na Wide-API-je. To je načeloma pozitivno, saj robustno podpira mednarodne znake. Vendar: legacy-koda je pogosto skozi leta zrasla okoli predpostavk »1 znak = 1 bajt«, »PChar je PAnsiChar« ali »Length() ustreza številu bajtov«.
Tipični razlogi, zakaj so migracije zahtevnejše:
- Implicitne konverzije sicer potekajo, a spreminjajo podatke (še posebej pri datotekah, vmesnikih ali poljih BLOB/tekst v bazi).
- Na bajt usmerjena koda (tokovi, bufferji, hashing, šifriranje) postane neopazno napačna, če se vsebina nizov interpretira kot surovi bajti.
- Tretje komponente so deloma le ANSI, ali uporabljajo lastne tipe nizov in povratne klice.
- Zunanji kontekst (Windows-APIs, COM, tisk/poročanje, EDI, CSV, XML/JSON) pričakuje določena kodiranja.
Cilj zato ni »spremeniti čim manj«, temveč ciljano tam, kjer je treba določiti pretoke podatkov in kodiranja. Čista Unicode-migracija je tudi priložnost, da nejasne meje kodiranja končno dokumentiramo in testiramo.
Tehnične osnove: Delphi-tipi nizov, kodiranja in njihove posledice
string, UnicodeString, AnsiString, WideString – kaj v projektu zares šteje
Za migracijo je odločilno, kateri tipi se uporabljajo na vmesnikih in v jedrnih funkcijah:
- string: Od Delphi 2009 UnicodeString (UTF-16, z upravljanjem referenc, semantika nespremenljivosti prek Copy-on-Write).
- AnsiString: Niz bajtov z dodeljeno kodeno stranjo (odvisno od različice Delphi je lahko kodejna stran shranjena). Uporaben, kadar zunanji vmesnik izrecno zahteva določeno 8-bitno kodiranje.
- UTF8String: V novejših različicah Delphi pogosto kot alias/AnsiString z UTF-8 kodejno stranjo; uporaben za REST/JSON in mnoge protokole.
- WideString: BSTR (COM), upravljanje z uporabo SysAllocString; danes večinoma potreben le za specifične COM-interope.
- PChar: V Unicode-Delphi PWideChar. To je ena pogostih točk zloma pri Windows-klicih API-jev.
Če se ti tipi mešajo, nastajajo konverzije. Nekatere so pravilne, druge presenetljive: konverzija je pravilna le, če veste, katera kodejna stran izvira na viru in katera se pričakuje na cilju.
UTF-16 interno, UTF-8 eksterno: praktično vodilo
V VCL-aplikacijah Delphi je pogosto smiselno interno dosledno delati z string (UTF-16). Eksterno ({{NBML_TERM_7_50780f47}}, datoteke, messaging) v praksi prevladuje UTF-8. Robusten pristop je zato:
- Intern: string/UnicodeString kot standard.
- Meje: pri vhodu/izhodu eksplicitno konvertirati preko TEncoding.UTF8 (ali definirane ANSI-kodene strani).
- Na bajt osnovane obdelave: TBytes namesto nizov.
To zmanjša implicitne konverzije in naredi odgovornosti preverljive: »Kje se iz bajtov naredi tekst in s katerim kodiranjem?«
Inventura: Kje se Unicode v starih Delphi-projektih tipično sesuje
Preden se lotite kode, se izplača strukturirana inventura. Pri Unicode-migraciji starih Delphi-projektov so viri napak ponavadi skoncentrirani na nekaj vročih točk.
1) Dostop do baze podatkov in tipi polj (BDE, ADO, FireDAC)
Mnogi stari projekti še uporabljajo BDE ali starejše plasti dostopa do podatkov. Pogoste težave so:
- Povezava charsetov baze podatkov z Delphi-nizi (ANSI proti Unicode-poljem).
- »Tekst« v BLOB-ih ali memo-poljih brez definirane kodirne strani.
- SQL-statementi kot nizi, ki se pri umlautih/Unicode-znakih različno interpretirajo.
Če že načrtujete modernizacijo, se Unicode-migracija dobro poveže z čiščenjem dostopa do podatkov, npr. v smeri BDE-Ablosung mit nativer Anbindung in jasne konfiguracije charsetov (kot pri PostgreSQL ali MariaDB). Pomembno: migracija ne bi smela avtomatsko pomeniti migracije baze podatkov, vendar mora biti vmesnik med DB in Delphi enoznačen.
2) Datotečno in stream-I/O: CSV, INI, lastni formati, uvoz/izvoz
Klasika: datoteke so bile prej brane/pisane z AssignFile/ReadLn, TFileStream ali TStringList.LoadFromFile brez nastavitve kodiranja. V Unicode-Delphi nato Delphi heuristično odloča (BOM) ali uporabi privzeta kodiranja. To vodi do:
- napačno interpretiranih umlautov (ä, ö) v CSV/log-datotekah,
- napačnih dolžinskih podatkov v lastnih formatih,
- neskladnosti s partnerji, ki pričakujejo ISO-8859-1 ali Windows-1252.
Čista rešitev je, da za vsak format datoteke določite fiksno kodiranje in ga zavežete v kodi ter dokumentaciji. Za CSV/JSON je UTF-8 praviloma standard, za stare vmesnike včasih Windows-1252. Ključno je eksplicitnost.
3) Windows-API, PChar, velikosti bufferjev in obdelava sporočil
Mnoge Delphi-aplikacije kličo WinAPI-funkcije ali delajo z buffri. Pogoste točke loma:
- Uporaba PChar v povezavi s funkcijami, ki imajo ANSI- ali Wide-variant (…A/…W).
- Velikosti bufferjev se računajo v bajtih, medtem ko je Char v UTF-16 2 bajta.
- Pointer-aritmetika in layouti recordov, ki temeljijo na 1-bajtnih znakih.
Tukaj je potreben natančen refaktor: bodisi dosledna uporaba Wide-API-jev ali zavestna uporaba ANSI-variant in AnsiString/kodepaga. »Nekako se prevede« ni kriterij kakovosti.
4) COM, ActiveX, Office-automatizacija in tretje knjižnice
COM-interface pogosto delajo z BSTR (WideString). Stare različice Delphi so imele druge privzete nize, tako da je koda »naključno« delovala. V Unicode-Delphi pogosto nastanejo dvojne konverzije ali napačne predpostavke tipov v wrapperjih. Tudi tretje knjižnice so kritične: nekatere dajejo callbacke kot PAnsiChar, druge pričakujejo ničelno terminirane bajt-nize.
Tukaj se izplača klasificirati odvisnosti: katera knjižnica je pripravljena na Unicode, katera ni in katero je možno zamenjati ali zapreti v adapter. Zapiranje (encapsulation) je pogosto najhitrejša pot, da se Unicode-legacy omeji na jasno opisan del.
Strategija: Unicode-migracija starih Delphi-projektov kot kontroliran program modernizacije
Najbolj varna pot je večstopenjski program, ki tveganja zgodaj naredi vidna in hkrati ohranja aplikacijo delujočo.
Korak 1: Določite obseg in prioritizirajte code-hotspote
Ne vsak izvorni tekst zahteva takojšnjo prilagoditev. Prioritizirajte glede na podatkovni tok in tveganje:
- Vmesniki navzven (REST-API, TCP/IP, datoteke, e-pošta, tisk/poročanje).
- Dostop do podatkov (SQL, ORM/Datamodule, BDE/FireDAC-plasti).
- Utility-funkcije blizu nizov (parserji, formatterji, encoder/decoder).
- Integracije (COM, DLL-importi, strojna vmesja).
Rezultat naj bo seznam mest, kjer »kodiranje pomeni specifikacijo«. Ta mesta se kasneje naredijo testna.
Korak 2: Projektne možnosti prevajalnika in opozorila namenoma zaostrite
V mnogih projektih so bila opozorila skozi leta izključena. Za Unicode-migracijo je to kontraproduktivno. Smiselno je opozorila ponovno omogočiti in konverzijska opozorila jemati resno. Dodatno pomaga, če projektno določite pravila, npr.: brez implicitnih AnsiString-konverzij na I/O-mejah, uporaba TEncoding pri datotečnih operacijah, brez »PChar-trikov« brez jasno določenega konteksta.
Korak 3: Vpeljite »meje kodiranja« kot tehnično plast
Praktičen arhitekturni ukrep je uvedba majhnih adapterjev/helperjev, ki natanko določijo, kako zunanji podatki vstopajo in izstopajo. Primeri:
- CSV-reader/-writer: vedno z TEncoding.UTF8 (ali definirano kodepago) in jasnimi pravilniki za separatorje.
- REST-client/server: JSON vedno kot UTF-8-bajti, header pravilno nastavit, body ne streamati kot »string«.
- Windows-API-wrapper: centralne funkcije, ki Wide/Ansi čisto kapsulirajo.
S tem preprečite, da bi se »odločitve o kodiranju« razpršile po celotni codebazi.
Tipične pasti v kodi in kako jih čisto odpraviti
Length, SizeOf, ByteLength: ko se dolžina znakov in število bajtov razhajata
V časih ANSI se je Length(s) pogosto zlorabljal kot število bajtov. V UTF-16 je to napačno. Če potrebujete polja bajtov, eksplicitno konvertirajte:
- Za UTF-8: TEncoding.UTF8.GetBytes(s)
- Za definirano ANSI-kodepago: TEncoding.GetEncoding(1252).GetBytes(s) (samo če je strokovno pravilno)
Za velikosti bufferjev pri klicih API preverite, ali funkcija pričakuje enote znakov ali bajtov. Veliko Wide-API-jev pričakuje število znakov, ne bajtov. Odločata dokumentacija in podpis, ne intuicija.
PAnsiChar vs. PWideChar: DLL-Imports in zunanji protokoli
Pri DLL-importih je velika nevarnost, da podpisi v Delphi-kodi ne več ustrezajo. Določite, kaj DLL pričakuje:
- Če DLL pričakuje UTF-8, je ujemanje običajno PAnsiChar(UTF8String), vendar morate nadzorovati življenjsko dobo in ničelno terminacijo.
- Če pričakuje UTF-16, potem uporabite PWideChar in wide-nize.
V vsakem primeru naj bodo importa kapsulirani v ločeni uniti, da se politika nizov ne razširi po celotnem projektu.
Formatiranje, pretvorbe velikosti črk, primerjanje: lokalne nastavitve in normalizacija
Unicode prinaša tudi semantična vprašanja: velika/mala črka ni enostavna v vseh jezikih in znaki imajo lahko različne normalne oblike. V tipičnih poslovnih aplikacijah to ni tako kritično kot v urejevalnikih besedil, vendar vpliva na:
- razvrščanje in filtriranje (npr. v gridih ali iskalnikih),
- case-insensitive primerjave za ključne vrednosti,
- generiranje imen datotek ali identifikatorjev.
Pomembno je jasno pravilo: kaj so »ključne« vrednosti (npr. številke artiklov, šifre strank), ki naj ostanejo ASCII-blizu, in kaj so »besedila«, ki morajo biti polno Unicode-zmožna. Ta ločitev zmanjša nadaljnje napake.
GUI/poročanje: fonti, tisk, PDF in obnašanje komponent
VCL je od Unicode-izdaj načeloma Unicode-zmožen, a praksa je odvisna od komponent in izhodnih poti. Tveganja nastanejo pri:
- starejših report-enginih ali PDF-generatorjih, ki pričakujejo ANSI,
- tiskanju črtnih kod/etiket, ki potrebujejo specifične kodene strani,
- hardkodiranih fontih ali naborih znakov.
Načrtujte zgodnje teste s konkretnimi primeri (imena, kraji, posebni znaki, ne-latinski pisavi, če so relevantne). Vrednost ni v tem, da »lahko Unicode«, temveč v dokazilu: »Ta izhod je v našem kontekstu pravilen.«
Podatki in vztrajnost: Unicode se ne konča pri kodi
Določite charsete in collations v bazah podatkov
Unicode-migracija je stabilna le, če so baze podatkov in gonilniki pravilno konfigurirani. Primeri:
- Pri PostgreSQL je UTF-8 običajno privzeto; vseeno je treba preveriti client-encoding in vedenje gonilnika.
- Pri SQL Server je pomembna razlika med VARCHAR in NVARCHAR; napačna izbira stolpca lahko povzroči izgubo znakov.
- Pri MariaDB/MySQL so charset/collation (npr. utf8mb4) odločilni, da se 4-bajtni znaki ne obrežejo.
V Delphi-kodi naj bodo parametri in tipi polj uporabljeni tako, da se Unicode ne »konvertira nazaj« na poti. FireDAC običajno ponuja boljši nadzor kot zelo stare plasti dostopa.
Legacy-datotečni formati: pravila migracije namesto tihe konverzije
Če vaša aplikacija že leta ustvarja datoteke (eksporti, arhivske datoteke, lastne strukture), morate določiti:
- Katere obstoječe datoteke ostanejo »takšne kot so« in se ob branju pravilno interpretirajo?
- Kateri formati se bodo dvignili na UTF-8?
- Ali obstajajo polja z verzijo/headerji, da ločeno prepoznamo nove in stare datoteke?
Tihih konverzij brez označbe se izognite, ker se napake pogosto pokažejo šele pozneje. Bolje: verzionirajte, jasno prepoznajte in ciljano migrirajte.
Kontrola kakovosti: testi, ki res poiščejo Unicode-težave
Unicode-napake so pogosto odvisne od podatkov. Zato »Happy Path« testi niso dovolj. Smiselna je zbirka testov, ki pokrije problematična mesta:
- Roundtrip-test: uvoz → obdelava → izvoz, nato primerjava po bajtih (pri definiranih formatih).
- DB-Roundtrip: zapis/brisanje besedil z umlauti, akcenti in morebitnimi ne-latinskimi znaki; preverjanje enakosti.
- Vmesniški testi: REST-zahteve kot UTF-8, headerji, JSON-escaping, logiranje.
- Regresija: reproduciranje starih podatkov in tipičnih uporabniških primerov, zlasti pri iskanju, filtriranju, sortiranja.
Za B2B-sisteme je pomembno tudi, da so napake opazne: logiranje ne sme uničevati kodiranj. Kdor piše loge kot ANSI, v primeru napake izgubi natanko informacijo, ki jo potrebuje.
Planiranje in obseg: kaj dejansko povečuje kompleksnost
Obseg dela pri Unicode-migraciji starih Delphi-projektov je manj odvisen od »števila vrstic kode« kot od povezanosti in zunanjih odvisnosti:
- Veliko integracij (DLL, COM, naprave, ERP/DMS/CRM) povečuje obseg testiranja, saj so na vsaki meji pomembna kodiranja.
- Historični formati (stari exporti, specifični CSV-ji strank) zahtevajo pravila migracije in strategije kompatibilnosti.
- Mešane Delphi-verzije ali več produktov iz iste codebaze povečajo koordinacijske zahteve.
- Stare plasti dostopa do podatkov (npr. BDE) lahko Unicode posredno blokirajo in nakazujejo na potrebna modernizacijska dela.
V praksi se je izkazalo za dobro strategijo, da najprej stabilizirate Unicode v jedru in v najkritičnejših podatkovnih tokovih. Nato postopoma nadgrajujete module. To zmanjša tveganje in prepreči dolge »big bang« faze brez izida.
Vključitev v modernizacijske poti: REST-storitve, services, večplatformnost
Unicode je pogosto temeljni predpogoj, ko želite obstoječo programsko opremo modernizirati. Tipična nadaljnja vprašanja so:
- REST-strežniki ali REST-API kot nadgradnja (JSON/UTF-8 pravilno obdelovati).
- Stabilno obratovanje Windows-storitev ali Linux-storitve (logiranje, konfiguracijske datoteke, protokoli).
- Postopen UI-modernizacijski potek v VCL, kasneje po potrebi večplatformni odjemalci.
Pomemben je vrstni red: če gradite nove vmesnike, naj bodo pravila kodiranja določena prej. Unicode-migracija »na hitro« med razvojem vmesnikov vodi v težko preverljive napake, ker se vzroki in učinki prepletajo.
Za notranje povezovanje v magazinu se kot sosednje teme smiselno uvrstijo npr. Delphi-modernizacija, FireDAC-dostop do podatkov ali arhitektura REST-strežnikov kot poglobljeni članki, tako da bralci lahko nato sledijo naslednjemu tehničnemu koraku.
Zaključek: Unicode-migracija je vprašanje tveganja – z ustrezno metodo pa obvladljivo
Unicode-migracija starih Delphi-projektov ni kozmetični upgrade, temveč korekcija temeljnih predpostavk o besedilu, bajtih in vmesnikih. Kdor pristopi strukturirano, pridobi več kot le »umlauti spet delujejo«: podatkovni tokovi postanejo enoznačni, integracije robustnejše in kasnejše modernizacije (npr. REST-strežniki, storitve, čiščenje baze podatkov) lažje, ker se kodiranja ne dogajajo več implicitno »nekje«.
Če za vašo Delphi-aplikacijo potrebujete konkreten migracijski načrt, analizo tveganj vročih točk ali podporo pri izvedbi, je najhitrejši naslednji korak tehnični uvodni pogovor o vaših okvirnih pogojih in odvisnostih: vzpostavite stik.
V strokovnem okolju imata pomembno vlogo tudi Delphi Unicode migracija in Delphi Ansi v Unicode, kadar morajo integracije, podatkovni tokovi in nadaljnji razvoj delovati usklajeno.
Posvetujte se o projektu ali modernizacijskem načrtu z Net-Base.