#include-once
Global $VARIABLE_UDF_COMPILE
If @Compiled And Not $VARIABLE_UDF_COMPILE Then MsgBox(0x30, 'Variable.au3 UDF', 'You have compiled your application with the Variable UDF included.' & @LF & 'If this was intentional, declare "Global $VARIABLE_UDF_COMPILE = True" above the #include.')
; #INDEX# =======================================================================================================================
; Title .........: Variable
; AutoIt Version : 3.2.13.5+
; Language ......: English
; Description ...: Functions for dumping variable information.
; Author(s) .....: Rob Saunders (rksaunders (at) gmail (dot) com)
; ===============================================================================================================================
; #CURRENT# =====================================================================================================================
; _VarDump
; ===============================================================================================================================
; #INTERNAL_USE_ONLY#============================================================================================================
; _VarDumpArray
; _VarDumpStruct
; ===============================================================================================================================
; #FUNCTION# ====================================================================================================================
; Name...........: _VarDump
; Description ...: This function returns information about a variable. Does not reset @error or @extended.
; Syntax.........: _VarDump(ByRef Const $vVar, $vDumpType = Default)
; Parameters ....: $vVar - The variable to analyze.
; $vDumpType - How to display the information. See Notes for keywords.
; Return values .: String: Information about the variable. This is returned regardless of the $vDumpType parameter.
; Author ........: Rob Saunders (rksaunders (at) gmail (dot) com)
; Remarks .......: Use one of the following keywords for the $vDumpType parameter:
; 1, ConsoleWrite, Console, CW = ConsoleWrite()
; 2, MsgBox, Msg, Box, MB = MsgBox()
; 3, ToolTip, Tip, TT = ToolTip()
; 4, Clipboard, Clip, CB = ClipPut()
; 5, GUI, Window, Text = Display in an edit control in a GUI window created on the fly
; You can add an asterisk "*" to the keyword to also return @ScriptLineNumber, @error, and @extended values. ie: "cw*"
; ===============================================================================================================================
Func _VarDump(ByRef Const $vVar, $vDumpType = '', $sIndent = '', $iLineNum = @ScriptLineNumber, $iError = @error, $iExtended = @extended)
Local $sVarDump, $sVarInfo
$vDumpType = StringReplace($vDumpType, '*', '')
If @extended Then
$sVarInfo = '*** Line: ' & $iLineNum & ', @error: ' & $iError & ', @extended: ' & $iExtended
EndIf
Local $sVarType = VarGetType($vVar)
$sVarDump &= $sVarType
Switch $sVarType
Case 'Array'
$sVarDump &= '(' & @CRLF & _VarDumpArray($vVar, $sIndent & @TAB) & @CRLF & $sIndent & ')'
Case 'DllStruct'
$sVarDump &= '(' & @CRLF & _VarDumpStruct($vVar, $sIndent & @TAB) & @CRLF & $sIndent & ')'
Case 'Binary'
$sVarDump &= '(' & BinaryLen($vVar) & ') ' & $vVar
Case 'Object'
$sVarDump &= '(' & ObjName($vVar) & ')'
Case 'String'
$sVarDump &= '(' & StringLen($vVar) & ') ' & $vVar
Case Else
If IsHWnd($vVar) Then $sVarDump &= '/HWnd'
$sVarDump &= '(' & $vVar & ')'
EndSwitch
If $sVarInfo And StringRegExp($sVarDump, '[\r\n]') Then
$sVarDump = $sVarInfo & @CRLF & $sVarDump
ElseIf $sVarInfo Then
$sVarDump = $sVarDump & ' ' & $sVarInfo
EndIf
Switch $vDumpType
Case 1, 'ConsoleWrite', 'Console', 'CW'
ConsoleWrite($sVarDump & @CRLF)
Case 2, 'MsgBox', 'Msg', 'Box', 'MB'
MsgBox(0x2040, 'VarDump', $sVarDump)
Case 3, 'ToolTip', 'Tip', 'TT'
ToolTip($sVarDump)
Case 4, 'Clipboard', 'Clip', 'CB'
ClipPut($sVarDump)
Case 5, 'GUI', 'Window', 'Text'
Local $iGUIOnEventMode = Opt('GUIOnEventMode', 0)
Local $guiVarDump = GUICreate('VarDump', 300, 200, Default, Default, 0xCF0000) ; $WS_OVERLAPPEDWINDOW
GUICtrlCreateEdit($sVarDump, 0, 0, 300, 200)
GUICtrlSetResizing(-1, 102) ; $GUI_DOCKBORDERS
GUISetState(@SW_SHOW, $guiVarDump)
Do
Until GUIGetMsg() = -3 ; $GUI_EVENT_CLOSE
GUIDelete($guiVarDump)
Opt('GUIOnEventMode', $iGUIOnEventMode)
EndSwitch
Return SetError($iError, $iExtended, StringReplace($sVarDump, Chr(0), ' '))
EndFunc
; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: _VarDumpArray
; Description ...: Details an array variable.
; Syntax.........: _VarDumpArray(ByRef Const $aArray)
; Parameters ....: $aArray - array to analyze.
; Return values .: String: Structured output of array contents.
; Author ........: Rob Saunders (rksaunders (at) gmail (dot) com)
; ===============================================================================================================================
Func _VarDumpArray(ByRef Const $aArray, $sIndent = '')
If Not IsArray($aArray) Then
; This should only happen if end user calls function directly on non-array
Return $sIndent & '!! Variable is not array'
EndIf
Local $iDimensions = UBound($aArray, 0) ; How many dimensions in array
If $iDimensions < 1 Then
; This should be impossible, but error check for safety
; -> As of 3.3.9.6 this (ie: Dim $array[0]) became possible
Return $sIndent & '!! Array has 0 dimensions'
EndIf
Local $aIndexes[$iDimensions] ; Tracker for reading indexes
Local $aUBounds[$iDimensions] ; List of dimension sizes
For $i = 0 To $iDimensions - 1
$aIndexes[$i] = 0
$aUBounds[$i] = UBound($aArray, $i + 1) ; Store each dimension size
If $aUBounds[$i] < 1 Then
Return $sIndent & '!! Array dimension [' & $i + 1 & '] size is ' & $aUBounds[$i]
EndIf
Next
Local $sArrayIndex ; Indexing string for individual item reading (ie: "[1][2][3]")
Local $sArrayRead ; Value of array item
Local $bDone ; Indicates if overall array has finished parsing
Local $sDump ; Output string
While 1
$bDone = True ; Default to true, flag false if dimension read is not done
; Reset and build indexing string
$sArrayIndex = ''
For $i = 0 To $iDimensions - 1
$sArrayIndex &= '[' & $aIndexes[$i] & ']'
If $aIndexes[$i] < $aUBounds[$i] - 1 Then $bDone = False ; Indexing has not reached end of dimension: NOT done
Next
$sArrayRead = Execute('$aArray' & $sArrayIndex) ; Read the darn value already!
If @error Then ; Item read failed, cancel the whole listing
$sDump &= $sIndent & $sArrayIndex & ' !! Error reading index'
ExitLoop
Else
; Add the index and value to the output string
$sDump &= $sIndent & $sArrayIndex & ' => ' & _VarDump($sArrayRead, '', $sIndent)
If $bDone Then
Return $sDump
Else
; If not done, line break and loop again
$sDump &= @CRLF
EndIf
EndIf
; Ok, I didn't comment this before and it took me forever to figure out what was going on so this is going to be verbose.
; The reason this FOR loop goes backwards is because it's stepping through the last dimension first and working it's way back.
; Just like when you increase a number, you increment the right-most digit first, ie: the "one's place".
; So for example, if we have an array of [X][X][X] - it starts with the third dimension [ ][ ][X] and steps through it one at a time,
; like this: [0][0][0], [0][0][1], [0][0][2], etc. If the index hasn't hit the dimension's ubound then the FOR loop exits, so all that
; gets incremented is that one dimension index. However, if it has met or exceeded that dimension's ubound, then it resets it to 0 and
; the FOR loop gets to continue to the next dimension up (ie: [ ][X][ ]), and now we get [0][1][0], [0][1][1], [0][1][2], etc.
For $i = $iDimensions - 1 To 0 Step -1
$aIndexes[$i] += 1
If $aIndexes[$i] >= $aUBounds[$i] Then
$aIndexes[$i] = 0
If $i = $iDimensions - 1 Then $sDump &= @CRLF
; The above line puts an extra line break between groups of the last dimension. ie:
; [0][0][0] => String(0)
; [0][0][1] => String(0)
; [0][0][2] => String(0)
;
; [0][1][0] => String(0)
; [0][1][1] => String(0)
; [0][1][2] => String(0)
Else
ExitLoop
EndIf
Next
WEnd
Return $sDump
EndFunc
; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: _VarDumpStruct
; Description ...: Details a struct variable.
; Syntax.........: VarDumpStruct(ByRef $tStruct)
; Parameters ....: $tStruct - struct to analyze.
; Return values .: String: Structured output of struct details/contents.
; Author ........: Rob Saunders (rksaunders (at) gmail (dot) com)
; ===============================================================================================================================
Func _VarDumpStruct(ByRef Const $tStruct, $sIndent = '')
Local $iElement = 1, $vRead, $vTest
; Do the initial $sDump contents - Standard fare for any struct
Local $sDump = _
$sIndent & '[Size] => ' & DllStructGetSize($tStruct) & @CRLF & _
$sIndent & '[Ptr] => ' & DllStructGetPtr($tStruct)
While 1
$vRead = DllStructGetData($tStruct, $iElement)
If @error = 2 Then ExitLoop ; If we've overstepped $iElement we're done
$vTest = VarGetType($vRead)
If $vTest = 'String' Or $vTest = 'Binary' Then
; Test the vartype for String or Binary, in which case we dump now because we already have the full contents
$sDump &= @CRLF & $sIndent & '[' & $iElement & '] => ' & _VarDump($vRead, '', $sIndent)
Else
; Here we'll test to see if the element is an array by looking for an index of 2.
DllStructGetData($tStruct, $iElement, 2)
If @error Then
; @error means no index, which means no array, which means we can just dump the stored $vRead from above
$sDump &= @CRLF & $sIndent & '[' & $iElement & '] => ' & _VarDump($vRead, '', $sIndent)
Else
; If we get no @error then that means that index 2 was valid, so we'll just start from 1 and work our way up til we @error again
Local $sSubDump, $iIndex = 1, $iCount = 0, $iNonEmpties = 0
While 1
$vRead = DllStructGetData($tStruct, $iElement, $iIndex)
If @error Then ExitLoop ; And that's the limit of this array, so we're done.
If $vRead Then
; We're skipping empty indices just to cut down on some of the clutter from large structs
$sSubDump &= @CRLF & @TAB & $sIndent & '[' & $iIndex & '] => ' & _VarDump($vRead)
$iNonEmpties += 1
EndIf
$iIndex += 1
WEnd
$iIndex -= 1
$sDump &= @CRLF & $sIndent & '[' & $iElement & '] => Array('
If $iNonEmpties < $iIndex Then
$sDump &= @CRLF & $sIndent & @TAB & '> Skipping empty indices (' & ($iIndex-$iNonEmpties) & '/' & $iIndex & ')'
EndIf
$sDump &= $sSubDump & @CRLF & $sIndent & ')'
EndIf
EndIf
$iElement += 1
WEnd
Return $sDump
EndFunc