英文:
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
文件的差异(很难看)。
这是一个带有一些窗体的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).
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来说:
所以,回答您的问题 - 确保您使用的是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
使用 TStreamAdapter
将 TMemoryStream
包装为Win32 IStream
接口,然后调用Win32 ImageList_Write/Ex()
函数将 HIMAGELIST
的数据写入 TMemoryStream
(以预-ComCtrl32v6格式),然后将 TMemoryStream
中的字节写入DFM。 Bitmap
数据的开头的字节 49 4C
是Win32 API IL
流格式的标识符,而字节 42 4D
是 HIMAGELIST
持有的实际位图图像的标识符。
在您的截图中,只有一个字节被更改(在其他过去的报告中,可能有更多字节被更改)。根据 这个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实际上并未使用该字段。
这正是您所看到的 - 以前的 cGrow
值 78 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论