1 (изменено: Шикарная услуга плана, 2021-12-16 18:55:25)

Тема: AHK: Class Gui ObjBindMethod блокирует деструктор __Delete

По всей видимости если применить ObjBindMethod к методу того же объекта, то он блокирует деструктор класса.
Вот код, который иллюстрирует проблему:


#SingleInstance force


gui1 := new myClass("gui1") 
gui1.Show(100, 200)

gui2 := new myClass("gui2")
gui2.Show(100, 300)

Sleep, 4000

; уничтожение объектов
gui1 := {} 
gui2 := 1



class myClass
{
    __New(name)
    {
        this.name := name
        Gui, % this.name . ": New"
        Gui, % this.name . ": Add", Text,, % "this is Gui named: " . this.name
        ; OnMessage( 0x200, ObjBindMethod(this, "onMouseMove") ) ; если расскомментировать, хрен ты удалишь обьект
    }

    Show(x := "Center", y := "Center")
    {
        Gui, % this.name . ": Show", % "x" . x . "y" . y, NA
    }

    __Delete()
    {
        Gui, % this.name . ": Destroy"
        Msgbox, % "destroy " . this.name
    }

    onMouseMove( wparam, lparam, msg, hwnd ) ; добавляет возможность перетаскивать мышкой в любом месте интерфейса
    {
        if wparam = 1 ; LButton
            PostMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
    }
}


Esc::ExitApp

Вероятно требуется какая-то хитрость, но я пока не могу понять какая.

2

Re: AHK: Class Gui ObjBindMethod блокирует деструктор __Delete

Обязательно через класс создавать окна?

Win10x64, AHK v1.1.37.01 (Unicode 64-bit) | AHK-Wiki | Переменные и выражения | RegEx101

3

Re: AHK: Class Gui ObjBindMethod блокирует деструктор __Delete

Примерно так:

#SingleInstance force


gui1 := new myClass("gui1") 
gui1.Show(100, 200)

gui2 := new myClass("gui2")
gui2.Show(100, 300)

Sleep, 4000

; уничтожение объектов
gui1 := {} 
gui2 := 1



class myClass
{
   __New(name)
   {
      this.name := name
      Gui, % this.name . ": New"
      Gui, % this.name . ": Add", Text,, % "this is Gui named: " . this.name
      this.OnMessage := new this._OnMessage
   }

   Show(x := "Center", y := "Center")
   {
      Gui, % this.name . ": Show", % "x" . x . "y" . y, NA
   }

   __Delete()
   {
      Gui, % this.name . ": Destroy"
      this.OnMessage.Clear()
      Msgbox, % "destroy " . this.name
   }
   
   class _OnMessage {
      __New() {
         this.fn := ObjBindMethod(this, "onMouseMove")
         OnMessage(0x200, this.fn)
      }

      onMouseMove( wparam, lparam, msg, hwnd ) ; добавляет возможность перетаскивать мышкой в любом месте интерфейса
      {
         if wparam = 1 ; LButton
            PostMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
      }
      
      Clear() {
         OnMessage(0x200, this.fn, 0)
      }
   }
}


Esc::ExitApp
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

4

Re: AHK: Class Gui ObjBindMethod блокирует деструктор __Delete

__Михаил__, да
teadrinker ого, спасибо, это работает, держи плюс в карму!

Вот еще задачка, а как по средней кнопке мыши закрывать интерфейс,  как сослаться в функции обрабатывающей событие на деструктор этого объекта?
Код для примера:


#SingleInstance force


gui1 := new myClass("gui1")
gui1.Show(100, 200)

gui2 := new myClass("gui2")
gui2.Show(100, 300)

Sleep, 4000

; уничтожение объектов
gui1 := {}
gui2 := 1



class myClass
{
   __New(name)
   {
      this.name := name
      Gui, % this.name . ": New"
      Gui, % this.name . ": Add", Text,, % "this is Gui named: " . this.name
      this.OnMessage := new this._OnMessage
   }

   Show(x := "Center", y := "Center")
   {
      Gui, % this.name . ": Show", % "x" . x . "y" . y, NA
   }

   __Delete()
   {
      Gui, % this.name . ": Destroy"
      this.OnMessage.Clear()
      stdout("destroy " . this.name)
   }

   class _OnMessage {
      __New() {
         this.fn := ObjBindMethod(this, "onMouseMove")
         this.fn2 := ObjBindMethod(this, "onMButtonUp")
         OnMessage(0x200, this.fn)
         OnMessage(0x0208, this.fn2)
      }

      onMouseMove( wparam, lparam, msg, hwnd ) ; добавляет возможность перетаскивать мышкой в любом месте интерфейса
      {
         if wparam = 1 ; LButton
            PostMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
      }

      onMButtonUp( wparam, lparam, msg, hwnd )
      {
         ; по нажатию средней кнопки мыши - закрывать интерфейс
         stdout("wparam: " . wparam . ", lparam: " . lparam . ", msg: " . Format("{1:#x}", msg) . ", hwnd: " . Format("{1:#x}", hwnd) )
      }

      Clear() {
         OnMessage(0x200, this.fn, 0)
         OnMessage(0x0208, this.fn2, 0)
      }
   }
}


Esc::ExitApp

5

Re: AHK: Class Gui ObjBindMethod блокирует деструктор __Delete

Вроде никак, можно просто удалить ресурсы.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6 (изменено: Шикарная услуга плана, 2021-12-22 18:04:18)

Re: AHK: Class Gui ObjBindMethod блокирует деструктор __Delete

Вот такое решение нашлось форуме официального сайта:
https://www.autohotkey.com/boards/viewtopic.php?t=6966

А вот пример переделанного кода:


#SingleInstance, force

Test1 := new TestClass("Test1", 300, 200)
Test2 := new TestClass("Test2", 450, 200)
Test3 := new TestClass("Test3", 600, 200)
return

Esc::ExitApp

class TestClass {
    static DerivedObjectsCount := 0, DerivedObjects := {}
    static LBUTTONDOWNFunc := ObjBindMethod(TestClass, "WM_LBUTTONDOWN")    ; store the BoundFunc object so you may remove the message handler
    static MBUTTONUPFunc := ObjBindMethod(TestClass, "WM_MBUTTONUP")    ; store the BoundFunc object so you may remove the message handler
    __New(Name, x, y) {
        Gui, New, +Hwndhwnd
        Gui %hwnd%: -Caption +AlwaysOnTop +ToolWindow +OwnDialogs
        Gui %hwnd%: Add, Text, x5 y5 w100 h20, % Name
        Gui %hwnd%: Add, Text, x5 y30 w100 h20 hwndhPos, % x ", " y
        Gui %hwnd%: Show, % "x" x " y" y " w110 h55 NA"
        this.hPos := hPos, this.hwnd := hwnd, this.Name := Name
        TestClass.DerivedObjectsCount += 1 			; пояснить
        if (TestClass.DerivedObjectsCount > 0)
        {
            OnMessage(0x201, this.LBUTTONDOWNFunc)  ; activate the message handler
            OnMessage(0x208, this.MBUTTONUPFunc)  ; activate the message handler
        }

        TestClass.DerivedObjects[hwnd] := &this     ; store object's address
    }

    WM_LBUTTONDOWN() {
        if (TestClass.DerivedObjects.HasKey(A_Gui)) { ; GUI which belongs to object derived from this class
            PostMessage, 0xA1, 2
            KeyWait, LButton
            obj := Object(TestClass.DerivedObjects[A_Gui])
            WinGetPos, x,y,,, % "ahk_id " obj.hwnd
            GuiControl, % obj.hwnd ":", % obj.hPos, % x ", " y
            return 0    ; no need to call any other message handlers
        }
    }

    WM_MBUTTONUP()
    {
        if (TestClass.DerivedObjects.HasKey(A_Gui)) { ; GUI which belongs to object derived from this class
            obj := Object(TestClass.DerivedObjects[A_Gui])
            obj.__Delete()

            return 0    ; no need to call any other message handlers
        }
    }

    __Delete() {
        hwnd := this.hwnd
        if(WinExist("ahk_id " . hwnd))
        {
            Gui %hwnd%: Destroy 	; если гуи был уничтожен "извне"
            stdout(hwnd . " deleted")
        }

        TestClass.DerivedObjects.Delete(hwnd)           ; Lexikos: "Use .Delete(hwnd) or .Remove(hwnd, "") and not .Remove(hwnd)."
        TestClass.DerivedObjectsCount -= 1
        if (TestClass.DerivedObjectsCount = 0)          ; if no more derived objects
        {
            OnMessage(0x201, this.LBUTTONDOWNFunc, 0)   ; remove message handler
            OnMessage(0x208, this.MBUTTONUPFunc, 0)   ; remove message handler
        }
    }
}

Уважаемого teadrinker прошу пояснить использование имени класса в конструкторе (комментарий "пояснить").

7

Re: AHK: Class Gui ObjBindMethod блокирует деструктор __Delete

Идея была в том, что OnMessage для каждого сообщения вызывается только один (самый первый) раз, а не при каждом создании инстанса класса. Но здесь ошибка, должно быть:

if (TestClass.DerivedObjectsCount = 0)
{
   OnMessage(0x201, this.LBUTTONDOWNFunc)  ; activate the message handler
   OnMessage(0x208, this.MBUTTONUPFunc)  ; activate the message handler
}
TestClass.DerivedObjectsCount += 1

При создании инстанса увеличивается свойство DerivedObjectsCount, которое принадлежит самому классу, то-есть читается во всех инстансах. Соответственно при удалении инстанса это значение уменьшается, при достижении нуля удаляется обработчик сообщения.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder