Jump to content

NM_CUSTOMDRAW not firing when run as 64bit


Recommended Posts

Hello.

I can't seem to figure out why when #AutoIt3Wrapper_UseX64=Y is present, the $NM_CUSTOMDRAW notification is not firing:

#AutoIt3Wrapper_UseX64=Y
#include <GUIConstants.au3>
#include <FontConstants.au3>
#include <WinAPIShellEx.au3>
#include <GuiListView.au3>

Opt( "MustDeclareVars", 1 )

Global Const $tagNMCUSTOMDRAW = "struct;" & $tagNMHDR & ";dword dwDrawStage;handle hdc;" & $tagRECT & _
                                ";dword_ptr dwItemSpec;uint uItemState;lparam lItemlParam;endstruct"

Global $hHeader
Global $bgColor = 0x00FF00
Global $bgColorBrush = _WinAPI_CreateSolidBrush( $bgColor ) ; Brush with background color

Example()


Func Example()
    ; Create GUI
    GUICreate( "Styles, fonts, colors", 420, 320 )

    ; Create ListView
    Local $idListView = GUICtrlCreateListView( "", 10, 10, 400, 300, -1, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT+$LVS_EX_HEADERDRAGDROP )
    Local $hListView = GUICtrlGetHandle( $idListView )
    $hHeader = _GUICtrlListView_GetHeader( $hListView )

    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

    ; Add columns to ListView
    _GUICtrlListView_AddColumn( $hListView, "", 99 )
    _GUICtrlListView_AddColumn( $hListView, "", 99 )
    _GUICtrlListView_AddColumn( $hListView, "", 99 )
    _GUICtrlListView_AddColumn( $hListView, "", 99 )

    ; Fill ListView
    For $i = 0 To 9
        GUICtrlCreateListViewItem( $i & "/1|" & $i & "/2|" & $i & "/3|" & $i & "/4", $idListView )
    Next

    ; Show GUI
    GUISetState( @SW_SHOW )

    ; Message loop
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    _WinAPI_DeleteObject($bgColorBrush)
    GUIDelete()
EndFunc

Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam)
    Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
    Local $hWndFrom = HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
    Local $iCode = DllStructGetData( $tNMHDR, "Code" )
    Switch $hWndFrom
        Case $hHeader
            Switch $iCode
                Case $NM_CUSTOMDRAW
                    Local $tNMCustomDraw = DllStructCreate( $tagNMCUSTOMDRAW, $lParam )
                    Local $dwDrawStage = DllStructGetData( $tNMCustomDraw, "dwDrawStage" )
                    Switch $dwDrawStage                  ; Holds a value that specifies the drawing stage
                        Case $CDDS_PREPAINT                ; Before the paint cycle begins
                            Return $CDRF_NOTIFYITEMDRAW      ; Notify parent window of any item related drawing operations

                        Case $CDDS_ITEMPREPAINT            ; Before an item is drawn: Default painting (frames and background)
                            Return $CDRF_NOTIFYPOSTPAINT     ; Notify parent window of any post item related drawing operations

                        Case $CDDS_ITEMPOSTPAINT           ; After an item is drawn: Custom painting (item texts)
                            Local $iIndex = DllStructGetData( $tNMCustomDraw, "dwItemSpec" ) ; Item index
                            Local $hDC = DllStructGetData( $tNMCustomDraw, "hdc" )           ; Device context
                            _WinAPI_SetBkMode( $hDC, $TRANSPARENT )                          ; Transparent background
                            ; set background color
                            Local $tRECT = DllStructCreate( $tagRECT )
                            DllStructSetData( $tRECT, 1, DllStructGetData( $tNMCustomDraw, 6 ) + 1 )
                            DllStructSetData( $tRECT, 2, DllStructGetData( $tNMCustomDraw, 7 ) + 1 )
                            DllStructSetData( $tRECT, 3, DllStructGetData( $tNMCustomDraw, 8 ) - 2 )
                            DllStructSetData( $tRECT, 4, DllStructGetData( $tNMCustomDraw, 9 ) - 2 )
                            _WinAPI_FillRect( $hDC, $tRECT, $bgColorBrush )
                            ; First line
                            DllStructSetData( $tNMCustomDraw, "Left",   DllStructGetData( $tNMCustomDraw, "Left" )  + 2 ) ; Left margin
                            DllStructSetData( $tNMCustomDraw, "Right",  DllStructGetData( $tNMCustomDraw, "Right" ) - 2 ) ; Right margin
                            DllStructSetData( $tNMCustomDraw, "Top",    DllStructGetData( $tNMCustomDraw, "Top" )   + 2 )                     ; 2 pixel top margin
                            DllStructSetData( $tNMCustomDraw, "Bottom", DllStructGetData( $tNMCustomDraw, "Top" )   + 16 ) ; Line height
                            DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", "Col" & $iIndex, "int", StringLen( "Col" & $iIndex ), "struct*", DllStructGetPtr( $tNMCustomDraw, "Left" ), "uint", $DT_CENTER ) ; _WinAPI_DrawText
                            Return $CDRF_NEWFONT             ; $CDRF_NEWFONT must be returned after changing font or colors
                    EndSwitch
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc

If #AutoIt3Wrapper_UseX64=Y  removed, the listview header displays in green color as expected.

Any ideas?

Thank you.

Link to post
Share on other sites
  • Moderators

VAN0,

We discovered this same problem in my GUIListViewEx thread - LarsJ explains it in detail here. Not a lot you can do about it I am afraid, other then forcing to x86.

M23

Edited by Melba23
Typo

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to post
Share on other sites
On 3/10/2019 at 4:43 PM, LarsJ said:

Then I've replaced the GUIRegisterMsg technique with the Subclassing technique

Here the code works in all 4 situations: Windows 7/10, 32/64 bit.

In the GUIRegisterMsg example, only CDDS_PREPAINT notifications are displayed in SciTE console. The problem seems to be that the CDRF_NOTIFYITEMDRAW return values are not received by the operating system. Therefore, the operating system does not generate CDDS_ITEMPREPAINT or CDDS_ITEMPOSTPAINT notifications and the header items are not colored.

Because the Subclassing example works, it excludes errors in the $tagNMLVCUSTOMDRAW structure.

It seems like there is an error in the internal AutoIt code associated with the GUIRegisterMsg function when the code is run as 64 bit code. The return values from the WM_NOTIFY message handler are not properly forwarded to the operating system. The error seems also to be related to the fact that the header control is a second level child in relation to the GUI. Colors does work for a listview control which is a first level child.

Solution: Implement the WM_NOTIFY message handler in GUIListViewEx.au3 using the Subclassing technique. This is not necessarily completely trivial. In my example above, it looks easy. But this is also a very small example.

Lars.

My understanding is that GUIRegisterMsg register callback for entire gui and it's content. So when I tried register $hGUI with Subclassing, it fails the same way GUIRegisterMsg does

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=y

Opt( "MustDeclareVars", 1 )

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

Global $hHeader, $aHdrTexts[8], $hGUI

Example()

Func Example()
  ; Create GUI
  $hGUI = GUICreate( "ListView Header Colors - Subclassing", 840, 420 )

  ; Create ListView
  Local $idListView = GUICtrlCreateListView( "", 10, 10, 820, 400 )
  _GUICtrlListView_SetExtendedListViewStyle ( $idListView, $LVS_EX_FULLROWSELECT )

  ; Add columns to ListView
  For $i = 0 To 7
    _GUICtrlListView_AddColumn( $idListView, "Column " & $i, 99 )
    $aHdrTexts[$i] = "Column " & $i
  Next

  ; Fill ListView
  For $i = 0 To 99
    GUICtrlCreateListViewItem( $i & "/0|" & $i & "/1|" & $i & "/2|" & $i & "/3|" & _
                               $i & "/4|" & $i & "/5|" & $i & "/6|" & $i & "/7|", $idListView )
  Next

  ; Header control
  $hHeader = _GUICtrlListView_GetHeader( $idListView )

  ; Register ListView WM_NOTIFY message handler
  Local $pLV_WM_NOTIFY = DllCallbackGetPtr( DllCallbackRegister( "LV_WM_NOTIFY", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
  DllCall( "comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $hGUI, "ptr", $pLV_WM_NOTIFY, "uint_ptr", 0, "dword_ptr", 0 ) ; $iSubclassId = 0, $pData = 0
  ;_WinAPI_SetWindowSubclass( $hListView, $pLV_WM_NOTIFY, 0, 0 ) ; $iSubclassId = 0, $pData = 0

  ; Show GUI
  GUISetState( @SW_SHOW )

  ; Message loop
  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $hGUI, "ptr", $pLV_WM_NOTIFY, "uint_ptr", 0 ) ; $iSubclassId = 0
  ;_WinAPI_RemoveWindowSubclass( $hListView, $pLV_WM_NOTIFY, 0 ) ; $iSubclassId = 0
  GUIDelete()
EndFunc

; ListView WM_NOTIFY message handler
Func LV_WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData )
  If $iMsg <> 0x004E Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] ; 0x004E = $WM_NOTIFY

  Local Static $hHdrBrush = _WinAPI_CreateSolidBrush( 0xCCFFFF ) ; Yellow, BGR
  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam ), $hWndFrom = HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) ), $iCode = DllStructGetData( $tNMHDR, "Code" )

  Switch $hWndFrom
    Case $hHeader
      Switch $iCode
        Case $NM_CUSTOMDRAW
          Local $tNMCustomDraw = DllStructCreate( $tagNMLVCUSTOMDRAW, $lParam )
          Local $dwDrawStage = DllStructGetData( $tNMCustomDraw, "dwDrawStage" )
          Switch $dwDrawStage              ; Holds a value that specifies the drawing stage
            Case $CDDS_PREPAINT            ; Before the paint cycle begins
              ConsoleWrite( "CDDS_PREPAINT" & @CRLF )
              Return $CDRF_NOTIFYITEMDRAW  ; Notify parent window of any item related drawing operations

            Case $CDDS_ITEMPREPAINT        ; Before an item is drawn: Default painting (frames and background)
              ConsoleWrite( "CDDS_ITEMPREPAINT" & @CRLF )
              Return $CDRF_NOTIFYPOSTPAINT ; Notify parent window of any post item related drawing operations

            Case $CDDS_ITEMPOSTPAINT       ; After an item is drawn: Custom painting (item texts)
              ConsoleWrite( "CDDS_ITEMPOSTPAINT" & @CRLF )
              Local $iIndex = DllStructGetData( $tNMCustomDraw, "dwItemSpec" ) ; Item index
              Local $hDC = DllStructGetData( $tNMCustomDraw, "hdc" )           ; Device context
              _WinAPI_SetBkMode( $hDC, $TRANSPARENT )                          ; Transparent background
              Local $tRECT = DllStructCreate( $tagRECT )
              DllStructSetData( $tRECT, 1, DllStructGetData( $tNMCustomDraw, 6 ) + 1 )
              DllStructSetData( $tRECT, 2, DllStructGetData( $tNMCustomDraw, 7 ) + 1 )
              DllStructSetData( $tRECT, 3, DllStructGetData( $tNMCustomDraw, 8 ) - 2 )
              DllStructSetData( $tRECT, 4, DllStructGetData( $tNMCustomDraw, 9 ) - 2 )
              _WinAPI_FillRect( $hDC, $tRECT, $hHdrBrush )                     ; Background color
              DllStructSetData( $tNMCustomDraw, "Left",   DllStructGetData( $tNMCustomDraw, "Left" )  + 4 ) ; Left margin
              DllStructSetData( $tNMCustomDraw, "Right",  DllStructGetData( $tNMCustomDraw, "Right" ) - 4 ) ; Right margin
              DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $aHdrTexts[$iIndex], "int", StringLen( $aHdrTexts[$iIndex] ), "struct*", DllStructGetPtr( $tNMCustomDraw, "Left" ), "uint", $DT_LEFT+$DT_SINGLELINE+$DT_VCENTER ) ; _WinAPI_DrawText
              Return $CDRF_NEWFONT         ; $CDRF_NEWFONT must be returned after changing font or colors
          EndSwitch
      EndSwitch
  EndSwitch

  ; Call next function in subclass chain
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $iSubclassId, $pData
EndFunc

Does it mean if we want use WM_NOTIFY for other components, we'll have to register separate function for each?

Link to post
Share on other sites
  • Moderators

VAN0,

No, please stick to your own thread as this has nothing to do with my UDF.

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to post
Share on other sites

Thank you for moving my reply to this topic.

So it seems even Subclassing has the same exact issue when it's register to GUI window itself, rather than to a specific control. Perhaps it's not GUIRegisterMsg issue after all .

Link to post
Share on other sites

An important information here is that WM_NOTIFY messages are sent to the parent window/control. Subclass the listview, which is the parent of the header control, instead of the GUI and the code will work.

Link to post
Share on other sites

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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...