首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 專案遷移至 Unicode (Tiburon/RAD Studio 2009+) 完整指南

     編輯:Delphi

前言

RAD Studio 2009 (代號 Tiburon) 引入全面 Unicode 支援,預設字串類型從 AnsiString 改為 UnicodeString (UTF-16)。這是 Delphi 歷來最大型的斷層式變更,現有專案遷移需系統性處理字串、指標、API 呼叫、資料流與結構體對齊等問題。

核心類型變更對照表

類型舊版 (≤2007)新版 (≥2009)備註
stringAnsiStringUnicodeString預設字串類型變更,影響所有未明確指定類型的字串變數
CharAnsiChar (1 byte)WideChar (2 bytes)字元大小翻倍
PCharPAnsiCharPWideChar指標類型同步變更
AnsiString預設字串保留,結構擴充新增 CodePage、ElemSize 欄位,與 UnicodeString 結構對齊
WideStringBSTR 相容不變無引用計數,COM 專用

UnicodeString 內部結構

type
  StrRec = record
    CodePage: Word;      // 新增:字碼頁
    ElemSize: Word;      // 新增:元素大小 (1=Ansi, 2=Wide)
    refCount: Integer;   // 引用計數
    Len: Integer;        // 字元長度 (非位元組數)
    case Integer of
      1: array[0..0] of AnsiChar;
      2: array[0..0] of WideChar;
  end;

關鍵點:Length() 回傳字元數,SizeOf() 回傳位元組數,兩者在 UnicodeString 下不再相等。

遷移檢查清單:高風險區域

1. 字串長度與位元組計算

// 錯誤:假設 Char = 1 byte
SetLength(S, 100);
FillChar(S[1], Length(S), 0); // 僅清除前 100 bytes,應為 200 bytes

// 正確
FillChar(S[1], Length(S) * SizeOf(Char), 0);
// 或
FillChar(Pointer(S)^, Length(S) * SizeOf(Char), 0);

2. 檔案/資料流讀寫

// 錯誤:Length 為字元數,Stream 需位元組數
Stream.Write(Pointer(S)^, Length(S));

// 正確 Unicode 寫法
Stream.Write(Pointer(S)^, Length(S) * SizeOf(Char));

// 正確 Ansi 寫法 (明確編碼)
var AnsiBuf: AnsiString;
AnsiBuf := S; // 隱含轉換使用 Default 字碼頁
Stream.Write(Pointer(AnsiBuf)^, Length(AnsiBuf)); // AnsiChar = 1 byte

// 最佳實務:使用 TEncoding
Stream.Write(TEncoding.UTF8.GetBytes(S), Length(TEncoding.UTF8.GetBytes(S)));

3. Windows API 呼叫

VCL 現在預設呼叫 Unicode 版 API (尾碼 W)。PChar 對應 PWideChar。

// 正確範例:GetModuleFileName
function ModuleFileName(Handle: HMODULE): string;
var
  Buffer: array[0..MAX_PATH] of Char; // Char = WideChar
begin
  SetString(Result, Buffer, GetModuleFileName(Handle, Buffer, Length(Buffer)));
end;

// 正確範例:GetWindowText
function WindowCaption(Handle: HWND): string;
begin
  SetLength(Result, 1024);
  SetLength(Result, GetWindowText(Handle, PChar(Result), Length(Result)));
end;

4. 結構體大小與指標運算

// 錯誤:SizeOf(Buffer) 為位元組數,API 需字元數
Count := SizeOf(Buffer);
GetWindowText(Handle, Buffer, Count);

// 正確
Count := Length(Buffer); // 字元數

// 指標運算:改用 PByte 而非 PChar
// 舊碼
Result := PChar(Node) + Offset;
// 新碼
Result := PByte(Node) + Offset; // 位元組偏移
// 或開啟指標運算
{$POINTERMATH ON}
Result := Pointer(NativeUInt(Node) + Offset);

5. FillChar 非零填充

// 舊碼:填充 #9 (Tab)
FillChar(Buf, Length(Buf), #9); // 實際寫入 $0909 (兩個 WideChar)

// 正確:使用 StringOfChar
StrPCopy(Buf, StringOfChar(#9, Length(Buf)));

6. 登錄讀寫

// RegQueryValueEx 的 lpcbData 為位元組數
Len := MAX_PATH * SizeOf(Char);
if RegQueryValueEx(Key, PChar(Name), nil, nil, @Data[0], @Len) = ERROR_SUCCESS then
  SetString(Result, Data, Len div SizeOf(Char) - 1);

7. CreateProcess 命令列緩衝區

CreateProcessW 會修改 lpCommandLine 緩衝區內容,不可傳入常數字串或引用計數 -1 的字串。

// 錯誤
CreateProcess(nil, 'cmd.exe', ...);
CreateProcess(nil, PChar(ConstStr), ...);

// 正確:使用可寫入緩衝區
var CmdLine: string;
CmdLine := 'cmd.exe';
CreateProcess(nil, PChar(CmdLine), ...);

8. 字元分類與 LeadBytes

// 舊碼
if Str[I] in LeadBytes then ...

// 新碼:使用 IsLeadChar (支援 ANSI/Unicode 多載)
if IsLeadChar(Str[I]) then ...

// 字元分類:使用 TCharacter 靜態類別
uses Character;
if TCharacter.IsDigit(C) then ...
if TCharacter.IsSurrogatePair(High, Low) then ...

編譯器指令與多版本相容

{$IFDEF UNICODE}
  // Unicode 專用碼
  FName := PWideChar(S);
{$ELSE}
  // Ansi 專用碼
  FName := PAnsiChar(AnsiString(S));
{$ENDIF}

// C++Builder
#ifdef _DELPHI_STRING_UNICODE
  // Unicode
#endif

資料元件變更

  • TStrings 內部儲存 UnicodeString
  • TStringStream 重寫為內部儲存 ANSI,建議改用 TStringBuilder 或指定 TEncoding
  • TEncoding 提供 UTF-8/UTF-16/BOM 支援,TEncoding.Default 對應系統作用字碼頁

遷移策略建議

  1. 靜態分析:使用編譯器警告「Implicit string cast」、「String parameter mismatch」定位問題點
  2. 模組化測試:優先遷移核心庫,建立單元測試驗證字串處理邏輯
  3. 明確類型:新代碼盡量使用 UnicodeString、AnsiString、UTF8String 明確宣告,避免裸 string
  4. 資料層隔離:資料庫存取層統一轉換編碼,UI 層純 Unicode
  5. 第三方元件:確認元件廠商已釋出 Unicode 相容版本

結語

Unicode 遷移是一次性投資,長期收益包括:原生支援多國語言、消除字碼頁轉換 Bug、符合現代作業系統與資料庫預期。循序漸進、善用編譯器診斷、建立回歸測試,是成功遷移的關鍵三要素。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved