Official
Unicode support was added in
Delphi 2009 with code name
Tiburón.
In Delphi Versions <= 2007, string was an alias for AnsiString (a single-byte character string type).
In Delphi Versions >= 2009, this changed, and string became an alias for UnicodeString (a multi-byte, UTF-16 character string type), while AnsiString remained available as a type for 8-bit character data.
The change was a major shift to include full Unicode support and has significant implications for code written in Delphi <= 2007 that is being migrated to Delphi 2009 or later.
Key Differences and Migration Implications
In Delphi 2007 and older (ANSI) Length() returns number of bytes, in Delphi 2009 and newer (Unicode) returns number of characters (elements).
String Alias:
- Delphi 2007 and earlier: String = AnsiString. The data was 8-bit and its interpretation (code page) was generally linked to the operating system's regional settings.
- Delphi 2009 and later: String = UnicodeString. The data is 16-bit (WideChar), typically using UTF-16 encoding, allowing for a broad range of characters.
Character Types:
- Delphi 2007: Char = AnsiChar (8-bit).
- Delphi 2009: Char = WideChar (16-bit).
| Type / Function
|
Delphi 7, 2007
|
Delphi 2009, XE
|
| byte
|
byte
|
byte (no change)
|
| String
|
AnsiString
|
UnicodeString
|
| Char
|
AnsiChar
|
WideChar
|
| PChar
|
PAnsiChar
|
PWideChar
|
| SizeOf(Char)
|
1 byte
|
2 bytes
|
Data Handling:
- Direct assignments between AnsiString and the new UnicodeString in Delphi 2009 involve implicit conversions, which can cause to performance issues or, more importantly, data loss if not handled carefully. The compiler will often issue warnings for these conversions.
- The internal structure of AnsiString itself changed slightly between versions (e.g., in Delphi 2009, it stores a code page number, which was not present in Delphi 2007's AnsiString), making sharing strings between DLLs compiled with different versions problematic.
Migration pieces of Advice
To migrate code that uses strings between these versions, you generally have preferred approaches:
- Explicitly Ansi-fy the Code (Short-term fix): Replace all instances of the generic string, Char, and PChar with their explicit 8-bit equivalents: AnsiString, AnsiChar and PAnsiChar. This retains the old behavior but means your application will not leverage Unicode natively and will still encounter conversion overhead when interacting with the rest of the Unicode VCL/RTL in Delphi 2009.
- Resort to Unicode: Update your code to use the native UnicodeString type throughout your application. Use AnsiString only at the boundaries where you interact with legacy data, files, or external APIs that require ANSI encoding (e.g., when saving or loading data from an older database). Use functions like
TEncoding.GetString to properly decode data.
- The correction isn’t simple recompilation, but to audit every PChar, every SizeOf(Char) assumption, and every place where raw bytes to a file or socket expecting ANSI.
// Delphi <= 2007 code — assumes 1 byte per char, breaks silently under Unicode
procedure TrimBuffer(var Buf: array of Char; MaxLen: Integer);
var
P: PChar;
begin
P := @Buf[0];
Inc(P, MaxLen); // wrong stride Char is 2 bytes
P^ := #0;
end;
- Compiler directives and conditional code if code must support both old and new Delphi versions simultaneously.
The Version constants are not intuitive but they may lead to mistakes:
{$IFDEF VER150} // Delphi 7
{$IFDEF VER185} // Delphi 2007 Win32 has VER180 & VER185
{$IFDEF VER200} // Delphi 2009 Tiburon
{$IFDEF VER210} // Delphi 2010 Weaver
Another way to use the UNICODE directive:
{$IFDEF UNICODE}
// Delphi 2009+ logic
{$ELSE}
// Legacy Delphi logic
{$ENDIF}
Prefer to use CompilerVersion than Version constants
...
If the codebase has already passed one migration, it likely has {$IFDEF} blocks keyed to old version constants. Don’t delete these blindly — audit each one with attention.
Use code with caution!
- Compile and watch Fix Warnings: Pay close attention to "Implicit string cast" warnings. They highlight where the compiler is silently converting between ANSI and Unicode strings, which can cause data loss or unplanned behavior.
- Character Buffer Allocations with
SetLength and GetMem
If code allocates memory based on the assumption that 1 character equals 1 byte, it will allocate only half the required memory in Delphi 2009+, leading to buffer overflows.
Length returns the number of elements in the string or in an array. Length for the strings with 8 bit element types (ANSI, UTF-8) gives the number of bytes since the number of bytes is the same as the number of elements, but the Length for the strings with 16 bit elements (UTF-16) is half the number of bytes because each element has 2 bytes.
Wrong Code:
GetMem(Buffer, Length(MyString)); // Allocates half the needed bytes!
Correct Code
GetMem(Buffer, Length(MyString) * SizeOf(Char));
- File I/O (BlockRead / BlockWrite & TStream) in reading or writing strings directly to files using raw byte streams will now write 2 bytes per character, breaking backwards compatibility with old file formats.
Fix, if you need to save text files in traditional ANSI/ASCII format, explicitly use AnsiString or pass TEncoding.ANSI (e.g., StringList.LoadFromFile('file.txt', Tencoding.ANSI)) to TStringList.SaveToFile or TStringList.LoadFromFile.
var
LegacyStr: AnsiString;
begin
LegacyStr := AnsiString(MyUnicodeString);
Stream.Write(LegacyStr[1], Length(LegacyStr));
end;
- Windows API Calls brings surprises out of (
PChar casting)
Delphi automatically maps Windows API functions to their Unicode (W) variants instead of ANSI (A) variants.
For example: SetWindowText calls the SetWindowTextW.
If PAnsiChar is explicitly being passed to an API that now expects PWideChar, the compiler will give error. Types must be updated to PChar to let Delphi handle it natively, or explicitly call SetWindowTextA if the ANSI is used.
- Pointer math must be reviewed: if a
PChar pointer Inc(P) is incremented, it now jumps by 2 bytes instead of 1. If there is manual byte-level parsing, the pointer type must be changed to PByte or PAnsiChar.
- If old code used string or
AnsiString as binary Data buffers to load raw binary data, encrypted data, or file streams, this will now break because the runtime will try to parse or convert invalid UTF-16 surrogate pairs.
Change the variable type from string to RawByteString or TBytes (an array of bytes) to fix and completely bypass Unicode conversion logic.
- Ensure that database drivers and components are updated to versions that support Unicode data types (
TWideStringField instead of TStringField if storing non-ASCII data).
The ANSI function to Ansi-fy. This table is not a divine revelation, but my personal reference book.
|
Ansi-fy |
Wide Char, Unicode |
Units |
const
C = '...'; |
const
C: AnsiString = '...'; |
|
|
| CompareText |
AnsiCompareText |
|
SysUtils; AnsiStrings |
| CompareStr |
AnsiCompareStr |
|
SysUtils; AnsiStrings |
| FindWindow |
FindWindowA |
FindWindowW (in Delphi Versions >= 2009 the same as FindWindowW) |
|
| GetDiskFreeSpace |
GetDiskFreeSpaceA |
GetDiskFreeSpace (in Delphi Versions >= 2009 calls GetDiskFreeSpaceW) |
Windows |
| GetTempPath |
GetTempPathA |
GetTempPath (in Delphi Versions >= 2009 calls GetTempPathW) |
Windows |
| ShellExecute |
ShellExecuteA |
ShellExecuteW (in Delphi Versions >= 2009 the same as ShellExecute) |
ShellApi |
| ShellExecute |
ShellExecuteA |
ShellExecuteW (in Delphi Versions >= 2009 the same as ShellExecute) |
ShellApi |
| ShellExecuteInfo |
ShellExecuteInfoA |
ShellExecuteInfoW (in Delphi Versions >= 2009 the same as ShellExecuteInfo) |
|
| SHGetPathFromIDList |
SHGetPathFromIDListA |
SHGetPathFromIDList (in Delphi Versions >= 2009 calls SHGetPathFromIDListW) |
ShlObj |
| StrAlloc |
AnsiStrAlloc |
in Delphi Versions >= 2009 StrAlloc calls WideStrAlloc |
SysUtils |
Delphi 2009 has got the unit
AnsiStrings.pas appeared in
..\source\Win32\rtl\common.
This article will not be complete, some text was removed intentionally. If you are unable to apply this, please contact for paid consultation.
Данная статья не будет полной, часть текста была удалена намеренно. Если вам не удалось применить, обращайтесь за платной консультацией.