;-- TIME_STAMP   2018-12-06 10:09:38   v 0.1

#include <WinAPI.au3>
#include <WinAPIShellEx.au3>
#include <ButtonConstants.au3>
#include <WindowsConstants.au3>

Opt("GUIOnEventMode", True)
$hWnd = GUICreate("Subclassing Example", 600, 400)
GUISetOnEvent(-3, onClosePressed)
$cButton1 = GUICtrlCreateButton("Button 1" & @CRLF & "AutoIt Internal", 0, 0, 200, 200, $BS_MULTILINE)
GUICtrlSetOnEvent(-1, onButtonPressed)
$cButton2 = GUICtrlCreateButton("Button 2" & @CRLF & "Subclassing", 200, 0, 200, 200, $BS_MULTILINE)
GUICtrlSetOnEvent(-1, onButtonPressed)
$cButton3 = GUICtrlCreateButton("Button 3" & @CRLF & "Replaced WndProc", 400, 0, 200, 200, $BS_MULTILINE)
GUICtrlSetOnEvent(-1, onButtonPressed)
$cLog = GUICtrlCreateEdit("=== Action Log ===" & @CRLF, 0, 200, 600, 200)
GUICtrlSetFont(-1, 9, 400, 0, 'Courier New')
;subclassing
$hSCCB = DllCallbackRegister(subclassProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
$pSCCB = DllCallbackGetPtr($hSCCB)
_WinAPI_SetWindowSubclass(GUICtrlGetHandle($cButton2), $pSCCB, 1000, 0)
;replacing wndproc
$hRWCB = DllCallbackRegister(wndProc, "lresult", "hwnd;uint;wparam;lparam")
$pRWCB = DllCallbackGetPtr($hRWCB)
$pOldWndProc = _WinAPI_GetWindowLong(GUICtrlGetHandle($cButton3), $GWL_WNDPROC)
;~ _WinAPI_SetWindowLong(GUICtrlGetHandle($cButton3), $GWL_USERDATA, $pOldWndProc) ; fuktioniert nicht, weil $GWL_USERDATA wohl intern von WinProg benutzt wird.
writeLog('! $GWL_USERDATA = ' & _WinAPI_GetWindowLong($hWnd, $GWL_USERDATA))
_WinAPI_SetWindowLong(GUICtrlGetHandle($cButton3), $GWL_WNDPROC, $pRWCB)
;exit routine to free resources
OnAutoItExitRegister(onExitApp)
GUISetState()

While True
    Sleep(20)
WEnd

Func onClosePressed()
    Exit
EndFunc

Func onButtonPressed()
    writeLog("+ AutoIt      : " & StringFormat("Button with ID %4i pressed! $GWL_USERDATA = %i", @GUI_CtrlId, _WinAPI_GetWindowLong($hWnd, $GWL_USERDATA)))
EndFunc

Func onExitApp()
    _WinAPI_RemoveWindowSubclass(GUICtrlGetHandle($cButton2), $pSCCB, 1000)
    DllCallbackFree($hSCCB)
    _WinAPI_SetWindowLong(GUICtrlGetHandle($cButton3), $GWL_WNDPROC, $pOldWndProc)
    DllCallbackFree($hRWCB)
EndFunc

Func writeLog($s)
;~     GUICtrlSetData($cLog, GUICtrlRead($cLog) & @CRLF & StringFormat("[%02d:%02d:%02d.%03d] %s", @HOUR, @MIN, @SEC, @MSEC, $s))
    GUICtrlSetData($cLog, StringFormat("[%02d:%02d:%02d.%03d] %s\r\n", @HOUR, @MIN, @SEC, @MSEC, $s), 1)
EndFunc

Func _IsButton($hWnd)
	Local $hParent = _WinAPI_GetParent($hWnd), $aCursorInfo = GUIGetCursorInfo($hParent), $iError = @error
	ConsoleWrite('$hWnd = ' & $hWnd & ' $hParent = ' & $hParent & ' $iError = ' & $iError & ' UBound($aCursorInfo) = ' & UBound($aCursorInfo) & @CRLF)
	Return ($iError ? "@error = " & $iError : $aCursorInfo[4] ? StringFormat('Button with ID %4i pressed!', $aCursorInfo[4]) : "$WM_LBUTTONDOWN")
EndFunc

;external called functions

Func subclassProc($hWnd, $iMsg, $iWParam, $iLParam, $iID, $pData)
    If $iMsg = $WM_LBUTTONDOWN Then writeLog("> subclassProc: " & _IsButton($hWnd) & " $GWL_USERDATA = " & _WinAPI_GetWindowLong($hWnd, $GWL_USERDATA))

;~ 	Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $iWParam, $iLParam)
	Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $iWParam, "lparam", $iLParam)[0]
EndFunc

Func wndProc($hWnd, $iMsg, $iWParam, $iLParam)
    If $iMsg = $WM_LBUTTONDOWN Then writeLog("- wndProc     : " & _IsButton($hWnd) & " $GWL_USERDATA = " & _WinAPI_GetWindowLong($hWnd, $GWL_USERDATA))

;~     $pOldWndProc = _WinAPI_GetWindowLong($hWnd, $GWL_USERDATA) ; fuktioniert nicht, weil $GWL_USERDATA wohl intern von WinProg benutzt wird.

;~     Return _WinAPI_CallWindowProc($pOldWndProc, $hWnd, $iMsg, $iWParam, $iLParam)
	Return DllCall("user32.dll", "lresult", "CallWindowProc", "ptr", $pOldWndProc, "hwnd", $hWnd, "uint", $iMsg, "wparam", $iWParam, "lparam", $iLParam)[0]
EndFunc