如何防止Embarcadero C++Builder不停地更改DFM中的位图代码?

huangapple go评论62阅读模式
英文:

How to prevent Embarcadero C++Builder changing the bitmap code within the DFM all the time?

问题

I am your Chinese translator. Here is the translated text:

我必须使用C++Builder工作。根据心情,IDE会在DFM文件中更改十六进制编码的位图属性,即使我在GUI中没有做任何更改。

我正在使用GIT,但这会导致.dfm文件的差异(很难看)。

如何防止Embarcadero C++Builder不停地更改DFM中的位图代码?

这是一个带有一些窗体的VCL项目。随机更改的Bitmap值来自TImageList

有没有办法防止IDE一直更改DFM文件?

英文:

I have to work with C++Builder. Depending on the mood, the IDE changes the hex-encoded bitmap property within the DFM file. Even if I have not changed anything within the GUI.

I'm using GIT and all the time this will result in a diff of the .dfm (which is ugly).

如何防止Embarcadero C++Builder不停地更改DFM中的位图代码?

It is a VCL project with some Forms. The Bitmap value which will change randomly is from a TImageList.

Is there a way to prevent that the IDE will change the DFM all the time?

答案1

得分: 1

这是一个已知问题。

最初在XE版本中报告,并被标记为“不会处理”(存档链接):

QC 92769:TImageList在dfm中的位图数据每次以文本形式查看表单(alt-F12)时都会更改

后来,在XE8版本中再次报告,并在10.3版本中标记为已修复,至少对于VCL来说:

RSP-13666:TImageList的位图数据不断更改

所以,回答您的问题 - 确保您使用的是C++Builder 10.3或更高版本,因为问题已经得到了修复。

至少对于VCL来说是这样。对于FMX,仍然存在一个类似的问题仍然未解决:

RSP-11259:每次保存FMX表单时,FMX都会更改二进制图像数据


一些背景信息:

在内部,Bitmap 属性是通过DFM系统使用 TFiler::DefineBinaryProperty() 方法进行流传输的。这导致DFM系统使用内部的 TMemoryStream 来从/到DFM中读取/写入 Bitmap 数据。

TImageList 实际上并不持有位图图像,它持有一个Win32 ImageList 控件 作为 HIMAGELIST 句柄(存储在 TImageList::Handle 属性中)。Win32 API 在内部处理实际的位图。

在写入DFM时,TImageList 使用 TStreamAdapterTMemoryStream 包装为Win32 IStream 接口,然后调用Win32 ImageList_Write/Ex() 函数将 HIMAGELIST 的数据写入 TMemoryStream(以预-ComCtrl32v6格式),然后将 TMemoryStream 中的字节写入DFM。 Bitmap 数据的开头的字节 49 4C 是Win32 API IL 流格式的标识符,而字节 42 4DHIMAGELIST 持有的实际位图图像的标识符。

在您的截图中,只有一个字节被更改(在其他过去的报告中,可能有更多字节被更改)。根据 这个WineHQ代码 (以及我找到的其他第三方存储库),被更改的字节属于 HIMAGELIST 流头部的 ILHEAD::cGrow 字段:

当系统需要为新图像腾出空间时,图像列表可以增长的图像数。此参数表示调整大小的图像列表可以包含的新图像数量。

这对应于 TImageList::AllocBy 属性:

设置当需要为新图像腾出空间时,图像列表增长的图像数。

TImageList 将其 HIMAGELIST 写入DFM时,它首先允许Windows以任何它想要的方式将 HIMAGELIST 数据写入 IStream。然后,在XE4和10.3之间的某个时候(我没有准确版本的源代码),新逻辑被添加,使得 TImageList 回去并用它自己的 AllocBy 值覆盖 cGrow 值。这是为了解决在RSP-13666中提到的这个问题:

在上一个问题的最终评论中,您指出问题的根本原因是 ImageList_WriteEx API似乎更新了一个未记录的计数器。这与我对问题的调查结果一致。

在那个“以前的问题”(QC#92769)中,它说:

Wine写入了一个_ILHEAD结构体,而更改的来自该结构体的问题字节是WORD cMaxImage和WORD cGrow。这些对应于列表中当前分配的图像数量,理论上,一旦添加的图像超过该数量,列表将增长多少。 每次将图像列表流传输,它似乎都会将cGrow增加4,这使我认为也许Windows实际上并未使用该字段。

这正是您所看到的 - 以前的 cGrow78 02(632)被新值 7C 02(636)替换。

QC工单还说:

调整流数据后,将其更改为0似乎不会立即破坏它。但是就像我们所说的,这似乎是有风险的。

还有:

  • 不幸的是,报告的问题是由本机Win32 ImageList控件引起的,确切地说是在方法ImageList_WriteEx()中找到的,该方法将图像列表写入流。这不是Rad Studio的错误,因此其解决方案不是简单的修复。

  • 与此问题相关的Microsoft的报告错误(ID:428868)但在VS中。

正如上面所述,当 TImageList 写入DFM流时,它确实使用了 ImageList_WriteEx()。最终,Embarcadero决定冒险并修补了 ImageList_WriteEx() 写入的流数据。

英文:

This is a known issue.

Originally reported in XE, and closed as "Wont Do" (archived link):

QC 92769: bitmap data of TImageList in dfm changes everytime the form is viewed as text (alt-F12)

Then later, reported again in XE8, and closed as fixed in 10.3, at least for VCL:

RSP-13666: Bitmap data of TImageList changes constantly

So, to answer your question - make sure you are using C++Builder 10.3 or later, as the problem has already been corrected.

At least for VCL, anyway. There is a similar report still open for FMX:

RSP-11259: Every FMX form save changes binary image data in FMX


Some background info:

Internally, the Bitmap property is streamed through the DFM system using the TFiler::DefineBinaryProperty() method. This causes the DFM system to use an internal TMemoryStream for reading/writing the Bitmap data from/into the DFM.

TImageList does not actually hold a bitmap image, it holds a Win32 ImageList control as an HIMAGELIST handle (stored in the TImageList::Handle property). The Win32 API deals with the actual bitmap internally.

When writing to the DFM, TImageList wraps the TMemoryStream with a TStreamAdapter to get a Win32 IStream interface, and then calls the Win32 ImageList_Write/Ex() function to write the HIMAGELIST's data into the TMemoryStream (in a pre-ComCtrl32v6 format), and then the bytes in the TMemoryStream are written into the DFM. The bytes 49 4C at the beginning of the Bitmap data are an identifier for the Win32 API's IL stream format, and the bytes 42 4D are an identifier for the actual bitmap image that the HIMAGELIST holds.

In your screenshot, there is only 1 byte being changed (in other past reports, there were more bytes being changed). According to this WineHQ code (and various other 3rd party repos that I found), that changed byte belongs to the ILHEAD::cGrow field of the HIMAGELIST's stream header:

> The number of images by which the image list can grow when the system needs to make room for new images. This parameter represents the number of new images that the resized image list can contain.

Which corresponds to the TImageList::AllocBy property:

> Sets the number of images by which the image list grows when it needs to make room for new images.

When TImageList is writing its HIMAGELIST to the DFM, it first lets Windows write the HIMAGELIST data to the IStream however it wants. Then, sometime between XE4 and 10.3 (I don't have sources to pinpoint the exact version), new logic was added to have TImageList then go back and overwrite the cGrow value with its own AllocBy value instead. This was added to address this issue mentioned in RSP-13666:

> In the final comments of the previous issue, you noted that the root cause of the issue is that the ImageList_WriteEx API seems to update an undocumented counter. This agrees with my own investigation of the issue.

In that "previous issue" (QC #92769), it says:

> Wine writes an _ILHEAD struct, and the offending bytes from that struct that change are WORD cMaxImage, and WORD cGrow. These correspond to the number of images currently allocated in the list, and theoretically, how much the list will grow by once you add more than that amount. Each time the image list gets streamed, it seems to increase cGrow by 4, which makes me think that maybe Windows isn't actually using that field.

Which is exactly what you are seeing happen - the earlier cGrow value of 78 02 (632) being replaced with a new value of 7C 02 (636).

The QC ticket also says:

> It seems that tweaking the stream after the fact and changing it to 0 doesn't immediately break it. But like we said that seems risky

And:

> - Unfortunately the issue that is reported is caused by native Win32 ImageList control, exactly found in the method ImageList_WriteEx(), that writes an image list to a stream. It is not a Rad Studio bug then its solution is not a simple fixing.
>
> - There is a reported bug (ID: 428868) to Microsoft related to this problem but in VS

And, as noted above, TImageList does use ImageList_WriteEx() when writing to a DFM stream. And Embarcadero eventually decided to take the risk and patch the stream data that ImageList_WriteEx() writes.

huangapple
  • 本文由 发表于 2023年4月19日 16:21:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76052230.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定