Unicode-migreringen af ældre Delphi-projekter er i mange virksomheder et nødvendigt skridt, fordi eksisterende applikationer ellers støder på begrænsninger ved internationale data, moderne operativsystemer, integrationer og nye interfaces. I praksis er det sjældent et „recompile og færdigt“. Delphi har siden Unicode-versionerne (fra Delphi 2009) foretaget fundamentale ændringer af standard-string-typene. Dermed flyttes antagelser om tegnkodning, hukommelseslayout og API-signaturer. Den, der undervurderer det, producerer snigende datafejl, ødelagte eksporter, uklare supporttilfælde og sikkerhedsrisici.
Denne artikel giver en teknisk holdbar fremgangsmåde: Hvordan I analyserer bestanden, afgrænser scope fornuftigt, reducerer risici ved hotspots (databaser, filer, Windows-APIer, COM, REST-services) og sikrer migreringen, så drift og videreudvikling kan køre parallelt. Fokus er på Delphi-typiske faldgruber i VCL-applikationer, services og grænseflader – med blik for moderniseringsveje, hvori emner som BDE-afløsning med native tilslutning, REST-servere eller multiplatform senere kan placeres.
Hvorfor Unicode-omstillingen i Delphi så ofte er „større end antaget“
I klassiske Delphi-versioner var string en ANSI-string (afhængig af systemcodepage). Siden Delphi 2009 er string som standard en UnicodeString (UTF-16). Samtidig blev mange biblioteker og VCL-klasser omlagt til Wide-APIer. Det er i grunden positivt, fordi det robust understøtter internationale tegn. Men: Legacy-kode er ofte vokset over år omkring antagelserne „1 tegn = 1 byte“, „PChar er PAnsiChar“ eller „Length() svarer til byte-antal“.
De typiske årsager til, at migrationer bliver mere omfangsrige:
- Implicitte konverteringer gennemføres visse steder, men ændrer data (især ved filer, interfaces eller database-blob-/tekstfelter).
- Byte-orienteret kode (streams, buffere, hashing, kryptering) bliver ubemærket forkert, når string-indhold behandles som bytes.
- Tredjepartskomponenter er delvist ANSI-only eller bruger egne string-typer og callbacks.
- Eksternt miljø (Windows-APIer, COM, print/reporting, EDI, CSV, XML/JSON) forventer bestemte encodninger.
Målet bør derfor ikke være at „ændre så lidt som muligt“, men at ændre målrettet dér, hvor dataflow og encodninger skal defineres. En ordentlig Unicode-migrering er også en mulighed for endeligt at dokumentere og teste uklare kodningsgrænser.
Tekniske grundlag: Delphi-stringtyper, encodninger og deres bivirkninger
string, UnicodeString, AnsiString, WideString – hvad der virkelig betyder noget i projektet
For migreringen er det afgørende, hvilke typer der anvendes ved grænseflader og i kernefunktioner:
- string: Siden Delphi 2009 en UnicodeString (UTF-16, reference-counted, immutable-semantik via Copy-on-Write).
- AnsiString: Byte-string med tilknyttet codepage (afhængig af Delphi-version kan en codepage medføres). Velegnet når en ekstern grænseflade eksplicit kræver en bestemt 8-bit-encoding.
- UTF8String: I nyere Delphi-versioner ofte som alias/AnsiString med UTF-8-codepage; praktisk til REST/JSON og mange protokoller.
- WideString: BSTR (COM), hukommelsesadministreret via SysAllocString; i dag mest nødvendigt for bestemte COM-interops.
- PChar: I Unicode-Delphi PWideChar. Det er et af de hyppigste sammenbrudspunkter ved Windows-API-calls.
Når disse typer blandes, opstår konverteringer. Nogle er korrekte, andre overraskende: En konvertering er kun „rigtig“, hvis I ved, hvilken codepage der findes ved kilden, og hvad målet forventer.
UTF-16 internt, UTF-8 eksternt: en praksisorienteret rettesnor
I Delphi-VCL-applikationer giver det ofte mening at arbejde konsekvent internt med string (UTF-16). Eksternt (REST, filer, messaging) dominerer i praksis UTF-8. En robust linje er derfor:
- Internt: string/UnicodeString som standard.
- Grænser: Ved input/output eksplicit konvertere via TEncoding.UTF8 (eller definerede ANSI-codepages).
- Byte-baseret behandling: TBytes i stedet for strings.
Det reducerer implicitte konverteringer og gør ansvarsfordelingen kontrollerbar: „Hvor bliver bytes til tekst, og med hvilken encoding?“
Statusopgørelse: Hvor Unicode typisk bryder i gamle Delphi-projekter
Før I rører ved kode, er en struktureret inventar nyttig. I Unicode-migreringen af ældre Delphi-projekter er fejlene ofte ikke jævnt fordelt, men koncentreret i nogle hotspots.
1) Databaseadgang og felttyper (BDE, ADO, FireDAC)
Mange ældre projekter bruger stadig BDE eller ældre dataadgangslag. Her er problemerne ofte:
- Tildeling af database-charset til Delphi-strings (ANSI vs. Unicode-felttyper).
- „Tekst“ i BLOBs eller memo-felter uden defineret kodning.
- SQL-udtryk som strings, der ved umlauter/Unicode-tegn kan fortolkes forskelligt.
Hvis der alligevel planlægges modernisering, kan en Unicode-migrering kombineres med oprydning af dataadgangen, fx mod BDE-Ablosung mit nativer Anbindung og klar charset-konfiguration (som fx ved PostgreSQL eller MariaDB). Vigtigt: En migrering bør ikke automatisk tvinge en database-migrering, men grænsefladen mellem DB og Delphi skal være entydig.
2) Fil- og stream-I/O: CSV, INI, proprietære formater, import/eksport
En klassiker: Filer blev tidligere læst/skrevet med AssignFile/ReadLn, TFileStream eller TStringList.LoadFromFile uden at sætte encoding. I Unicode-Delphi beslutter så miljøet heuristisk (BOM) eller bruger default-encodings. Det fører til:
- forkert fortolkede umlauter (ä, ö) i CSV/logfiler,
- fejl i længdeangivelser i proprietære formater,
- inkompatibiliteter med eksterne partnere, der forventer ISO-8859-1 eller Windows-1252.
En ren løsning er at definere et fast encoding pr. filformat og forankre det i kode og dokumentation. For CSV/JSON er UTF-8 som regel den rigtige standard; for gamle interfaces nogle gange Windows-1252. Det afgørende er eksplicithed.
3) Windows-API, PChar, bufferstørrelser og message-håndtering
Mange Delphi-applikationer kalder WinAPI-funktioner eller arbejder med buffere. Hyppige brudflader:
- Brug af PChar sammen med funktioner, der har ANSI- eller Wide-varianter (…A/…W).
- Bufferstørrelser regnes i bytes, men Char er i UTF-16 2 byte.
- Pointeraritmetik og record-layouts, der antager 1-byte-chars.
Her kræves præcist refactoring: enten konsekvent bruge Wide-APIer eller bevidst kalde ANSI-varianten og arbejde med AnsiString/codepage. „Det kompilerer på en eller anden måde“ er ikke et kvalitetskriterium.
4) COM, ActiveX, Office-automation og tredjepartsbiblioteker
COM-interfaces arbejder ofte med BSTR (WideString). Ældre Delphi-versioner havde andre default-strings, så kode kunne passe „tilfældigt“. I Unicode-Delphi opstår ofte dobbelte konverteringer eller forkerte typeantagelser i wrappers. Tredjepartsbiblioteker er ligeledes kritiske: Nogle leverer callbacks som PAnsiChar, andre forventer nullterminerede byte-strings.
Her er det nyttigt at klassificere afhængigheder: Hvilket bibliotek er Unicode-ready, hvilket er ikke, og hvilket kan udskiftes eller kapsles ind? En kapsling er ofte den hurtigste vej til at isolere Unicode-legacy i et klart afgrænset område.
Strategi: Unicode-migrering af ældre Delphi-projekter som et kontrolleret moderniseringsprogram
Den migrationssikreste fremgangsmåde er et flertrinsprogram, der gør risici synlige tidligt og holder applikationen kørende.
Trin 1: Definér scope og prioriter kode-hotspots
Ikke al kildekode behøver øjeblikkeligt ændringer. Prioritér efter dataflow og risiko:
- Eksterne grænseflader (REST-API, TCP/IP, filer, e-mail, print/reporting).
- Dataadgang (SQL, ORM/datamoduler, BDE/FireDAC-lag).
- String-nære utilities (parsers, formatters, encoder/decoder).
- Integrationer (COM, DLL-imports, hardware-tilslutninger).
Resultatet bør være en liste over steder, hvor „encoding er en specifikation“. Disse punkter gøres testbare senere.
Trin 2: Slib compiler-/projektindstillinger og advarsler skarpt
I mange projekter er advarsler blevet slået fra gennem årene. Til en Unicode-migrering er det kontraproduktivt. Genaktiver advarsler og tag konverteringsadvarsler alvorligt. Derudover hjælper det at fastsætte projektomfattende regler, fx: ingen implicitte AnsiString-konverteringer ved I/O-grænser, brug af TEncoding ved filoperationer, ingen „PChar-tricks“ uden klart kontekst.
Trin 3: Indfør „encoding-grænser“ som en teknisk lag
Et praktisk arkitekturgreb er introduktionen af små adaptere/helpers, der entydigt definerer, hvordan eksterne data kommer ind og ud. Eksempler:
- CSV-reader/-writer: altid med TEncoding.UTF8 (eller defineret codepage) og klare separatorregler.
- REST-client/server: JSON altid som UTF-8-bytes, headers sat korrekt, body ikke „stringbaseret“ streamet.
- Windows-API-wrapper: centrale funktioner, der rent kapsler Wide/Ansi.
Så forhindrer I, at „encoding-beslutninger“ spredes tilfældigt i kodebasen.
Typiske kodefælder og hvordan man retter dem korrekt
Length, SizeOf, ByteLength: når tegnlængde og bytestørrelse adskiller sig
I ANSI-tider blev Length(s) ofte misbrugt som byteantal. I UTF-16 er det forkert. Når I har brug for byte-arrays, så konverter eksplicit:
- For UTF-8: TEncoding.UTF8.GetBytes(s)
- For en defineret ANSI-codepage: TEncoding.GetEncoding(1252).GetBytes(s) (kun når det er fagligt korrekt)
For bufferstørrelser ved API-calls gælder: Tjek om funktionen forventer tegn- eller byte-enheder. Mange Wide-APIer forventer tegnantal, ikke bytes. Dokumentation og signatur afgør, ikke intuition.
PAnsiChar vs. PWideChar: DLL-imports og eksterne protokoller
Ved DLL-imports er risikoen stor for, at signaturer i Delphi-koden ikke længere passer. Fastlæg, hvad DLL’en forventer:
- Forventer DLL’en UTF-8? Så er overlevering som PAnsiChar(UTF8String) almindeligt, men I skal kontrollere levetid og null-terminering.
- Forventer den UTF-16? Så brug PWideChar og wide-strings.
Under alle omstændigheder bør imports kapsles i en separat unit, så string-politikken ikke spredes gennem hele projektet.
Formatering, case-konvertering, sammenligning: locale og normalisering
Unicode medfører også semantiske emner: store-/småbogstaver er ikke trivialt i alle sprog, og tegn kan have forskellige normalformer. I typiske virksomhedsapplikationer er det mindre kritisk end i consumer-tekstbehandling, men det berører:
- sortering og filtrering (fx i grids eller søgefunktioner),
- case-insensitive sammenligninger for nøgleværdier,
- generering af filnavne eller identifikatorer.
Det er vigtigt at have en klar regel: Hvad er „nøgler“ (fx artikelnumre, kundekoder), som bør holdes ASCII-nære, og hvad er „tekster“, der skal være fuldt Unicode-kompatible? Den adskillelse reducerer efterfølgende fejl.
GUI/reporting: skrifttyper, print, PDF og komponentadfærd
VCL er siden Unicode-versionerne grundlæggende Unicode-kompatibel, men i praksis afhænger det af komponenter og output-paths. Risici opstår ved:
- ældre rapport-engines eller PDF-generatorer, der antager ANSI,
- stregkode-/label-printere, der kræver bestemte codepages,
- hardkodede fonte eller tegnsæt.
Planlæg tidlige tests med realistiske eksempeldata (navne, steder, specialtegn, ikke-latinske skriftsæt hvis relevant). Værdien ligger ikke i „kan Unicode“, men i dokumenteret bevis: „Dette output er korrekt i vores kontekst.“
Data og persistens: Unicode stopper ikke ved koden
Fastlæg database-charsets og collations korrekt
En Unicode-migrering er kun stabil, når databaser og drivere er korrekt konfigureret. Eksempler:
- Hos PostgreSQL er UTF-8 normalt standard; alligevel skal client-encoding og driveradfærd kontrolleres.
- Hos SQL Server er sondringen mellem VARCHAR og NVARCHAR relevant; forkerte kolonner kan miste tegn.
- Hos MariaDB/MySQL er charset/collation (fx utf8mb4) afgørende, så 4-byte-tegn ikke afskæres.
I Delphi-koden bør parameterer og felttyper anvendes, så Unicode ikke „konverteres tilbage“ på vejen. FireDAC giver her ofte bedre kontrol end meget gamle adgangslag.
Legacy-filformater: migrationsregler i stedet for stille konvertering
Hvis jeres applikation over år har produceret filer (eksportformater, arkivfiler, proprietære strukturer), skal I definere:
- Hvilke eksisterende filer forbliver „som de er“ og læses korrekt?
- Hvilke formater opgraderes til UTF-8?
- Findes der versionsfelter/headers, så nye og gamle filer tydeligt kan skilles?
Stille konvertering uden markering er risikabelt, fordi fejl ofte først opdages sent. Bedre: versionér, identificér klart og migrér målrettet.
Kvalitetssikring: tests der faktisk finder Unicode-problemer
Unicode-fejl er ofte dataafhængige. Derfor er „happy path“-tests utilstrækkelige. Et testset bør dække de problematiske områder:
- Roundtrip-tests: Import → behandling → eksport, efterfølgende byte-nøjagtig sammenligning (for definerede formater).
- DB-roundtrip: Skriv/læs tekster med umlauter, accenter og eventuelt ikke-latinske tegn; test for lighed.
- Interface-tests: REST-requests som UTF-8, headers, JSON-escaping, logging.
- Regression: Reproducer gamle data og typiske brugerflows, især ved søgning, filtrering, sortering.
For B2B-systemer er det derudover vigtigt, at fejl er observerbare: Logging bør ikke ødelægge encodninger. Den, der skriver logs som ANSI, mister i fejltilfælde netop den information, man har brug for.
Planlægning og indsats: Hvad der reelt driver kompleksiteten
Indsatsen i Unicode-migreringen af ældre Delphi-projekter afhænger mindre af „linjer kode“ og mere af koblinger og eksterne afhængigheder:
- Mange integrationer (DLLs, COM, enheder, ERP/DMS/CRM) øger testomfanget, fordi encodinger er relevante ved hver grænse.
- Historiske formater (gamle eksporter, kundespecifikke CSV’er) kræver migrationsregler og kompatibilitetsstrategier.
- Blandede Delphi-versioner eller flere produkter fra én kodebase øger koordineringsbehovet.
- Gamle dataadgangslag (fx BDE) kan indirekte blokere Unicode og pege mod modernisering.
I praksis har en strategi vist sig effektiv, hvor Unicode først stabiliseres i kernen og i de mest kritiske dataflows. Derefter kan moduler gradvist bringes med. Det reducerer risiko og undgår lange „big bang“-faser uden release.
Placering i moderniseringsveje: REST, services, multiplatform
Unicode er ofte en grundpille, når legacy-software skal moderniseres. Typiske opfølgende spørgsmål er:
- REST-servere eller REST-APIer kan tilføres (JSON/UTF-8 håndteres korrekt).
- Windows-services eller Linux-services kan drives stabilt (logging, konfigfiler, protokoller).
- Trinvis UI-modernisering i VCL, senere eventuelt multiplatform-klienter.
Rækkefølgen er vigtig: Når I bygger nye interfaces, bør encoding-regler være fastlagt på forhånd. En Unicode-migrering „ved siden af“ under interfaceudvikling leder ellers til svære at teste fejlbilleder, fordi årsag og virkning blandes sammen.
Til intern linkning i magasinet giver det mening at samle relaterede emner som Delphi-modernisering, FireDAC-dataadgang eller arkitektur for REST-servere som uddybende artikler, så læseren kan gå videre til næste tekniske trin.
Konklusion: Unicode-migrering er et risikoområde – med den rette metode bliver det planlagt
Unicode-migreringen af ældre Delphi-projekter er ikke et kosmetisk upgrade, men en korrektion af grundlæggende antagelser om tekst, bytes og grænseflader. Den, der arbejder struktureret, opnår dog mere end „umlauter virker igen“: Dataflows bliver entydige, integrationer mere robuste, og efterfølgende modernisering (fx REST-servere, services, databaseoprensning) bliver enklere, fordi encodninger ikke længere sker implicit „et eller andet sted“.
Hvis I til jeres Delphi-applikation har brug for en konkret migreringsplan, en risikoanalyse af hotspots eller støtte til implementering, er det hurtigste næste skridt et teknisk indledende møde om jeres rammebetingelser og afhængigheder: Kontakt os.
I det faglige miljø spiller også Delphi Unicode Migration og Delphi Ansi Zu Unicode en vigtig rolle, når integrationer, dataflows og videreudvikling skal spille sammen.