When opening a form which contains an inherited TImageList in Delphi (2010 in this case, but it may do it for other versions), the IDE invariably (I can't find any rhyme or rhythm to it) adds the image data again to the inherited form. This then increases the size of the executable. any one know why, and how I can stop it happening? I repeat, the image has not changed.
This is a known issue with TImageList, and there's not much you can do about it except to keep deleting the garbage data again. (BeyondCompare can be very useful here, especially in conjunction with source control.) If you want to see it fixed, please vote for the QC report on it.
What I do is put the imagelist on a data module and then add it to the forms uses clause. The form designer will be able to see the image list
This isn't a "solution" to the problem, but more an explanation of what is going on. The image data for a given image list is stored as a binary blob of data. This blob of data is obtained from the underlying IMAGELIST implementation from the comctl32.dll.
What is likely happening is that for some reason the Windows implementation down in comctl32.dll is streaming the image data differently between the "ancestor" instance and the "descendant" instance. To Windows, there is no relationship between these two instances.
The way form-inheritance works is that it does a property-by-property comparison between the "descendant" and the "ancestor" while streaming to determine if a given property should be written to the form. Because the image data is an opaque blob of goo, all we can do is a byte-for-byte comparison between what the TImageList instance on the ancestor writes and what the TImageList instance on the descendant writes. If only one byte were different, the streaming system has no choice but to write the data from the descendant instance on the presumption that something changed. For instance (and I don't really know the details since it is opaque), if the blob of goo contained a timestamp, it is conceivable that each time it is written the data would be different.
My recommendation: never keep images in .dfm files. Always put them in resource files and regain control of your app.
Related
Hi I need to drag an image from one application to another. Both are Delphi FMX applications. I was successful in doing it within one application but now need to transfer between two separate applications, ie no memory sharing. I would be happy to save all info into a file and transfer the file name in the operation. I do detect the dragover event in the receiving application but the data property is nil and I don't know how to populate it with a useful value.
I am using Windows 10, Delphi XE10.3
Any example, explanation will be helpful
Thank you
Did you find an answer?
I am trying something similar. Andreas is right about proper OLE, but it will be a lot of work to implement in FireMonkey.
The problem is that FireMonkey, on Windows, only interprets external drags of files (by their name and path) or unicode text. It can accept other external drags, but extracts no information so it is useless.
Look in FMX.Platform.Win.
TWinFropTarget.GetDataObject() is the culprit.
PlatformWin.FDragAndDropActive is true for internal drags and false for external.
External drags of CF_HDROP (filenames) and CF_UNICODETEXT (unicode text) only are interpreted.
To implement yourself steps would be
Roll your own IDropTarget.
Register it with Windows. See TPlatformWin.CreateWindow(), call to RegisterDragDrop()
Create a mechanism for freeing it, which is all TWinWindowHandle.FWinDropTarget does, but FWinDropTarget is private.
Hook it up to the form or component that will receive the drop.
Enhance GetDataObject, or whatever replacement you have decided, to interpret the external drags that are of interest. This requires more research.
Above generally covers the receiver.
The sender requires yet more research for how to initialise a Drag Drop of the content of interest in Windows.
Repeat for other platforms as required!
Drag and Drop component suite for VCL, https://torry.net/pages.php?id=233, probably has a lot of the code to do this, although I have not looked at it.
If anyone tries this, please update this thread.
Andreas and my answers are about dragging the image itself. In the question you suggest that sending the filename would be acceptable. Following further research to solve my own issue I can give you the categoric answer for this solution, which avoids would be significantly less work.
Drag Drop, as you already know, involves a Source, a Target and Data.
Fire Monkey Drag Drop implementation for internal drags (i.e. within the application) uses its own data format, namely the Data record. For internal drags only the Data.Source property is populated.
For external drags (i.e. between applications, whether FMX or not) OLE has to be used for the data.
All drags can be received by all open applications. But unless OLE data has been set by the Source the Destination will be unable to interpret what the drag is or where it is from. Your question states "I do detect the dragover event in the receiving application but the data property is nil". You are experiencing the situation I am describing.
Delphi FMX will interpret and decode OLE data received for Files and Text content. Files is a list of filenames with paths, not the files themselves. Other OLE content is ignored, such as image. See my previous answer for how to enhance FMX to interpret other OLE content.
Delphi FMX does not set OLE data. Therefore to implement drag drop from a Fire Monkey application to another application, including another Fire Monkey application, it is necessary to write you own start drag code which populates OLE data. In my previous answer this is written more briefly as "The sender requires yet more research for how to initialise a Drag Drop of the content of interest in Windows."
Such Drag Start code would be called from OnMouseDown event of the Source control. For Windows I think
create a COleDataSource
set the data
call its DoDragDrop method
I have not tried it because my application will not be a Source of inter-application drags.
The Delphi VCL TFileOpenDialog has a property called ClientGUID. Embarcadero documentation says it:
...holds a GUID associated with a dialog's persisted state. Persisted
states for a dialog can include such things as its position and
size...
But that is all it says. I would like to know more. My testing shows that the dialog Size and Position do persist between application sessions so they are being stored somewhere.
But where is this information being stored? (I have searched the registry and hard drives for the GUIDs I have been testing but cannot find them anywhere.)
And, is it only Size and Position or do other properties also persist? (If it is only Size and Position then it's not really very useful to me.)
I also asked this question on Experts Exchange and the answer given there is that the persisted properties are stored in the Windows Registry in this key:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\CIDSizeMRU
The values stored there are binary, which explains why the initial text searches for my GUID failed.
My testing shows that the persisted properties for FileOpenDialog include size, position and path. There may be be more but I don't know.
I would like to be able to read the original text of a component (published) property at runtime after it has been (potentially) changed.
The context is that I'm writing an extension of a translation library that we used in our application. That library is old and not supported by the supplier any more so I am on my own to make it work.
Unfortunately, the way the library is coded makes it impossible to use from an ISAPI Dll (for instance, in an Intraweb application). The dictionary part works but the automated translation component does not (mostly because it tries to overwrite some code section in memory). I'm therefore trying to salvage the parts that works (form translation tools and dictionary storage) while rewriting the part that doesn't (well, only the elements that I'm interested in, really).
I'm, however, stopped by the fact that, once a component text property has been translated, it will not match the original text any more and won't be found in the dictionary.
That code is supposed to work in Delphi 7 although I'm planning to migrate it to XE5 as soon as I have enough time for that.
"Once a component text property has been translated, it will not match
the original text any more and won't be found in the dictionary."
Can you keep a separate lookup map yourself, of translated content to original? Add to this any time a string is replaced with its translated content. That way you can go back and forth between the original and translated at will.
In XE5, the easiest way would be to use a TDictionary. In D7, you may have to roll your own container.
You can make a procedure that save the original values to a List or some array, then call that procedure in the Form's Loaded method (you have to override it, and don't forget to call inherited at the end of it.) Then search for the directory entry in that list. The Form's Loaded method is called after all the components is loaded from the DFM but before the FormCreate. So here you can find all the original properties.
From what I understand, what you want is to get back the value of string properties as they are stored in the DFM at compile time.
So, I guess the most reliable way to do so would be from the DFM itself. As far as I know, DFMs are always stored inside the binaries as resources (though there might be some exceptions...). I looked into doing something similar a while ago. I didn't manage to make it work as R&D time ran out (I only had a couple of hours), but if you want to look into it, I'd start with
TCustomForm.Create
InitInheritedComponent
InternalReadComponentRes
TStream.ReadComponent
TReader.ReadRootComponent
Maybe someone can confirm whether this approach can work or not and what are the caveat, but until then, I think it's a valid research direction for you.
Over time I've rolled my own formats for saving and loading object properties but on having to revisit this I'm wondering about using Delphi's own text DFM format. I know tha this is really an 'internal' format, but the reader for it now seems pretty well defined and it copes with all types of property. Has anyone any comments about possible pitfalls?
I wouldn't really say that DFM is an 'internal format'. Sure Delphi uses it internally for forms and datamodules, but TReader and TWriter classes that perform streaming are publicly accessible and even documented. So they are clearly intended for end users as well.
Now, the possible problem is when you save a stream and later one of the classes in the stream changes so that the stream is not compatible any more. You may have seen this in Delphi if you attempt to open a form saved in D2007+ in D7 (missing property). But even if it happens, it's not too hard to resolve. You will get an exception that will report the exact property that is causing the problem. You also have to register all classes that you want to stream with RegisterClass.
DFM can be stored in binary or text format. Even if you store it Binary you can convert it to Text (using ObjectBinaryToText), once in text format, it's easy to fix.
So, the problems you may get happen due to incompatible changes in the structure, but those have nothing to do with DFM mechanism itself, and would also happen using any other streaming mechanism.
As for longevity, you can still open DFM's saved with D1 in the latest Delphi. So as long as you keep backward compatibility in mind, you have nothing to fear.
In conclusion, the choice of any particular format, DFM, XML, JSON, your own... doesn't really affect longevity. They all require same level of compatibility.
The reasons for choosing the format have more to do with decisions regarding:
interoperability with other apps/services
size/speed/human readability
But you didn't mention any of those in the question.
So I suggest using DFM over roll your own, as it would mean less code to maintain.
I am trying to deserialize an old file format that was serialized in Delphi, it uses binary seralization. I know nothing about the structure of the file except some very high level records that are in it.
What steps would you take to solve this problem? Any tools etc?
A good hexeditor, and use the gray matter to identify structures.
If you get a hint what kind of file it is, you can search for more specialized tools.
Running the unix/Linux "file" command can be good too (*) See Barry's comment below for how it works. It can be a quick check for common filetypes like DBF,ZIP etc hidden by using a different extension.
(*) there are 3rd party builds for windows, but they might lag in versions. If you can do it on a recent *nix distro, it is advised to do so.
The serialization process simply loops over all published properties and streams their value to a text file. If you do not know the exact classes that were streamed to the file you will have a very hard time deserializing the file. (if not impossible)
A good hex editor is first. If the file is read without buffering (eg read directly from a TFileStream) you could gain some information when using ProcMon from SysInternals; You can see exactly what data is read in what chunks and thus determine more quickly where the boundaries are between the structures you already identified.