Modify

#4091 new Bug

Header painting dysfunctional when subclassed in ListView

Reported by: Nine Owned by:
Milestone: Component: AutoIt
Version: 3.3.18.0 Severity: None
Keywords: Cc:

Description

The following line is not executed properly when run in x64 while it works perfectly in x86.

If $tItem.Code <> $NM_CUSTOMDRAW Then Return $GUI_RUNDEFMSG

Here the full example. Toggle the useX64 to see difference.
This code was prepared by mlipok.

#AutoIt3Wrapper_UseX64=Y

; From Nine
#include <WindowsConstants.au3>
#include <ColorConstants.au3>
#include <GUIConstants.au3>
#include <GuiListView.au3>
#include <WinAPI.au3>

Opt("MustDeclareVars", True)

Example()

Func Example()
    GUICreate("Example")
    Local $idListview = GUICtrlCreateListView("", 10, 10, 350, 200, -1, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_DOUBLEBUFFER))
    Local $idButton = GUICtrlCreateButton("Test", 10, 220, 70, 20)
    GUICtrlSendMsg($idListview, $LVM_GETHEADER, 0, 0)

    GUISetState(@SW_SHOW)
    GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)

    _GUICtrlListView_AddColumn($idListview, "Column 1", 100)
    _GUICtrlListView_AddItem($idListview, "Row 1: Col 1", 0)

    While True
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idButton
                ConsoleWrite("Test button clicked" & @CRLF)
        EndSwitch
    WEnd
EndFunc   ;==>Example

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam
    Local Static $iCounter = 0

    $iCounter += 1
    Local $tItem = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
    ConsoleWrite("+ COUNTER = " & $iCounter & " >>> " & _WinAPI_GetClassName($tItem.hWndFrom) & @CRLF)

    ; I thought that for testing AutoIt x64 we do not need anything more but it is not true
    ; when the next line is commented, the COUNTER ends on 5 in x32  ..... but ends on 4 in x64
;~  Return $GUI_RUNDEFMSG

    #QUESTION = why COUNTER ends with 4 in x64 instead of doing the 5 processing as in x32 ?
    #NOTE =  I see that in x32 there is additionall step in the middle of processing ; + COUNTER = 3 >>> SysHeader32

    If _WinAPI_GetClassName($tItem.hWndFrom) <> "SysHeader32" Then Return $GUI_RUNDEFMSG ; we are focusing only on "SysHeader32"

    If $tItem.Code <> $NM_CUSTOMDRAW Then Return $GUI_RUNDEFMSG

    Local $tRect = DllStructCreate($tagRECT, DllStructGetPtr($tItem, "left"))
    Local $hBrush, $hPen

    If _WinAPI_GetClassName($tItem.hWndFrom) = "SysHeader32" Then
    ConsoleWrite("! 1st CheckPoint = " & @ScriptLineNumber & @CRLF)
        If $tItem.dwDrawStage = $CDDS_PREPAINT Then Return $CDRF_NOTIFYITEMDRAW
    ConsoleWrite("! 2nd CheckPoint = " & @ScriptLineNumber & @CRLF)

        If Not $tItem.dwItemSpec Then $tRect.left += 5
        $tRect.bottom -= 1
        $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0xA0A0A0)
        _WinAPI_SelectObject($tItem.hDC, $hPen)
        $hBrush = _WinAPI_CreateSolidBrush(0xFFCCDD)
        _WinAPI_SelectObject($tItem.hDC, $hBrush)
        _WinAPI_Rectangle($tItem.hDC, $tRect)
        _WinAPI_DeleteObject($hPen)
        _WinAPI_DeleteObject($hBrush)

        $tRect.Left += 5
        $tRect.Top += 3
        _WinAPI_SetTextColor($tItem.hDC, _WinAPI_SwitchColor($COLOR_BLACK))
        _WinAPI_SetBkMode($tItem.hDC, $TRANSPARENT)
        _WinAPI_DrawText($tItem.hDC, _GUICtrlHeader_GetItemText($tItem.hWndFrom, $tItem.dwItemSpec), $tRect, $DT_LEFT)

        Return $CDRF_SKIPDEFAULT
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

Attachments (0)

Change History (2)

comment:1 by mLipok, on Apr 26, 2026 at 12:08:17 PM

My note
I think that WM_NOTIFY() function registered by GUIRegisterMsg() in x64 is not allways called.

comment:2 by MattyD, on Apr 26, 2026 at 4:51:13 PM

Guessing this is probably a windows thing as returning _WinAPI_DefWindowProcW instead of $GUI_RUNDEFMSG doesn't change the behavior.

The below works in both x64 & x86 when we handle the listview's WM_NOTIFY messages rather than the GUI's.

#AutoIt3Wrapper_UseX64=Y

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

Global Const $tagNMCUSTOMDRAWINFO = "struct;" & $tagNMHDR & ";dword DrawStage;handle hdc;" & _
                $tagRECT & ";dword_ptr ItemSpec;uint ItemState;lparam lItemParam;endstruct"

Global $hSubclassProc, $pSubclassProc

Example()

Func Example()
        GUICreate("Example")

        Local $idListview = GUICtrlCreateListView("", 10, 10, 350, 200, -1, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_DOUBLEBUFFER))
        $hSubclassProc = DllCallbackRegister("_SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
        $pSubclassProc = DllCallbackGetPtr($hSubclassProc)
        _WinAPI_SetWindowSubclass(GUICtrlGetHandle($idListview), $pSubclassProc, 0)

        GUISetState()

        _GUICtrlListView_AddColumn($idListview, "Column 1", 100)
        _GUICtrlListView_AddItem($idListview, "Row 1: Col 1", 0)

        While True
                Switch GUIGetMsg()
                        Case $GUI_EVENT_CLOSE
                                ExitLoop
                EndSwitch
        WEnd


        GUIDelete()
        DllCallbackFree($hSubclassProc)
EndFunc   ;==>Example

Func _SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iSubclassID, $pData)
        #forceref $hWnd, $iMsg, $wParam, $lParam, $iSubclassID, $pData
        Local Static $hBrush, $hPen

        Switch $iMsg
                Case $WM_DESTROY
                        _WinAPI_DeleteObject($hPen)
                        _WinAPI_DeleteObject($hBrush)
                        _WinAPI_RemoveWindowSubclass($hWnd, $pSubclassProc, $iSubclassID)
                        $hPen = 0
                        $hBrush = 0

                Case $WM_NOTIFY

                        Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)

                        If _WinAPI_GetClassName($tNMHDR.hWndFrom) <> "SysHeader32" Then Return $GUI_RUNDEFMSG

                        If $tNMHDR.Code = $NM_CUSTOMDRAW Then
                                Local $tNMCD = DllStructCreate($tagNMCUSTOMDRAWINFO, $lParam)

                                Switch $tNMCD.DrawStage
                                        Case $CDDS_PREPAINT
                                                ;Returning $CDRF_NOTIFYITEMDRAW here should trigger the $CDDS_ITEMPREPAINT draw stage.
                                                ; When this code is in the GUI's WM_NOTIFY msg handler rather than the control's, 
                                                ; we don't hit the CDDS_ITEMPREPAINT stage. This is only in x64 though, the trigger 
                                                ; works fine for x86.
                                                Return $CDRF_NOTIFYITEMDRAW

                                        Case $CDDS_ITEMPREPAINT
                                                If Not $hBrush Then $hBrush = _WinAPI_CreateSolidBrush(0xFFCCDD)
                                                If Not $hPen Then $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0xA0A0A0)
                                                _WinAPI_SelectObject($tNMCD.hDC, $hPen)
                                                _WinAPI_SelectObject($tNMCD.hDC, $hBrush)

                                                Local $tRect = DllStructCreate($tagRECT, DllStructGetPtr($tNMCD, "Left"))
                                                If Not $tNMCD.dwItemSpec Then $tRect.left += 5
                                                $tRect.bottom -= 1

                                                _WinAPI_Rectangle($tNMCD.hDC, $tRect)
                                                _WinAPI_SetTextColor($tNMCD.hDC, 0)
                                                _WinAPI_SetBkMode($tNMCD.hDC, $TRANSPARENT)
                                                $tRect.Left += 5
                                                $tRect.Top += 3
                                                _WinAPI_DrawText($tNMCD.hDC, _GUICtrlHeader_GetItemText($tNMCD.hWndFrom, $tNMCD.dwItemSpec), $tRect, $DT_LEFT)

                                                Return $CDRF_SKIPDEFAULT

                                EndSwitch
                        EndIf
        EndSwitch

        Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_SubclassProc

Modify Ticket

Action
as new The ticket will remain with no owner.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.