[转载]现有 Delphi 项目迁移到 Tiburon 中的注意事项 (下)

接上文

  • 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;
  • SysUtils.AppendStr 函数
AppendStr 函数已经废弃了,因为它与 AnsiString 硬编码在一起,而且没有 Unicode 的版本可以替换,所以下面的代码

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 中并不存在。
  • 使用 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 将指向错误的数据。
  • 变体开放数组(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 的变化。

带字符的集合类型

您可能需要修改下列类型:
  • <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)
    • 您可以继承子类实现特殊的编码
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 将是广大爱好者最喜欢的版本之一。

全文完。
原文地址:https://www.cnblogs.com/luckForever/p/7255314.html