Popular Post LarsJ Posted September 30, 2018 Popular Post Share 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 Zedna, FrancescoDiMuro, KaFu 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 Link to comment Share on other sites More sharing options...
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