Jump to content

Recommended Posts

Posted (edited)

GUIRegisterMsg20 is an extended version of GUIRegisterMsg, and GUIRegisterMsgEx is an extended version of GUIRegisterMsg20.

Example A:

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include "GUIRegisterMsgEx.au3"

__ExampleA()
Func __ExampleA()
    Local $hGUI1 = GUICreate("Window A", 300, 200, 100, 100)
    GUISetState(@SW_SHOW, $hGUI1)

    Local $hGUI2 = GUICreate("Window B", 300, 200, 450, 100)
    GUISetState(@SW_SHOW, $hGUI2)

    GUIRegisterMsgEx($hGUI1, $WM_LBUTTONDOWN, __OnLeftClickWindowA)
    GUIRegisterMsgEx($hGUI2, $WM_LBUTTONDOWN, __OnLeftClickWindowB)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                Exit
        EndSwitch
    WEnd
    GUIDelete($hGUI2)
    GUIDelete($hGUI1)
EndFunc

Func __OnLeftClickWindowA($hWnd, $iMsg, $wParam, $lParam, $iid, $Pdata)
    ConsoleWrite("-> Clicked on Window A! (Handle: " & $hWnd & ")" & @CRLF)
    Return 0xCAFEBABE
EndFunc

Func __OnLeftClickWindowB($hWnd, $iMsg, $wParam, $lParam, $iid, $Pdata)
    ConsoleWrite("-> Clicked on Window B! (Handle: " & $hWnd & ")" & @CRLF)
    Return 0xCAFEBABE
EndFunc

Example B:

#include <GuiConstants.au3>
#include <GuiHeader.au3>
#include <WinAPI.au3>
#include <Misc.au3>
#include <WinAPITheme.au3>
#include <GuiListView.au3>
#include "GUIRegisterMsgEx.au3"

AutoItSetOption("MustDeclareVars", 1)

__ExampleB()
Func __ExampleB()
    Local $hGUI = GUICreate("Header Subclass Framework Test", 500, 400)
    GUISetBkColor(0x000000)

    Local $idListView_A = GUICtrlCreateListView("Items List|SubItems1|SubItems2", 10, 10, 480, 150, $LVS_REPORT, _
                                                            BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_HEADERDRAGDROP))
    GUICtrlSetBkColor($idListView_A, 0x121212)
    GUICtrlSetColor($idListView_A, 0xFFFFFF)

    Local $hListView_A = GUICtrlGetHandle($idListView_A)
    Local $hHeader_A = _GUICtrlListView_GetHeader($hListView_A)

    GUICtrlCreateListViewItem("item_A1|item_B1|item_C1", $idListView_A)
    GUICtrlCreateListViewItem("item_A2|item_B2|item_C2", $idListView_A)
    GUICtrlCreateListViewItem("item_A3|item_B3|item_C3", $idListView_A)
    _GUICtrlListView_SetColumnWidth($idListView_A, 2, $LVSCW_AUTOSIZE_USEHEADER)

    GUIRegisterMsgEx($hListView_A, $WM_NOTIFY, "MY_WM_NOTIFY", $hHeader_A)
    GUIRegisterMsgEx($hHeader_A, $WM_MOUSEMOVE, "MY_WM_NOTIFY", $hHeader_A)
    GUIRegisterMsgEx($hHeader_A, $WM_MOUSELEAVE, "MY_WM_NOTIFY", $hHeader_A)
    GUISetState(@SW_SHOW, $hGUI)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE
    GUIDelete($hGUI)
EndFunc

Func MY_WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover = False, $tPoint
    Local $hBrush

    Switch $iMsg
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            Local $hFrom = $tNMHDR.hWndFrom
            Switch $tNMHDR.Code
                Case $NM_CUSTOMDRAW
                    If _WinAPI_GetClassName($hFrom) = "sysheader32" Then
                        Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
                        Switch $tCustDraw.dwDrawStage
                            Case $CDDS_PREPAINT
                                Return $CDRF_NOTIFYITEMDRAW

                            Case $CDDS_ITEMPREPAINT
                                Local $tRect = DllStructCreate($tagRECT, DllStructGetPtr($tCustDraw, "Left"))
                                _WinAPI_SetBkMode($tCustDraw.hDC, 1)
                                _WinAPI_SetTextColor($tCustDraw.hDC, 0xFFFFFF)

                                If $bHover And _WinAPI_PtInRect($tRect, $tPoint) Then
                                    $hBrush = _WinAPI_CreateSolidBrush(_IsPressed(0x01) ? 0x00008B : 0x0000FF)
                                Else
                                    $hBrush = _WinAPI_CreateSolidBrush(0x202020)
                               EndIf
                                _WinAPI_InflateRect($tRect, -1, 0)
                                _WinAPI_FillRect($tCustDraw.hDC, $tRect, $hBrush)
                                _WinAPI_DeleteObject($hBrush)

                                _WinAPI_InflateRect($tRect, -5, -2)

                                GUIRegisterMsgEx_Bypass($tCustDraw.hWndFrom, True)              ;~~ same as doing _WinAPI_RemoveWindowSubclass() to prevent crashes....
                                Local $text = _GUICtrlHeader_GetItemText($tCustDraw.hWndFrom, $tCustDraw.dwItemSpec)
                                GUIRegisterMsgEx_Bypass($tCustDraw.hWndFrom, False, $pData)     ;~~ same as doing _WinAPI_SetWindowSubclass()

                                _WinAPI_DrawText($tCustDraw.hDC, $text, $tRect, BitOR($DT_LEFT, $DT_NOCLIP))
                                Return $CDRF_SKIPDEFAULT
                        EndSwitch
                    EndIf
            EndSwitch

        Case $WM_MOUSEMOVE
            If $pData = $hWnd Then
                $bHover = True
                $tPoint = _WinAPI_GetMousePos(True, $hWnd)
                _WinAPI_InvalidateRect($hWnd, 0, False)
                _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
            EndIf

        Case $WM_MOUSELEAVE
            If $pData = $hWnd Then
                $bHover = False
                _WinAPI_InvalidateRect($hWnd, 0, False)
            EndIf
    EndSwitch
    Return 0xCAFEBABE
EndFunc

 

GUIRegisterMsgEx.au3

Edited by jugador
Posted

This is really neat. I do feel like if you are extending something, you should give a few points about what has been improved over the previous UDF.

I like the technique that you use with $__g_bThreadLocked to call DefSubclassProc if the procedure is still within the user function/proc. This is clearly intended to prevent subclass procedure lockups/crashes. Very smart. :)

The GUIRegisterMsgEx_Bypass() is nice too. I assume this was related to the subclass crashes that we talked about before with the Header. I'm glad that you found a way to avoid those subclass lockups.

Posted (edited)

@WildByDesign I used $__g_bThreadLocked as an extra precautionary measure. The code runs fine without it and shows no abnormalities. 

Need to remove $__g_bThreadLocked or else _DateProc will not work.
also changed "GUI_RUNDEFMSG" to 0xCAFEBABE in this version.

Example C:

;~ https://www.autoitscript.com/forum/topic/213268-samplecontrolsau3-in-dark-mode/

#include <WindowsConstants.au3>
#include <GuiConstants.au3>
#include <GuiDateTimePicker.au3>
#include <GuiMonthCal.au3>
#include <WinAPI.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include "GUIRegisterMsgEx.au3"

Opt("MustDeclareVars", True)
Global Const $PRF_CLIENT = 0x0004, $COLOR_BORDER = 0x404040, $COLOR_BG_DARK = 0x202020, $COLOR_TEXT_LIGHT = 0xFFFFFF, $COLOR_BORDER_LIGHT = 0xD8D8D8
Global $g_bHover = False

__ExampleC()
Func __ExampleC()
    Local $hGUI = GUICreate("Sample GUI", 400, 240)

    Local $idDate = GUICtrlCreateDate("", 15, 15, 200, 24)
    Local $hDate = GUICtrlGetHandle($idDate)

    GUIRegisterMsgEx($hGUI, $WM_NOTIFY, "_WM_NOTIFY")
    GUIRegisterMsgEx($hDate, $WM_PAINT, "_DateProc")
    GUIRegisterMsgEx($hDate, $WM_ERASEBKGND, "_DateProc")
    GUIRegisterMsgEx($hDate, $WM_SETFOCUS, "_DateProc")
    GUIRegisterMsgEx($hDate, $WM_KILLFOCUS, "_DateProc")
    GUIRegisterMsgEx($hDate, $WM_MOUSEMOVE, "_DateProc")
    GUIRegisterMsgEx($hDate, $WM_MOUSELEAVE, "_DateProc")
    GUISetState()

    While True
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd
    GUIDelete()
EndFunc   ;==>Example

Func _DateProc($hWnd, $iMsg, $wParam, $lParam, $iid, $Pdata)
    Switch $iMsg
        Case $WM_PAINT
            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            Local $tClient = _WinAPI_GetClientRect($hWnd)
            Local $iW = $tClient.Right
            Local $iH = $tClient.Bottom

            ; --- Memory DC for flicker-free rendering ---
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            ; 1. Let Windows draw the light-mode control into memory DC
           _WinAPI_DefSubclassProc($hWnd, $WM_PRINTCLIENT, $hMemDC, $PRF_CLIENT)

            ; 2. Invert all pixels (background becomes black, text white, selection orange)
            Local $tRect = DllStructCreate($tagRECT)
            $tRect.right = $iW
            $tRect.bottom = $iH
            _WinAPI_InvertRect($hMemDC, $tRect)

            ; --- 3. PIXEL HACK: destroy orange highlight & set background color ---
            Local $iSize = $iW * $iH
            Local $tPixels = DllStructCreate("dword c[" & $iSize & "]")
            ; Load pixel array directly from bitmap memory
            Local $iBytes = DllCall("gdi32.dll", "long", "GetBitmapBits", "handle", $hBitmap, "long", $iSize * 4, "ptr", DllStructGetPtr($tPixels))[0]

            If $iBytes = $iSize * 4 Then
                Local $iPixel, $r, $g, $b, $iGray
                For $i = 1 To $iSize
                    $iPixel = $tPixels.c(($i))

                    ; Split into color channels
                    $b = BitAND($iPixel, 0xFF)
                    $g = BitAND(BitShift($iPixel, 8), 0xFF)
                    $r = BitAND(BitShift($iPixel, 16), 0xFF)

                    ; Convert to grayscale (orange becomes mid-gray)
                    $iGray = Int(($r + $g + $b) / 3)

                    ; Very dark pixel = inverted white background
                    If $iGray < 15 Then
                        $iPixel = $COLOR_BG_DARK ; Replace with exact GUI background color
                    Else
                        ; Grayscale value for text (white) and selection (gray)
                        $iPixel = BitOR(BitShift($iGray, -16), BitShift($iGray, -8), $iGray)
                    EndIf

                    $tPixels.c(($i)) = $iPixel
                Next
                ; Write cleaned pixels back into the bitmap
                DllCall("gdi32.dll", "long", "SetBitmapBits", "handle", $hBitmap, "long", $iSize * 4, "ptr", DllStructGetPtr($tPixels))
            EndIf
            ; --- END PIXEL HACK ---

            ; --- Border color (hover effect) ---
            Local $iBorderColor = $COLOR_BORDER
            If _WinAPI_GetFocus() = $hWnd Then $iBorderColor = $COLOR_BORDER
            Local $tCursorPos = DllStructCreate($tagPOINT)
            DllCall("user32.dll", "bool", "GetCursorPos", "struct*", $tCursorPos)
            DllCall("user32.dll", "bool", "ScreenToClient", "hwnd", $hWnd, "struct*", $tCursorPos)
            If $tCursorPos.X >= 0 And $tCursorPos.X <= $iW And $tCursorPos.Y >= 0 And $tCursorPos.Y <= $iH Then
                $iBorderColor = $COLOR_BORDER_LIGHT
            EndIf

            ; --- Draw border ---
            Local $hPen = _WinAPI_CreatePen(0, 1, _ColorToCOLORREF($iBorderColor))
            Local $hNullBr = _WinAPI_GetStockObject(5)
            Local $hOldPen = _WinAPI_SelectObject($hMemDC, $hPen)
            Local $hOldBr = _WinAPI_SelectObject($hMemDC, $hNullBr)
            DllCall("gdi32.dll", "bool", "Rectangle", "handle", $hMemDC, "int", 0, "int", 0, "int", $iW, "int", $iH)
            _WinAPI_SelectObject($hMemDC, $hOldPen)
            _WinAPI_SelectObject($hMemDC, $hOldBr)
            _WinAPI_DeleteObject($hPen)

            ; --- Copy finished result to screen in one step (no flicker) ---
            _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY)

            ; --- Cleanup ---
            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)
            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

        Case $WM_ERASEBKGND
            Return 1

        Case $WM_SETFOCUS, $WM_KILLFOCUS
            _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return 0

        Case $WM_MOUSEMOVE
            _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
            If Not $g_bHover Then
                $g_bHover = True
                _WinAPI_InvalidateRect($hWnd, 0, False)
            EndIf
            Return 0

        Case $WM_MOUSELEAVE
            $g_bHover = False
            _WinAPI_InvalidateRect($hWnd, 0, False)
    EndSwitch
    Return 0xCAFEBABE
EndFunc   ;==>_DateProc

Func _WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam, $iid, $Pdata)
    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd($tNMHDR.hWndFrom)
    Local $iCode = $tNMHDR.Code
    If _WinAPI_GetClassName($hWndFrom) = "SysDateTimePick32" Then
        Switch $iCode
            Case $NM_SETFOCUS
                _WinAPI_SetThemeAppProperties(0)

            Case $DTN_DROPDOWN
                _WinAPI_SetWindowTheme($hWndFrom, "", "")
                _SendMessage($hWndFrom, $DTM_SETMCCOLOR, 4, $COLOR_BG_DARK)

                Local $iCtrl = _GUICtrlDTP_GetMonthCal($hWndFrom)
                _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TEXT,         $COLOR_TEXT_LIGHT)
                _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TITLEBK,      $COLOR_BG_DARK)
                _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TITLETEXT,    $COLOR_TEXT_LIGHT)
                _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_MONTHBK,      $COLOR_BG_DARK)
                _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TRAILINGTEXT, $COLOR_TEXT_LIGHT)
        EndSwitch
    EndIf
    Return 0xCAFEBABE
EndFunc   ;==>_WM_NOTIFY

Func _ColorToCOLORREF($iColor) ;RGB to BGR
    Local $iR = BitAND(BitShift($iColor, 16), 0xFF)
    Local $iG = BitAND(BitShift($iColor, 8), 0xFF)
    Local $iB = BitAND($iColor, 0xFF)
    Return BitOR(BitShift($iB, -16), BitShift($iG, -8), $iR)
EndFunc   ;==>_ColorToCOLORREF

Example D:

;~ https://www.autoitscript.com/forum/topic/212545-resizable-dragable-windows-controls/page/2/#findComment-1539579

#include <guiConstants.au3>
#include <winapi.au3>
#include <MenuConstants.au3>
#include "GUIRegisterMsgEx.au3"

Opt("MustDeclareVars", True)

Global $bRestore

__ExampleD()
Func __ExampleD()
  Local $hGUI = GUICreate("WM_NCHITTEST", 300, 200, -1, -1, $WS_OVERLAPPEDWINDOW)
  Local $idBtn = GUICtrlCreateButton("Button 1", 4, 4, 80, 80, $WS_CLIPSIBLINGS)
  Local $idBtn2 = GUICtrlCreateButton("Button 2", 90, 4, 80, 80, $WS_CLIPSIBLINGS)
  Local $idLockButtons = GUICtrlCreateCheckbox("Lock Buttons", 200, 20, 80, 20)

  GUIRegisterMsgEx($hGUI, $WM_SYSCOMMAND, "WM_SYSCOMMAND")

  GUIRegisterMsgEx($idBtn, $WM_NCHITTEST, "ButtonMoveProc")
  GUIRegisterMsgEx($idBtn, $WM_SETCURSOR, "ButtonMoveProc")
  GUIRegisterMsgEx($idBtn, $WM_WINDOWPOSCHANGING, "ButtonMoveProc", $hGUI)

  GUIRegisterMsgEx($idBtn2, $WM_NCHITTEST, "ButtonMoveProc")
  GUIRegisterMsgEx($idBtn2, $WM_SETCURSOR, "ButtonMoveProc")
  GUIRegisterMsgEx($idBtn2, $WM_WINDOWPOSCHANGING, "ButtonMoveProc", $hGUI)
  GUISetState()

  Local $iMsg
  While True
    $iMsg = GUIGetMsg()
    Switch $iMsg
      Case $GUI_EVENT_CLOSE
        ExitLoop
      Case $idLockButtons
        If GUICtrlRead($iMsg) = $GUI_CHECKED Then
          ; True to disable
          GUIRegisterMsgEx_Bypass($idBtn, True)
          GUIRegisterMsgEx_Bypass($idBtn2, True)
        Else
          ; False to re-enable
          GUIRegisterMsgEx_Bypass($idBtn, False)
          GUIRegisterMsgEx_Bypass($idBtn2, False)
        EndIf
      Case $idBtn, $idBtn2
        ConsoleWrite("Click " & GUICtrlRead($iMsg) & @CRLF)
      Case $GUI_EVENT_RESTORE
        $bRestore = False
    EndSwitch
  WEnd

  GUIDelete()
EndFunc   ;==>Example

Func ButtonMoveProc($hWnd, $iMsg, $wParam, $lParam, $iid, $Pdata)
  Local $tPos, $aPos, $iRet, $aPoint[2], $iSrc, $iEvent, $hCursor

  Switch $iMsg
    Case $WM_NCHITTEST
      $aPos = WinGetPos($hWnd)
      $aPoint[0] = BitAND($lParam, 0xFFFF)
      $aPoint[1] = BitShift($lParam, 16)

      $iRet = $HTCAPTION
      If $aPoint[0] - $aPos[0] < 10 Then $iRet = $HTLEFT
      If $aPoint[0] - $aPos[0] > ($aPos[2] - 10) Then $iRet = $HTRIGHT

      If $aPoint[1] - $aPos[1] < 10 Then
        Switch $iRet
          Case $HTLEFT
            $iRet = $HTTOPLEFT
          Case $HTRIGHT
            $iRet = $HTTOPRIGHT
          Case Else
            $iRet = $HTTOP
        EndSwitch
      ElseIf $aPoint[1] - $aPos[1] > ($aPos[3] - 10) Then
        Switch $iRet
          Case $HTLEFT
            $iRet = $HTBOTTOMLEFT
          Case $HTRIGHT
            $iRet = $HTBOTTOMRIGHT
          Case Else
            $iRet = $HTBOTTOM
        EndSwitch
      EndIf
      Return $iRet

    Case $WM_SETCURSOR
      $iSrc = BitAND($lParam, 0xFFFF)
      $iEvent = BitShift($lParam, 16)
      If $iSrc = $HTCAPTION And $iEvent = $WM_LBUTTONDOWN Then
        _WinAPI_RedrawWindow($hWnd, 0, 0, $RDW_INVALIDATE + $RDW_FRAME)
        $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZEALL)
        _WinAPI_SetCursor($hCursor)
        Return True
      EndIf

    Case $WM_WINDOWPOSCHANGING
      If $bRestore Then
        ConsoleWrite("WM_WINDOWPOSCHANGING" & @CRLF)
        $tPos = DllStructCreate($tagWINDOWPOS, $lParam)
        $aPos = ControlGetPos($Pdata, "", $hWnd)
        ConsoleWrite($aPos[0] & "/" & $aPos[1] & "/" & $aPos[2] & "/" & $aPos[3] & @CRLF)
        $tPos.X = $aPos[0]
        $tPos.Y = $aPos[1]
        $tPos.CX = $aPos[2]
        $tPos.CY = $aPos[3]
        Return 0
      EndIf
  EndSwitch
  Return 0xCAFEBABE
EndFunc   ;==>ButtonMoveProc

Func WM_SYSCOMMAND($hWnd, $iMsg, $wParam, $lParam, $iid, $Pdata)
  If $wParam = $SC_RESTORE Then
    ConsoleWrite("---Start Restore---" & @CRLF)
    $bRestore = True
  EndIf
  Return 0xCAFEBABE
EndFunc   ;==>WM_SYSCOMMAND

 

Edited by jugador
Posted (edited)
1 hour ago, jugador said:

I used $__g_bThreadLocked as an extra precautionary measure. The code runs fine without it and shows no abnormalities

I was shocked by how often it would hit $__g_bThreadLocked when I tried it with GUIDarkTheme UDF. It hit it often enough to cause flickering of controls. Sometimes they would be left with light mode color. Especially the WM_CTLCOLOR* messages.

In particular, it would hit it constantly during GUI resizing. AutoIt does not handle GUI resizing very well in general even without subclassing.

Edited by WildByDesign

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...