Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion

Eurekalog 7 Reports an attempt to use a Destroyed Object when closing a Dock Panel

Hi,
I've tried to implement LMD Docking into one of my existing applications and I found that EurekaLog was reporting an attempt to use a freed object.
I've spent time today to ensure its not my code as in one instance it was but after fixing I ended up with the same issue.
I decide to open the pDockingDemo project provide and enable EurekaLog and have found the same attempt to use a detroyed object when closing panel using the X on the panel.
I've attached the EurekaLog bug report (I hope it helps). I've not had any false positives from EurekaLog so I do not think the problem is in their code.
I'm using LMD 2018 Release 2018.3 and EurekaLog 7.7 on RAD Studio 10.2.3 Tokyo Enterprise.
If you need any more information then please let me know.
regards
Dave.
txt
txt
pDockingDemo_SEASONSFALL0001_20181028121624.txt
80K

Comments

  • 16 Comments sorted by Votes Date Added
  • I cannot reproduce this issue. I installed Eurekalog, and imitated "bad" code to check that all setup is made properly. However, no problems occured with closing docking panels, including dynamically created text-document panels, including floating panels.
  • Hi,

    I have the same issue and found the problem and solution:

    procedure TLMDDockPanel.WndProc(var Message: TMessage);
    begin
      inherited;                                  // Eventually calls BeginAutoDrag.

      if psBeginAutoDragCalled in FState then     // Dispatch disabled by Vcl code
      begin                                       // mouse messages to support Vcl's
        Exclude(Fstate, psBeginAutoDragCalled);   // default mouse calls chain, e.g.
        try
         Dispatch(Message);                        // MouseDown virtual procedure.
        except
         // Do nothing
        end;
      end;
    end;

    Dispatch calls an already destroyed panel, surrounding it with a try..except is a workaround not solving the problem.
    In debug mode it works as expected, in normal mode I have to use the workaround.

    Regards,
    Peer 
  • I'm glad you've managed to reproduce the bug. Alas, I don't have the code so I cannot add the workaround. I had the same issue that in debug mode (in the IDE) it worked but standalone it did not.
    regards
    Dave
  • Posts: 0 Accepted Answer Vote Up0Vote Down
    >> I don't have the code so I cannot add the workaround. <<
    FYI: All our products include full source code. Just download the source installer from your customer area (it is a separate installer called "Source").
  • A.C.D. De Lier BV, can you check, whether the following will work instead of try-except:

    if not (csDestroying in ComponentState) then
      Dispatch(Message);

    If it will work for you, please let me know, and I'll add this to LMD code-base.
  • edited November 2018 Posts: 16Vote Up0Vote Down
    Rafael,
    Thanks for letting me know about the source. Unfortunately with 10.2.3 I could not compile my app with the source so I had to add the LMDDckSite.pas file to my app to be able to test the code.
    Alas, the code above does not work. The state of the ComponentState property I think is undefined at this time as I get the following from the debugger for that property at the point in time it is called:
    [csWriting,csDesigning,csAncestor,csUpdating,csFreeNotification,(out of bound) 12,(out of bound) 14,(out of bound) 15]
    regards
    Dave.
  • Okay, I tried something else that worked...
    I created an enumerate for the dock panel as follows:
      TDGHLMDDockPanelState = (ldpsUnknown, ldpsAlive, ldpsDead);
    In the dock panel I added a new private field:
        FDGHLMDDOckPanelState : TDGHLMDDockPanelState;
    In the constructor I set the value as follows:
      FDGHLMDDOckPanelState := ldpsAlive;
    And in the destructor as follows:
      FDGHLMDDOckPanelState := ldpsDead;
    and updated the WndProc as follows:
        if FDGHLMDDOckPanelState = ldpsAlive then
          Dispatch(Message);
    Now this worked and EurekaLog didn't raise an issue BUT FDGHLMDDockPanelState is undefined at this point as the debugger says its value is "(out of bounds) 116" and I think its pure luck that this works.
    I think the correct solution would be to unhook the WndProc somehow in the destructor but I suspect thats deep in the VCL code so at this time I do not know how to achieve this.
    regards
    Dave
  • Sorry, I accepted the answer for the source code instead of rejecting the code suggestion. Can anyone reverse this?
    regards
    Dave.
  • edited December 2018 Posts: 0Vote Up0Vote Down
    Can you try one more thing. Please add the call to standard Destroying method to the panel's destructor as shown:

    { ---------------------------------------------------------------------------- }
    destructor TLMDDockPanel.Destroy;
    begin
      Destroying;  // <--------

      InternalSetLoadingSite(nil);
      InternalResetSite;
      UpdateHook(False);

      Images := nil;
      HeaderMenu := nil;
      FImageChangeLink.Free;
      FDsgDragRect.Free;

      inherited;
    end;

    It should set standard csDestroying flag from the destructor begining, and thus, my proposal with "if not (csDestroying in ComponentState) then..." should begin to work. So, we will need no additional enumeration.
    I've actually already incorporated this way into latest code version, but, please let me know how it will work.
  • Unfortunately, no this doesn't work as the memory is being accessed after the object is destroyed...

    ComponentState = [csWriting,csFixups,csFreeNotification,(out of bound) 14,(out of bound) 15]

    Question: What is it in the destructor chain that removed this components WndProc from the messaging system?

    regards
    Dave.
  • So I've done a little more debugging of the original problem and the call to the WndProc in TLMDDockPanel is originating from TApplication.ProcessMessages and the message ID is 45089 which definitely is not a standard WM_#### message as WM_USER is 1024 and above. Looking through your code I cannot see anything above WM_ISTHEMEDCONTROL = WM_USER + 2398. So I've implemented the following which I think is safer...

        If Message.Msg <= WM_USER + 2398 Then
          Dispatch(Message);                      // MouseDown virtual procedure.

    regard
    Dave.
  • This is quite strange, because Destroying is effectively the same as declaring additional flag field. However, I've actually was unable to reproduce situation with Destroying or any other method, like enumeration field, ect.

    Also, Release of docking panels, which is used by caFree in OnClose event handlers, have been implemented analogous to standard TForm.Release. It actually uses asynchronous PostMessage with custom message Id to be sure that real destruction is performed outside of all other logic flow.

    David, can you create a small demo and send it to our support email (https://lmd.de/mfs) along with description of steps to reproduce.
  • Eugene,
    I've sent an email with the relevant files and instructions.
    Thanks in advance.
    regards
    Dave.
  • Fixed.
  • Eugene,
    Thanks for the fix. I assume it will appear in the next release cycle so for now I need to include the modified source?
    regards
    Dave
  • Already published as a part of 2019.1 release.
Sign In or Register to comment.