Modify ↓
Opened on Apr 26, 2026 at 11:42:12 AM
Last modified on Apr 26, 2026 at 4:51:13 PM
#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: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
Note:
See TracTickets
for help on using tickets.

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