【问题标题】:StrAlloc not working after migrating to Delphi XE7迁移到 Delphi XE7 后,StrAlloc 无法正常工作
【发布时间】:2015-11-30 11:11:34
【问题描述】:

我正在开发一个最近从 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 个字节,这可能是个问题。但是我尝试了多种方法,但没有任何效果。

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

【问题讨论】:

  • SaveToTextStream 以什么编码存储数据?修复代码的正确方法取决于使用的编码。三种现实可能性是 1) 与 UnicodeString (UTF-16LE) 2) UTF-8 相同的编码 3) 与 AnsiString 相同的编码(取决于 Windows 活动代码页)。
  • 为什么这被否决了??

标签: delphi unicode delphi-2007 delphi-xe7


【解决方案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 提供什么功能,但我相信你知道。

【讨论】:

  • +1。这。声称要移植到 unicode delphi 却不知道自己在做什么的人,会对他们所做的一切进行哈希处理。除非有必要,否则手动分配(使用 StrAlloc 或 GetMem)是一种代码异味。将即使在 Delphi 7 时代也很愚蠢的代码留在其中,是一个勉强能胜任的开发人员的标志。
  • 嗨@DavidHeffernan,你能看一下编辑后的代码吗?我已经提到了 SaveToTextStream 的代码。
  • ObjectBinaryToText 使用什么编码?
  • 可以看到Text已经由WriteAsciiStr和WriteUTF8Str组成。这是否说明了 UTF-8 编码?
  • 我认为您需要清楚编码是什么。无论是什么,您都需要将其与您的字符串流相匹配。无论如何,我给了您集中在概念上的高级答案。您接受了一个答案,该答案提供了没有解释的代码作为问题,这可能具有误导性。在我看来,您应该尝试理解和学习,而不是仅仅粘贴您不清楚其后果的代码。
【解决方案2】:

通过参考上面hvdDavid 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 提供准确的编码文本数据。

【讨论】:

  • 这行得通! SaveToTextStream 接受 TStream 作为输入参数。正是我需要的。谢谢。
  • 这对我来说似乎是相当糟糕的建议。正如所写的代码强制使用 ANSI。当然应该避免这种情况。 @jetty,你真的接受向 Unicode 的转变吗? SaveToTextStream 是如何实现的? TCReport 是否包含文本表示?
  • @DavidHeffernan 是的,强制使用 ANSI 是正确的。如果TCReport 像你说的那样有ReportAsText 方法会更好。我建议在最后提出一点警告,因为我不知道SaveToTextStream 是如何实现的,假设转换为 ANSI 是安全的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-18
  • 2013-07-07
  • 2019-04-24
相关资源
最近更新 更多