Jump to content

Can you have two or more subclasses for the same GUI handle?


Go to solution Solved by MattyD,

Recommended Posts

Posted

This is more of a question for anyone with more familiarity with subclassing. So I don't necessarily need code examples in return, unless of course that helps to explain things more.

I have subclasses for some individual handles for some specific window classes. However, for this question, I am referring specifically to the main GUI window.

For example, instead of:

GUIRegisterMsg($WM_ACTIVATE, __GUIDarkMenu_WM_ACTIVATE)
GUIRegisterMsg($WM_WINDOWPOSCHANGED, __GUIDarkMenu_WM_WINDOWPOSCHANGED)
GUIRegisterMsg($WM_MEASUREITEM, __GUIDarkMenu_WM_MEASUREITEM)
GUIRegisterMsg($WM_DRAWITEM, __GUIDarkMenu_WM_DRAWITEM)

I have successfully switched to the following:

;...
$g_pMenuWndProc = DllCallbackRegister("__GUIDarkTheme_MenuProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
_WinAPI_SetWindowSubclass($g_hGui, DllCallbackGetPtr($g_pMenuWndProc), 1)
;...
Func __GUIDarkTheme_MenuProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    #forceref $iID, $pData
    Local $sContinue = $GUI_RUNDEFMSG
    Switch $iMsg
        Case $WM_ACTIVATE
            $sContinue = __GUIDarkMenu_WM_ACTIVATE($hWnd, $iMsg, $wParam, $lParam)
        Case $WM_DRAWITEM
            $sContinue = __GUIDarkMenu_WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam)
        Case $WM_MEASUREITEM
            $sContinue = __GUIDarkMenu_WM_MEASUREITEM($hWnd, $iMsg, $wParam, $lParam)
        Case $WM_WINDOWPOSCHANGED
            $sContinue = __GUIDarkMenu_WM_WINDOWPOSCHANGED($hWnd, $iMsg, $wParam, $lParam)
    EndSwitch
    If $sContinue = $GUI_RUNDEFMSG Then Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>__GUIDarkTheme_MenuProc

This is works perfectly well. But I am wondering now about expanding. Those all cover the functionality needed for subclassing the GUI menubar.

I am wondering if I can (or even should?) use another window procedure for the other GUI handle related window messages. I have GUIRegisterMsg for $WM_NOTIFY, $WM_CTLCOLORLISTBOX, $WM_CTLCOLOREDIT, and $WM_SIZE. And since they are not related to GUI menubar, this I why I am curious about another procedure. And so this leads to my question from the title:

Can you have two or more subclasses for the same GUI handle?

For the record, I have never had success subclassing $WM_SIZE for the main GUI handle. I have only had success with GUIRegisterMsg and $WM_SIZE.

Thank you for your time. :)

  • Solution
Posted

Yeah its certainly possible, but its not what you'd normally see.

There is also a risk of one of one subclass consuming messages which you might need up the chain. 

#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <GUIConstants.au3>

Global Const $tagMINMAXINFO = "struct;long ptReserved[2];long ptMaxSize[2];long ptMaxPosition[2];long ptMinTrackSize[2];long ptMaxTrackSize[2];endstruct"
Global $__g_hWndProc = DllCallbackRegister("WndProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
Global $__g_pWndProc = DllCallbackGetPtr($__g_hWndProc)

Global $__g_hWndProc2 = DllCallbackRegister("WndProc2", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
Global $__g_pWndProc2 = DllCallbackGetPtr($__g_hWndProc2)

Global $__g_hGui = GUICreate("Test", 400, 200, -1, -1, $WS_OVERLAPPEDWINDOW)
_WinAPI_SetWindowSubclass($__g_hGui, $__g_pWndProc, 1)
_WinAPI_SetWindowSubclass($__g_hGui, $__g_pWndProc2, 2)
GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

GUIDelete($__g_hGui)
DllCallbackFree($__g_hWndProc)
DllCallbackFree($__g_hWndProc2)

Func WndProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_ERASEBKGND
            _WinAPI_FillRect($wParam, _WinAPI_GetClientRect($hWnd), _WinAPI_GetStockObject($BLACK_BRUSH))
            Return True

        Case $WM_DESTROY
            _WinAPI_RemoveWindowSubclass($hWnd, $__g_pWndProc, $iID)
            Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)

        Case Else
            Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)

    EndSwitch
EndFunc

Func WndProc2($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_GETMINMAXINFO ;Limit resizing

            Local $tMINMAXINFO = DllStructCreate($tagMINMAXINFO, $lParam)
            $tMINMAXINFO.ptMinTrackSize(1) = 200
            $tMINMAXINFO.ptMinTrackSize(2) = 100
            $tMINMAXINFO.ptMaxTrackSize(1) = 500
            $tMINMAXINFO.ptMaxTrackSize(2) = 300

            Return 0

        Case $WM_DESTROY
            _WinAPI_RemoveWindowSubclass($hWnd, $__g_pWndProc2, $iID)
            Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)

        Case Else
            Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)

    EndSwitch
EndFunc
Posted

Thank you, Matty. That answered my question perfectly and the example is very helpful. You always have an excellent way of making complex things look so much easier. And this example is the best for me to follow and learn from. I really appreciate your help. :)

Posted (edited)

_WinAPI_SetWindowSubclass($__g_hGui, $__g_pWndProc2, 2)

A latent question would be: that UINT_PTR , how can I make it unique for my UDF, as to not interfere with other scripts ? can I pass TimerInit() ? 🤯

Edit: I guess something higher than 10000 as a Const, like 54437 or whatever, to be known as reserved.

Edited by argumentum
answered

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
58 minutes ago, argumentum said:

A latent question would be: that UINT_PTR , how can I make it unique for my UDF, as to not interfere with other scripts ?

Hello. If not mistaken, the subclass ID doesn't have to be unique, for example this should work :

_WinAPI_SetWindowSubclass($__g_hGui, $__g_pWndProc, 1)
_WinAPI_SetWindowSubclass($__g_hGui, $__g_pWndProc2, 1)

And this should work too :

_WinAPI_SetWindowSubclass($__g_hGui, $__g_pWndProc, 1)
_WinAPI_SetWindowSubclass($__g_hGui, $__g_pWndProc, 2)

It's the combination of the subclass procedure + subclass ID that matters, so it shouldn't matter if the user already uses the same subclass ID in his own subclassing functions,

@MattyD what's your opinion, because I may be totally wrong on this.
Thanks

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted

yep, you are correct.  In fact, its the triplet which is used as an identifier when registering the subclass. It's a combo of  window handle + Subclass proc ptr +  subclass id. 

Apparently you may wish to subclass a window multiple times, but using the same subclass proc.  You could set the SubClassID to associated hwnds or object ptrs or something.  But this is an edge case.

I've used CtrlIds for the SubclassID before when subclassing things like buttons, but there's no hard and fast rule. If we're not using the subclassid param for anything meaningful then we should probably just use 0.  (In hindsight I should have done that in the example!)

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...