Popular Post LarsJ Posted September 30, 2018 Popular Post Posted September 30, 2018 (edited) In GUIRegisterMsg20 - Subclassing Made Easy we saw that it's possible to execute too much code in the WM_NOTIFY message handler in ListView1.au3: expandcollapse popupFunc WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData ) ; $iSubclassId = 0, $pData = 0 If $iMsg <> $WM_NOTIFY Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] Local Static $tText = DllStructCreate( "wchar[100]" ), $pText = DllStructGetPtr( $tText ) Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ) Switch DllStructGetData( DllStructCreate( $tagNMHDR, $lParam ), "Code" ) Case $LVN_GETDISPINFOW ; Fill virtual listview Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $lParam ) If Not BitAND( DllStructGetData( $tNMLVDISPINFO, "Mask" ), $LVIF_TEXT ) Then Return Local $sItem = $aItems[DllStructGetData($tNMLVDISPINFO,"Item")][DllStructGetData($tNMLVDISPINFO,"SubItem")] DllStructSetData( $tText, 1, $sItem ) DllStructSetData( $tNMLVDISPINFO, "TextMax", StringLen( $sItem ) ) DllStructSetData( $tNMLVDISPINFO, "Text", $pText ) Return Case $NM_CUSTOMDRAW ; Draw back colors Local $tNMLVCUSTOMDRAW = DllStructCreate( $tagNMLVCUSTOMDRAW, $lParam ) Local $dwDrawStage = DllStructGetData( $tNMLVCUSTOMDRAW, "dwDrawStage" ), $iItem Switch $dwDrawStage ; Holds a value that specifies the drawing stage Case $CDDS_PREPAINT ; Before the paint cycle begins Return $CDRF_NOTIFYITEMDRAW ; Notify the parent window of any item-related drawing operations Case $CDDS_ITEMPREPAINT ; Before painting an item Return $CDRF_NOTIFYSUBITEMDRAW ; Notify the parent window of any subitem-related drawing operations Case $CDDS_ITEMPREPAINT + $CDDS_SUBITEM ; Before painting a subitem $iItem = DllStructGetData( $tNMLVCUSTOMDRAW, "dwItemSpec" ) If GUICtrlSendMsg( $idListView, $LVM_GETITEMSTATE, $iItem, $LVIS_SELECTED ) Then Return $CDRF_NOTIFYPOSTPAINT ; Selected item DllStructSetData( $tNMLVCUSTOMDRAW, "ClrTextBk", $aColors[$iItem][DllStructGetData($tNMLVCUSTOMDRAW,"iSubItem")] ) ; Normal item Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors Case $CDDS_ITEMPOSTPAINT + $CDDS_SUBITEM ; After painting a subitem Local $hDC = DllStructGetData( $tNMLVCUSTOMDRAW, "hdc" ) ; Subitem rectangle $iItem = DllStructGetData( $tNMLVCUSTOMDRAW, "dwItemSpec" ) Local $iSubItem = DllStructGetData( $tNMLVCUSTOMDRAW, "iSubItem" ) DllStructSetData( $tRect, "Left", $LVIR_LABEL ) DllStructSetData( $tRect, "Top", $iSubItem ) GUICtrlSendMsg( $idListView, $LVM_GETSUBITEMRECT, $iItem, $pRect ) DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + 2 ) DllStructSetData( $tRect, "Top", DllStructGetData( $tRect, "Top" ) + 1 ) DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Right" ) - 2 ) DllStructSetData( $tRect, "Bottom", DllStructGetData( $tRect, "Bottom" ) - 1 ) ; Subitem back color Local $hBrush = DllCall( "gdi32.dll", "handle", "CreateSolidBrush", "int", $aColors[$iItem][$iSubItem] )[0] ; _WinAPI_CreateSolidBrush DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrush ) ; _WinAPI_FillRect DllCall( "gdi32.dll", "bool", "DeleteObject", "handle", $hBrush ) ; _WinAPI_DeleteObject ; Draw subitem text DllStructSetData( $tRect, "Top", DllStructGetData( $tRect, "Top" ) + 1 ) DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0 ) ; _WinAPI_SetTextColor DllCall( "gdi32.dll", "int", "SetBkMode", "handle", $hDC, "int", $TRANSPARENT ) ; _WinAPI_SetBkMode DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $aItems[$iItem][$iSubItem], "int", -1, "struct*", $tRect, "uint", $DT_CENTER ) ; _WinAPI_DrawText Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors EndSwitch EndSwitch Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] #forceref $hWnd, $iMsg, $wParam, $iSubclassId, $pData EndFunc The code here is a full subclass implementation (not based on the UDF). You can provoke the error this way: Click a row in the listview (not one of the very top rows). Press Shift+Down arrow to select multiple rows. The problem will arise after just a few rows. Press Ctrl+Break in SciTE to stop the code. One way to make the code faster is to use compiled code. Let's take a closer look at the subclass implementation: Local $pWM_NOTIFY = DllCallbackGetPtr( DllCallbackRegister( "WM_NOTIFY", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) ) DllCall( "comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $hGui, "ptr", $pWM_NOTIFY, "uint_ptr", 0, "dword_ptr", 0 ) ; $iSubclassId = 0, $pData = 0 $pWM_NOTIFY is a pointer to the message handler function. To replace the AutoIt message handler with compiled code, we can implement the message handler in C/C++ code in a dll-file and replace $pWM_NOTIFY with a pointer to the compiled code. The C/C++ function definition looks like this: LRESULT CALLBACK __stdcall MsgHandler( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) We can get a pointer to the message handler this way: Local $hModule = _WinAPI_LoadLibrary( "Subclass.dll" ) Local $pMsgHandler = _WinAPI_GetProcAddress( $hModule, "MsgHandler" ) Now we can simply replace $pWM_NOTIFY with $pMsgHandler in SetWindowSubclass function. And of course we need to pass the $aItems and $aColors arrays to the compiled code in the dll-file. But we already know how to do that. Note that because $pMsgHandler is used directly in SetWindowSubclass function, we can avoid both the DllCall and DllCallAddress commands. Thus, we avoid a severe overhead in relation to these commands (that the AutoIt code interpreter should interpret a code line for each message (for a huge number of messages), and that DllCall and DllCallAddress are complicated and time-consuming commands). AutoIt code in ListView2.au3: expandcollapse popup#include <GUIConstantsEx.au3> #include "GuiListViewEx.au3" #include "AccArrays.au3" Opt( "MustDeclareVars", 1 ) Global $apsa[2] ; $psaItems, $psaColors Example() Func Example() ; Rows and columns Local $iRows = 10000, $iCols = 8 ; Create GUI Local $hGui = GUICreate( "Virtual and Custom Drawn ListView", 820, 620 ) ; Create ListView Local $idListView = GUICtrlCreateListView( "", 10, 10, 800, 600, $GUI_SS_DEFAULT_LISTVIEW+$LVS_OWNERDATA-$LVS_SINGLESEL ) _GUICtrlListView_SetExtendedListViewStyle( $idListView, $LVS_EX_DOUBLEBUFFER+$LVS_EX_FULLROWSELECT ) Local $hListView = GUICtrlGetHandle( $idListView ) ; Add columns For $i = 0 To $iCols - 1 _GUICtrlListView_AddColumn( $idListView, "Col " & $i, 96, 2 ) Next ; ListView items Local $aItems[$iRows][$iCols] For $i = 0 To $iRows - 1 For $j = 0 To $iCols - 1 $aItems[$i][$j] = $i & "/" & $j Next Next ; ListView colors Local $aColors[$iRows][$iCols] Local $aLVColors = [ 0xCCCCFF, 0xCCFFFF, 0xCCFFCC, 0xFFFFCC, 0xFFCCCC, 0xFFCCFF ] ; BGR For $i = 0 To $iRows - 1 For $j = 0 To $iCols - 1 $aColors[$i][$j] = $aLVColors[Random( 0,5,1 )] Next Next ; Load dll-file Local $hDll = DllOpen( @AutoItX64 ? "Subclass_x64.dll" : "Subclass.dll" ) Local $hModule = _WinAPI_LoadLibrary( @AutoItX64 ? "Subclass_x64.dll" : "Subclass.dll" ) ; $aItems and $aColors Local $psaItemsData, $psaColorsData AccArrays02( SafeArrays, $aItems, $aColors ) SafeArrayAccessData( $apsa[0], $psaItemsData ) SafeArrayAccessData( $apsa[1], $psaColorsData ) ; Pass data to compiled code DllCall( $hDll, "none", "PassData", "hwnd", $hListView, "int", $iCols, "ptr", $psaItemsData, "ptr", $psaColorsData ) ; Register WM_NOTIFY message handler Local $pMsgHandler = _WinAPI_GetProcAddress( $hModule, "MsgHandler" ) DllCall( "comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $hGui, "ptr", $pMsgHandler, "uint_ptr", 0, "dword_ptr", 0 ) ; $iSubclassId = 0, $pData = 0 ; Set number of rows in virtual ListView GUICtrlSendMsg( $idListView, $LVM_SETITEMCOUNT, $iRows, 0 ) ; Adjust height of GUI and ListView to fit 30 rows Local $iLvHeight = _GUICtrlListView_GetHeightToFitRows( $idListView, 30 ) WinMove( $hGui, "", Default, Default, Default, WinGetPos( $hGui )[3] - WinGetClientSize( $hGui )[1] + $iLvHeight + 20 ) WinMove( GUICtrlGetHandle( $idListView ), "", Default, Default, Default, $iLvHeight ) ; Show GUI GUISetState( @SW_SHOW ) ; Message loop While 1 Switch GUIGetMsg() Case $GUI_EVENT_RESTORE ; Adjust height of GUI and ListView to fit 30 rows $iLvHeight = _GUICtrlListView_GetHeightToFitRows( $idListView, 30 ) WinMove( $hGui, "", Default, Default, Default, WinGetPos( $hGui )[3] - WinGetClientSize( $hGui )[1] + $iLvHeight + 20 ) WinMove( GUICtrlGetHandle( $idListView ), "", Default, Default, Default, $iLvHeight ) Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd ; Cleanup DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $hGui, "ptr", $pMsgHandler, "uint_ptr", 0 ) ; $iSubclassId = 0 SafeArrayUnaccessData( $apsa[0] ) SafeArrayUnaccessData( $apsa[1] ) _WinAPI_FreeLibrary( $hModule ) DllClose( $hDLL ) GUIDelete() EndFunc Func SafeArrays( $pvItems, $pvColors ) SafeArrayCopy( DllStructGetData( DllStructCreate( "ptr", $pvItems + 8 ), 1 ), $apsa[0] ) SafeArrayCopy( DllStructGetData( DllStructCreate( "ptr", $pvColors + 8 ), 1 ), $apsa[1] ) EndFunc C/C++ code in Subclass.cpp: expandcollapse popup#include <Windows.h> #include <CommCtrl.h> #include <OleAuto.h> // Globals HWND g_hListView; int g_iColumns; VARIANT* g_psaItems; VARIANT* g_psaColors; void __stdcall PassData( HWND hListView, int iColumns, VARIANT* psaItems, VARIANT* psaColors ) { g_hListView = hListView; g_iColumns = iColumns; g_psaItems = psaItems; g_psaColors = psaColors; } LRESULT CALLBACK __stdcall MsgHandler( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { if ( uMsg != WM_NOTIFY ) return DefSubclassProc( hWnd, uMsg, wParam, lParam ); NMLVDISPINFOW* plvdi; NMLVCUSTOMDRAW* plvcd; BSTR pBstr; DWORD_PTR iItem; RECT r; HBRUSH hBrush; switch ( ((LPNMHDR)lParam)->code ) { case LVN_GETDISPINFOW: // Fill virtual listview plvdi = (NMLVDISPINFOW*)lParam; if ( ( plvdi->item.mask & LVIF_TEXT ) == 0 ) return TRUE; pBstr = g_psaItems[plvdi->item.iItem*g_iColumns+plvdi->item.iSubItem].bstrVal; plvdi->item.pszText = pBstr; plvdi->item.cchTextMax = SysStringLen( pBstr ); return TRUE; case NM_CUSTOMDRAW: // Draw back colors plvcd = (NMLVCUSTOMDRAW*)lParam; switch ( plvcd->nmcd.dwDrawStage ) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: return CDRF_NOTIFYSUBITEMDRAW; case CDDS_ITEMPREPAINT | CDDS_SUBITEM: iItem = plvcd->nmcd.dwItemSpec; if ( SendMessageW( g_hListView, LVM_GETITEMSTATE, iItem, LVIS_SELECTED ) ) return CDRF_NOTIFYPOSTPAINT; plvcd->clrTextBk = g_psaColors[iItem*g_iColumns+plvcd->iSubItem].intVal; return CDRF_NEWFONT; case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: // Subitem rectangle iItem = plvcd->nmcd.dwItemSpec; r.left = LVIR_LABEL; r.top = plvcd->iSubItem; SendMessageW( g_hListView, LVM_GETSUBITEMRECT, iItem, (LPARAM)&r ); r.left += 2; r.right -= 2; r.top += 1; r.bottom -= 1; // Subitem back color hBrush = CreateSolidBrush( g_psaColors[iItem*g_iColumns+plvcd->iSubItem].intVal ); FillRect( plvcd->nmcd.hdc, &r, hBrush ); DeleteObject( hBrush ); // Draw subitem text SetTextColor( plvcd->nmcd.hdc, 0 ); SetBkMode( plvcd->nmcd.hdc, TRANSPARENT ); pBstr = g_psaItems[iItem*g_iColumns+plvcd->iSubItem].bstrVal; r.top += 1; DrawTextW( plvcd->nmcd.hdc, pBstr, SysStringLen( pBstr ), &r, DT_CENTER ); return CDRF_NEWFONT; } } return DefSubclassProc( hWnd, uMsg, wParam, lParam ); } Zip-file ListView1.au3 is pure AutoIt code. In ListView2.au3 the WM_NOTIFY message handler is replaced with compiled code. Note that the zip-file contains two small dll-files. If you are using Microsoft SmartScreen or Microsoft Windows Defender, there is a likelihood of fake virus detection, even if the dll-files are created with Microsoft Visual Studio. Most other antivirus products will not detect fake viruses. You need AutoIt 3.3.12 or later. Tested on Windows 7 and Windows 10. Comments are welcome. Let me know if there are any issues. FastSubclassing.7z Edited September 30, 2018 by LarsJ KaFu, Danyfirex, FrancescoDiMuro and 3 others 3 3 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now