Skip to content

CodeSnip crashes after resume from hibernation #70

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
delphidabbler opened this issue Dec 11, 2022 · 6 comments
Closed

CodeSnip crashes after resume from hibernation #70

delphidabbler opened this issue Dec 11, 2022 · 6 comments
Assignees
Labels
bug Bug report completed Work has been completed on this issue and changes have been committed to `develop` branch..

Comments

@delphidabbler
Copy link
Owner

When CodeSnip is running and Windows is hibernated and resumed CodeSnip crashes with an unrecoverable exception.

Selecting "Terminate" sometimes results in an endless loop and the process has to be killed with task manager.

@delphidabbler delphidabbler self-assigned this Dec 11, 2022
@delphidabbler delphidabbler added accepted Accepted for implementation / fixing bug Bug report needs investigation Investigation required to confirm problem labels Dec 11, 2022
@delphidabbler
Copy link
Owner Author

delphidabbler commented Dec 11, 2022

This is a long standing, intractable bug. Dunno why it didn't make it into issues until now.

I suspect it's something to do with the web browser control, but haven't confirmed it. (❌😊 probably not see #70 (comment))

@delphidabbler
Copy link
Owner Author

Just tried 5 hibernations with CodeSnip running from the Delphi XE debugger and stand-alone and not a single crash.

⚠️ This is going to be hard to track down!

@delphidabbler
Copy link
Owner Author

Crash happened today while debugging.

A stack trace indicates that that exception is a fault reading a zero pointer in the Overview pane. The culprit would appear to be an IView (non)instance.

I think I'd never selected anything in the Overview pane before the crash, which could explain the null pointer exception.

@delphidabbler
Copy link
Owner Author

Had another crash under the debugger.

Crash is reported as occurring in line

Result := ViewItem.IsUserDefined;

Stack trace is (method followed by indented line in method, where available), with method raising exception listed at top:

TOverviewFrame.TTVDraw.IsUserDefinedNode
    Result := ViewItem.IsUserDefined;
TSnippetsTVDraw.CustomDrawItem
    TV.Canvas.Font.Color :=
      Preferences.DBHeadingColours[IsUserDefinedNode(Node)];
TCustomTreeView.CustomDrawItem
TCustomTreeView.CNNotify
TControl.WndProc
TWinControl.WndProc
TCustomTreeView.WndProc
DoControlMsg
TWinControl.WndProc
TWinControl.MainWndProc
StdWndProc
{various calls into user32 & common controls library}
TWinControl.DefaultHandler
TWinControl.WndProc
TCustomTreeView.WndProc
StdWinProc
{various calls into user32}
TTreeNode.SetText
TTreeNodes.ReadNodeData
TCustomTreeView.CreateWnd
TWinControl.CreateHandle
TWinControl.UpdateControlState
TWinControl.CMRecreateWnd
TControl.WndProc
TWinControl.WndProc
TCustomTreeView.WndProc
TWinControl.RecreateWnd
TWinControl.WndProc
TCustomTreeView.WndProc

@delphidabbler delphidabbler moved this to To do in CodeSnip May 23, 2023
@delphidabbler
Copy link
Owner Author

It seems that the problem is that, sometimes (not always) Windows recreates the tree view displayed in the overview pane and all its nodes. The tree view nodes are custom classes that have a property that reference an IView instance relating to the displayed items. Unfortunately when Windows recreates the nodes the IView property is set to nil.

This explains the nil IView references that have been causing the access violation.

The solution used in the fix is to handle the Windows messages sent when the computer hibernates and resumes. On hibernation the state of the tree view is recorded. On restoration we assume that the tree view is corrupted and so forcibly rebuild it and restore the saved state.

There is a problem thought. The message we handle is issued twice after resuming from hibernation. There is no easy way to tell which message has been issued. Therefore the tree view is rebuilt twice. There is not much performance penalty to this, so we can let it go. The potential problem is that if the tree view is recreated it happens after the 1st message and before the 2nd. Should, for example, the message only be triggered once then the bug will be back!

Even after all this it is possible that the program will redraw the tree view before the IView instances are restored. I've added code to the tree node custom drawing code to test if a node's IView instance is nil. This leads to some nodes not being drawn correctly. However, this doesn't matter because the tree view is forcibly redrawn again after the IView instances are restored.

All in all, I've not totally happy with this solution, which is more of a work around than a fix, but it's the best I can come up with without completely revising the overview frame code.

This fix was implemented at commit 58be37c.

@delphidabbler delphidabbler added completed Work has been completed on this issue and changes have been committed to `develop` branch.. and removed accepted Accepted for implementation / fixing needs investigation Investigation required to confirm problem labels Apr 13, 2025
@delphidabbler delphidabbler moved this from Accepted to Done - awaiting release in CodeSnip Apr 13, 2025
@delphidabbler
Copy link
Owner Author

Just tried 5 hibernations with CodeSnip running from the Delphi XE debugger and stand-alone and not a single crash.

To replicate I can force this by closing and opening the lid of my laptop, then hibernating it and restoring using the power button. This is strange because closing the lid is configured to do nothing!

@delphidabbler delphidabbler removed this from the Confirmed for next patch milestone Apr 13, 2025
delphidabbler added a commit that referenced this issue Apr 14, 2025
Following a discussion in the comments the DelphiDabbler Blog post at
https://tinyurl.com/mrp76mdy it seems that was not a good idea to rely
upon handling WM_POWERBROADCAST's PBT_APMPOWERSTATUSCHANGE event to
restore the overview pane's tree view nodes to the expected state after
Windows has recreated the tree view in an invalid state.

So I've modified the code to only rely on the PBT_APMSUSPEND event of
WM_POWERBROADCAST and not PBT_APMPOWERSTATUSCHANGE. PBT_APMSUSPEND is
handled to prepare for hibernation by not only saving the tree view's
state (as per the previous fix) but also setting an event handler that
gets called only when the tree view's window gets recreated by Windows
AND the treeview contains nodes with nil IView pointers. When called,
the event handler rebuilds the tree view with nodes containing valid
IView references.

The problem is that the event needs to be triggered from the
TTreeView.CreateWnd method that gets called when Windows recreates the
tree view. Since TTreeView exposes no suitable events, the only way is
to inject a suitable event using a nasty hack. Not good practise.

Note that all the methods that depend on the hack have been given names
beginning with "_HACK_" to make it obvious where the naughtiness lies.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug Bug report completed Work has been completed on this issue and changes have been committed to `develop` branch..
Projects
Status: Done - awaiting release
Development

When branches are created from issues, their pull requests are automatically linked.

1 participant