Opened on Apr 26, 2026 at 11:42:12 AM
Last modified on Apr 27, 2026 at 11:09:47 AM
#4091 new Bug
Header painting dysfunctional when subclassed in ListView — at Version 4
| Reported by: | Nine | Owned by: | |
|---|---|---|---|
| Milestone: | Component: | AutoIt | |
| Version: | 3.3.18.0 | Severity: | None |
| Keywords: | Cc: |
Description (last modified by )
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
Change History (4)
comment:2 by , 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
comment:3 by , on Apr 26, 2026 at 7:41:45 PM
@mattyd - I know about this workaround. In the other track I made, I was also using the listview proc instead of the GUI. But I still think there is a bug here that we should address.
comment:4 by , on Apr 27, 2026 at 11:09:47 AM
| Description: | modified (diff) |
|---|

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