【问题标题】:What can cause System.Move to occasionally give wrong results?什么会导致 System.Move 偶尔给出错误结果?
【发布时间】:2018-10-06 19:27:36
【问题描述】:

最近几天,我们在使用第三方开发的数据库组件时遇到了一些奇怪的问题。几个月来,这些组件没有任何变化。 最近几天更改的代码是我们自己的代码,我们还更新了由另一个第三方开发的 gui 组件。

调试后我发现在其中一个数据库组件过程中调用 System.Move 有时会给出错误的结果!

请从数据库组件中查看下面的代码并阅读我的 cmets。这种不一致的行为怎么会发生? 谁能告诉我如何继续找到这种不一致行为的原因? 注意!我不认为这段代码有什么问题,它只是用来解释问题的“症状”。 我的猜测是由于我们的代码或更新的 gui-component-code 导致某种内存损坏或其他原因。

编辑:查看下面链接的博文。看来这可能与我的问题有关。至少当我读到它确认 System.Move 可以给出错误的结果: http://blog.excastle.com/2007/08/28/delphi-bug-of-the-day-fpu-stack-leak/

编辑: 很抱歉没有早点发布我的“解决方案”,但它来了: 使用 Delphi 2007 时,我的问题通过使用 FastMove 替换 System.Move 解决了。 升级到 Delphi 2010 后,我还没有遇到这个问题,我们不再使用 FastMove。

Procedure InternalDescribe;
var 
  cbufl: sb4; //sb4=LongInt
  cbuf: array[0..30] of char;
  cbufp: PChar;
  //....
begin
  //..Some code
  repeat
    //...Some code to initialize cbufp and cbufl

    //On the 15. iteration the values immediately Before Move are always these:
    //cbufp = 'STDPRODUCTSTOREDELEMENTSCOUNT'
    //cbuf = ('S', 'T', 'A', 'T', 'U', 'S', #0, 'E', 'V', 'A', 'R', 'R', 'E', 'C', 'I', 'D', #0, 'D', 'U', 'C', 'T', 'I', 'D', #0, #0, #0, #0, #0, #0, #0, #0)
    //cbufl = 29

    Move(cbufp^, cbuf, cbufl);

    //Values immediately After Move should then be:
    //cbuf = ('S', 'T', 'D', 'P', 'R', 'O', 'D', 'U', 'C', 'T', 'S', 'T', 'O', 'R', 'E', 'D', 'E', 'L', 'E', 'M', 'E', 'N', 'T', 'S', 'C', 'O', 'U', 'N', 'T', #0, #0)

    //But sometimes this Move results in this value( 1 in 5..15 times):
    //cbuf = ('S', 'T', 'D', 'P', 'R', 'O', 'D', 'U', 'C', 'T', 'S', 'T', 'O', 'R', 'E', 'D', #0, #0, #0, #0, #0, 'N', 'T', 'S', 'C', 'O', 'U', 'N', 'T', #0, #0) }

  until SomeCondition; 
  //...Some more code
end;

【问题讨论】:

  • 您确实应该包括您正在使用的 Delphi 版本,以及您最近是否升级了您的 Delphi 版本。它使人们更容易回答您的问题。
  • 这是用于 Win32 的 Delphi 2007。此外,我们使用 FastMM4。 (这不是我的问题,但我认识他)
  • 我现在已经修复了我的帐户,并将我的所有 cmets 从我的“答案”中移出,并将它们变成了真正的 cmets。

标签: delphi


【解决方案1】:

Move 不会给出错误的结果,或者至少我从未见过它会给出错误的结果。缓冲区中更有可能出现意外情况。尝试在此例程中添加对 Windows.OutputDebugString 的调用,以查看您在之前和之后复制的内容。

【讨论】:

  • 我知道 System.Move 通常不会给出错误的结果,但据我所知,这一次确实如此。如果您在代码中查看我的 cmets,我实际上已经看到了 System.Move 的正确输入有时会给出错误的结果。
【解决方案2】:

小心 - 您假设 Char = 1 字节。在 D2009 之前这很好,但在 D2009 和 D2010 中,一个字符是 2 个字节。移动始终适用于字节。升级到 D2009 或 D2010 后会不会出现这些问题?

【讨论】:

  • 我们使用的是 Delphi 2007,所以这不是我的问题的原因。
【解决方案3】:

我可以确认它有时确实会失败。我只是花了几天时间追踪它。简直不敢相信。在我们的例子中,我们有 .NET 2.0,在 IIS 6 或 IIS7 下运行的网站调用一些用 Delphi 2007 编写的 COM 组件,在中等负载下,它会突然开始无法移动 28 个字节的第 16-19 个字节 - 有时。大多数时候它都有效。您很可能会在 9..31 字节范围内移动大小时遇到​​问题。

我们最终在每个 System.Move() 之后进行了 CompareMem() 检查,发现 ComparewMem 有时会失败 - 这是在堆栈上分配的两个缓冲区/数组/结构之间移动!男孩让我很惊讶!

复制需要很长时间。从本质上讲,从 D2006 开始的 System.Move 是不可靠的,因为 FPU 堆栈上留下了一些东西。如果 FPU 堆栈清晰,一切都会好起来的。

上面提到的博客文章条目是正确的。不管修复是什么,它都不会影响 system.Move(),因此如果你有一个用 Delphi 2006 或更高版本编写的 DLL/COM,你会在某个阶段遇到问题。

我检查了 D2010 并且 System.Move 中的代码没有更改。在我们的例子中,我将 System.Move 恢复到 Delphi 7 版本 - 只需使用 make 文件重新编译所有系统单元。

【讨论】:

  • 我确认。面临同样的错误。经过数小时的调试,使用 System.Move 获得了位置,比较原始和复制的结构显示了差异!使用 CustomMove 程序进行简单测试,逐字节复制修复了该错误。德尔福 2007.
【解决方案4】:

我有同样的问题:似乎 FPU 堆栈并不总是被 PNG + StretchBlt 正确清除?
Memory corruption in System.Move due to changed 8087CW mode (png + stretchblt)

我认为 System.Move 必须在移动之前清除 FPU 堆栈?

【讨论】:

    【解决方案5】:

    仅供参考(以防其他人也有同样的问题):我们为客户升级了我们的软件,并且在我们的应用程序启动时整个触摸屏被锁定!窗户被完全冻结了!必须重新启动电脑(关闭电源)。花了一些时间找出完全冻结的原因。

    幸运的是,我们在 FastMove.LargeSSEMove 中有一个(只有 1 个!)AV 堆栈跟踪。 我在 fastmove 中禁用了 SSE 的使用,问题就消失了。

    顺便说一句:触摸屏有一个带有 S3 芯片组的威盛 Nehemiah cpu。

    【讨论】:

      【解决方案6】:

      是否可以在不修改当前代码的情况下恢复到旧的 GUI 组件代码?这样,您就可以确定是您的代码还是 GUI 组件。

      另一个问题是,您是否使用多个线程。

      编辑:出于测试原因,我只是想让您恢复 GUI 组件。您应该将它们更新到最新版本。但我有另一个尝试给你。必须在移动操作之前尝试将缓冲区归零吗?请参阅FillChar 过程以实现此目的。这有帮助吗?

      【讨论】:

      • 是的,可以恢复。我曾计划尝试这个,但我从未尝试过。我实际上很确定问题来自 gui 组件,因为当我从我的代码中“分离”gui 时,我还没有看到问题。但是,我不想只是恢复并对此感到满意,因为我们出于某种原因升级了组件。但我会尝试只是为了确认/“反驳”我的怀疑。太糟糕了,我几天都不上班。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-18
      • 2015-07-14
      • 2014-10-13
      • 1970-01-01
      相关资源
      最近更新 更多