zero-plusplus / vscode-autohotkey-debug

https://marketplace.visualstudio.com/items?itemName=zero-plusplus.vscode-autohotkey-debug
52 stars 4 forks source link

"Error: Gui has no window" when debug adapter evaluates a destroyed Gui object #317

Open SAbboushi opened 8 months ago

SAbboushi commented 8 months ago

It seems MyGui.Destroy()

leaves behind a useless Gui object in the MyGui variable https://www.autohotkey.com/boards/viewtopic.php?style=19&t=108635#p483346

I suspect this is what causes the debug adapter to throw "Error: Gui has no window" if/when it tries to evaluate the "destroyed" Gui object when e.g. the Gui object's variable name is visible in the VARIABLES pane (I note that MyGui.base.Hwnd shows as "\<error>").

I find this makes it very difficult to debug the script because the error continually displays at breakpoints.

Anything you can do to overcome this problem? My thoughts:

Any thoughts?

To reproduce, single step through code and expand "Global" in the VARIABLES pane before executing MyGui.Destroy():

MyGui := Gui()
MyGui.Show("w500 h50")
MyGui.Destroy()

x := 1
zero-plusplus commented 8 months ago

Issue confirmed. It seems difficult to work around this issue as an error dialogue is displayed when communicating with the debugger.


From this point on, in my opinion, Lexicos' suggestion of MyGui := "" is a reasonable solution.

This is because destroying the GUI and destructors of object have different roles.


If this issue is to be solved by refactoring, it may be helpful to be aware of the Destroy method so that it is unaware of it, as follows.

;  The instance is recovered in garbage collection when it has finished executing createGui and calls the deconstructor __DELETE method, which automatically calls Gui's Destroy method.
createGui()
createGui() {
  instance := MyGui()
}
class MyGui extends Gui {
  __DELETE() {
    this.Destroy()
  }
}

A practical example in a simple application using Gui in AutoHotkey.

The Exit method can be used to destroy AppGui and remove the access method to the instance.

MyApp := App()
MyApp.Run()
MyApp.Exit()

class App {
  gui := ""
  Run() {
    this.gui := AppGui()
  }
  Exit() {
    this.gui := ""
  }
}

class AppGui extends Gui {
  __DELETE() {
    this.Destroy()
  }
}
Lexikos commented 5 months ago

AutoHotkey/AutoHotkey@e06bcb7a suppresses the error dialogs and AutoHotkey/AutoHotkey@2c9e300d allows context_get or property_get to continue when an enumerator throws.

For current/past versions:

In a Gui subclass, you can work around it with

Destroy() {
    this.DefineProp('__Enum', {value: "Destroyed"})
    super.Destroy()
}

or just redefine __Enum when you call Destroy. The workaround can be applied to all Gui objects like so:

Gui.Prototype.DefineProp('Destroy', {call: ((destroy, this) =>
    destroy(this.DefineProp('__Enum', {value: "Destroyed"}))
).Bind(Gui.Prototype.Destroy)})

The debugger does not raise an error or call __Enum if it is not an object.


It is not possible for the extension to suppress errors during evaluation of property_get or context_get because the debugger is already in a break state. Breakpoints (whether -t line or -t exception) are ignored because there is no way within the DBGp spec to notify the client that a breakpoint has been reached in that situation. Normally it is done by responding to the last continuation command, but if the debugger was already in a break state, any continuation commands that have been sent have already received their response.

SAbboushi commented 5 months ago

Thanks for the details

zero-plusplus commented 5 months ago

Thanks for the fix.

I will ensure that this issue is fixed in testing before releasing vscode-autohotkey-debug v2.0.0.