转自:http://cnpack.org/showdetail.php?id=598&lang=zh-cn
现有 Delphi 项目迁移到 Tiburon 中的注意事项
翻译:峪飞鹰
2008-08-30
随着 Embarcadero 8 月 25 号发布 RAD Studio 2009 (Tiburon) 以来(Tiburon 的 RTM 日期可能要延后到 9 - 10 月),随着 Tiburon 全面支持 Unicode,现有的 Delphi / C++ Builder 项目要迁移到 Unicode 下应该注意些什么也成为大家最为关心的问题。Tiburon 对 Unicode 的支持不仅仅是将原来 类型映射为 AnsiString 的 String 类型直接改成 WideString,而是对 AnsiString 结构作出修改,同时增加了 UnicodeString 类型来完美支持 Unicode。这意味着,要想平稳迁移到 Unicode 下,程序员不得不对现有代码作出一定的修改。
在 Tiburon 以前的版本中,AnsiString 和 WideString 除了 data size 不同外,在功能上是相同的。早先版本的 AnsiString 的结构如下:
Format of AnsiString Data Type <wbr></wbr>
Reference Count <wbr></wbr>
|
Length <wbr></wbr>
|
String Data (Byte sized) <wbr></wbr>
|
Null Term<wbr></wbr>
|
-8 <wbr></wbr>
|
-4 <wbr></wbr>
|
0 <wbr></wbr>
|
Length <wbr></wbr>
|
而这个结构在 Tiburon 中已经发生变化,AnsiString 增加了两个新的 fields, 一个是 CodePage,一个是 ElemSize,这样做可以让新版的 AnsiString 和 UnicodeString 在结构上保持一致。
而 WideString 类型在早先的版本中用来保存双字节数据。其本质和 Windows
BSTR 是一样的。在 Tiburon 中 WideString 仍然是为 COM 保持兼容的,也就是说它依然没有引用计数,相比较而言,UnicodeString 在性能和效率上将会是 COM 以外的程序首选的字符类型。
闪亮登场的 UnicodeString 类型
Tiburon 中,新的、默认的 string 就是 UnicodeString。这个类型既可以包含 ANSI 字符,也可以包含 Unicode 字符。下面是 UnicodeString 类型的结构:
Format of UnicodeString Data Type <wbr></wbr>
CodePage<wbr></wbr>
|
Element Size<wbr></wbr>
|
Reference Count <wbr></wbr>
|
Length <wbr></wbr>
|
String Data (element sized) <wbr></wbr>
|
Null Term<wbr></wbr>
|
-12 <wbr></wbr>
|
-10 <wbr></wbr>
|
-8 <wbr></wbr>
|
-4 <wbr></wbr>
|
0 <wbr></wbr>
|
Length * elementsize <wbr></wbr>
|
UnicodeString 和 AnsiString 都是如上的结构,尽管 UnicodeString 包含是双字节数据,AnsiString 包含的是单字节的。
用 Object Pascal 语言来描述 UnicodeString 的结构,应该是这样:
type
<wbr>StrRec = <span style="font-weight: bold;">record</span><br><wbr><wbr> CodePage: Word;<br><wbr><wbr> ElemSize: Word;<br><wbr><wbr> refCount: Integer;<br><wbr><wbr> Len: Integer;<br><wbr><wbr><span style="font-weight: bold;">case</span> Integer <span style="font-weight: bold;">of</span><br><wbr><wbr><wbr><wbr> 1: <span style="font-weight: bold;">array</span>[0..0] <span style="font-weight: bold;">of</span> AnsiChar;<br><wbr><wbr><wbr><wbr> 2: <span style="font-weight: bold;">array</span>[0..0] <span style="font-weight: bold;">of</span> WideChar;<br><wbr><span style="font-weight: bold;">end</span>;<br><br> UnicodeString 增加了 code page 字段和 element size 来描述字符串内容,这使得 UnicodeString 和其它类型的字符串可以很好的相兼容,所以 AnsiString 和 UnicodeString 可以很方便的互相转换,唯一要注意的是,当把 UnicodeString 向下转型到 AnsiString 的时候,可能会丢失数据,因此强烈建议你不要这么做。UnicodeString 保存的是 UTF-16 字符。<br><br> 在旧的环境下,可以使用编译标志 Unicode 来判断编译环境是否支持 UnicodeString,以便您可以在同一套代码中维护不同版本的字符支持环境。编译指令如下:<br><br><span style="font-family: monospace;">Delphi 使用:<br><wbr><wbr><wbr> {$IFDEF Unicode}<br> C++ Builder 使用:<br><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></span> #ifdef _DELPHI_STRING_UNICODE<br><br><span style="font-weight: bold;">变化概要:</span><br><ul> <li>String 类型映射为 UnicodeString 而不是 AnsiString</li> <li>Char 类型映射为 WideChar(2 bytes not 1 byte), 并且是 UTF-16 字符</li> <li>PChar 类型映射为 PWideChar</li> <li>C++ 中,System::String 映射到 UnicodeString 类</li> <li>Delphi 中,AnsiString 映射为早先版本中默认的 string </li> </ul> <span style="font-weight: bold;">未变化概要:</span><br><ul> <li>AnsiString</li> <li>WideString</li> <li>AnsiChar</li> <li>PAnsiChar</li> <li>隐式转换仍然可用</li> <li>用户的活动页代码(The user's active code page)控制着模式(ANSI vs. Unicode),所以 AnsiString 仍然可以支持 </li> </ul> 由于这些变化,代码编写上也出现了一些值得注意的情况,特别是在你打算将旧有的项目迁移到 Tiburon 下时更是如此。下面就列出一些发生的变化情况以及编写代码时应该注意的注意事项。<br><br><span style="font-weight: bold;">下面的操作将不再依赖字符 Size:</span><br><ul> <li>合并字符串</li> <li style="list-style-type: none;"> <ul> <li><string var="">+ <string var=""></string></string></li> <li><string var="">+ <literal></literal></string></li> <li> <literal>+ <literal></literal></literal> </li> <li> <span class="Element146">Concat(<string> , <string>)</string></string></span> </li> </ul> </li> <li><span class="Element146">标准字符串函数</span></li> <li style="list-style-type: none;"> <ul> <li><span class="Element146">Length(<string>)返回字符元素的长度,此值可能和字符在字节长度上并不匹配。SizeOf 函数则返回数据的字节长度,这意味着 SizeOf 和 Length 的返回值可能是不同的</string></span></li> <li><span class="Element146">Copy(<string>, <start>, <length>)返回的 SubString 基于字符元素</length></start></string></span></li> <li> <span class="Element146">Pos(<substr>,<string>)返回第一个字符元素的序号</string></substr></span> </li> </ul> </li> <li><span class="Element146">操作</span></li> <li style="list-style-type: none;"> <ul> <li> <string><comparison op=""><string></string></comparison></string><br> </li> <li><span class="Element146">CompareStr()</span></li> <li><span class="Element146">CompareText()</span></li> <li> <span class="Element146">...</span> </li> </ul> </li> <li><span class="Element146">FillChar(<struct or="" memory="">)</struct></span></li> <li style="list-style-type: none;"> <ul> <li><span class="Element146">FillChar(Rect, SizeOf(Rect), #0)</span></li> <li> <span class="Element146">FillChar(WndClassEx, SizeOf(TWndClassEx), #0)</span>. 使用的时候注意 <span class="Element146">WndClassEx.cbSize := SizeOf(TWndClassEx)</span> </li> </ul> </li> <li>Windows API</li> <li style="list-style-type: none;"> <ul> <li>API 默认使用 WideString (*W)形态的版本</li> <li>PChar(<string>)具有相同的语义 </string> </li> </ul> </li> </ul> <span style="font-weight: bold;">范例:</span><br><div style="margin-left: 40px;"> <span class="Element146"><span style="font-weight: bold;">GetModuleFileName:</span><br> function ModuleFileName(Handle: HMODULE): string;<br> var<br><wbr> Buffer: array[0..MAX_PATH] of Char;<br> begin<br><wbr> SetString(Result, Buffer, GetModuleFileName(Handle, Buffer, Length(Buffer)));<br> end;<br><br></wbr></wbr></span><span class="Element146"><span style="font-weight: bold;">GetWindowText:</span><br> function WindowCaption(Handle: HWND): string;<br> begin<br><wbr> SetLength(Result, 1024);<br><wbr> SetLength(Result, GetWindowText(Handle, PChar(Result), Length(Result)));<br> end;<br><br><span style="font-weight: bold;">字符串索引:</span><br> function StripHotKeys(const S: string): string;<br> var<br><wbr> I, J: Integer;<br><wbr> LastChar: Char;<br> begin<br><wbr> SetLength(Result, Length(S));<br><wbr> J := 0;<br><wbr> LastChar := #0;<br><wbr> for I := 1 to Length(S) do<br><wbr> begin<br><wbr><wbr><wbr> if (S[I] <> '&') or (LastChar = '&') then<br><wbr><wbr><wbr> begin<br><wbr><wbr><wbr><wbr><wbr> Inc(J);<br><wbr><wbr><wbr><wbr><wbr> Result[J] := S[I];<br><wbr><wbr><wbr> end;<br><wbr><wbr><wbr> LastChar := S[I];<br><wbr> end;<br><wbr> SetLength(Result, J);<br> end;<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></span> </div> <br><span style="color: rgb(153, 153, 153); font-style: italic;">接上文</span><br><br><span style="font-weight: bold;">依赖字符 Size 的代码结构:</span><br><br> 在 Tiburon 中,下列列表中列出的这些函数和特性依赖字符 Size,并且已经包含了一个“轻便”的版本,迁移代码的时候只需要将列出的代码迁移到后面提供的轻便版本即可。<br><br><ul><li> <span class="Element146">SizeOf(<char array="">)</char></span> 替换为 <span class="Element146">Length(<char array="">)</char></span> </li></ul> <div style="margin-left: 80px;"> <span style="font-weight: bold;">范例:</span><br> var<br><wbr> Count: Integer;<br><wbr> Buffer: array[0..MAX_PATH - 1] of Char;<br> begin<br><wbr> // 现有代码 - 当 string = UnicodeString 的时候这段代码是错的<br><wbr> Count := SizeOf(Buffer);<br><wbr> GetWindowText(Handle, Buffer, Count);<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><br><wbr> // 正确的应该是下面这样<br><wbr> Count := Length(Buffer); // <<-- Count 应该是 Chars 而不是 Bytes<br><wbr> GetWindowText(Handle, Buffer, Count);<br> end;<br> SizeOf 返回的是数组的字节数,而 GetWindowText 的 Counts 参数需要的是字符数,所以这里需要把 SizeOf 换成 Length。<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul><li> <span class="Element146">Move(<char buffer="">... CharCount)</char></span> 替换为 <span class="Element146">Move(<char buffer=""> ,,, CharCount * SizeOf(Char))</char></span> </li></ul> <div style="margin-left: 80px;">var<br><wbr><wbr> Count: Integer;<br><wbr><wbr> Buf1, Buf2: array[0..255] of Char;<br> begin<br><wbr> // 现有代码 - 当 string = UnicodeString (char = 2 bytes) 时,下面的代码是错误的<br><wbr> Count := Length(Buf1);<br><wbr> Move(Buf1, Buf2, Count);<br><wbr><wbr><br><wbr> // 正确的写法应该是<br><wbr> Count := SizeOf(Buf1);<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // <<-- Specify buffer size in bytes<br><wbr> Count := Length(Buf1) * SizeOf(Char); // <<-- Specify buffer size in bytes<br><wbr> Move(Buf1, Buf2, Count);<br> end;<br> 由于 Length 返回的是字符数,而 Move 的 Count 参数需要的是字节数,所以应该用 SizeOf 或者 Length(Buf1) * SizeOf(Char) 替换 Length(Buf1)。<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul><li> <span class="Element146">Stream 的 Read/Write 替换为</span> <span class="Element146">AnsiString</span>, <span class="Element146">SizeOf(Char),或者使用</span> <span class="Element146">TEncoding 类</span> </li></ul> <div style="margin-left: 80px;"> <span style="font-weight: bold;">调用 Read/ReadBuffer 方法的范例:</span><br> var<br><wbr> S: string;<br><wbr> L: Integer;<br><wbr> Stream: TStream;<br><wbr> Temp: AnsiString;<br> begin<br><wbr> // 现有代码- 当 string = UnicodeString 时它是不正确的<br><wbr> Stream.Read(L, SizeOf(Integer));<br><wbr> SetLength(S, L);<br><wbr> Stream.Read(Pointer(S)^, L);<br><wbr><wbr><br><wbr> // 正确的 Unicode 写法如下<br><wbr> Stream.Read(L, SizeOf(Integer));<br><wbr> SetLength(S, L);<br><wbr> Stream.Read(Pointer(S)^, L * SizeOf(Char));<wbr> // <<-- Specify buffer size in bytes<br><wbr><wbr><br><wbr> //正确的 Ansi 写法如下<br><wbr> Stream.Read(L, SizeOf(Integer));<br><wbr> SetLength(Temp, L);<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // <<-- 使用临时的变量 AnsiString<br><wbr> Stream.Read(Pointer(Temp)^, L * SizeOf(AnsiChar));<wbr> // <<-- Specify buffer size in bytes<br><wbr> S := Temp;<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // <<-- 放宽 string 到 Unicode<br> end;<br> 上面的解决方案依赖于您存储在 Stream 中的字符串的编码格式,更好的读取和转换他们建议使用 TEncoding 类。<br><br><span style="font-weight: bold;">调用 Write/WriteBuffer 的范例:</span><br> var<br><wbr> S: string;<br><wbr> Stream: TStream;<br><wbr> Temp: AnsiString;<br> begin<br><wbr> // 现有代码 - 当 string = UnicodeString 时它是错的<br><wbr> Stream.Write(Pointer(S)^, Length(S));<br><wbr><wbr><br><wbr> // 正确的读取 Unicode 的代码<br><wbr> Stream.Write(Pointer(S)^, Length(S) * SizeOf(Char)); // <<-- Specify buffer size in bytes<br><wbr><wbr><br><wbr> // 正确的读取 Ansi 的代码<br><wbr> Temp := S;<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // <<-- Use temporary AnsiString<br><wbr> Stream.Write(Pointer(Temp)^, Length(Temp) * SizeOf(AnsiChar));// <<-- Specify buffer size in bytes<br> end;<br> 上面的解决方案依赖于您要存储进 Stream 中的字符串的编码格式,建议使用 TEncoding 类来更好的对格式进行处理。<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul><li> <span class="Element146">FillChar(<char array="">, <size>, <ansichar>)</ansichar></size></char></span> 如果采用 #0 填充,<size> 替换为<wbr><size><span class="Element146">* SizeOf(Char);如果填充其它字符,替换为</span> <span class="Element146">StringOfChar</span> 函数 </size></wbr></size> </li></ul> <div style="margin-left: 80px;"> <span style="font-weight: bold;">范例:</span><br> var<br><wbr> Count: Integer;<br><wbr> Buffer: array[0..255] of Char;<br> begin<br><wbr><wbr> // 现有代码 - 当 string = UnicodeString ( char = 2 字节) 时,这段代码是错的<br><wbr><wbr> Count := Length(Buffer);<br><wbr><wbr> FillChar(Buffer, Count, 0);<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><br><wbr><wbr> // 正确的代码应该写作下面这样<br><wbr><wbr> Count := SizeOf(Buffer);<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // <<-- Specify buffer size in bytes<br><wbr><wbr> Count := Length(Buffer) * SizeOf(Char); // <<-- Specify buffer size in bytes<br><wbr><wbr> FillChar(Buffer, Count, 0);<br> end;<br> Length 返回的是字符数,而 FillChar 的 Count 参数需要的是字节数,所以必须用 SizeOf 替换 Length,或者使用 Length * SizeOf(Char)。<br><br> 另外,需要注意的是,Tiburon 中 Char 等于 2 个字节,FillChar 填充的时候确是按照 Bytes 来计算的,所以,下面的代码<br><br> var<br><wbr> Buf: array[0..32] of Char;<br> begin<br><wbr> FillChar(Buf, Length(Buf), #9);<br> end;<br><br> 并不是向目标中填充 $09,而是 $0909,要得到正确的数值,应该改写成下面这样:<br><br> var<br><wbr> Buf: array[0..32] of Char;<br> begin<br><wbr> StrPCopy(Buf, StringOfChar(#9, Length(Buf)));<br> ...<br> end;<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul><li> <span class="Element146">GetProcAddress(<module>, <pansichar>)</pansichar></module></span><wbr></wbr> </li></ul> <div style="margin-left: 80px;">由于 GetProcAddres 没有对应的 *W (Unicode) 版本的 API,所以只能使用下面的代码来正确调用它:<br> procedure CallLibraryProc(const LibraryName, ProcName: string);<br> var<br><wbr> Handle: THandle;<br><wbr> RegisterProc: function: HResult stdcall;<br> begin<br><wbr> Handle := LoadOleControlLibrary(LibraryName, True);<br><wbr> @RegisterProc := GetProcAddress(Handle, <span style="text-decoration: underline;">PAnsiChar</span>(<span style="text-decoration: underline;">AnsiString</span>(ProcName)));<br> end;<br><br></wbr></wbr></wbr></wbr> </div> <ul><li>RegQueryValueEx 函数 </li></ul> <div style="margin-left: 80px;">由于 RegQueryValueEx 函数的 <strong><span style="color: rgb(0, 0, 0);">Len</span></strong> 指定的是字节数,而不是字符数,所以 Unicode 版本中它的大小是实际需要大小的 2 倍,所以这样的代码:<br><br> Len := MAX_PATH;<br> if RegQueryValueEx(reg, PChar(Name), nil, nil, PByte(@Data[0]), @Len) = ERROR_SUCCESS<br> then<br><wbr> SetString(Result, Data, Len - 1) // Len includes #0<br> else<br><wbr> RaiseLastOSError;<br><br> 应该换成下面这样:<br><br> Len := MAX_PATH * SizeOf(Char);<br> if RegQueryValueEx(reg, PChar(Name), nil, nil, PByte(@Data[0]), @Len) = ERROR_SUCCES<br> then<br><wbr> SetString(Result, Data, <span style="text-decoration: underline;">Len div SizeOf(Char) - 1</span>) // Len includes #0, Len contains the number of bytes<br> else<br><wbr> RaiseLastOSError;<br></wbr></wbr></wbr></wbr> </div> <ul><li>CreateProcessW 函数 </li></ul> <div style="margin-left: 80px;">在 Unicode 版本的 CreateProcess 函数中,其行为和 ANSI 的版本略有不同。Unicode 的 CreateProcessW 会改变参数 <span class="Element146">lpCommandLine 传入的数据,因此调用 CreateProcess / CreateProcessW 的时候,不可以给</span> <span class="Element146">lpCommandLine 赋值常量,或者是一个变量指向的常量,否则函数会抛出</span> access violations 的异常。下面是错误的代码:<br><br> // 传入了一个 string 常量<br> CreateProcess(nil, 'foo.exe', nil, nil, False, 0,<br><wbr> nil, nil, StartupInfo, ProcessInfo);<br><br> // 传入了一个常量表达式<br><wbr> const<br><wbr><wbr><wbr> cMyExe = 'foo.exe'<br><wbr> CreateProcess(nil, cMyExe, nil, nil, False, 0,<br><wbr><wbr><wbr> nil, nil, StartupInfo, ProcessInfo);<br><br> // 传入了一个引用计数为 -1 的字符串:<br> const<br><wbr> cMyExe = 'foo.exe'<br> var<br><wbr> sMyExe: string;<br><wbr> sMyExe := cMyExe;<br><wbr> CreateProcess(nil, PChar(sMyExe), nil, nil, False, 0, nil, nil, StartupInfo, ProcessInfo);<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul><li>LeadBytes 常量 </li></ul> <div style="margin-left: 80px;">早先的版本中 LeadBytes 常量包含了本地系统中所有可以作为双字节字符 LeadByte 的列表,常常有这样的代码:<br> if Str[I] in LeadBytes then<br><br> 现在你需要将它改成调用 IsLeaderChar 函数<br> if IsLeadChar(Str[I]) then<br> </div> <ul><li>使用 TMemoryStream 类 </li></ul> <div style="margin-left: 80px;">当您需要用 TMemoryStream 写入一个文本文件的时候,最好在写入任何字符数据进去之前先写入一个 Byte Order Mark (BOM):<br> var<br><wbr> Bom: TBytes;<br> begin<br><wbr> ...<br><wbr> Bom := TEncoding.UTF8.GetPreamble;<br><wbr> Write(Bom[0], Length(Bom));<br><br> 而任何写入的字符需要被转换成 UTF-8 编码:<br> var<br><wbr> Temp: Utf8String;<br> begin<br><wbr> ...<br><wbr> Temp := Utf8Encode(Str); // Str 是要写入文件的字符<br><wbr> Write(Pointer(Temp)^, Length(Temp));<br><wbr>//Write(Pointer(Str)^, Length(Str)); 原来写入字符串的代码<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <br><span style="color: rgb(153, 153, 153); font-style: italic;">接上文</span><br><br><ul><li>MultiByteToWideChar 函数 </li></ul> <div style="margin-left: 80px;">调用 Windows API MultiByteToWideChar 函数可以简单的用一个任务替代,下面是一个是用 MultiByteToWideChar 的例子:<br><br> procedure TWideCharStrList.AddString(const S: string);<br> var<br><wbr> Size, D: Integer;<br> begin<br><wbr> Size := SizeOf(S);<br><wbr> D := (Size + 1) * SizeOf(WideChar);<br><wbr> FList[FUsed] := AllocMem(D);<br><wbr> MultiByteToWideChar(0, 0, PChar(S), Size, FList[FUsed], D);<br><wbr> Inc(FUsed);<br> end;<br><br> 转换到 Unicode 下可以写作这样(同时支持 Unicode 和 ANSI 字符):<br><br> procedure TWideCharStrList.AddString(const S: string);<br> {$IFNDEF UNICODE}<br> var<br><wbr> L, D: Integer;<br> {$ENDIF}<br> begin<br> {$IFDEF UNICODE}<br><wbr> FList[FUsed] := StrNew(PWideChar(S));<br> {$ELSE}<br><wbr> L := Length(S);<br><wbr> D := (L + 1) * SizeOf(WideChar);<br><wbr> FList[FUsed] := AllocMem(D);<br><wbr> MultiByteToWideChar(0, 0, PAnsiChar(S), L, FList[FUsed], D);<br> {$ENDIF}<br><wbr> Inc(FUsed);<br> end;<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul><li>SysUtils.AppendStr 函数 </li></ul> <div style="margin-left: 80px;">AppendStr 函数已经废弃了,因为它与 AnsiString 硬编码在一起,而且没有 Unicode 的版本可以替换,所以下面的代码<br><br> AppendStr(String1, String2);<br><br> 应该换成:<br><br> String1 := String1 + String2;<br><br> 您也可以使用新的 TStringBuilder 类来替换。<br> </div> <ul><li>使用 Named Threads </li></ul> <div style="margin-left: 80px;">现有 Delphi 代码中使用了 Named Threads 的代码必须修改了。在早先的版本中,当你需要在分类(gallery)中用一个新的 <span style="font-weight: bold;">Thread Object</span> 去创建一个 Thread 的时候,需要在新的 Thread 单元中建立下面的类型:<br><br> type<br> TThreadNameInfo = record<br><wbr> FType: LongWord; // must be 0x1000<br><wbr> FName: PChar; // pointer to name (in user address space)<br><wbr> FThreadID: LongWord; // thread ID (-1 indicates caller thread)<br><wbr> FFlags: LongWord; // reserved for future use, must be zero<br> end;<br><br> 在调试器中,Named Thread 的处理器期待 FName 成员是 ANSI 字符,不是 Unicode,所以上面的声明必须改成下面这样:<br><br> type<br> TThreadNameInfo = record<br><wbr> FType: LongWord; // must be 0x1000<br><wbr> FName: <span style="text-decoration: underline;">PAnsiChar</span>; // pointer to name (in user address space)<br><wbr> FThreadID: LongWord; // thread ID (-1 indicates caller thread)<br><wbr> FFlags: LongWord; // reserved for future use, must be zero<br> end;<br><br> 在新版本中上述声明已经修改,提示这段代码是需要您注意早先版本中您手工创建并声明的代码需要您自己修改。<br><br> 如果您需要在 Named Thread 中使用 Unicode 字符,您必须将字符串格式化成 UTF-8 编码,调试器可以完全支持改编码。例如:<br><br> ThreadNameInfo.FName := UTF8String('UnicodeThread_фис');<br><br> 注意:C++ Builder 里面一直使用的是正确的代码,所以上述问题在 C++ Builder 中并不存在。<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul><li>使用 PChar 转换的指针运算 </li></ul> <div style="margin-left: 80px;">在 Tiburon 更早的版本中,并不是所有的指针类型都支持指针运算。因为这样,为了让无类型指针也支持指针运算,许多代码都将其转化成 PChar 操作。现在,可以使用 Tiburon 中的新编译条件 {<span class="Element146">$POINTERMATH} 来指示编译器允许指针运算,特别是允许 PByte 的指针运算。{$POINTERMATH ON/OFF} 可以打开/禁止对任意指针变量的运算,增减指针实际操作的是指针元素的大小。<br><br> 下面的例子是一个将某类型指针转换成 PChar 后的指针运算:<br><br> function TCustomVirtualStringTree<wbr>.InternalData(Node: PVirtualNode): Pointer;<br> begin<br><wbr> if (Node = FRoot) or (Node = nil) then<br><wbr><wbr><wbr> Result := nil<br><wbr> else<br><wbr><wbr><wbr> Result := PChar(Node) + FInternalDataOffset;<br> end;<br><br> 您应该将其修改成 PByte 而不是 PChar:<br><br> function TCustomVirtualStringTree<wbr>.InternalData(Node: PVirtualNode): Pointer;<br> begin<br><wbr> if (Node = FRoot) or (Node = nil) then<br><wbr><wbr><wbr> Result := nil<br><wbr> else<br><wbr><wbr><wbr> Result := <span style="text-decoration: underline;">PByte(Node)</span> + FInternalDataOffset;<br> end;<br><br> 在上面的例子中,Node 真实的数据不是 PChar 的数据。将其强制转换成 PChar 的操作在早先的版本中是正常的,因为早先版本中</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></span> <span class="Element146">SizeOf(Char) == Sizeof(Byte)。但是现在不同了,所以这样的代码必须从 PChar 改换成 PByte。如果不做这样的更改,返回的 Pointer 将指向错误的数据。</span> <span class="Element146"><br></span> </div> <ul><li>变体开放数组(Variant Open Array)参数 </li></ul> <div style="margin-left: 80px;">如果你的代码中有使用 TVarRec 类型去处理开放数组的话,你可能需要为其添加对 <span class="Element146">vtUnicodeString</span> 的支持。参看下列示例:<br><br> procedure RegisterPropertiesInCate<wbr>gory(const CategoryName: string;<br><wbr> const Filters: array of const); overload;<br> var<br> I: Integer;<br> begin<br><wbr> if Assigned(RegisterPropertyInCatego<wbr>ryProc) then<br><wbr><wbr><wbr> for I := Low(Filters) to High(Filters) do<br><wbr><wbr><wbr><wbr><wbr> with Filters[I] do<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr> case vType of<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> vtPointer:<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> RegisterPropertyInCatego<wbr>ryProc(CategoryName, nil,<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> PTypeInfo(vPointer), );<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> vtClass:<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> RegisterPropertyInCatego<wbr>ryProc(CategoryName, vClass, nil, );<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> vtAnsiString:<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> RegisterPropertyInCatego<wbr>ryProc(CategoryName, nil, nil,<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> string(vAnsiString));<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> vtUnicodeString:<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> RegisterPropertyInCatego<wbr>ryProc(CategoryName, nil, nil,<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> string(vUnicodeString));<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr> else<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> raise Exception.CreateResFmt(@sInvalidFilter, [I, vType]);<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr> end;<br><wbr>end;<br></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr> </div> <ul> <li>其他需要注意的代码:</li> <li style="list-style-type: none;"> <ul> <li><span class="Element146">AllocMem(</span></li> <li><span class="Element146">AnsiChar</span></li> <li><span class="Element146">of AnsiChar</span></li> <li><span class="Element146">AnsiString</span></li> <li><span class="Element146">of Char</span></li> <li><span class="Element146">Copy(</span></li> <li><span class="Element146">GetMem(</span></li> <li><span class="Element146">Length(</span></li> <li><span class="Element146">PAnsiChar(</span></li> <li><span class="Element146">Pointer(</span></li> <li><span class="Element146">Seek(</span></li> <li><span class="Element146">ShortString</span></li> <li> <span class="Element146">string[</span> </li> </ul> </li> </ul> <div style="margin-left: 40px;">代码中包含上述写法的地方可能需要修改以适应 UnicodeString 的变化。<br> </div> <br><span style="font-weight: bold;">带字符的集合类型</span><br><br> 您可能需要修改下列类型:<br><ul> <li><char>in <set class="Element146" of=""><span>AnsiChar</span>> 这样的代码生成的程序是正确的(>#255 的字符不会包含在集合内)。编译器会提出 "WideChar reduced in set operations" 的警告,基于您代码的需要,您可以关闭这个警告,或者使用 CharInSet 函数替代。</set></char></li> <li><char>in LeadBytes 全局的 LeadBytes 变量包含的是本地 MBCS Ansi 字符的集合。UTF-16 格式也有 LeadChar 的概念((#$D800 - #$DBFF 是高 surrogate, #$DC00 - #$DFFF 是低 surrogate)。因此建议使用 overload 函数 IsLeadChar 来判断,该函数的 ANSI 版本检测 LeadBytes,WideChar 版本检测 high/low surrogate。</char></li> <li>字符分类 使用静态类 <span class="Element146">TCharacter。</span><span class="Element146">Character 单元中提供了一些函数对字符分类:</span><span class="Element146">IsDigit</span>, <span class="Element146">IsLetter</span>, <span class="Element146">IsLetterOrDigit</span>, <span class="Element146">IsSymbol</span>, <span class="Element146">IsWhiteSpace</span>, <span class="Element146">IsSurrogatePair,等等。</span> </li> </ul> <span style="font-weight: bold;">应当心这些结构</span><br><br> 您需要检查下列可能引起错误的结构:<br><ul> <li>模糊的类型转换</li> <li style="list-style-type: none;"> <ul> <li><span class="Element146">AnsiString(Pointer(foo))</span></li> <li> <span class="Element146">检查正确性:代码是什么意图?</span> </li> </ul> </li> <li><span class="Element146">可疑的类型转换引发的警告</span></li> <li style="list-style-type: none;"> <ul> <li><span class="Element146">PChar(<ansistring var="">)</ansistring></span></li> <li> <span class="Element146">PAnsiChar(<unicodestring var="">)</unicodestring></span> </li> </ul> </li> <li> <span class="Element146">直接建立、操作、访问 string 的内部结构。例如 AnsiString 的内部结构已经发生变化,所以这样的操作是危险的。您应该使用</span> <span class="Element146">StringRefCount</span>, <span class="Element146">StringCodePage</span>, <span class="Element146">StringElementSize 等方法来获得额外信息。</span> </li> </ul> <span style="font-weight: bold;">控件和类</span><br><ul> <li>TStrings: 内部存储的是 UnicodeStrings。</li> <li>TWideString:(可能被废弃)没有更改,内部使用 WideString (BSTR)</li> <li>TStringStream</li> <li style="list-style-type: none;"> <ul> <li>被重写成内部存储 ANSI 字符</li> <li>字符编码可以被重载</li> <li>考虑使用 TStringBuilder 替代 TStringStream 来逐步构建字符串 </li> </ul> </li> <li>TEncoding</li> <li style="list-style-type: none;"> <ul> <li>Default 属性是用户活动页码(users’ active code page)</li> <li>支持 UTF-8</li> <li>支持 UTF-16, big 和 little endian</li> <li>支持 Byte Order Mark (BOM)</li> <li>您可以继承子类实现特殊的编码 </li> </ul> </li> </ul> <span style="font-weight: bold;">Byte Order Mark</span><br><br> BOM 必须添加到文件中以便判断文件的编码方式。<br><ul> <li>UTF-8 使用 EF BB EF</li> <li>UTF-16 Little Endian 使用 FF FE</li> <li>UTF-16 Big Endian 使用 FE FF </li> </ul> 做好这些注意事项,将帮助您顺利地把旧有项目迁移到 Tiburon 的 Unicode 下。当然,如果您开发的是多版本控件,或者是希望项目能在多个版本中编译,您最好根据这些特性定义适当的编译条件,以便让代码更好的被更低的版本的编译器支持和编译。<br><br> 本文基于 Tiburon 帮助编写,如有翻译错误或描述不准确的地方欢迎大家指正!相信这次 Delphi / C++ Builder 2009 将是广大爱好者最喜欢的版本之一。<br><br> 全文完。 <div id="MySignature">作者: <a href="http://www.cnblogs.com/iinsnian/" target="_blank">陆岛工作室</a> </div> </wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>