[转载]现有 Delphi 项目迁移到 Tiburon 中的注意事项 (下)
接上文
带字符的集合类型
您可能需要修改下列类型:
您需要检查下列可能引起错误的结构:
BOM 必须添加到文件中以便判断文件的编码方式。
本文基于 Tiburon 帮助编写,如有翻译错误或描述不准确的地方欢迎大家指正!相信这次 Delphi / C++ Builder 2009 将是广大爱好者最喜欢的版本之一。
全文完。
- 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;
procedure TWideCharStrList.AddString(const S: string);
var
begin
end;
转换到 Unicode 下可以写作这样(同时支持 Unicode 和 ANSI 字符):
procedure TWideCharStrList.AddString(const S: string);
{$IFNDEF UNICODE}
var
{$ENDIF}
begin
{$IFDEF UNICODE}
{$ELSE}
{$ENDIF}
end;
- SysUtils.AppendStr 函数
AppendStr 函数已经废弃了,因为它与 AnsiString
硬编码在一起,而且没有 Unicode 的版本可以替换,所以下面的代码
AppendStr(String1, String2);
应该换成:
String1 := String1 + String2;
您也可以使用新的 TStringBuilder 类来替换。
AppendStr(String1, String2);
应该换成:
String1 := String1 + String2;
您也可以使用新的 TStringBuilder 类来替换。
- 使用 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;
在调试器中,Named Thread 的处理器期待 FName 成员是 ANSI 字符,不是 Unicode,所以上面的声明必须改成下面这样:
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;
在新版本中上述声明已经修改,提示这段代码是需要您注意早先版本中您手工创建并声明的代码需要您自己修改。
如果您需要在 Named Thread 中使用 Unicode 字符,您必须将字符串格式化成 UTF-8 编码,调试器可以完全支持改编码。例如:
ThreadNameInfo.FName := UTF8String('UnicodeThread_фис');
注意:C++ Builder 里面一直使用的是正确的代码,所以上述问题在 C++ Builder 中并不存在。
type
TThreadNameInfo = record
end;
在调试器中,Named Thread 的处理器期待 FName 成员是 ANSI 字符,不是 Unicode,所以上面的声明必须改成下面这样:
type
TThreadNameInfo = record
end;
在新版本中上述声明已经修改,提示这段代码是需要您注意早先版本中您手工创建并声明的代码需要您自己修改。
如果您需要在 Named Thread 中使用 Unicode 字符,您必须将字符串格式化成 UTF-8 编码,调试器可以完全支持改编码。例如:
ThreadNameInfo.FName := UTF8String('UnicodeThread_фис');
注意:C++ Builder 里面一直使用的是正确的代码,所以上述问题在 C++ Builder 中并不存在。
- 使用 PChar 转换的指针运算
在 Tiburon
更早的版本中,并不是所有的指针类型都支持指针运算。因为这样,为了让无类型指针也支持指针运算,许多代码都将其转化成 PChar
操作。现在,可以使用 Tiburon 中的新编译条件 {$POINTERMATH}
来指示编译器允许指针运算,特别是允许 PByte 的指针运算。{$POINTERMATH ON/OFF}
可以打开/禁止对任意指针变量的运算,增减指针实际操作的是指针元素的大小。
下面的例子是一个将某类型指针转换成 PChar 后的指针运算:
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;
在上面的例子中,Node 真实的数据不是 PChar 的数据。将其强制转换成 PChar 的操作在早先的版本中是正常的,因为早先版本中 SizeOf(Char)
== Sizeof(Byte)。但是现在不同了,所以这样的代码必须从 PChar 改换成 PByte。如果不做这样的更改,返回的
Pointer 将指向错误的数据。
下面的例子是一个将某类型指针转换成 PChar 后的指针运算:
function TCustomVirtualStringTree
begin
end;
您应该将其修改成 PByte 而不是 PChar:
function TCustomVirtualStringTree
begin
end;
在上面的例子中,Node 真实的数据不是 PChar 的数据。将其强制转换成 PChar 的操作在早先的版本中是正常的,因为早先版本中
- 变体开放数组(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;
procedure RegisterPropertiesInCate
var
I: Integer;
begin
- 其他需要注意的代码:
-
- AllocMem(
- AnsiChar
- of AnsiChar
- AnsiString
- of Char
- Copy(
- GetMem(
- Length(
- PAnsiChar(
- Pointer(
- Seek(
- ShortString
- string[
代码中包含上述写法的地方可能需要修改以适应 UnicodeString
的变化。
带字符的集合类型
您可能需要修改下列类型:
- <Char> in <set of AnsiChar> 这样的代码生成的程序是正确的(>#255 的字符不会包含在集合内)。编译器会提出 "WideChar reduced in set operations" 的警告,基于您代码的需要,您可以关闭这个警告,或者使用 CharInSet 函数替代。
- <Char> in LeadBytes 全局的 LeadBytes 变量包含的是本地 MBCS Ansi 字符的集合。UTF-16 格式也有 LeadChar 的概念((#$D800 - #$DBFF 是高 surrogate, #$DC00 - #$DFFF 是低 surrogate)。因此建议使用 overload 函数 IsLeadChar 来判断,该函数的 ANSI 版本检测 LeadBytes,WideChar 版本检测 high/low surrogate。
- 字符分类 使用静态类 TCharacter。Character 单元中提供了一些函数对字符分类:IsDigit, IsLetter, IsLetterOrDigit, IsSymbol, IsWhiteSpace, IsSurrogatePair,等等。
您需要检查下列可能引起错误的结构:
- 模糊的类型转换
-
- AnsiString(Pointer(foo))
- 检查正确性:代码是什么意图?
- 可疑的类型转换引发的警告
-
- PChar(<AnsiString var>)
- PAnsiChar(<UnicodeString var>)
- 直接建立、操作、访问 string 的内部结构。例如 AnsiString 的内部结构已经发生变化,所以这样的操作是危险的。您应该使用 StringRefCount, StringCodePage, StringElementSize 等方法来获得额外信息。
- TStrings: 内部存储的是 UnicodeStrings。
- TWideString:(可能被废弃)没有更改,内部使用 WideString (BSTR)
- TStringStream
-
- 被重写成内部存储 ANSI 字符
- 字符编码可以被重载
- 考虑使用 TStringBuilder 替代 TStringStream 来逐步构建字符串
- TEncoding
-
- Default 属性是用户活动页码(users’ active code page)
- 支持 UTF-8
- 支持 UTF-16, big 和 little endian
- 支持 Byte Order Mark (BOM)
- 您可以继承子类实现特殊的编码
BOM 必须添加到文件中以便判断文件的编码方式。
- UTF-8 使用 EF BB EF
- UTF-16 Little Endian 使用 FF FE
- UTF-16 Big Endian 使用 FE FF
本文基于 Tiburon 帮助编写,如有翻译错误或描述不准确的地方欢迎大家指正!相信这次 Delphi / C++ Builder 2009 将是广大爱好者最喜欢的版本之一。
全文完。