I suppose for Window handles (if I understood the documentation of the GetWindowText call correctly), the text can be retrieved from outside which wouldn't incur a message, but talk about tons of work for miniscule benefit - just food for thought for out-of-process reporting.
Difficult to say what else might incur some execution within the EXE as a result of some APIs without seeing the full list of calls madExcept performs against leaked resources to interrogate them for information to show on the report, but from what I gathered, messages can only be sent to an HWND so this is likely the only place (but thinking about it - maybe there's some socket that's associated with a window, and maybe some info-retrieving call on that socket can cause a message to be fired off, maybe some scenario like that might popup in the future). But to save time in the future, in case anything like this happens again, madExcept could simply catch the exception and indicate to the user in some way. It's not ideal to show that leak report process crashed, but it's not ideal that it just quietly goes down. At the least, it could just abort the leak report generation and show what it's got so far, and some error message indicating there was some issue, or it could just skip that particular resource and move on to the next one (but try/excepting over AV's isn't ideal, so stopping may be the best bet). It might also help identify spurious crashes that I've been getting when clicking around the little 'creating leak report' window. It's up to you to assess the severity of these things obviously, and decide when to fix etc, but I am sure glad you're thinking of fixing this one especially as I've just reproduced it in Delphi 12 as well, but it's more rare (something to do with memory addresses still pointing to valid locations after objects are freed, so it's getting away with it more often).
This particular issue is mostly due to how Delphi designed the TPopupList and TPopupMenu implementation. If I understood correctly, instead of setting up a WndProc for each TPopupMenu, Delphi creates only 1 window and WndProc for all of them - the one created by TPopupList. With some assembly contraption I barely grasp, Delphi manages to set up this shared WndProc in such a way that it's able to retrieve the object instance of TPopupMenu and call its WndProc method (virtual - so needs to interrogate the instance a little - as far as I understand - in order to call it, rather than just doing a CALL). But on finalization, when TPopupList is freed, it does not destroy its FWindow HWND. FWindow is only destroyed when the last TPopupMenu has been freed. So, because of those unfreed TPopupMenus, the window stays up. If a message is sent to it, in the assembly in WndProc, when object instance is being retrieved, it would get a freed TPopupList.
As far as I gather, this is the only place in Delphi VCL that does this - other usages of AllocateHwnd appear to be 1 handle (AllocateHwnd) per object (e.g. TTimer).
As to why D7 is falling over every time and D12 does not isn't 100% clear, but it seems things have been rejigged so much that D12 gets away with it. The related code looks the same. In D7, the following code attempts to call [ecx+$10] while ECX is 1:
- D7-Freed-TPopupList-WndProc-Call.png (41.94 KiB) Viewed 452 times
In D12, ECX points to a valid memory location most of the time for some reason, but I did have it crash on me once today on the same line (I am sure it did it before, but I can now confirm it definitely happens). In any case - D7 or D12 - entering the call gets us into procedure TPopupList.MainWndProc where Self has been freed. It's the pointers in the Freed PopupList that determine whether AV occurs or not. Looking at the TPopupList after it's been freed (when WM_GETTEXT is received):
- D7-vs-D12-Freed-TPopupList-Data.png (8.79 KiB) Viewed 452 times
In D12, FList is nil, in D7 it's garbage. Something else is probably nil in D12 that makes that WndProc virtual call work. But really, I don't fully understand this - how D12 manages to somehow call it OK most of the time, against a freed object. I tried turning off ASLR (which I'm not sure if applies to 32-bit anyway) as well as turning off manifest file - no difference. On some tests, D7 got away with it also, but most of the time it crashes (e.g. if I leak TPopupMenu in FormCreate instead of in ButtonClick, for a while, it didn't crash, now it does again!). It's a fluke I think, that D12 experiences this less.
Well, I've had enough of this, as I'm sure you have
Good weekend.