迁移到 Delphi XE7 后 StrAlloc 不工作

StrAlloc not working after migrating to Delphi XE7

我正在开发最近从 Delphi 2007 升级到 XE7 的应用程序。有一种特殊情况,TMemoryStream 到 PChar 的转换失败。这是代码:

procedure TCReport.CopyToClipboard;
var
  CTextStream: TMemoryStream;
  PValue: PChar;
begin
    CTextStream := TMemoryStream.Create;
    //Assume that this code is saving a report column to CTextStream
    //Verified that the value in CTextStream is correct
    Self.SaveToTextStream(CTextStream);

    //The value stored in PValue below is corrupt
    PValue := StrAlloc(CTextStream.Size + 1);
    CTextStream.Read(PValue^, CTextStream.Size + 1);
    PValue[CTextStream.Size] := #0;

    { Copy text stream to clipboard }
    Clipboard.Clear;
    Clipboard.SetTextBuf(PValue);

    CTextStream.Free;

    StrDispose(PValue);
end;

为 SaveToTextStream 添加代码:

procedure TCReport.SaveToTextStream(CTextStream: TStream);
var
  CBinaryMemoryStream: TMemoryStream;
  CWriter: TWriter;
begin

  CBinaryMemoryStream := TMemoryStream.Create;
  CWriter := TWriter.Create(CBinaryMemoryStream, 24);

  try
    CWriter.Ancestor := nil;
    CWriter.WriteRootComponent(Self);
    CWriter.Free;

    CBinaryMemoryStream.Position := 0;

    { Convert Binary 'WriteComponent' stream to text}

    ObjectBinaryToText(CBinaryMemoryStream, CTextStream);
    CTextStream.Position := 0;
  finally
    CBinaryMemoryStream.Free;
  end;
end;

我观察到 StrLen(PChar) 的大小也是 TMemoryStream 的一半。但在 Delphi 2007 年,它与 TMemoryStream 的大小相同。

我知道上面的代码假设 char 的大小为 1 个字节,这可能是个问题。但是我尝试了多种方法,都没有用。

您能否建议一种更好的方法来进行此转换?

再一次,这是 Delphi 2009 年及以后使用 Unicode 文本的问题。 Delphi 2007 年及更早版本:

  • CharAnsiChar.
  • 的别名
  • PCharPAnsiChar.
  • 的别名
  • stringAnsiString.
  • 的别名

在 Delphi 2009 年及以后:

  • CharWideChar 的别名。
  • PCharPWideChar.
  • 的别名
  • stringUnicodeString.
  • 的别名

您的代码是假设 PCharPAnsiChar 编写的。因此你的问题。无论如何,您需要停止使用 StrAlloc。通过在此处手动分配堆内存,您正在让自己的生活变得艰难。让编译器完成工作。

您需要在字符串变量中获取您的文本,然后只需执行以下操作:

Clipboard.AsText := MyStrVariable;

获取字符串的最佳方式取决于 TCReport 提供的工具。我希望它会直接产生一个字符串,在这种情况下你会写这样的东西:

procedure TCReport.CopyToClipboard;
begin
  Clipboard.AsText := Self.ReportAsText;
end;

我猜你的 TCReport 提供了哪些功能,但我相信你知道。

参考上面hvd and David Heffernan写的内容,一种可能的方法是将CopyToClipboard上的CTextStream更改为TStringStream,如下所示:

procedure TCReport.CopyToClipboard;
var
  CTextStream: TStringStream;
begin
    CTextStream := TStringStream.Create;
    try
      //Assume no error with Self.SaveToTextStream
      Self.SaveToTextStream(CTextStream);

      { Copy text stream to clipboard }
      Clipboard.AsText := CTextStream.DataString;
    finally
      CTextStream.Free;
    end;
end;

但是您应该确保 SaveToTextStream 函数为 CTextStream 提供了准确的编码文本数据。