Leaderboard
Popular Content
Showing content with the highest reputation on 03/13/2021 in all areas
-
OverviewFirst part First part of this example was published March/April 2021 and contains these main sections: Simple implementation of a virtual treeview created from data in a source file Multi-line treeview items and a discussion of an example in the help file Other sections about Item images, Performance tests and Treeview structure Second part Second part of the example has started up in January 2023: Updated all first part examples (item param offset, performance optimizations) Short summary of first part and further development of Real virtual treeviews TreeView posts Other posts and examples regarding treeviews: Saving/reading item levels and texts is the starting point for this example Use of colors and fonts in treeview items through custom draw code Virtual treeviews The basic idea behind implementing both a virtual treeview and a virtual listview is to store as much data as possible in the data source and as little data as possible in the treeview and listview. The purpose is especially to performance optimize creation of the treeview and listview. A virtual listview is created with the LVS_OWNERDATA style. To populate listview items and subitems, you respond to LVN_GETDISPINFO notifications contained in WM_NOTIFY messages. A virtual treeview isn't created based on a particular style of the treeview. A virtual treeview is created by setting LPSTR_TEXTCALLBACK and I_CHILDRENCALLBACK values in the TVITEM structure used to create the treeview items. The LPSTR_TEXTCALLBACK and I_CHILDRENCALLBACK values cause TVN_GETDISPINFO notifications (also contained in WM_NOTIFY messages) to be generated. To populate treeview items and child items, you respond to these TVN_GETDISPINFO notifications. Create treeview from data sourceIn the attempt to implement a virtual treeview, the first step is to create the treeview based on a data source e.g. a simple text file. This post demonstrates how to uniquely store a treeview in a text file and then uniquely restore the same treeview. It's sufficient to store treeview item levels and texts. This is a slightly modified version of TreeView.txt: 0|0 0|1 0|2 1|3 1|This 1|is 2|6 2|a 2|very 2|nice 3|10 3|TreeView 0|, (comma) 1|13 1|indeed. 0|15 And a slightly modified version of the code to recreate the treeview (1) Conventional TreeView.au3) #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 <WindowsConstants.au3> #include <GuiTreeView.au3> Global $hTreeView Example() Func Example() ; Create GUI Local $hGui = GUICreate( "Create Conventional TreeView From File", 400, 300, 600, 300, $GUI_SS_DEFAULT_GUI ) ; Create TreeView Local $idTreeView = GUICtrlCreateTreeView( 4, 4, 392, 292, $GUI_SS_DEFAULT_TREEVIEW, $WS_EX_CLIENTEDGE ) $hTreeView = GUICtrlGetHandle( $idTreeView ) ; Read level and text of TreeView items and create TreeView CreateTreeView( FileReadToArray( "TreeView.txt" ) ) ; Show GUI GUISetState( @SW_SHOW, $hGui ) ; Loop While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd ; Cleanup GUIDelete( $hGui ) EndFunc ; Create conventional TreeView Func CreateTreeView( $aItems ) ; TreeView item information Local $aItem, $hItem ; TreeView level information Local $aLevels[100], $iLevel = 0, $iLevelPrev = 0 ; $aLevels[$iLevel] contains the last item of that level ; Add TreeView root $aItem = StringSplit( $aItems[0], "|", 2 ) $hItem = _GUICtrlTreeView_Add( $hTreeView, 0, $aItem[1] ) $aLevels[$iLevel] = $hItem ; Add TreeView items For $i = 1 To UBound( $aItems ) - 1 $aItem = StringSplit( $aItems[$i], "|", 2 ) $iLevel = $aItem[0] If $iLevel <> $iLevelPrev Then $hItem = $iLevel > $iLevelPrev ? _GUICtrlTreeView_AddChild( $hTreeView, $aLevels[$iLevelPrev], $aItem[1] ) _ ; A child of the previous level : _GUICtrlTreeView_Add( $hTreeView, $aLevels[$iLevel], $aItem[1] ) ; A sibling of the level $iLevelPrev = $iLevel Else ; $iLevel = $iLevelPrev $hItem = _GUICtrlTreeView_Add( $hTreeView, $aLevels[$iLevel], $aItem[1] ) ; A sibling of the level EndIf $aLevels[$iLevel] = $hItem Next ; $aLevels[$iLevel] contains the last item of that level ; Expand all child items _GUICtrlTreeView_Expand( $hTreeView ) EndFunc Reduced and optimized functionIn the CreateTreeView() function at bottom of the code above, the treeview is created using _GUICtrlTreeView_Add() and _GUICtrlTreeView_AddChild() from GuiTreeView.au3. Both of these functions executes __GUICtrlTreeView_AddItem(), which is an internal function. This internal function fills the TVITEM structures where you can set the LPSTR_TEXTCALLBACK and I_CHILDRENCALLBACK values. The second step is to reduce and optimize the code in CreateTreeView() so that the TVITEM structure is filled in directly in this function. This is the reduced and optimized function (2) Optimized TreeView.au3) ; Create TreeView with optimized code Func CreateTreeView( $aItems ) ; TreeView item information Local $aItem ; TreeView level information Local $aLevels[100][2], $iLevel = 0, $iLevelPrev = 0 ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level ; TreeView text buffer Local $iBuffer, $tBuffer = DllStructCreate( "wchar Text[50]" ), $pBuffer = DllStructGetPtr( $tBuffer ) ; TreeView insert structure Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert ) DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST ) DllStructSetData( $tInsert, "Mask", $TVIF_TEXT ) DllStructSetData( $tInsert, "Text", $pBuffer ) ; Add TreeView root $aItem = StringSplit( $aItems[0], "|", 2 ) $iBuffer = 2 * StringLen( $aItem[1] ) + 2 DllStructSetData( $tBuffer, "Text", $aItem[1] ) DllStructSetData( $tInsert, "TextMax", $iBuffer ) $aLevels[$iLevel][1] = NULL DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] ) $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert ) ; Add TreeView items For $i = 1 To UBound( $aItems ) - 1 $aItem = StringSplit( $aItems[$i], "|", 2 ) $iBuffer = 2 * StringLen( $aItem[1] ) + 2 DllStructSetData( $tBuffer, "Text", $aItem[1] ) DllStructSetData( $tInsert, "TextMax", $iBuffer ) $iLevel = $aItem[0] If $iLevel <> $iLevelPrev Then $aLevels[$iLevel][1] = $iLevel > $iLevelPrev ? $aLevels[$iLevelPrev][0] _ ; A child of the previous level : GUICtrlSendMsg( $idTreeView, $TVM_GETNEXTITEM, $TVGN_PARENT, $aLevels[$iLevel][0] ) ; A sibling of the level $iLevelPrev = $iLevel EndIf DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] ) $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert ) Next ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level ; Expand all child items _GUICtrlTreeView_Expand( $hTreeView ) EndFunc Semi-virtual treeviewThe third step is to implement a semi-virtual treeview, where only the tree structure itself but not the item texts are created in CreateTreeView(). Instead of filling in the texts, we set the LPSTR_TEXTCALLBACK value in the TVITEM structure. And then we fill in the texts as needed through a WM_NOTIFY message handler and TVN_GETDISPINFO notifications (3) Semi-Virtual TreeView.au3) ; Create TreeView structure Func CreateTreeView( $aItems ) ; TreeView level information Local $aLevels[100][2], $iLevel = 0, $iLevelPrev = 0 ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level ; TreeView insert structure Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert ) DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST ) DllStructSetData( $tInsert, "Mask", $TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT ) DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK ; Add TreeView root $aLevels[$iLevel][1] = Ptr(0) DllStructSetData( $tInsert, "Param", 0 ) DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] ) $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert ) ; Add TreeView items For $i = 1 To UBound( $aItems ) - 1 $iLevel = StringSplit( $aItems[$i], "|", 2 )[0] If $iLevel <> $iLevelPrev Then $aLevels[$iLevel][1] = $iLevel > $iLevelPrev ? $aLevels[$iLevelPrev][0] _ ; A child of the previous level : GUICtrlSendMsg( $idTreeView, $TVM_GETNEXTITEM, $TVGN_PARENT, $aLevels[$iLevel][0] ) ; A sibling of the level $iLevelPrev = $iLevel EndIf DllStructSetData( $tInsert, "Param", $i ) DllStructSetData( $tInsert, "Parent", $aLevels[$iLevel][1] ) $aLevels[$iLevel][0] = GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert ) Next ; $aLevels[$iLevel][0]/[1] contains the last item/parent of that level ; Expand all child items _GUICtrlTreeView_Expand( $hTreeView ) EndFunc Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam ) Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam ) Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) ) Case $hTreeView Switch DllStructGetData( $tNMHDR, "Code" ) Case $TVN_GETDISPINFOW ; Display TreeView item text Local Static $tBuffer = DllStructCreate( "wchar Text[50]" ), $pBuffer = DllStructGetPtr( $tBuffer ) Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $sText = StringSplit( $aItems[DllStructGetData($tDispInfo,"Param")], "|", 2 )[1] DllStructSetData( $tBuffer, "Text", $sText ) DllStructSetData( $tDispInfo, "Text", $pBuffer ) DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $sText ) + 2 ) EndSwitch EndSwitch #forceref $hWnd, $iMsg, $wParam EndFunc Note that this reduces and optimizes the code in CreateTreeView() even more because the buffer for text strings is no longer needed. Instead, the buffer is populated in the WM_NOTIFY message handler. This technique has the great advantage that only visible treeview items are filled with text strings. Because item texts are only filled in when the items are visible in the treeview, it's necessary to establish a connection between the treeview items and the data source that contains the texts. The data source, which is a simple text file, is loaded into an array. The connection between a treeview item and the data source is the index in this array. Therefore, the array index is stored in the Param field of the TVITEM structure. Virtual treeviewTo make the treeview completely virtual, the fourth and final step is to create and display child items only when required. That is, when an end user clicks the expand button to the left of a treeview item that actually contains child items. And only direct children of this item will be created. If an end user doesn't click an expand button (because these child items are not the ones he's looking for), the child items in question will not be created in the tree structure at all. To make the treeview completely virtual only first level (level-0) items are created in CreateTreeView(). Instead of creating child items, we set the I_CHILDRENCALLBACK value in the TVITEM structure. And then we create child items as needed through a WM_NOTIFY message handler and TVN_GETDISPINFO and TVN_ITEMEXPANDING notifications (4) Virtual TreeView.au3) ; Create TreeView structure Func CreateTreeView( $aLevel0 ) ; TreeView insert structure Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert ) DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST ) DllStructSetData( $tInsert, "Mask", $TVIF_CHILDREN+$TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT ) DllStructSetData( $tInsert, "Parent", NULL ) DllStructSetData( $tInsert, "Children", -1 ) ; $I_CHILDRENCALLBACK DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK ; Add TreeView items For $i = 0 To UBound( $aLevel0 ) - 1 DllStructSetData( $tInsert, "Param", $aLevel0[$i] ) GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert ) Next EndFunc Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam ) Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam ) Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) ) Case $hTreeView Switch DllStructGetData( $tNMHDR, "Code" ) Case $TVN_GETDISPINFOW ; Display TreeView item text Local Static $tBuffer = DllStructCreate( "wchar Text[50]" ), $pBuffer = DllStructGetPtr( $tBuffer ) Local $tDispInfo = DllStructCreate( $tagNMTVDISPINFO, $lParam ), $iIndex = DllStructGetData( $tDispInfo, "Param" ), $sText = StringSplit( $aItems[$iIndex], "|", 2 )[3] DllStructSetData( $tBuffer, "Text", $sText ) DllStructSetData( $tDispInfo, "Text", $pBuffer ) DllStructSetData( $tDispInfo, "TextMax", 2 * StringLen( $sText ) + 2 ) DllStructSetData( $tDispInfo, "Children", StringSplit( $aItems[$iIndex], "|", 2 )[1] = 0 ? 0 : 1 ) Case $TVN_ITEMEXPANDINGW ; Create TreeView structure for childs Local $tTreeView = DllStructCreate( $tagNMTREEVIEW, $lParam ) If BitAND( DllStructGetData( $tTreeView, "Action" ), $TVE_EXPAND ) <> $TVE_EXPAND _ Or Int( StringSplit( $aItems[DllStructGetData($tTreeView,"NewParam")], "|", 2 )[2] ) Then Return ; TreeView insert structure Local $tInsert = DllStructCreate( $tagTVINSERTSTRUCT ), $pInsert = DllStructGetPtr( $tInsert ) DllStructSetData( $tInsert, "InsertAfter", $TVI_LAST ) DllStructSetData( $tInsert, "Mask", $TVIF_CHILDREN+$TVIF_HANDLE+$TVIF_PARAM+$TVIF_TEXT ) DllStructSetData( $tInsert, "Parent", DllStructGetData( $tTreeView, "NewhItem" ) ) DllStructSetData( $tInsert, "Children", -1 ) ; $I_CHILDRENCALLBACK DllStructSetData( $tInsert, "Text", -1 ) ; $LPSTR_TEXTCALLBACK ; Add TreeView children Local $iNewIndex = DllStructGetData( $tTreeView, "NewParam" ) + 1 For $i = 0 To StringSplit( $aItems[$iNewIndex-1], "|", 2 )[1] - 1 DllStructSetData( $tInsert, "Param", $iNewIndex + $i ) GUICtrlSendMsg( $idTreeView, $TVM_INSERTITEMW, 0, $pInsert ) Next ; Indicate that children have been added $aItems[$iNewIndex-1] = StringReplace( $aItems[$iNewIndex-1], 7, "1" ) EndSwitch EndSwitch #forceref $hWnd, $iMsg, $wParam EndFunc Creating only first level (level-0) items in CreateTreeView() is the crucial step that truly optimizes creation of the treeview. CreateTreeView() in this version is very simple and fast. $aLevel0 is an index of first level (level-0) items. To handle TVN_GETDISPINFO and TVN_ITEMEXPANDING notifications regarding child items, more information is needed in the source file (TreeView-4.txt) 0|000|0|0 0|000|0|1 0|003|0|2 1|000|0|3 1|000|0|This 1|004|0|is 2|000|0|6 2|000|0|a 2|000|0|very 2|002|0|nice 3|000|0|10 3|000|0|TreeView 0|002|0|, (comma) 1|000|0|13 1|000|0|indeed. 0|000|0|15 It's the number of child items in the second field, and a flag to indicate whether child items have already been created in the third field. At top of the TVN_ITEMEXPANDING code section, it's checked if a treeview item with child items is expanded or collapsed and if these child items are already created: Case $TVN_ITEMEXPANDINGW ; Create TreeView structure for childs Local $tTreeView = DllStructCreate( $tagNMTREEVIEW, $lParam ) If BitAND( DllStructGetData( $tTreeView, "Action" ), $TVE_EXPAND ) <> $TVE_EXPAND _ Or Int( StringSplit( $aItems[DllStructGetData($tTreeView,"NewParam")], "|", 2 )[2] ) Then Return The rest of the code in the TVN_ITEMEXPANDING section creates the child items. This code is again very simple and fast. 100,000 items5) 100,000 Items.au3 creates a virtual treeview with 100,000 items based on the 100,000.txt source file. Of course, creating the treeview is lightning fast. 7z-fileThe 7z-file contains source code for UDFs and examples. 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. VirtualTreeView.7z5 points
-
Member Number
JockoDundee reacted to Melba23 for a topic
JockoDundee, Pinned in "Example Scripts": But it is no longer maintained and I have no idea how up to date it is or if it even runs under the current release. M231 point -
isn't this all you needed or is it now about expanding buttons as well let me know if in some case this wont work (you would simply need to add new cases, added or ignored - windows styles ..) #include <WinAPISysWin.au3> #include <WindowsConstants.au3> MsgBox("", "Buttons Count", TaskbarButtonsCount()) Func TaskbarButtonsCount() Local $iCount = 0, $style, $ahw = _WinAPI_EnumWindowsTop() For $i = 1 To $ahw[0][0] - 1 If Not _WinAPI_GetWindowLong($ahw[$i][0], $GWL_HWNDPARENT) And _ Not BitAND(_WinAPI_GetWindowLong($ahw[$i][0], $GWL_STYLE), $WS_POPUP) Then $iCount += 1 EndIf Next Return $iCount EndFunc1 point
-
SSD Size & Longevity/Resilience/Speed
Earthshine reacted to jchd for a topic
Yeah, I've long used 8 HP SAS drives (15k rpm) but these server-class HDDs are much more expensive than today's SSDs having a similar capacity. I still have my Areca ARC1882I RAID controller but I don't have any use of it now.1 point -
UIAutomation is the way to go. Fortunately, I have an already written script for it : #include <Constants.au3> #include "Includes\CUIAutomation2.au3" ; https://www.autoitscript.com/forum/topic/153520-iuiautomation-ms-framework-automate-chrome-ff-ie/ Opt("MustDeclareVars", 1) Global $pElement, $pCondition, $pCondition1, $pCondition2, $pElementArray, $iElements, $vValue Global $hCtrl, $oUIAutomation, $oElement, $oElementArray ; Get taskbar handle $hCtrl = ControlGetHandle("[Class:Shell_TrayWnd]", "", "MSTaskListWClass1") ; Get UIAutomation object $oUIAutomation = ObjCreateInterface($sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation) ; Get taskbar element $oUIAutomation.ElementFromHandle($hCtrl, $pElement) $oElement = ObjCreateInterface($pElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) ; Get condition (ControlType = Button) $oUIAutomation.CreatePropertyCondition($UIA_ControlTypePropertyId, $UIA_ButtonControlTypeId, $pCondition1) ; Get condition (ControlType = MenuItem) $oUIAutomation.CreatePropertyCondition($UIA_ControlTypePropertyId, $UIA_MenuItemControlTypeId, $pCondition2) ; Create OR condition $oUIAutomation.CreateOrCondition($pCondition1, $pCondition2, $pCondition) ; Find all buttons and menu items $oElement.FindAll($TreeScope_Children, $pCondition, $pElementArray) $oElementArray = ObjCreateInterface($pElementArray, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray) $oElementArray.Length($iElements) ; Get array of buttons and menu items Global $aElements[$iElements] For $i = 0 To $iElements - 1 $oElementArray.GetElement($i, $pElement) $aElements[$i] = ObjCreateInterface($pElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) Next Local $iCount = 0 For $i = 0 To UBound($aElements) - 1 $aElements[$i].GetCurrentPropertyValue($UIA_NamePropertyId, $vValue) ConsoleWrite("Name:" & $vValue) $aElements[$i].GetCurrentPropertyValue($UIA_LegacyIAccessibleStatePropertyId, $vValue) ConsoleWrite (" / State " & $vValue & @CRLF) If $vValue Then $iCount += 1 Next MsgBox ($MB_SYSTEMMODAL, "Info", "There is " & $iCount & " programs running in System Toolbar")1 point
-
The second downside is that RAM is small; even with 64 GB, many of my datasets wouldn't fit in there. I second the motion;ImDisk is the best, free, open-source RAM disk manager I've ever used.1 point
-
RegExp help please
pixelsearch reacted to dmob for a topic
@pixelsearch Thank you for the lesson, always good to learn other methods1 point -
SSD Size & Longevity/Resilience/Speed
argumentum reacted to TheDcoder for a topic
Excellent suggestion, I use the RAM for all I/O heavy operations, it is the most durable AND the most fastest medium to process data The only downside is that it is volatile, so no long-term storage. But it works great as long as your machine is powered There are quite a few programs to do this in Windows, but I personally use ImDisk (which also supports mounting of disk images), I also think it offers an option for persistent RAM storage by automatically backing everything up in an image before shutting down. Strictly speaking, NVMe is a connection interface, the underlying storage system is always and SSD. I assume you are referring to SATA SSDs by "SSD" -- I personally use the SSD for my OS partition as well as my home partition as it makes things fast, but for everything else, I use my other "SSD", i.e slow spinning disk1 point -
SSD Size & Longevity/Resilience/Speed
TheDcoder reacted to argumentum for a topic
See if you can use a RAM disk for faster read/write. Also, I rather use NVMe over SSD. If for long time storage, use something to attend to bit-rot.1 point -
RegExp help please
dmob reacted to pixelsearch for a topic
@dmob : imho you were nearly there : #include <Array.au3> $txt = "{A.Item}, R{B.Some} {B.Other} text string {C.Date} {C.Item}{C.Thing}" $arr1 = StringRegExp($txt,"({[^}]*})", 3) _ArrayDisplay($arr1, "Nine's negated way") $arr2 = StringRegExp($txt,"(?U)({.*})", 3) _ArrayDisplay($arr2, "Lazy way #1") $arr3 = StringRegExp($txt,"({.*?})", 3) _ArrayDisplay($arr3, "Lazy way #2")1 point -
Ohh, ok, didnt know you needed a member number for that, never taken it myself Welcome to the forums, programming can be very rewarding, getting a line of code to work can be like making a goal in a soccer match, or making a strike bowling, a "hole in one" in Golf, or beating that big boss in a computer game. Have fun1 point
-
How to best obtain the current two digit year?
argumentum reacted to FrancescoDiMuro for a topic
@cag8f ConsoleWrite("Two Digits Year: " & _TwoDigitsYear() & @CRLF) Func _TwoDigitsYear() Return StringRight(@YEAR, 2) EndFunc1 point -
declare many labels at the same time
argumentum reacted to Musashi for a topic
It's not that bad at all, just a matter of etiquette . The problem with crossposting is, that helpers spend their time without knowing about the respective responses in the other forum. Since we are all volunteers, time is precious and should not be wasted.1 point -
You are right, under the hood it is the same function. I don't know each of the Winapi GDI function which Yashied has added to his famous WinAPIEx UDF. WinAPIEx UDF was added to Autoit meanwhile.1 point
-
Hello @jugador I hadn't had time to review. Are you trying to read from AutoIt script instead using Excel? If It's so You could do it these way. It's a little messed but you can clear it. #AutoIt3Wrapper_UseX64=N #include <AutoItConstants.au3> #include <WinAPI.au3> #include <array.au3> #include ".\Includes\AccVarsUtilities.au3" Global Const $sCLSID_App = "{86541CE9-EA39-4175-B37A-FDAE655AD30C}" Global Const $sIID_IScripRTD = "{EC0E6191-DB51-11D3-8F3E-00C04F3651B8}" Global Const $sIID_IRTDUpdateEvent = "{A43788C1-D91B-11D3-8F39-00C04F3651B8}" Global Const $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _ "AddRef dword();" & _ "Release dword();" Global Const $tagIDispatch = $tagIUnknown & _ "GetTypeInfoCount hresult(dword*);" & _ "GetTypeInfo hresult(dword;dword;ptr*);" & _ "GetIDsOfNames hresult(struct*;struct*;dword;dword;struct*);" & _ "Invoke hresult(uint;struct*;dword;word;struct*;struct*;ptr;uint*);" Global Const $tagIScripRTD = $tagIDispatch & _ "ServerStart hresult(ptr;long*);" & _ "ConnectData hresult(long;ptr*;bool*;variant*);" & _ "RefreshData hresult(long*;ptr*);" & _ "DisconnectData hresult(long);" & _ "Heartbeat hresult(long*);" & _ "ServerTerminate hresult();" Global Const $tag_IRTDUpdateEvent = $tagIDispatch & _ "UpdateNotify hresult();" & _ "get_HeartbeatInterval hresult(long);" & _ "put_HeartbeatInterval hresult(long*);" & _ "Disconnect hresult();" Global Const $sIID_IUnknown = "{00000000-0000-0000-C000-000000000046}" Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") Global $g_oApp_ObjCast=0 _Test() Func _Test() Local $oApp_Obj = ObjCreateInterface($sCLSID_App, $sIID_IScripRTD) ConsoleWrite("IsObj($oApp_Obj): " & IsObj($oApp_Obj) & @CRLF) $g_oApp_ObjCast = ObjCreateInterface($oApp_Obj, $sIID_IScripRTD, $tagIScripRTD, False) ConsoleWrite("IsObj($g_oApp_ObjCast): " & IsObj($g_oApp_ObjCast) & @CRLF) Local $tIRTDUpdateEvents Local $ooIRTDUpdateEvents = ObjectFromTag("IRTDUpdateEvent_", $tag_IRTDUpdateEvent, $tIRTDUpdateEvents, False, False, $sIID_IRTDUpdateEvent) ConsoleWrite("IsObj($ooIRTDUpdateEvents): " & IsObj($ooIRTDUpdateEvents) & @CRLF) Local $pooIRTDUpdateEvents = $ooIRTDUpdateEvents() ConsoleWrite("$iHeartbeat: " & $oApp_Obj.Heartbeat() & @CRLF) Local $iHeartbeat = 0 $g_oApp_ObjCast.Heartbeat($iHeartbeat) ConsoleWrite("$iHeartbeat: " & $iHeartbeat & @CRLF) Local $iServerStatus = 0 ;~ $oApp_Obj.ServerStart($prt_IUnknown) ;this will fail $g_oApp_ObjCast.ServerStart($pooIRTDUpdateEvents, $iServerStatus) ConsoleWrite("$iHeartbeat: " & $iHeartbeat & @CRLF) Local $iTopidId = 0 Local $aStrings[] = ["test"] Local $pSafeArrayStrings AccVars_ArrayToSafeArray($aStrings, $pSafeArrayStrings) Local $bBool = True Local $aVariant = 0 $g_oApp_ObjCast.ConnectData($iTopidId, $pSafeArrayStrings, $bBool, $aVariant) ConsoleWrite("$iTopidId: " & $iTopidId & @CRLF) ConsoleWrite("$aStrings: " & IsArray($aStrings) & @CRLF) ConsoleWrite("$bBool: " & $bBool & @CRLF) ConsoleWrite("$aVariant: " & $aVariant & @CRLF) Local $aStringsData[0] Local $pSafeArrayData While Sleep(30) ;~ AccVars_ArrayToSafeArray($aStringsData, $pSafeArrayData) ;~ $g_oApp_ObjCast.RefreshData($iTopidId, $pSafeArrayData) ;~ AccVars_SafeArrayToArray($pSafeArrayData, $aStringsData) ;~ _ArrayDisplay($aStringsData) WEnd EndFunc ;==>_Test ;;============= Func IRTDUpdateEvent_UpdateNotify($pSelf) ConsoleWrite('> UpdateNotify: ' & @CRLF) Local $aStringsData[0] Local $pSafeArrayData AccVars_ArrayToSafeArray($aStringsData, $pSafeArrayData) $g_oApp_ObjCast.RefreshData(0, $pSafeArrayData) AccVars_SafeArrayToArray($pSafeArrayData, $aStringsData) ConsoleWrite(_ArrayToString($aStringsData) & @CRLF) Return $S_OK EndFunc ;==>IRTDUpdateEvent_UpdateNotify Func IRTDUpdateEvent_GetHeartbeatInterval($pSelf, $pvalue) Local $S_result = 0 ConsoleWrite('> $S_result: ' & $S_result & @CRLF) Return $S_result EndFunc ;==>IRTDUpdateEvent_GetHeartbeatInterval Func IRTDUpdateEvent_PutHeartbeatInterval($pSelf, $pvalue) ConsoleWrite('> $pvalue: ' & $pvalue & @CRLF) Return $S_OK EndFunc ;==>IRTDUpdateEvent_PutHeartbeatInterval Func IRTDUpdateEvent_Disconnect($pSelf) Return $S_OK EndFunc ;==>IRTDUpdateEvent_Disconnect Func IRTDUpdateEvent_QueryInterface($pSelf, $pRIID, $pObj) Local $sIID = StringFromGUID($pRIID) If $sIID = $sIID_IUnknown Then DllStructSetData(DllStructCreate("ptr", $pObj), 1, $pSelf) Return $S_OK ElseIf $sIID = $sIID_IRTDUpdateEvent Then DllStructSetData(DllStructCreate("ptr", $pObj), 1, $pSelf) Return $S_OK Else Return $E_NOINTERFACE EndIf EndFunc ;==>IRTDUpdateEvent_QueryInterface Func IRTDUpdateEvent_AddRef($pSelf) ConsoleWrite('> AddRef: ' & @CRLF) Return 1 EndFunc ;==>IRTDUpdateEvent_AddRef Func IRTDUpdateEvent_Release($pSelf) ConsoleWrite('> Release: ' & @CRLF) Return 1 EndFunc ;==>IRTDUpdateEvent_Release ;;============= Func IRTDUpdateEvent_GetTypeInfoCount($pSelf) ; Ret: long Par: ptr EndFunc ;==>IRTDUpdateEvent_GetTypeInfoCount Func IRTDUpdateEvent_GetTypeInfo($pSelf) ; Ret: long Par: dword;dword;ptr EndFunc ;==>IRTDUpdateEvent_GetTypeInfo Func IRTDUpdateEvent_GetIDsOfNames($pSelf) ; Ret: long Par: ptr;ptr;dword;dword;ptr EndFunc ;==>IRTDUpdateEvent_GetIDsOfNames Func IRTDUpdateEvent_Invoke($pSelf) ; Ret: long Par: uint;ptr;dword;word;ptr;ptr;ptr;ptr EndFunc ;==>IRTDUpdateEvent_Invoke Func StringFromGUID($pGUID) Local $aResult = DllCall("ole32.dll", "int", "StringFromGUID2", "struct*", $pGUID, "wstr", "", "int", 40) If @error Then Return SetError(@error, @extended, "") Return SetExtended($aResult[0], $aResult[2]) EndFunc ;==>StringFromGUID ;;-------- ;;-------- Func ObjectFromTag($sFunctionPrefix, $tagInterface, ByRef $tInterface, $fPrint = False, $bIsUnknown = Default, $sIID = "{00000000-0000-0000-C000-000000000046}") ; last param is IID_IUnknown by default If $bIsUnknown = Default Then $bIsUnknown = True Local $sInterface = $tagInterface ; copy interface description Local $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _ "AddRef dword();" & _ "Release dword();" ; Adding IUnknown methods If $bIsUnknown Then $tagInterface = $tagIUnknown & $tagInterface ; Below line is really simple even though it looks super complex. It's just written weird to fit in one line, not to steal your attention Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($tagInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3) Local $iUbound = UBound($aMethods) Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback ; Allocation $tInterface = DllStructCreate("int RefCount;int Size;ptr Object;ptr Methods[" & $iUbound & "];int_ptr Callbacks[" & $iUbound & "];ulong_ptr Slots[16]") ; 16 pointer sized elements more to create space for possible private props If @error Then Return SetError(1, 0, 0) For $i = 0 To $iUbound - 1 $aSplit = StringSplit($aMethods[$i], "|", 2) If UBound($aSplit) <> 2 Then ReDim $aSplit[2] $sNamePart = $aSplit[0] $sTagPart = $aSplit[1] $sMethod = $sFunctionPrefix & $sNamePart If $fPrint Then Local $iPar = StringInStr($sTagPart, ";", 2), $t If $iPar Then $t = "Ret: " & StringLeft($sTagPart, $iPar - 1) & " " & _ "Par: " & StringRight($sTagPart, StringLen($sTagPart) - $iPar) Else $t = "Ret: " & $sTagPart EndIf Local $s = "Func " & $sMethod & _ "( $pSelf ) ; " & $t & @CRLF & _ "EndFunc" & @CRLF ConsoleWrite($s) EndIf $aTagPart = StringSplit($sTagPart, ";", 2) $sRet = $aTagPart[0] $sParams = StringReplace($sTagPart, $sRet, "", 1) $sParams = "ptr" & $sParams $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams) If @error Then ConsoleWrite('! ' & @error & ' ' & $sMethod & @CRLF & @CRLF) EndIf DllStructSetData($tInterface, "Methods", DllCallbackGetPtr($hCallback), $i + 1) ; save callback pointer DllStructSetData($tInterface, "Callbacks", $hCallback, $i + 1) ; save callback handle Next DllStructSetData($tInterface, "RefCount", 1) ; initial ref count is 1 DllStructSetData($tInterface, "Size", $iUbound) ; number of interface methods DllStructSetData($tInterface, "Object", DllStructGetPtr($tInterface, "Methods")) ; Interface method pointers Return ObjCreateInterface(DllStructGetPtr($tInterface, "Object"), $sIID, $sInterface, $bIsUnknown) ; pointer that's wrapped into object EndFunc ;==>ObjectFromTag ;;-------- ; #FUNCTION# ============================================================================= ; Name...........: _ErrFunc() ; ======================================================================================== ; User's COM error function. Will be called if COM error occurs #forceref $oErrorHandler Func _ErrFunc($oError) ; Do anything here. ConsoleWrite(@ScriptName & " (" & $oError.scriptline & ") : ==> COM Error intercepted !" & @CRLF & _ @TAB & "err.number is: " & @TAB & @TAB & "0x" & Hex($oError.number) & @CRLF & _ @TAB & "err.windescription:" & @TAB & $oError.windescription & @CRLF & _ @TAB & "err.description is: " & @TAB & $oError.description & @CRLF & _ @TAB & "err.source is: " & @TAB & @TAB & $oError.source & @CRLF & _ @TAB & "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _ @TAB & "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _ @TAB & "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _ @TAB & "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _ @TAB & "err.retcode is: " & @TAB & "0x" & Hex($oError.retcode) & @CRLF & @CRLF) EndFunc ;==>_ErrFunc PD: Make sure to add #include ".\Includes\AccVarsUtilities.au3" It's from our dear user @LarsJ from here. Saludos1 point