On Script Exit, Dictionary is destroyed in Window Procedure and replaced with empty string Topic is solved

Report problems with documented functionality
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

On Script Exit, Dictionary is destroyed in Window Procedure and replaced with empty string

Post by iseahound » 28 May 2022, 10:35

As the object / class destruction order has been discussed multiple times already, I am in doubt as to whether this would be fixed.

However, the same code does not throw on v1, likely due to the weaker type system.

Code: Select all

hwnd := CreateWindow("MyWindow")
Sleep 7000
Reload

WindowClass() {
   ; The window class shares the name of this class.
   cls := "myClass"
   wc := Buffer(A_PtrSize = 4 ? 48:80) ; sizeof(WNDCLASSEX) = 48, 80

   ; Check if the window class is already registered.
   hInstance := DllCall("GetModuleHandle", "ptr", 0, "ptr")
   if DllCall("GetClassInfoEx", "ptr", hInstance, "str", cls, "ptr", wc)
      return cls

   ; Create window data.
   pWndProc := CallbackCreate(WindowProc)
   hCursor := DllCall("LoadCursor", "ptr", 0, "ptr", 32512, "ptr") ; IDC_ARROW
   hBrush := DllCall("GetStockObject", "int", 5, "ptr") ; Hollow_brush

   ; struct tagWNDCLASSEXA - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa
   ; struct tagWNDCLASSEXW - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexw
   _ := (A_PtrSize = 4)
      NumPut(  "uint",     wc.size, wc,         0) ; cbSize
      NumPut(  "uint",         0x8, wc,         4) ; style
      NumPut(   "ptr",    pWndProc, wc,         8) ; lpfnWndProc
      NumPut(   "int",           0, wc, _ ? 12:16) ; cbClsExtra
      NumPut(   "int",           0, wc, _ ? 16:20) ; cbWndExtra
      NumPut(   "ptr",           0, wc, _ ? 20:24) ; hInstance
      NumPut(   "ptr",           0, wc, _ ? 24:32) ; hIcon
      NumPut(   "ptr",     hCursor, wc, _ ? 28:40) ; hCursor
      NumPut(   "ptr",      hBrush, wc, _ ? 32:48) ; hbrBackground
      NumPut(   "ptr",           0, wc, _ ? 36:56) ; lpszMenuName
      NumPut(   "ptr", StrPtr(cls), wc, _ ? 40:64) ; lpszClassName
      NumPut(   "ptr",           0, wc, _ ? 44:72) ; hIconSm

   ; Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
   DllCall("RegisterClassEx", "ptr", wc, "ushort")

   ; Return the class name as a string.
   return cls

   ; Define window behavior.
   WindowProc(hwnd, uMsg, wParam, lParam) {

      ; WM_DESTROY
      if (uMsg = 0x2) {
         MsgBox "I am destroyed."
      }

      static dict :=
      {
         0x0201  : "LeftMouseDown",
         0x0202  : "LeftMouseUp",
         0x0203  : "LeftMouseDoubleClick",
         0x0204  : "RightMouseDown",
         0x0205  : "RightMouseUp",
         0x0206  : "RightMouseDoubleClick",
         0x0207  : "MiddleMouseDown",
         0x0208  : "MiddleMouseUp",
         0x0209  : "MiddleMouseDoubleClick",
         0x02A1  : "MouseOver",
         0x02A3  : "MouseLeave"
      }

      ; Process windows messages by invoking the associated callback.
      for message, event in dict.OwnProps()
         if (uMsg = message)
            MsgBox "message is: " message

      return DllCall("DefWindowProc", "ptr", hwnd, "uint", uMsg, "uptr", wParam, "ptr", lParam, "ptr")
   }
}

CreateWindow(title := "MyTitle") {
   ; Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
   WS_POPUP                  := 0x80000000   ; Allow small windows.
   WS_VISIBLE                := 0x10000000   ; Show on creation.

   ; Extended Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
   WS_EX_TOPMOST             :=        0x8   ; Always on top.
   WS_EX_TOOLWINDOW          :=       0x80   ; Hides from Alt+Tab menu. Removes small icon.
   WS_EX_LAYERED             :=    0x80000   ; For UpdateLayeredWindow.

   style := WS_POPUP | WS_VISIBLE
   styleEx := WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED

   ; Prevent the script from exiting early.
   Persistent(True)

   dpi := DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
   hwnd := DllCall("CreateWindowEx"
            ,   "uint", styleEx | WS_EX_LAYERED  ; dwExStyle
            ,    "str", WindowClass()            ; lpClassName
            ,    "str", title                    ; lpWindowName
            ,   "uint", style                    ; dwStyle
            ,    "int", 100
            ,    "int", 100
            ,    "int", 200
            ,    "int", 200
            ,    "ptr", A_ScriptHwnd
            ,    "ptr", 0                        ; hMenu
            ,    "ptr", 0                        ; hInstance
            ,    "ptr", 0                        ; lpParam
            ,    "ptr")
   DllCall("SetThreadDpiAwarenessContext", "ptr", dpi, "ptr")

   return hwnd
}

iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: On Script Exit, Dictionary is destroyed in Window Procedure and replaced with empty string

Post by iseahound » 28 May 2022, 10:43

Out of curiosity I replaced the object with a Map().
image.png
image.png (23.31 KiB) Viewed 717 times

Code: Select all

hwnd := CreateWindow("MyWindow")
Sleep 7000
Reload

WindowClass() {
   ; The window class shares the name of this class.
   cls := "myClass"
   wc := Buffer(A_PtrSize = 4 ? 48:80) ; sizeof(WNDCLASSEX) = 48, 80

   ; Check if the window class is already registered.
   hInstance := DllCall("GetModuleHandle", "ptr", 0, "ptr")
   if DllCall("GetClassInfoEx", "ptr", hInstance, "str", cls, "ptr", wc)
      return cls

   ; Create window data.
   pWndProc := CallbackCreate(WindowProc)
   hCursor := DllCall("LoadCursor", "ptr", 0, "ptr", 32512, "ptr") ; IDC_ARROW
   hBrush := DllCall("GetStockObject", "int", 5, "ptr") ; Hollow_brush

   ; struct tagWNDCLASSEXA - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa
   ; struct tagWNDCLASSEXW - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexw
   _ := (A_PtrSize = 4)
      NumPut(  "uint",     wc.size, wc,         0) ; cbSize
      NumPut(  "uint",         0x8, wc,         4) ; style
      NumPut(   "ptr",    pWndProc, wc,         8) ; lpfnWndProc
      NumPut(   "int",           0, wc, _ ? 12:16) ; cbClsExtra
      NumPut(   "int",           0, wc, _ ? 16:20) ; cbWndExtra
      NumPut(   "ptr",           0, wc, _ ? 20:24) ; hInstance
      NumPut(   "ptr",           0, wc, _ ? 24:32) ; hIcon
      NumPut(   "ptr",     hCursor, wc, _ ? 28:40) ; hCursor
      NumPut(   "ptr",      hBrush, wc, _ ? 32:48) ; hbrBackground
      NumPut(   "ptr",           0, wc, _ ? 36:56) ; lpszMenuName
      NumPut(   "ptr", StrPtr(cls), wc, _ ? 40:64) ; lpszClassName
      NumPut(   "ptr",           0, wc, _ ? 44:72) ; hIconSm

   ; Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
   DllCall("RegisterClassEx", "ptr", wc, "ushort")

   ; Return the class name as a string.
   return cls

   ; Define window behavior.
   WindowProc(hwnd, uMsg, wParam, lParam) {

      ; WM_DESTROY
      if (uMsg = 0x2) {
         MsgBox "I am destroyed."
      }

      static dict := Map(
         0x0201  , "LeftMouseDown",
         0x0202  , "LeftMouseUp",
         0x0203  , "LeftMouseDoubleClick",
         0x0204  , "RightMouseDown",
         0x0205  , "RightMouseUp",
         0x0206  , "RightMouseDoubleClick",
         0x0207  , "MiddleMouseDown",
         0x0208  , "MiddleMouseUp",
         0x0209  , "MiddleMouseDoubleClick",
         0x02A1  , "MouseOver",
         0x02A3  , "MouseLeave"
      )

      ; Process windows messages by invoking the associated callback.
      for message, event in dict
         if (uMsg = message)
            MsgBox "message is: " message

      return DllCall("DefWindowProc", "ptr", hwnd, "uint", uMsg, "uptr", wParam, "ptr", lParam, "ptr")
   }
}

CreateWindow(title := "MyTitle") {
   ; Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
   WS_POPUP                  := 0x80000000   ; Allow small windows.
   WS_VISIBLE                := 0x10000000   ; Show on creation.

   ; Extended Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
   WS_EX_TOPMOST             :=        0x8   ; Always on top.
   WS_EX_TOOLWINDOW          :=       0x80   ; Hides from Alt+Tab menu. Removes small icon.
   WS_EX_LAYERED             :=    0x80000   ; For UpdateLayeredWindow.

   style := WS_POPUP | WS_VISIBLE
   styleEx := WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED

   ; Prevent the script from exiting early.
   Persistent(True)

   dpi := DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
   hwnd := DllCall("CreateWindowEx"
            ,   "uint", styleEx | WS_EX_LAYERED  ; dwExStyle
            ,    "str", WindowClass()            ; lpClassName
            ,    "str", title                    ; lpWindowName
            ,   "uint", style                    ; dwStyle
            ,    "int", 100
            ,    "int", 100
            ,    "int", 200
            ,    "int", 200
            ,    "ptr", A_ScriptHwnd
            ,    "ptr", 0                        ; hMenu
            ,    "ptr", 0                        ; hInstance
            ,    "ptr", 0                        ; lpParam
            ,    "ptr")
   DllCall("SetThreadDpiAwarenessContext", "ptr", dpi, "ptr")

   return hwnd
}

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: On Script Exit, Dictionary is destroyed in Window Procedure and replaced with empty string  Topic is solved

Post by lexikos » 29 May 2022, 22:12

It appears that you already have the answer, that this isn't a bug.

iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: On Script Exit, Dictionary is destroyed in Window Procedure and replaced with empty string

Post by iseahound » 30 May 2022, 23:07

Wrapping a for loop in a try statement still feels rather dirty to me.

Code: Select all

try for message, event in dict.OwnProps()
   pass
Out of curiosity, what is the rationale for destroying static objects in a function, without destroying the entire function itself? Why allow a hollowed-out function to be called?

Because another equally valid fix is removing static.


Post Reply

Return to “Bug Reports”