是什么导致System.Move偶尔会给出错误的结果?

时间:2009-09-23 14:08:43

标签: delphi

最近几天,我们的第三方开发的数据库组件出现了一些奇怪的问题。几个月来这些组件一直没有变化。 HAS最近几天更改的代码是我们自己的代码,我们还更新了由另一个第三方开发的gui组件。

调试后我发现在其中一个数据库组件过程中调用System.Move偶尔会产生错误的结果!

请从数据库组件中查看以下代码并阅读我的评论。这种不一致的行为怎么会发生? 谁能让我知道如何找到这种不一致行为的原因? NB!我不认为这个代码有什么问题,它只能解释问题“症状”。 我的猜测是由于我们的代码或更新的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;

6 个答案:

答案 0 :(得分:6)

Move并没有给出错误的结果,或者至少我从未见过它所做的任何情况。你更有可能在缓冲区中遇到意想不到的东西。尝试在此例程中添加对Windows.OutputDebugString的调用,以查看您之前和之后要复制的内容。

答案 1 :(得分:2)

小心 - 你假设Char = 1个字节。在D2009之前没问题,但在D2009和D2010中,char是2个字节。移动始终使用字节。升级到D2009或D2010后,是否可能出现这些问题?

答案 2 :(得分:1)

我可以确认它有时会失败。我花了几天时间跟踪它。无法相信。在我们的例子中,我们有.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文件重新编译所有系统单元。

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

仅供参考(如果其他人也有同样的问题):我们为客户升级了我们的软件,并在我们的应用程序启动时锁定了完整的触摸屏! Windows完全冻结了!电脑必须重新启动(关机)。花了一些时间才弄清楚完全冻结的原因。

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

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

相关问题