而 WideString 类型在早先的版本中用来保存双字节数据。其本质和 Windows BSTR 是一样的。在 Tiburon 中 WideString 仍然是为 COM 保持兼容的,也就是说它依然没有引用计数,相比较而言,UnicodeString 在性能和效率上将会是 COM 以外的程序首选的字符类型。
type StrRec = record CodePage: Word; ElemSize: Word; refCount: Integer; Len: Integer; case Integer of 1: array[0..0] of AnsiChar; 2: array[0..0] of WideChar; end;
FillChar(Rect, SizeOf(Rect), #0) FillChar(WndClassEx, SizeOf(TWndClassEx), #0). 使用的时候注意 WndClassEx.cbSize := SizeOf(TWndClassEx) Windows API
API 默认使用 WideString (*W)形态的版本 PChar()具有相同的语义 范例:
GetModuleFileName: function ModuleFileName(Handle: HMODULE): string; var Buffer: array[0..MAX_PATH] of Char; 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;
字符串索引: function StripHotKeys(const S: string): string; var I, J: Integer; LastChar: Char; begin SetLength(Result, Length(S)); J := 0; LastChar := #0; for I := 1 to Length(S) do begin if (S[I] <> '&') or (LastChar = '&') then begin Inc(J); Result[J] := S[I]; end; LastChar := S[I]; end; SetLength(Result, J); end;
Len := MAX_PATH; if RegQueryValueEx(reg, PChar(Name), nil, nil, PByte(@Data[0]), @Len) = ERROR_SUCCESS then SetString(Result, Data, Len - 1) // Len includes #0 else RaiseLastOSError;
应该换成下面这样:
Len := MAX_PATH * SizeOf(Char); if RegQueryValueEx(reg, PChar(Name), nil, nil, PByte(@Data[0]), @Len) = ERROR_SUCCES then SetString(Result, Data, Len div SizeOf(Char) - 1) // Len includes #0, Len contains the number of bytes else RaiseLastOSError;
LeadBytes 常量 早先的版本中 LeadBytes 常量包含了本地系统中所有可以作为双字节字符 LeadByte 的列表,常常有这样的代码: if Str[I] in LeadBytes then
现在你需要将它改成调用 IsLeaderChar 函数 if IsLeadChar(Str[I]) then
使用 TMemoryStream 类 当您需要用 TMemoryStream 写入一个文本文件的时候,最好在写入任何字符数据进去之前先写入一个 Byte Order Mark (BOM): var Bom: TBytes; begin ... Bom := TEncoding.UTF8.GetPreamble; Write(Bom[0], Length(Bom));
而任何写入的字符需要被转换成 UTF-8 编码: var Temp: Utf8String; begin ... Temp := Utf8Encode(Str); // Str 是要写入文件的字符 Write(Pointer(Temp)^, Length(Temp)); //Write(Pointer(Str)^, Length(Str)); 原来写入字符串的代码
接上文
MultiByteToWideChar 函数 调用 Windows API MultiByteToWideChar 函数可以简单的用一个任务替代,下面是一个是用 MultiByteToWideChar 的例子:
procedure TWideCharStrList.AddString(const S: string); var Size, D: Integer; begin Size := SizeOf(S); D := (Size + 1) * SizeOf(WideChar); FList[FUsed] := AllocMem(D); MultiByteToWideChar(0, 0, PChar(S), Size, FList[FUsed], D); Inc(FUsed); end;
转换到 Unicode 下可以写作这样(同时支持 Unicode 和 ANSI 字符):
procedure TWideCharStrList.AddString(const S: string); {$IFNDEF UNICODE} var L, D: Integer; {$ENDIF} begin {$IFDEF UNICODE} FList[FUsed] := StrNew(PWideChar(S)); {$ELSE} L := Length(S); D := (L + 1) * SizeOf(WideChar); FList[FUsed] := AllocMem(D); MultiByteToWideChar(0, 0, PAnsiChar(S), L, FList[FUsed], D); {$ENDIF} Inc(FUsed); end;
使用 Named Threads 现有 Delphi 代码中使用了 Named Threads 的代码必须修改了。在早先的版本中,当你需要在分类(gallery)中用一个新的 Thread Object 去创建一个 Thread 的时候,需要在新的 Thread 单元中建立下面的类型:
type TThreadNameInfo = record FType: LongWord; // must be 0x1000 FName: PChar; // pointer to name (in user address space) FThreadID: LongWord; // thread ID (-1 indicates caller thread) FFlags: LongWord; // reserved for future use, must be zero end;
type TThreadNameInfo = record FType: LongWord; // must be 0x1000 FName: PAnsiChar; // pointer to name (in user address space) FThreadID: LongWord; // thread ID (-1 indicates caller thread) FFlags: LongWord; // reserved for future use, must be zero end;
function TCustomVirtualStringTree.InternalData(Node: PVirtualNode): Pointer; begin if (Node = FRoot) or (Node = nil) then Result := nil else Result := PChar(Node) + FInternalDataOffset; end;
您应该将其修改成 PByte 而不是 PChar:
function TCustomVirtualStringTree.InternalData(Node: PVirtualNode): Pointer; begin if (Node = FRoot) or (Node = nil) then Result := nil else Result := PByte(Node) + FInternalDataOffset; end;
变体开放数组(Variant Open Array)参数 如果你的代码中有使用 TVarRec 类型去处理开放数组的话,你可能需要为其添加对 vtUnicodeString 的支持。参看下列示例:
procedure RegisterPropertiesInCategory(const CategoryName: string; const Filters: array of const); overload; var I: Integer; begin if Assigned(RegisterPropertyInCategoryProc) then for I := Low(Filters) to High(Filters) do with Filters[I] do case vType of vtPointer: RegisterPropertyInCategoryProc(CategoryName, nil, PTypeInfo(vPointer), ); vtClass: RegisterPropertyInCategoryProc(CategoryName, vClass, nil, ); vtAnsiString: RegisterPropertyInCategoryProc(CategoryName, nil, nil, string(vAnsiString)); vtUnicodeString: RegisterPropertyInCategoryProc(CategoryName, nil, nil, string(vUnicodeString)); else raise Exception.CreateResFmt(@sInvalidFilter, [I, vType]); end; end;
其他需要注意的代码:
AllocMem( AnsiChar of AnsiChar AnsiString of Char Copy( GetMem( Length( PAnsiChar( Pointer( Seek( ShortString string[ 代码中包含上述写法的地方可能需要修改以适应 UnicodeString 的变化。
Default 属性是用户活动页码(users’ active code page) 支持 UTF-8 支持 UTF-16, big 和 little endian 支持 Byte Order Mark (BOM) 您可以继承子类实现特殊的编码 Byte Order Mark
BOM 必须添加到文件中以便判断文件的编码方式。
UTF-8 使用 EF BB EF UTF-16 Little Endian 使用 FF FE UTF-16 Big Endian 使用 FE FF 做好这些注意事项,将帮助您顺利地把旧有项目迁移到 Tiburon 的 Unicode 下。当然,如果您开发的是多版本控件,或者是希望项目能在多个版本中编译,您最好根据这些特性定义适当的编译条件,以便让代码更好的被更低的版本的编译器支持和编译。
本文基于 Tiburon 帮助编写,如有翻译错误或描述不准确的地方欢迎大家指正!相信这次 Delphi / C++ Builder 2009 将是广大爱好者最喜欢的版本之一。