正在释放的句柄以某种方式损坏了?

发布于 2024-08-14 17:11:37 字数 1965 浏览 2 评论 0原文

奇怪的问题。也许有人可以提供一些见解。

  • 场景 1。我在内存中有一个 TBitmap,在进行复杂计算以计算每个像素的颜色时写入该 TBitmap。每隔一段时间(通常在填充位图的每条水平线之后),TBitmap 就会被绘制到表单上的图像 (image1.Canvas.Draw(0, 0, TBitmap)。大多数时候这工作正常,但我注意到如果每个位图行有许多缓慢的复杂计算(例如需要超过 30 秒或一分钟来计算),那么主窗体会出现短暂的“闪烁”,以某种方式擦除位图,因此 image.draw 调用只会绘制最新计算的行和前 y 行在位图中被清空。我通过在计算之前锁定位图来解决这个问题。

  • 场景 2. 这是主要的麻烦。我正在写入 TMemoryStream 而不是位图。同样的交易。进行计算以计算每个像素值,然后在此过程中使用 memstream.Write(bytevalue, 1) 将每个像素值写入 TMemoryStream。在所有计算结束时,我使用 memstream.SaveToFile('whatever.bmp') 将流保存到位图,然后使用 memstream.Free 释放流。如果计算很快,那么无论大小如何,流都会保存(我正在使用 10000x10000 尺寸进行测试)。

我什至可以知道生成的文件将被损坏,因为主应用程序窗口/表单确实有轻微的闪烁,就像正在重新绘制一样。当这种情况发生时,就好像位图和 TMemoryStream 的每个句柄都被杀死/刷新,因此现有数据被损坏。

有什么想法吗?这真的很糟糕。特别是当每个图像可能需要一个小时来创建时,却发现当它完成时,后台发生了一些事情并损坏了位图或 TMemoryStream。

有什么方法可以像使用位图一样锁定 TMemoryStream 句柄吗?这可能有帮助。或者一些声明告诉 Delphi“不要弄乱我的对象,即使它看起来应用程序花费了太长时间”

或者有人知道 Delphi 内部导致这种情况发生的后端原因吗?

TMemoryStream 在执行所有计算的过程中创建,因此是一个本地对象。对于位图问题,位图是程序外部的全局变量,并且它发生了,所以我认为这不是原因。

这也是在 Windows 7 下,但我注意到 Vista 下的原始位图问题。

更新1:

抱歉没有使用评论,但是文本大小有限制...

回复Remy(以及其他阅读此内容的人)...

单线程。对于内存流,如果计算速度快,它在 5000x5000 分辨率下工作正常,但如果计算速度慢,则失败。

作为一个基本框架,代码的思路是:

SetupMemorystream; 
for y:=0 to height do 
   for x:=0 to width do 
      DoCalcs;
      SetByteValue; 
   end; 
end; 
SaveStream; 

如果 DoCalcs 相对较快,那么一切都会按计划进行。如果速度很慢,那么我会得到 TMemoryStream 损坏,并且保存流的结果位图也损坏。

这与使用内存中的 TBitmap 相同,直到我发现我可以锁定位图,从而阻止 Delphi 和/或 Windows“在需要时”为其重新分配新句柄,从而破坏位图中的数据。

这太巧合了,以至于我们认为 TMemoryStream 及其句柄不会发生同样的问题。

更新2:

还有一点可能有用的信息。

当 TMemoryStream 保存成功时,生成的文件(对于 5000x5000 位图)大小为 75,000,054 字节。

当保存的流损坏时,它似乎是一个随机值(从句柄损坏到保存流的大小)。示例大小为 22 MB 和 9 MB。

当我在十六进制编辑器中查看生成的文件时,它显示文件的开头与标题块是正确的,但尾部以某种方式被截断。

这太奇怪了。不管怎样,我绝对可以在 SaveToFile 调用之后和释放它之前刷新 TMemoryStream 吗?

Strange problem. Maybe someone can give some insight.

  • Scenario 1. I have a TBitmap in memory that is written to while complex calculations take place to calculate the color of each pixel. Every so often (usually after every horizontal line that the bitmap is filled) the TBitmap is drawn to an image on a form (image1.Canvas.Draw(0, 0, TBitmap). Most of the time this works fine, but I noticed that if there are many slow complex calcs for each bitmap line (say taking more than 30 secs or a minute to calculate) then the main form has a momentary "flicker" that somehow erases the bitmap, so then the image.draw call only draws the latest calculated line and the first y lines are blanked out in the bitmap. I got around this by locking the bitmap prior to calculations.

  • Scenario 2. This is the main hassle. I am writing to a TMemoryStream rather than a bitmap. Same deal. Calculations take place to calculate each pixel value and then each pixel value is written to the TMemoryStream with memstream.Write(bytevalue, 1) during the process. At the end of all the calculations I save the stream to a bitmap with memstream.SaveToFile('whatever.bmp') and then free the stream with memstream.Free. If the calculation is quick then the stream saves no matter what size (i am doing tests with 10000x10000 dimensions).

I can even tell the resulting file will be corrupt as the main application window/form does have a slight flicker like it is being repainted. When that happens it is as if every handle to bitmaps and TMemoryStream is killed/refreshed so the existing data is corrupted.

Any ideas? This really sucks. Especially when each single image can take an hour to create only to find that when it finishes something in the background happened and corrupted the bitmap or TMemoryStream.

Is there any way I can lock a TMemoryStream handle like I can with a bitmap? That may help. Or some declaration to tell Delphi "Don't mess with my objects, even if it looks like the app is taking too long"

Or does anyone know the back end reason inside Delphi that causes this to happen.

The TMemoryStream is created inside the procedure that does all the calculations so is a local object. With the bitmap issue the bitmap was a global variable outside the procedure and it happened, so I don't think that is the cause.

This is also under Windows 7, but I noticed the original bitmap issue under Vista.

Update 1:

Sorry for not using the comments, but there is the restirction on text size...

In reply to Remy (and anyone else reading this)...

Single threaded. For the memorystream it works fine for 5000x5000 resolution if the calculations are quick, but fails if the cals are slow.

As a basic framework the code is along the lines of

SetupMemorystream; 
for y:=0 to height do 
   for x:=0 to width do 
      DoCalcs;
      SetByteValue; 
   end; 
end; 
SaveStream; 

If the DoCalcs is relatively speedy then all goes to plan. If it is slow then I get the TMemoryStream corruption and the resulting bitmap the stream is saved to is corrupt.

This was identical with using an in memory TBitmap until I discovered I could lock the bitmap which stops Delphi and/or Windows reallocating a new handle to it "when it wants to" which corrupts the data inside the bitmap.

This is too much of a coincidence to not think the same issue is not happening with the TMemoryStream and its handle.

Update 2:

One more maybe helpful bit of info.

When the TMemoryStream saves OK the resulting file (for a 5000x5000 bitmap) is 75,000,054 bytes in size.

When the saved stream is corrupt it seems to be a random value (of the size from when the handle was corrupted until the stream is saved). Example sizes have been 22 MB and 9 MB.

When I look at the resulting files is a hex editor it shows that the start of the files are correct with the header chunks, but the tail ends get truncated somehow.

This is so bizarre. Anyway I can absolutely for sure flush a TMemoryStream after a SaveToFile call and before freeing it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

猫腻 2024-08-21 17:11:38
  1. 在将每个字节写入内存流之前,将容量设置为位流的近似大小,这样就不会经常重新调整内存大小。这将加快速度

  2. 我认为你必须在 for 循环中从高度和宽度中减去 1

干杯

  1. Before writing each byte to memory stream, set the Capacity to approximated size of bit stream so that it does not re-size memory often. This will speed thing up

  2. I think you have to subtract 1 from Height and Width on for loop

Cheers

鲸落 2024-08-21 17:11:38

感谢大家的所有提示。循环在代码中确实有正确的 0 到 width-1 。

问题是宽度和高度的变量是全局变量,当调整主窗体大小时,它们在应用程序的其他地方发生了更改(它们通常跟踪屏幕上显示的图像,但我在此过程中使用了相同的变量来跟踪内存流位图宽度因此,当它们在循环之外发生变化并破坏了输出时,一旦我定位了宽度和高度变量,

我就应该知道这是我的错误。不是德尔福问题。

Thanks for all the hints guys. The loops did have correct 0 to width-1 in code.

Problem was the variables for width and height were globals that got changed elsewhere in the application when the main form was resized (they usually keep track of the onscreen displayed image, but I had used the same variables inside this procedure to track the memorystream bitmap width and height. So when they got changed outside the loops screwed up and corrupted the output. What a bugger of a problem to track down. Once I localized the width and height variables everything works as expected.

I should have known it was my error and not a Delphi issue.

筑梦 2024-08-21 17:11:38

当您释放用于写入的文件流时,不会检查关闭文件的调用是否有错误。因此,您的写入可能会失败,也许是在刷新大文件的最后一个块时,并且不会引发异常。

我最近遇到了这个问题:它在 QC 80148 中。但是您对此无能为力,因为 Windows CloseHandle 函数也不太可能返回任何错误。

When you free a filestream used for write, the call to close the file is not checked for errors. So your write can fail, perhaps on flushing out the last blocks of your large file, and no exception will be raised.

I got hit by this recently : it's in QC 80148. However there is not a lot you can do about it, because the Windows CloseHandle function is unlikely to return any error either.

断肠人 2024-08-21 17:11:38

仅通过假设正在发生的事情很难发现这样的错误。由于操作时间太长,如果发现错误就重复并等待,成本高昂。

我建议您记录该过程的每一步。您可能会得到一个巨大的日志,但它会告诉您哪里出了问题。重复这个过程并改进你的日志,这样你就会慢慢但肯定地找到问题的原因。

It is hard to find such an error by just assuming what is happening. Because the operation is so lenghtly it is expensive to just repeat and wait if error will be found.

I suggest you log every step of the process. You will get an enormous log probably, but it will show you where things go wrong. Repeat the process and improve your log, this way you will slowly but surelly find the cause of the problem.

浪荡不羁 2024-08-21 17:11:38

您可以使用 API 调用 FlushFileBuffers(filestream.Handle) 来刷新 tFileStream,但我的猜测是,首先发生了其他事情导致损坏,这可能就像您的循环一样简单从 0 到 width,而不是 1 到 width 或 0 到 width-1...如果不分析您的例程正在做什么来填充内存流,就很难说。

You can use the API call FlushFileBuffers(filestream.Handle) to flush a tFileStream, but my guess is that something else is happening to cause corruption in the first place, which could be as simple as your loop going from 0 to width rather than 1 to width or 0 to width-1...its hard to say without analyzing what your routines are doing to populate your memory stream.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文