WildByDesign Posted April 27 Author Posted April 27 1 hour ago, jugador said: _GUICtrlHeader_GetItemText is causing problems because of SendMessage. To fix this, only read the ListView header if the handle ($hFrom) changes. This is brilliant! Thank you. It solved the crash that I did not think was possible to solve. Also, your code makes it more efficient which is great.
argumentum Posted April 27 Posted April 27 3 hours ago, WildByDesign said: It solved the crash that I did not think was possible to solve. ..the crash will occur again if you have more _GUICtrlHeader_GetItemText() calls. This code avoids calling it unnecessarily. Is a clever approach given the circumstances but it does not fix the problem. Am clarifying/describing the solution for you not to believe that the issue is now corrected. Nine and ioa747 2 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
WildByDesign Posted April 27 Author Posted April 27 37 minutes ago, argumentum said: ..the crash will occur again if you have more _GUICtrlHeader_GetItemText() calls. This code avoids calling it unnecessarily. Is a clever approach given the circumstances but it does not fix the problem You are 100% right. SendMessage is a problem on x64 even with that recent fix from Nine. You’re right because if I enabled it for a GUI with multiple ListViews, the $hFrom value would change when going back and forth from different ListViews. That would trigger more GetItemText calls and cause SendMessage to crash. So I can’t really consider adding it (as option) to GUIDarkTheme UDF yet until all issues with that SendMessage are resolved on x64. argumentum 1
Nine Posted April 27 Posted April 27 One possible approach is to register all list views into a single global map variable before you enable the subclass. That way you would only read header titles once for each list view (outside the subclass proc). Again it is only a patch until a solution is found to this bug. ps. as much as I dislike global variables, they can be handy at times... WildByDesign and argumentum 2 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
jugador Posted April 27 Posted April 27 safe method is to use global variable but if want to avoid Global then store the ListView header string in a DllStruct and pass it as $pData during subclassing. no problem if there multiple ListView WildByDesign and ioa747 2
WildByDesign Posted April 27 Author Posted April 27 2 hours ago, Nine said: One possible approach is to register all list views into a single global map variable before you enable the subclass. That way you would only read header titles once for each list view (outside the subclass proc). I think that this is the best idea, for sure. I don’t know DllStruct at all unfortunately. But I am familiar with maps and arrays. Do you know if maps would be faster in this situation?
Nine Posted April 27 Posted April 27 Faster than ? DllStruct ? Should be similar IMO. Speed is not a real issue in most ListViews anyway. And I think map would be easier to program compare to dllstruct. WildByDesign and ioa747 2 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
WildByDesign Posted April 28 Author Posted April 28 (edited) I must admit, I spent an hour last night messing with arrays and another hour this morning messing with maps. I can't seem to figure out the best way to organize this in such a way that would be efficient for the DrawText line to process. Please keep in mind that this would potentially have to store header text for multiple ListViews. So I have to create the array or map from the perspective of holding header text for multiple ListViews. 18 hours ago, jugador said: if want to avoid Global then store the ListView header string in a DllStruct and pass it as $pData during subclassing. no problem if there multiple ListView From the perspective of having multiple ListViews, this DllStruct passing through the subclass $pData sounds like it might be the best option. The only problem is that I know nothing about creating DllStruct or processing the DllStruct data on the other side. $pData would need to have the Header handle and header text string. Is it possible to have the DllStruct store both in $pData? Edited April 28 by WildByDesign
argumentum Posted April 28 Posted April 28 3 hours ago, WildByDesign said: ... The only problem is that I know nothing about creating DllStruct ... ... time to learn. Is not that difficult if you try it. Spoiler dllsdtruct in autoit. how do i make an example of adding a parameter to an existing sctuct 🤔 In AutoIt, you cannot dynamically "append" a parameter to a DllStruct once it has been created because the memory for that structure is already allocated. [1, 2, 3] To achieve this, you must define a new structure string that includes the original parameters plus your new one, and then create a new struct using DllStructCreate(). [2, 3, 4] Example: Adding a Parameter to an Existing Struct This example shows how to take the definition of an existing structure, add a new variable to it, and "re-create" it. [3] ; 1. Define and create the original struct Local $sTagOriginal = "int var1;float var2" Local $tOriginal = DllStructCreate($sTagOriginal) DllStructSetData($tOriginal, "var1", 100) DllStructSetData($tOriginal, "var2", 12.5) ; 2. Define a new tag by appending a parameter to the original string Local $sTagNew = $sTagOriginal & ";char var3[128]" ; Adding a string parameter ; 3. Create the new struct Local $tNew = DllStructCreate($sTagNew) ; 4. (Optional) Copy data from the old struct to the new one DllStructSetData($tNew, "var1", DllStructGetData($tOriginal, "var1")) DllStructSetData($tNew, "var2", DllStructGetData($tOriginal, "var2")) ; 5. Set the new parameter DllStructSetData($tNew, "var3", "Added Parameter!") ; Display results MsgBox(64, "Struct Example", _ "Var1: " & DllStructGetData($tNew, "var1") & @CRLF & _ "Var2: " & DllStructGetData($tNew, "var2") & @CRLF & _ "Var3: " & DllStructGetData($tNew, "var3")) Key Functions Used DllStructCreate: Defines the C-style structure and its data types. DllStructSetData: Assigns values to specific elements within the struct by name or index. DllStructGetData: Retrieves values stored in the struct. [2, 5, 6, 7, 8] If you are working with pointers, you can also use DllStructCreate($sTagNew, DllStructGetPtr($tOriginal)) to create a new struct definition that maps over the same memory address, though this only works safely if the new structure doesn't exceed the memory originally allocated. [3, 9, 10] If you'd like, I can help you: Convert a C++ struct into an AutoIt tag. Work with nested structs (structs inside structs). Use these structs in a DllCall. [3, 11] [1] https://www.autoitscript.com [2] https://www.autoitscript.com [3] https://www.autoitscript.com [4] https://www.autoitscript.com [5] https://www.autoitscript.com [6] https://www.autoitscript.com [7] https://www.autoitscript.com [8] https://www.autoitscript.com [9] https://www.autoitscript.com [10] https://www.autoitscript.com [11] https://www.autoitscript.com ..so, go at it WildByDesign 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
argumentum Posted April 28 Posted April 28 (edited) in your case, lets say you have $tag="struct;int;int;bool;double;char[128];endstruct", you just add your thing: $tag="struct;int;int;bool;double;char[128];char var3[128];endstruct" Because you may think that the prior post with the AI example, is how it is, but is just an example. Once you get the hang of it, is a no brainer Edited April 28 by argumentum WildByDesign 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
jugador Posted April 28 Posted April 28 relooked at the code, but I don’t understand what these line is doing: _WinAPI_SetWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000, $hHeader) Keep _GUICtrlHeader_GetItemText($tCustDraw.hWndFrom, $tCustDraw.dwItemSpec) as it is, omit $hHeader Subclass lines, and use the DefSubclassProc fix. I think this will solve the crash. regarding the $pData query: Local $tStruct1 = DllStructCreate("ptr;wchar[256]") DllStructSetData($tStruct1, 1, $hHeader) DllStructSetData($tStruct1, 2, "Items List|SubItems1|SubItems2") Local $hSubClass = DllCallbackRegister(WM_NOTIFY, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") _WinAPI_SetWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), $hSubClass, DllStructGetPtr($tStruct1)) Case $CDDS_ITEMPREPAINT Local $tStruct1 = DllStructCreate("ptr;wchar[256]", $pData) Local $sString = DllStructGetData($tStruct1, 2) Local $aHeaderList = StringRegExp($sString, "[^|]+", 3) ;.... _WinAPI_DrawText($tCustDraw.hDC, $aHeaderList[$tCustDraw.dwItemSpec], $tRect, BitOR($DT_LEFT, $DT_NOCLIP)) WildByDesign 1
WildByDesign Posted April 28 Author Posted April 28 4 minutes ago, jugador said: relooked at the code, but I don’t understand what these line is doing: _WinAPI_SetWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000, $hHeader) This line sends to Header handle to the subclass process which compares the Header handle to the following: Case $WM_MOUSEMOVE If $pData = $hWnd Then $bHover = True $tPoint = _WinAPI_GetMousePos(True, $hWnd) _WinAPI_InvalidateRect($hWnd, 0, False) _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE) EndIf Case $WM_MOUSELEAVE If $pData = $hWnd Then $bHover = False _WinAPI_InvalidateRect($hWnd, 0, False) EndIf It is the $bHover boolean there that @Nine used to get the hover color painting of the header items.
WildByDesign Posted April 28 Author Posted April 28 2 hours ago, argumentum said: in your case, lets say you have $tag="struct;int;int;bool;double;char[128];endstruct", you just add your thing: $tag="struct;int;int;bool;double;char[128];char var3[128];endstruct" Because you may think that the prior post with the AI example, is how it is, but is just an example. Once you get the hang of it, is a no brainer Thank you so much. It looks like I've got some learning to do. It looks like an incredibly powerful functionality that, if I can understand it, I can put it to good usage. I like how it seems flexible. I will spend some time tonight reading more about it. I appreciate the occasional kick in the butt to get started with something new. I quite often need it. argumentum 1
WildByDesign Posted April 28 Author Posted April 28 (edited) I made a mistake initially and therefore edited my comment. After catching my own mistake, everything seems to be working perfectly. I will put it through some more time and more testing and report back on it later. Edited April 28 by WildByDesign
WildByDesign Posted April 28 Author Posted April 28 (edited) It's been a few hours now and it refuses to crash. Here is my current testing script with the fixes from @jugador expandcollapse popup#AutoIt3Wrapper_UseX64=n ; DPI awareness DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -2) ; From Nine #include <GuiConstants.au3> #include <GuiHeader.au3> #include <WinAPI.au3> #include <Misc.au3> #include <WinAPITheme.au3> #include <GuiListView.au3> AutoItSetOption("MustDeclareVars", 1) Global $hUser32Dll = DllOpen("user32.dll") Example() Func Example() Local $hGUI = GUICreate("Header ", 500, 300) GUISetBkColor(0x000000) Local $idListView = GUICtrlCreateListView("Items List|SubItems1|SubItems2", 10, 10, 480, 280, -1, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_HEADERDRAGDROP)) Local $hListView = GUICtrlGetHandle($idListView) ;Local $hHeader = GUICtrlSendMsg($idListView, $LVM_GETHEADER, 0, 0) Local $hHeader = _GUICtrlListView_GetHeader($idListview) _WinAPI_SetWindowTheme($hListView, "DarkMode_Explorer") _WinAPI_SetWindowTheme($hHeader, "DarkMode_ItemsView", "Header") GUICtrlSetBkColor($idListView, 0x000000) GUICtrlSetColor($idListView, 0xFFFFFF) ; get header info Local $sString Local $sCount = _GUICtrlHeader_GetItemCount($hHeader) For $i = 0 To $sCount - 1 $sString &= _GUICtrlHeader_GetItemText($hHeader, $i) & '|' Next Local $tStruct1 = DllStructCreate("struct;ptr;wchar[256];endstruct") DllStructSetData($tStruct1, 1, $hHeader) DllStructSetData($tStruct1, 2, $sString) Local $hSubClass = DllCallbackRegister(WM_NOTIFY, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") _WinAPI_SetWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), $hSubClass, DllStructGetPtr($tStruct1)) _WinAPI_SetWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000, $hHeader) For $i = 1 To 10 GUICtrlCreateListViewItem("item1|item2|item3", $idListview) Next _GUICtrlListView_SetColumnWidth($idListView, 2, $LVSCW_AUTOSIZE_USEHEADER) _WinAPI_DwmSetWindowAttributeEx($hGUI, 20, 1) ; Apply Acrylic material on newer Windows 11 builds If @OSBuild >= 22621 Then _WinAPI_DwmSetWindowAttributeEx($hGUI, 38, 3) _WinAPI_DwmExtendFrameIntoClientArea($hGUI, _WinAPI_CreateMargins(-1, -1, -1, -1)) EndIf ; remove focus rectangle from listview items GUICtrlSendMsg($idListView, $WM_CHANGEUISTATE, 65537, 0) GUISetState() Do Until GUIGetMsg() = $GUI_EVENT_CLOSE _WinAPI_RemoveWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000) _WinAPI_RemoveWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), $hSubClass) DllCallbackFree($hSubClass) DllClose($hUser32Dll) EndFunc ;==>Example Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam, $iID, $pData) Local Static $bHover, $tPoint Local $hBrush Switch $iMsg Case $WM_NOTIFY Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam) Local $hFrom = $tCustDraw.hWndFrom Switch $tCustDraw.Code Case $NM_CUSTOMDRAW If _WinAPI_GetClassName($hFrom) = "sysheader32" Then Local $tStruct1 = DllStructCreate("struct;ptr;wchar[256];endstruct", $pData) Local $sString = DllStructGetData($tStruct1, 2) Local $aHeaderList = StringRegExp($sString, "[^|]+", 3) Local $hHeader = DllStructGetData($tStruct1, 1) Switch $tCustDraw.dwDrawStage Case $CDDS_PREPAINT Return $CDRF_NOTIFYITEMDRAW Case $CDDS_ITEMPREPAINT Local $tRect = DllStructCreate($tagRECT, DllStructGetPtr($tCustDraw, "Left")) _WinAPI_SetBkMode($tCustDraw.hDC, 1) _WinAPI_SetTextColor($tCustDraw.hDC, 0xFFFFFF) If $bHover And _WinAPI_PtInRect($tRect, $tPoint) Then $hBrush = _WinAPI_CreateSolidBrush(_IsPressed($VK_LBUTTON, $hUser32Dll) ? 0x202020 : 0x303030) Else $hBrush = _WinAPI_CreateSolidBrush(0x121212) EndIf _WinAPI_InflateRect($tRect, -1, 0) _WinAPI_FillRect($tCustDraw.hDC, $tRect, $hBrush) _WinAPI_DeleteObject($hBrush) _WinAPI_InflateRect($tRect, -5, -3) Local $iFlags = BitOR($DT_LEFT, $DT_NOCLIP, $DT_VCENTER) _WinAPI_DrawText($tCustDraw.hDC, $aHeaderList[$tCustDraw.dwItemSpec], $tRect, $iFlags) Return BitOR($CDRF_SKIPDEFAULT, $CDRF_NEWFONT) EndSwitch EndIf Case $HDN_BEGINTRACKW $bHover = False _WinAPI_RemoveWindowSubclass($pData, DllCallbackGetPtr($iID), 1000) Case $HDN_ENDTRACKW _WinAPI_SetWindowSubclass($pData, DllCallbackGetPtr($iID), 1000, $pData) EndSwitch Case $WM_MOUSEMOVE If $pData = $hWnd Then $bHover = True $tPoint = _WinAPI_GetMousePos(True, $hWnd) _WinAPI_InvalidateRect($hWnd, 0, False) _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE) EndIf Case $WM_MOUSELEAVE If $pData = $hWnd Then $bHover = False _WinAPI_InvalidateRect($hWnd, 0, False) EndIf EndSwitch Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam) EndFunc ;==>WM_NOTIFY Func _WinAPI_DwmSetWindowAttributeEx($hWnd, $iAttribute, $iData) Switch $iAttribute Case 2, 3, 4, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, $DWMWA_USE_IMMERSIVE_DARK_MODE, 33, 34, 35, 36, 37, 38, 39, 40 Case Else Return SetError(1, 0, 0) EndSwitch Local $aCall = DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', $iAttribute, _ 'dword*', $iData, 'dword', 4) If @error Then Return SetError(@error + 10, @extended, 0) If $aCall[0] Then Return SetError(10, $aCall[0], 0) Return 1 EndFunc ;==>_WinAPI_DwmSetWindowAttributeEx Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam) Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _ 'lparam', $lParam)[0] EndFunc ;==>_WinAPI_DefSubclassProc Edited April 28 by WildByDesign
argumentum Posted April 28 Posted April 28 If a collection of datatypes is defined as in a "struct{}" in C declaration, the "STRUCT; ...; ENDSTRUCT;" must be used. This needs to be done to respect alignment inside the entire structure creation. No need if all datatypes are in the defined structure as an implicit structure alignment is done. ; Assign a Local constant variable the definition of a structure (read carefully the DllStructCreate remarks). Local Const $tagSTRUCT1 = "struct;int var1;byte var2;uint var3;char var4[128];endstruct" ..this is from the help file. Silly as it may seem, I think that adding "struct;" and ";endstruct" everywhere is a good mania to have. Is it needed ?, meh. Does it hurt ?, nope. WildByDesign 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
WildByDesign Posted April 28 Author Posted April 28 34 minutes ago, argumentum said: ..this is from the help file. Silly as it may seem, I think that adding "struct;" and ";endstruct" everywhere is a good mania to have. Thanks. I just updated the code in the post above with this change and nothing exploded, so we are good to go. 😄 GUIDarkTheme UDF 2.0 is so very close to being ready. It's going to be a big step up in theme design. But there is still lots to do after version 2. argumentum 1
WildByDesign Posted May 3 Author Posted May 3 @Nine I just wanted to follow up on this briefly. After the subclassing bug was fixed regarding the _WinAPI_DefSubclassProc function and the bug regarding the _GUICtrlHeader_GetItemText function (root issue there being the SendMsg), I was able to remove the following from the header subclass: Case $HDN_BEGINTRACKW $bHover = False _WinAPI_RemoveWindowSubclass($pData, DllCallbackGetPtr($iID), 1000) Case $HDN_ENDTRACKW _WinAPI_SetWindowSubclass($pData, DllCallbackGetPtr($iID), 1000, $pData) If I remember correctly, your purpose for adding that was to avoid a crash. I remember removing that during testing initially and it would crash anytime you try to resize the headers. So I assume crash avoidance was their purpose. Anyway, I just wanted to follow up and mention that they are no longer needed, as long as the fixes are in place. Although to be specific, the whole _GUICtrlHeader_GetItemText and SendMsg thing is still problematic and I had to work around it.
Nine Posted May 3 Posted May 3 4 hours ago, WildByDesign said: that they are no longer needed, as long as the fixes are in place You are right. I'm still stunned that a simple error check cause the whole GUI to crash. But I have seen some strange things before in ListView. FWIW, I tested with my approach of using a global map, and it also works correctly without handling those messages. WildByDesign 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
WildByDesign Posted May 3 Author Posted May 3 10 minutes ago, Nine said: FWIW, I tested with my approach of using a global map, and it also works correctly without handling those messages. If you have this available, would you be willing to share the code? I ended up going with a global array but only because my global map attempts failed. I could not figure out how to properly do the map.
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