Sign in to follow this  
Followers 0
ripdad

_ComputerGetSoftware

22 posts in this topic

#1 ·  Posted (edited)

Several years ago, June of 2010, I modified this function by >JSThePatriot,

and then made a small utility of it, called: InstalledSoftwareViewer.au3

Recently, I revisited that function, and came up with this:

; Last Updated: April 17, 2014

#RequireAdmin

#include 'array.au3'

Local $a = _ComputerGetSoftware()
If Not @error Then
    _ArrayDisplay($a)
EndIf
Exit


;===============================================
; #FUNCTION _ComputerGetSoftware()
; Author: JSThePatriot
; http://www.autoitscript.com/forum/topic/29404-computer-info-udfs/?hl=%20_computergetsoftware
;
; Modified by ripdad: April 15, 2014
;
; Remarks:
; Last two columns are bits (0 or 1).
; Blank Fields = no value found
;===============================================
Func _ComputerGetSoftware()

    Switch @OSArch
        Case 'X64'
            Local $sHKCU = 'HKEY_CURRENT_USER64', $sHKLM = 'HKEY_LOCAL_MACHINE64'
            Local $sSubKey1 = '\Software\Microsoft\Windows\CurrentVersion\Uninstall'
            Local $sSubKey2 = '\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
            Local $aKeys[4] = [3, $sHKCU & $sSubKey1, $sHKLM & $sSubKey1, $sHKLM & $sSubKey2]
        Case 'X86'
            Local $sHKCU = 'HKEY_CURRENT_USER', $sHKLM = 'HKEY_LOCAL_MACHINE'
            Local $sSubKey = '\Software\Microsoft\Windows\CurrentVersion\Uninstall'
            Local $aKeys[3] = [2, $sHKCU & $sSubKey, $sHKLM & $sSubKey]
        Case Else
            Return SetError(1)
    EndSwitch

    Local $array[5001][7] = [[5000, 'DisplayVersion', 'Publisher', 'UninstallString', 'InstallDate', 'MSI', 'NoRemove']]
    Local $sAppKey, $sDisplayName, $sKey, $UnInstKey, $index = 0

    For $i = 1 To $aKeys[0]
        $sKey = $aKeys[$i]
        For $j = 1 To $array[0][0]
            $sAppKey = RegEnumKey($sKey, $j)
            If @error Then ExitLoop
            $UnInstKey = $sKey & '\' & $sAppKey
            $sDisplayName = RegRead($UnInstKey, 'DisplayName')
            Select
                Case @error
                Case Not StringLen(StringStripWS($sDisplayName, 8))
                Case StringRegExp($sDisplayName, '(?i)(KB\d+)')
                Case RegRead($UnInstKey, 'SystemComponent')
                Case RegRead($UnInstKey, 'ParentKeyName')
                Case Else
                    $index += 1
                    $array[$index][0] = StringStripWS(StringReplace($sDisplayName, ' (remove only)', ''), 3)
                    $array[$index][1] = StringStripWS(RegRead($UnInstKey, 'DisplayVersion'), 3)
                    $array[$index][2] = StringStripWS(RegRead($UnInstKey, 'Publisher'), 3)
                    $array[$index][3] = StringStripWS(RegRead($UnInstKey, 'UninstallString'), 3)
                    $array[$index][4] = StringStripWS(RegRead($UnInstKey, 'InstallDate'), 3)
                    $array[$index][5] = StringStripWS(RegRead($UnInstKey, 'WindowsInstaller'), 3) = 1
                    $array[$index][6] = StringStripWS(RegRead($UnInstKey, 'NoRemove'), 3) = 1
            EndSelect
        Next
    Next

    ReDim $array[$index + 1][7]
    $array[0][0] = $index
    _ArraySort($array, 0, 1)
    Return $array
EndFunc

In the previous versions, HKCU was not checked for uninstalls.

I decided to run a test, using that key, and what do you know...

Five more entries were found!

So, I decided to run some other test's, and found some more!

Imagine that!

Well anyways, this script, should cover most of them.

If you have any questions - please let me know.

 

Edited by ripdad
1 person likes this

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites



Retrieves the desired info without sparks or smoke; works nicely.

Much faster than appwiz.cpl.

What would really be the cats' pajamas is to show install date.

Any pointers on how I could do that?

Share this post


Link to post
Share on other sites

What would really be the cats' pajamas is to show install date.

Any pointers on how I could do that?

 

That's no problem to add that. Just add another column to the array.

I have thought of that, but, not all entries have the install date.

As a matter of fact, less than half of them had the date code.

Which is why I decided not to add it, at the time.

But, I suppose I could go ahead and do it anyways, keeping in

mind what I mentioned above.


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

Okay, Updated with: InstallDate

I ran this on another machine, and most of the dates were available, but not all.

So, I guess it depends on the machine and other factors.

Also, sometimes the version will be included in the DisplayName, if the DisplayVersion is blank.


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

I'm aware that Windows 32bit, parses root keys that
have 64 added to them. Which to me, is kinda strange.

But personally, I don't want to take any chances when it
comes to the registry -- even if it's just reading keys.

As far as using the short-key, (ie: HKLM64)
I'm aware of this also. Same reason as above.
I don't want to take any chances.

Is there a good reason why? Yes there is!
I chased my tail One too many times debugging. (grin)

It stopped when I used the full and correct key name.
I haven't had that problem since. That was some years ago.

In any case, I don't see any problem with writing out the full and correct keys.

The only problem now (still on the topic of the registry), that
exists for me, is overcoming some other programs lock on a key,
which prevents any others attempting to write to it.

Normal keys locked -> bad programming.

It doesn't happen too often, but there is no graceful way to
handle it - except to reboot.

I run into that problem, several times a month.

- Multiple users, hmmm, thinking (about something else).

Well, if this didn't quite answer your second question, then please
rephrase the question.

---

On the other hand - this is the way I interpreted it - the first time:

You said, quote:
"Would both HKLM64 and HKCU64 be better for machines with multiple users?"

And my answer was:
What in the world are you talking about? (grin)
 


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

You say that when you used current use key it presented more software installs.

That means some software is only installed for current and not all users.

I'm wondering if some software is present in HKLM but not HKCU as it is opposite of what you discovered.


AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

The bulk of software entries is in HKLM. I found five others in HKCU on the machine I tested.

There were no duplicates between those two keys, when I tested. This was a 32bit Machine.

That doesn't mean that duplicates would not exist on some other machine.

In any case, the script handles both of those, including the one for Wow6432Node on a

64bit machine, which is a subkey of HKLM where I found the bulk of software entries on it.

With that said, I found a few duplicates between these keys:

HKLM...Uninstall --and-- HKLM...Wow6432Node...Uninstall

And also a few differing between those.

I also found two identical DisplayName entries in different uninstall subkeys,

with different uninstall strings. Of those two, one was for uninstalling the

software and the other was for repairing the existing install via MSI.

Others, were hard to make out and had various ways about them.

Some only had two entries in the subkey - the DisplayName and the Uninstall string.

Still, others had as many as 20 to 30 entries in theirs.

Another example of free-for-all standards, I guess.

Edited by ripdad

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

I found this post that might help with showing what you want.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

Well, that cleared up a few questions I had.

Thanks for posting the link.


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

This link might add some more information to the first link, especially in regards to the MSI installed programs. Specifically this part of the information that wasn't clarified as to how the GUID is changed.

 

When Add/Remove Programs encounters one of these, it looks at the key name (which will be a long GUID) and switches some parts of it around, then looks for a key named with this re-ordered GUID in the following location:

This post explains how the GUID is switched around.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

I modified the Array UDF to change the "user defined function" button to "uninstall selection", that button runs whatever string is in the uninstallstring of the row that is selected.  I know the proper way would be to use that button as designed and pass the return to a function of my own, but I'm not about to start doing things correctly now....Thus the entirety of the Array UDF pasted to the bottom of the script save for my changes to _ArrayDisplay.
 
 
 

#RequireAdmin

Local $a = _ComputerGetSoftware()

If Not @error Then
    _ArrayDisplay($a)
    exit
EndIf

;===============================================
; #FUNCTION _ComputerGetSoftware()
; Author: JSThePatriot
; http://www.autoitscript.com/forum/topic/29404-computer-info-udfs/?hl=%20_computergetsoftware
;
; Modified by ripdad: April 15, 2014
;
; Remarks:
; Last two columns are bits (0 or 1).
; Blank Fields = no value found
;===============================================
Func _ComputerGetSoftware()

    Switch @OSArch
        Case 'X64'
            Local $sHKCU = 'HKEY_CURRENT_USER64', $sHKLM = 'HKEY_LOCAL_MACHINE64'
            Local $sSubKey1 = '\Software\Microsoft\Windows\CurrentVersion\Uninstall'
            Local $sSubKey2 = '\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
            Local $aKeys[4] = [3, $sHKCU & $sSubKey1, $sHKLM & $sSubKey1, $sHKLM & $sSubKey2]
        Case 'X86'
            Local $sHKCU = 'HKEY_CURRENT_USER', $sHKLM = 'HKEY_LOCAL_MACHINE'
            Local $sSubKey = '\Software\Microsoft\Windows\CurrentVersion\Uninstall'
            Local $aKeys[3] = [2, $sHKCU & $sSubKey, $sHKLM & $sSubKey]
        Case Else
            Return SetError(1)
    EndSwitch

    Local $array[5001][7] = [[5000, 'DisplayVersion', 'Publisher', 'UninstallString', 'InstallDate', 'NoRemove', 'NoRepair']]
    Local $sAppKey, $sDisplayName, $sKey, $UnInstKey, $index = 0

    For $i = 1 To $aKeys[0]
        $sKey = $aKeys[$i]
        For $j = 1 To $array[0][0]
            $sAppKey = RegEnumKey($sKey, $j)
            If @error Then ExitLoop
            $UnInstKey = $sKey & '\' & $sAppKey
            $sDisplayName = RegRead($UnInstKey, 'DisplayName')
            If Not @error And StringLen(StringStripWS($sDisplayName, 8)) Then
                $index += 1
                $array[$index][0] = StringStripWS(StringReplace($sDisplayName, ' (remove only)', ''), 3)
                $array[$index][1] = StringStripWS(RegRead($UnInstKey, 'DisplayVersion'), 3)
                $array[$index][2] = StringStripWS(RegRead($UnInstKey, 'Publisher'), 3)
                $array[$index][3] = StringStripWS(RegRead($UnInstKey, 'UninstallString'), 3)
                $array[$index][4] = StringStripWS(RegRead($UnInstKey, 'InstallDate'), 3)
                $array[$index][5] = StringStripWS(RegRead($UnInstKey, 'NoRemove'), 3)
                $array[$index][6] = StringStripWS(RegRead($UnInstKey, 'NoRepair'), 3)
            EndIf
        Next
    Next

    ReDim $array[$index + 1][7]
    $array[0][0] = $index
    _ArraySort($array, 0, 1)
    Return $array
EndFunc



;------------------------------

#include-Once

; #INDEX# =======================================================================================================================
; Title .........: Array
; AutoIt Version : 3.2.10++
; Language ......: English
; Description ...: Functions for manipulating arrays.
; Author(s) .....: JdeB, Erik Pilsits, Ultima, Dale (Klaatu) Thompson, Cephas,randallc, Gary Frost, GEOSoft,
;                  Helias Gerassimou(hgeras), Brian Keene, Michael Michta, gcriaco, LazyCoder, Tylo, David Nuttall,
;                  Adam Moore (redndahead), SmOke_N, litlmike, Valik, Melba23
; ===============================================================================================================================

; #CURRENT# =====================================================================================================================
; _ArrayAdd
; _ArrayBinarySearch
; _ArrayCombinations
; _ArrayConcatenate
; _ArrayDelete
; _ArrayDisplay
; _ArrayFindAll
; _ArrayInsert
; _ArrayMax
; _ArrayMaxIndex
; _ArrayMin
; _ArrayMinIndex
; _ArrayPermute
; _ArrayPop
; _ArrayPush
; _ArrayReverse
; _ArraySearch
; _ArraySort
; _ArraySwap
; _ArrayToClip
; _ArrayToString
; _ArrayTranspose
; _ArrayTrim
; _ArrayUnique
; ===============================================================================================================================

; #INTERNAL_USE_ONLY# ===========================================================================================================
; __Array_Combinations
; __Array_ExeterInternal
; __Array_GetNext
; __ArrayDualPivotSort
; __ArrayQuickSort1D
; __ArrayQuickSort2D
; ===============================================================================================================================

; #FUNCTION# ====================================================================================================================
; Author ........: Jos van der Zande <jdeb at autoitscript dot com>
; Modified.......: Ultima - code cleanup
; ===============================================================================================================================
Func _ArrayAdd(ByRef $avArray, $vValue)
    If Not IsArray($avArray) Then Return SetError(1, 0, -1)
    If UBound($avArray, 0) <> 1 Then Return SetError(2, 0, -1)

    Local $iUBound = UBound($avArray)
    ReDim $avArray[$iUBound + 1]
    $avArray[$iUBound] = $vValue
    Return $iUBound
EndFunc   ;==>_ArrayAdd

; #FUNCTION# ====================================================================================================================
; Author ........: Jos van der Zande <jdeb at autoitscript dot com>
; Modified.......: Ultima - added $iEnd as parameter, code cleanup; Melba23 - added support for empty arrays
; ===============================================================================================================================
Func _ArrayBinarySearch(Const ByRef $avArray, $vValue, $iStart = 0, $iEnd = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, -1)
    If UBound($avArray, 0) <> 1 Then Return SetError(5, 0, -1)

    Local $iUBound = UBound($avArray) - 1
    If $iUBound = -1 Then Return SetError(6, 0, -1)

    ; Bounds checking
    If $iEnd < 1 Or $iEnd > $iUBound Then $iEnd = $iUBound
    If $iStart < 0 Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(4, 0, -1)

    Local $iMid = Int(($iEnd + $iStart) / 2)

    If $avArray[$iStart] > $vValue Or $avArray[$iEnd] < $vValue Then Return SetError(2, 0, -1)

    ; Search
    While $iStart <= $iMid And $vValue <> $avArray[$iMid]
        If $vValue < $avArray[$iMid] Then
            $iEnd = $iMid - 1
        Else
            $iStart = $iMid + 1
        EndIf
        $iMid = Int(($iEnd + $iStart) / 2)
    WEnd

    If $iStart > $iEnd Then Return SetError(3, 0, -1) ; Entry not found

    Return $iMid
EndFunc   ;==>_ArrayBinarySearch

; #FUNCTION# ====================================================================================================================
; Author ........: Erik Pilsits
; Modified.......: 07/08/2008
; ===============================================================================================================================
Func _ArrayCombinations(Const ByRef $avArray, $iSet, $sDelim = "")
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)
    If UBound($avArray, 0) <> 1 Then Return SetError(2, 0, 0)

    Local $iN = UBound($avArray)
    Local $iR = $iSet
    Local $aIdx[$iR]
    For $i = 0 To $iR - 1
        $aIdx[$i] = $i
    Next
    Local $iTotal = __Array_Combinations($iN, $iR)
    Local $iLeft = $iTotal
    Local $aResult[$iTotal + 1]
    $aResult[0] = $iTotal

    Local $iCount = 1
    While $iLeft > 0
        __Array_GetNext($iN, $iR, $iLeft, $iTotal, $aIdx)
        For $i = 0 To $iSet - 1
            $aResult[$iCount] &= $avArray[$aIdx[$i]] & $sDelim
        Next
        If $sDelim <> "" Then $aResult[$iCount] = StringTrimRight($aResult[$iCount], 1)
        $iCount += 1
    WEnd
    Return $aResult
EndFunc   ;==>_ArrayCombinations

; #FUNCTION# ====================================================================================================================
; Author ........: Ultima
; Modified.......: Partypooper - added target start index
; ===============================================================================================================================
Func _ArrayConcatenate(ByRef $avArrayTarget, Const ByRef $avArraySource, $iStart = 0)
    If Not IsArray($avArrayTarget) Then Return SetError(1, 0, 0)
    If Not IsArray($avArraySource) Then Return SetError(2, 0, 0)
    If UBound($avArrayTarget, 0) <> 1 Then
        If UBound($avArraySource, 0) <> 1 Then Return SetError(5, 0, 0)
        Return SetError(3, 0, 0)
    EndIf
    If UBound($avArraySource, 0) <> 1 Then Return SetError(4, 0, 0)

    Local $iUBoundTarget = UBound($avArrayTarget) - $iStart, $iUBoundSource = UBound($avArraySource)
    ReDim $avArrayTarget[$iUBoundTarget + $iUBoundSource]
    For $i = $iStart To $iUBoundSource - 1
        $avArrayTarget[$iUBoundTarget + $i] = $avArraySource[$i]
    Next

    Return $iUBoundTarget + $iUBoundSource
EndFunc   ;==>_ArrayConcatenate

; #FUNCTION# ====================================================================================================================
; Author ........: Cephas <cephas at clergy dot net>
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> - array passed ByRef
; ===============================================================================================================================
Func _ArrayDelete(ByRef $avArray, $iElement)
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)
    Local $iUBound = UBound($avArray, 1) - 1
    ; Bounds checking
    If $iElement < 0 Then $iElement = 0
    If $iElement > $iUBound Then $iElement = $iUBound

    ; Move items after $iElement up by 1
    Switch UBound($avArray, 0)
        Case 1
            For $i = $iElement To $iUBound - 1
                $avArray[$i] = $avArray[$i + 1]
            Next
            ReDim $avArray[$iUBound]
        Case 2
            Local $iSubMax = UBound($avArray, 2) - 1
            For $i = $iElement To $iUBound - 1
                For $j = 0 To $iSubMax
                    $avArray[$i][$j] = $avArray[$i + 1][$j]
                Next
            Next
            ReDim $avArray[$iUBound][$iSubMax + 1]
        Case Else
            Return SetError(3, 0, 0)
    EndSwitch

    Return $iUBound
EndFunc   ;==>_ArrayDelete

; #FUNCTION# ====================================================================================================================
; Author ........: randallc, Ultima
; Modified.......: Gary Frost (gafrost), Ultima, Zedna, jpm, Melba23, AZJIO, UEZ
; ===============================================================================================================================
Func _ArrayDisplay(Const ByRef $avArray, $sTitle = Default, $sArray_Range = Default, $iFlags = Default, $vUser_Separator = Default, $sHeader = Default, $iMax_ColWidth = Default, $iAlt_Color = Default, $hUser_Func = Default)

    Local Const $_ARRAYCONSTANT_MB_SYSTEMMODAL = 4096
    Local Const $_ARRAYCONSTANT_MB_ICONERROR = 16
    Local Const $_ARRAYCONSTANT_MB_YESNO = 4
    Local Const $_ARRAYCONSTANT_IDYES = 6

    ; Default values
    If $sTitle = Default Then $sTitle = "ArrayDisplay"
    If $sArray_Range = Default Then $sArray_Range = ""
    If $iFlags = Default Then $iFlags = 0
    If $vUser_Separator = Default Then $vUser_Separator = ""
    If $sHeader = Default Then $sHeader = ""
    If $iMax_ColWidth = Default Then $iMax_ColWidth = 350
    If $iAlt_Color = Default Then $iAlt_Color = 0
    If $hUser_Func = Default Then $hUser_Func = 0

    ; Check for transpose, column align and verbosity
    Local $iTranspose = BitAND($iFlags, 1)
    Local $iColAlign = BitAND($iFlags, 6) ; 0 = Left (default); 2 = Right; 4 = CenterLocal $iVerbose = BitAnd($iFlags, 8)
    Local $iVerbose = BitAND($iFlags, 8)

    ; Check valid array
    Local $sMsg = "", $iRet
    If IsArray($avArray) Then
        ; Dimension checking
        Local $iDimension = UBound($avArray, 0), $iRowCount = UBound($avArray, 1), $iColCount = UBound($avArray, 2)
        If $iDimension > 2 Then
            $sMsg = "Larger than 2D array passed to function"
            $iRet = 2
        EndIf
    Else
        $sMsg = "No array variable passed to function"
        $iRet = 1
    EndIf
    If $sMsg Then
        If $iVerbose And MsgBox($_ARRAYCONSTANT_MB_SYSTEMMODAL + $_ARRAYCONSTANT_MB_ICONERROR + $_ARRAYCONSTANT_MB_YESNO, _
                "ArrayDisplay Error: " & $sTitle, $sMsg & @CRLF & @CRLF & "Exit the script?") = $_ARRAYCONSTANT_IDYES Then
            Exit
        Else
            Return SetError($iRet, 0, "")
        EndIf
    EndIf

    ; Determine copy separator
    Local $iCW_ColWidth = Number($vUser_Separator)

    ; Separator handling
    Local $sAD_Separator = ChrW(0xFAB1)
    ; Set separator to use in this UDF and store existing one
    Local $sCurr_Separator = Opt("GUIDataSeparatorChar", $sAD_Separator)
    ; Set default user separator if required
    If $vUser_Separator = "" Then $vUser_Separator = $sCurr_Separator

    ; Declare variables
    Local $vTmp, $iRowLimit = 65525, $iColLimit = 250 ; Row = 64k control limit minus 4 buttons; Column - arbitrary limit

    ; Set original dimensions for data display
    Local $iDataRow = $iRowCount
    Local $iDataCol = $iColCount

    ; Set display limits for dimensions
    Local $iItem_Start = 0, $iItem_End = $iRowCount - 1, $iSubItem_Start = 0, $iSubItem_End = $iColCount - 1, $avDimSplit, $avRangeSplit
    ; Adjust for 1D array
    If $iDimension = 1 Then $iSubItem_End = 0
    ; Check for range set
    If $sArray_Range Then
        ; Force trailing | if required
        If (Not StringInStr($sArray_Range, "|")) Then
            $sArray_Range &= "|"
        EndIf
        ; Split into row|col
        $avDimSplit = StringSplit($sArray_Range, "|")
        ; Check valid ranges
        If Not @error Then
            $avRangeSplit = StringSplit($avDimSplit[1], ":")
            If @error Then
                $iItem_Start = 0
                If Number($avRangeSplit[1]) Then
                    $iItem_End = Number($avRangeSplit[1])
                EndIf
            Else
                $iItem_Start = Number($avRangeSplit[1])
                If Number($avRangeSplit[2]) Then
                    $iItem_End = Number($avRangeSplit[2])
                EndIf
            EndIf
            $avRangeSplit = StringSplit($avDimSplit[2], ":")
            If @error Then
                $iSubItem_Start = 0
                If Number($avRangeSplit[1]) Then
                    $iSubItem_End = Number($avRangeSplit[1])
                EndIf
            Else
                $iSubItem_Start = Number($avRangeSplit[1])
                If Number($avRangeSplit[2]) Then
                    $iSubItem_End = Number($avRangeSplit[2])
                EndIf
            EndIf
            ; Check limits
            If $iItem_Start < 0 Then $iItem_Start = 0
            If $iSubItem_Start < 0 Then $iSubItem_Start = 0
            If $iItem_End > $iRowCount - 1 Then $iItem_End = $iRowCount - 1
            If $iSubItem_End > $iColCount - 1 Then $iSubItem_End = $iColCount - 1
        EndIf
    EndIf

    ; Create data display
    Local $sDisplayData = "[" & $iDataRow
    ; Check if rows will be truncated
    Local $fTruncated = False
    If $iTranspose Then
        If $iItem_End - $iItem_Start > $iColLimit Then
            $fTruncated = True
            $iItem_End = $iItem_Start + $iColLimit - 1
        EndIf
    Else
        If $iItem_End - $iItem_Start > $iRowLimit Then
            $fTruncated = True
            $iItem_End = $iItem_Start + $iRowLimit - 1
        EndIf
    EndIf
    If $fTruncated Then
        $sDisplayData &= "*]"
    Else
        $sDisplayData &= "]"
    EndIf
    If $iDimension = 2 Then
        $sDisplayData &= " [" & $iDataCol
        If $iTranspose Then
            If $iSubItem_End - $iSubItem_Start > $iRowLimit Then
                $fTruncated = True
                $iSubItem_End = $iSubItem_Start + $iRowLimit - 1
            EndIf
        Else
            If $iSubItem_End - $iSubItem_Start > $iColLimit Then
                $fTruncated = True
                $iSubItem_End = $iSubItem_Start + $iColLimit - 1
            EndIf
        EndIf
        If $fTruncated Then
            $sDisplayData &= "*]"
        Else
            $sDisplayData &= "]"
        EndIf
    EndIf
    ; Create tooltip data
    Local $sTipData = ""
    If $fTruncated Then $sTipData &= "Truncated"
    If $sArray_Range Then
        If $sTipData Then $sTipData &= " - "
        $sTipData &= "Range set"
    EndIf
    If $iTranspose Then
        If $sTipData Then $sTipData &= " - "
        $sTipData &= "Transposed"
    EndIf

    ; Split custom header on separator
    Local $asHeader = StringSplit($sHeader, $sCurr_Separator, 2) ; No count element
    $sHeader = "Row"
    Local $iIndex = $iSubItem_Start
    If $iTranspose Then
        ; All default headers
        For $j = $iItem_Start To $iItem_End
            $sHeader &= $sAD_Separator & "Col " & $j
        Next
    Else
        ; Create custom header with available items
        If $asHeader[0] Then
            ; Set as many as available
            For $iIndex = $iSubItem_Start To $iSubItem_End
                ; Check custom header available
                If $iIndex >= UBound($asHeader) Then ExitLoop
                $sHeader &= $sAD_Separator & $asHeader[$iIndex]
            Next
        EndIf
        ; Add default headers to fill to end
        For $j = $iIndex To $iSubItem_End
            $sHeader &= $sAD_Separator & "Col " & $j
        Next
    EndIf

    ; Display splash dialog if required
    If $iVerbose And ($iItem_End - $iItem_Start) * ($iSubItem_End - $iSubItem_Start) > 10000 Then
        SplashTextOn("ArrayDisplay", "Preparing display" & @CRLF & @CRLF & "Please be patient", 300, 100)
    EndIf

    ; Convert array into ListViewItem compatible lines
    If $iTranspose Then
        ; Swap dimensions
        $vTmp = $iItem_Start
        $iItem_Start = $iSubItem_Start
        $iSubItem_Start = $vTmp
        $vTmp = $iItem_End
        $iItem_End = $iSubItem_End
        $iSubItem_End = $vTmp
    EndIf
    Local $avArrayText[$iItem_End - $iItem_Start + 1]
    For $i = $iItem_Start To $iItem_End
        $avArrayText[$i - $iItem_Start] = "[" & $i & "]"
        For $j = $iSubItem_Start To $iSubItem_End
            If $iDimension = 1 Then
                If $iTranspose Then
                    $vTmp = $avArray[$j]
                Else
                    $vTmp = $avArray[$i]
                EndIf
            Else
                If $iTranspose Then
                    $vTmp = $avArray[$j][$i]
                Else
                    $vTmp = $avArray[$i][$j]
                EndIf
            EndIf
            $avArrayText[$i - $iItem_Start] &= $sAD_Separator & $vTmp
        Next
    Next

    ; GUI Constants
    Local Const $_ARRAYCONSTANT_GUI_DOCKBOTTOM = 64
    Local Const $_ARRAYCONSTANT_GUI_DOCKBORDERS = 102
    Local Const $_ARRAYCONSTANT_GUI_DOCKHEIGHT = 512
    Local Const $_ARRAYCONSTANT_GUI_DOCKLEFT = 2
    Local Const $_ARRAYCONSTANT_GUI_DOCKRIGHT = 4
    Local Const $_ARRAYCONSTANT_GUI_DOCKHCENTER = 8
    Local Const $_ARRAYCONSTANT_GUI_EVENT_CLOSE = -3
    Local Const $_ARRAYCONSTANT_GUI_DISABLE = 128
    Local Const $_ARRAYCONSTANT_GUI_FOCUS = 256
    Local Const $_ARRAYCONSTANT_GUI_BKCOLOR_LV_ALTERNATE = 0xFE000000
    Local Const $_ARRAYCONSTANT_SS_CENTER = 0x1
    Local Const $_ARRAYCONSTANT_SS_CENTERIMAGE = 0x0200
    Local Const $_ARRAYCONSTANT_LVM_GETITEMCOUNT = (0x1000 + 4)
    Local Const $_ARRAYCONSTANT_LVM_GETITEMRECT = (0x1000 + 14)
    Local Const $_ARRAYCONSTANT_LVM_GETCOLUMNWIDTH = (0x1000 + 29)
    Local Const $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH = (0x1000 + 30)
    Local Const $_ARRAYCONSTANT_LVM_GETITEMSTATE = (0x1000 + 44)
    Local Const $_ARRAYCONSTANT_LVM_GETSELECTEDCOUNT = (0x1000 + 50)
    Local Const $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE = (0x1000 + 54)
    Local Const $_ARRAYCONSTANT_LVS_EX_GRIDLINES = 0x1
    Local Const $_ARRAYCONSTANT_LVIS_SELECTED = 0x2
    Local Const $_ARRAYCONSTANT_LVS_SHOWSELALWAYS = 0x8
    Local Const $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT = 0x20
    Local Const $_ARRAYCONSTANT_WS_EX_CLIENTEDGE = 0x0200
    Local Const $_ARRAYCONSTANT_WS_MAXIMIZEBOX = 0x00010000
    Local Const $_ARRAYCONSTANT_WS_MINIMIZEBOX = 0x00020000
    Local Const $_ARRAYCONSTANT_WS_SIZEBOX = 0x00040000
    Local Const $_ARRAYCONSTANT_WM_SETREDRAW = 11
    Local Const $_ARRAYCONSTANT_LVSCW_AUTOSIZE = -1

    ; Create GUI
    Local $iOrgWidth = 210, $iHeight = 200, $iMinSize = 250
    Local $hGUI = GUICreate($sTitle, $iOrgWidth, $iHeight, Default, Default, BitOR($_ARRAYCONSTANT_WS_SIZEBOX, $_ARRAYCONSTANT_WS_MINIMIZEBOX, $_ARRAYCONSTANT_WS_MAXIMIZEBOX))
    Local $aiGUISize = WinGetClientSize($hGUI)
    Local $iButtonWidth_2 = $aiGUISize[0] / 2
    Local $iButtonWidth_3 = $aiGUISize[0] / 3
    ; Create ListView
    Global $cListView = GUICtrlCreateListView($sHeader, 0, 0, $aiGUISize[0], $aiGUISize[1] - 46, $_ARRAYCONSTANT_LVS_SHOWSELALWAYS)
    GUICtrlSetBkColor($cListView, $_ARRAYCONSTANT_GUI_BKCOLOR_LV_ALTERNATE)
    GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_LVS_EX_GRIDLINES, $_ARRAYCONSTANT_LVS_EX_GRIDLINES)
    GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT, $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT)
    GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_WS_EX_CLIENTEDGE, $_ARRAYCONSTANT_WS_EX_CLIENTEDGE)
    ; Create buttons
    Local $cCopy_ID = GUICtrlCreateButton("Copy Data && Hdr/Row", 0, $aiGUISize[1] - 43, $iButtonWidth_2, 20)
    Local $cCopy_Data = GUICtrlCreateButton("Copy Data Only", $iButtonWidth_2, $aiGUISize[1] - 43, $iButtonWidth_2, 20)
    Local $cData_Label = GUICtrlCreateLabel($sDisplayData, 5, $aiGUISize[1] - 22, $iButtonWidth_3 - 5, 18, BitOR($_ARRAYCONSTANT_SS_CENTER, $_ARRAYCONSTANT_SS_CENTERIMAGE))
    Local $cUser_Func = GUICtrlCreateButton("Uninstall Selection", $iButtonWidth_3, $aiGUISize[1] - 23, $iButtonWidth_3, 20)
;~  If Not IsFunc($hUser_Func) Then GUICtrlSetState($cUser_Func, $_ARRAYCONSTANT_GUI_DISABLE)
    Local $cExit_Script = GUICtrlCreateButton("Exit Script", $iButtonWidth_3 * 2, $aiGUISize[1] - 23, $iButtonWidth_3, 20)
    ; Change label colour and create tooltip if required
    Select
        Case $fTruncated Or $iTranspose Or $sArray_Range
            GUICtrlSetColor($cData_Label, 0xFF0000)
            GUICtrlSetTip($cData_Label, $sTipData)
    EndSelect
    ; Set resizing
    GUICtrlSetResizing($cListView, $_ARRAYCONSTANT_GUI_DOCKBORDERS)
    GUICtrlSetResizing($cCopy_ID, $_ARRAYCONSTANT_GUI_DOCKLEFT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($cCopy_Data, $_ARRAYCONSTANT_GUI_DOCKRIGHT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($cData_Label, $_ARRAYCONSTANT_GUI_DOCKLEFT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($cUser_Func, $_ARRAYCONSTANT_GUI_DOCKHCENTER + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($cExit_Script, $_ARRAYCONSTANT_GUI_DOCKRIGHT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)

    ; Start ListView update
    GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_WM_SETREDRAW, 0, 0)

    ; Fill listview
    Local $cItem
    For $i = 0 To UBound($avArrayText) - 1
        $cItem = GUICtrlCreateListViewItem($avArrayText[$i], $cListView)
        If $iAlt_Color Then
            GUICtrlSetBkColor($cItem, $iAlt_Color)
        EndIf
    Next

    ; Align columns if required - $iColAlign = 2 for Right and 4 for Center
    If $iColAlign Then
        Local Const $_ARRAYCONSTANT_LVCF_FMT = 0x01
        Local Const $_ARRAYCONSTANT_LVM_SETCOLUMNW = (0x1000 + 96)
        Local $tColumn = DllStructCreate("uint Mask;int Fmt;int CX;ptr Text;int TextMax;int SubItem;int Image;int Order;int cxMin;int cxDefault;int cxIdeal")
        DllStructSetData($tColumn, "Mask", $_ARRAYCONSTANT_LVCF_FMT)
        DllStructSetData($tColumn, "Fmt", $iColAlign / 2) ; Left = 0; Right = 1; Center = 2
        Local $pColumn = DllStructGetPtr($tColumn)
        ; Loop through columns
        For $i = 1 To $iSubItem_End - $iSubItem_Start + 1
            GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_SETCOLUMNW, $i, $pColumn)
        Next
    EndIf

    ; End ListView update
    GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_WM_SETREDRAW, 1, 0)

    ; Allow for borders with and without vertical scrollbar
    Local $iBorder = 45
    If UBound($avArrayText) > 20 Then
        $iBorder += 20
    EndIf
    ; Adjust dialog width
    Local $iWidth = $iBorder, $iColWidth = 0, $aiColWidth[$iSubItem_End - $iSubItem_Start + 2], $iMin_ColWidth = 55
    ; Get required column widths to fit items
    For $i = 0 To $iSubItem_End - $iSubItem_Start + 1
        GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH, $i, $_ARRAYCONSTANT_LVSCW_AUTOSIZE)
        $iColWidth = GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_GETCOLUMNWIDTH, $i, 0)
        ; Set minimum if required
        If $iColWidth < $iMin_ColWidth Then
            GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH, $i, $iMin_ColWidth)
            $iColWidth = $iMin_ColWidth
        EndIf
        ; Add to total width
        $iWidth += $iColWidth
        ; Store  value
        $aiColWidth[$i] = $iColWidth
    Next
    ; Now check max size
    If $iWidth > @DesktopWidth - 100 Then
        ; Apply max col width limit to reduce width
        $iWidth = $iBorder
        For $i = 0 To $iSubItem_End - $iSubItem_Start + 1
            If $aiColWidth[$i] > $iMax_ColWidth Then
                ; Reset width
                GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH, $i, $iMax_ColWidth)
                $iWidth += $iMax_ColWidth
            Else
                ; Retain width
                $iWidth += $aiColWidth[$i]
            EndIf
        Next
    EndIf
    ; Check max/min width
    If $iWidth > @DesktopWidth - 100 Then
        $iWidth = @DesktopWidth - 100
    ElseIf $iWidth < $iMinSize Then
        $iWidth = $iMinSize
    EndIf

    ; Get row height
    Local $tRect = DllStructCreate("struct; long Left;long Top;long Right;long Bottom; endstruct")
    DllCall("user32.dll", "struct*", "SendMessageW", "hwnd", GUICtrlGetHandle($cListView), "uint", $_ARRAYCONSTANT_LVM_GETITEMRECT, "wparam", 0, "struct*", $tRect)
    ; Set required GUI height
    Local $aiWin_Pos = WinGetPos($hGUI)
    Local $aiLV_Pos = ControlGetPos($hGUI, "", $cListView)
    $iHeight = ((UBound($avArrayText) + 3) * (DllStructGetData($tRect, "Bottom") - DllStructGetData($tRect, "Top"))) + $aiWin_Pos[3] - $aiLV_Pos[3]
    ; Check min/max height
    If $iHeight > @DesktopHeight - 100 Then
        $iHeight = @DesktopHeight - 100
    ElseIf $iHeight < $iMinSize Then
        $iHeight = $iMinSize
    EndIf

    SplashOff()

    ; Display and resize dialog
    GUISetState(@SW_HIDE, $hGUI)
    WinMove($hGUI, "", (@DesktopWidth - $iWidth) / 2, (@DesktopHeight - $iHeight) / 2, $iWidth, $iHeight)
    GUISetState(@SW_SHOW, $hGUI)

    ; Switch to GetMessage mode
    Local $iOnEventMode = Opt("GUIOnEventMode", 0), $iMsg

    While 1

        $iMsg = GUIGetMsg() ; Variable needed to check which "Copy" button was pressed
        Switch $iMsg
            Case $_ARRAYCONSTANT_GUI_EVENT_CLOSE
                ExitLoop

            Case $cCopy_ID, $cCopy_Data
                ; Count selected rows
                Local $iSel_Count = GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_GETSELECTEDCOUNT, 0, 0)
                ; Display splash dialog if required
                If $iVerbose And (Not $iSel_Count) And ($iItem_End - $iItem_Start) * ($iSubItem_End - $iSubItem_Start) > 10000 Then
                    SplashTextOn("ArrayDisplay", "Copying data" & @CRLF & @CRLF & "Please be patient", 300, 100)
                EndIf
                ; Generate clipboard text
                Local $sClip = "", $sItem, $aSplit
                ; Add items
                For $i = 0 To $iItem_End - $iItem_Start
                    ; Skip if copying selected rows and item not selected
                    If $iSel_Count And Not (GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_GETITEMSTATE, $i, $_ARRAYCONSTANT_LVIS_SELECTED)) Then
                        ContinueLoop
                    EndIf
                    $sItem = $avArrayText[$i]
                    If $iMsg = $cCopy_Data Then
                        ; Remove row ID if required
                        $sItem = StringRegExpReplace($sItem, "^\[\d+\].(.*)$", "$1")
                    EndIf
                    If $iCW_ColWidth Then
                        ; Expand columns
                        $aSplit = StringSplit($sItem, $sAD_Separator)
                        $sItem = ""
                        For $j = 1 To $aSplit[0]
                            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
                        Next
                    Else
                        ; Use defined separator
                        $sItem = StringReplace($sItem, $sAD_Separator, $vUser_Separator)
                    EndIf
                    $sClip &= $sItem & @CRLF
                Next
                ; Add header line if required
                If $iMsg = $cCopy_ID Then
                    If $iCW_ColWidth Then
                        $aSplit = StringSplit($sHeader, $sAD_Separator)
                        $sItem = ""
                        For $j = 1 To $aSplit[0]
                            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
                        Next
                    Else
                        $sItem = StringReplace($sHeader, $sAD_Separator, $vUser_Separator)
                    EndIf
                    $sClip = $sItem & @CRLF & $sClip
                EndIf
                ;Send to clipboard
                ClipPut($sClip)
                ; Remove splash if used
                SplashOff()
                ; Refocus ListView
                GUICtrlSetState($cListView, $_ARRAYCONSTANT_GUI_FOCUS)

            Case $cUser_Func
                ; Get selected indices
                Local $aiSelItems[$iRowLimit] = [0]
                For $i = 0 To GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_GETITEMCOUNT, 0, 0)
                    If GUICtrlSendMsg($cListView, $_ARRAYCONSTANT_LVM_GETITEMSTATE, $i, $_ARRAYCONSTANT_LVIS_SELECTED) Then
                        $aiSelItems[0] += 1
                        $aiSelItems[$aiSelItems[0]] = $i + $iItem_Start
                    EndIf
                Next
                ReDim $aiSelItems[$aiSelItems[0] + 1]


                run($avArray[$aiSelItems[1]][3])
                exit

            Case $cExit_Script
                Exit
        EndSwitch
    WEnd

    ; Clear up
    GUIDelete($hGUI)
    Opt("GUIOnEventMode", $iOnEventMode) ; Reset original GUI mode
    Opt("GUIDataSeparatorChar", $sCurr_Separator) ; Reset original separator

    Return 1

EndFunc   ;==>_ArrayDisplay

; #FUNCTION# ====================================================================================================================
; Author ........: GEOSoft, Ultima
; Modified.......:
; ===============================================================================================================================
Func _ArrayFindAll(Const ByRef $avArray, $vValue, $iStart = 0, $iEnd = 0, $iCase = 0, $iCompare = 0, $iSubItem = 0)
    $iStart = _ArraySearch($avArray, $vValue, $iStart, $iEnd, $iCase, $iCompare, 1, $iSubItem)
    If @error Then Return SetError(@error, 0, -1)

    Local $iIndex = 0, $avResult[UBound($avArray)]
    Do
        $avResult[$iIndex] = $iStart
        $iIndex += 1
        $iStart = _ArraySearch($avArray, $vValue, $iStart + 1, $iEnd, $iCase, $iCompare, 1, $iSubItem)
    Until @error

    ReDim $avResult[$iIndex]
    Return $avResult
EndFunc   ;==>_ArrayFindAll

; #FUNCTION# ====================================================================================================================
; Author ........: Jos van der Zande <jdeb at autoitscript dot com>: Ultima - code cleanup; Melba23 - element position check
; ===============================================================================================================================
Func _ArrayInsert(ByRef $avArray, $iElement, $vValue = "")
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)
    If UBound($avArray, 0) <> 1 Then Return SetError(2, 0, 0)

    ; Check element in array bounds + 1
    If $iElement > UBound($avArray) Then Return SetError(3, 0, 0)

    ; Add 1 to the array
    Local $iUBound = UBound($avArray) + 1
    ReDim $avArray[$iUBound]

    ; Move all entries over til the specified element
    For $i = $iUBound - 1 To $iElement + 1 Step -1
        $avArray[$i] = $avArray[$i - 1]
    Next

    ; Add the value in the specified element
    $avArray[$iElement] = $vValue
    Return $iUBound
EndFunc   ;==>_ArrayInsert

; #FUNCTION# ====================================================================================================================
; Author ........: Cephas <cephas at clergy dot net>
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> - Added $iCompNumeric and $iStart parameters and logic, Ultima - added $iEnd parameter, code cleanup
; ===============================================================================================================================
Func _ArrayMax(Const ByRef $avArray, $iCompNumeric = 0, $iStart = 0, $iEnd = 0)
    Local $iResult = _ArrayMaxIndex($avArray, $iCompNumeric, $iStart, $iEnd)
    If @error Then Return SetError(@error, 0, "")
    Return $avArray[$iResult]
EndFunc   ;==>_ArrayMax

; #FUNCTION# ====================================================================================================================
; Author ........: Cephas <cephas at clergy dot net>
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> - Added $iCompNumeric and $iStart parameters and logic
; ===============================================================================================================================
Func _ArrayMaxIndex(Const ByRef $avArray, $iCompNumeric = 0, $iStart = 0, $iEnd = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, -1)
    If UBound($avArray, 0) <> 1 Then Return SetError(3, 0, -1)
    If Not UBound($avArray) Then Return SetError(4, 0, -1)

    Local $iUBound = UBound($avArray) - 1

    ; Bounds checking
    If $iEnd < 1 Or $iEnd > $iUBound Then $iEnd = $iUBound
    If $iStart < 0 Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(2, 0, -1)

    Local $iMaxIndex = $iStart

    ; Search
    If $iCompNumeric Then
        For $i = $iStart To $iEnd
            If Number($avArray[$iMaxIndex]) < Number($avArray[$i]) Then $iMaxIndex = $i
        Next
    Else
        For $i = $iStart To $iEnd
            If $avArray[$iMaxIndex] < $avArray[$i] Then $iMaxIndex = $i
        Next
    EndIf

    Return $iMaxIndex
EndFunc   ;==>_ArrayMaxIndex

; #FUNCTION# ====================================================================================================================
; Author ........: Cephas <cephas at clergy dot net>
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> - Added $iCompNumeric and $iStart parameters and logic, Ultima - added $iEnd parameter, code cleanup
; ===============================================================================================================================
Func _ArrayMin(Const ByRef $avArray, $iCompNumeric = 0, $iStart = 0, $iEnd = 0)
    Local $iResult = _ArrayMinIndex($avArray, $iCompNumeric, $iStart, $iEnd)
    If @error Then Return SetError(@error, 0, "")
    Return $avArray[$iResult]
EndFunc   ;==>_ArrayMin

; #FUNCTION# ====================================================================================================================
; Author ........: Cephas <cephas at clergy dot net>
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> - Added $iCompNumeric and $iStart parameters and logic
; ===============================================================================================================================
Func _ArrayMinIndex(Const ByRef $avArray, $iCompNumeric = 0, $iStart = 0, $iEnd = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, -1)
    If UBound($avArray, 0) <> 1 Then Return SetError(3, 0, -1)
    If Not UBound($avArray) Then Return SetError(4, 0, -1)

    Local $iUBound = UBound($avArray) - 1

    ; Bounds checking
    If $iEnd < 1 Or $iEnd > $iUBound Then $iEnd = $iUBound
    If $iStart < 0 Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(2, 0, -1)

    Local $iMinIndex = $iStart

    ; Search
    If $iCompNumeric Then
        For $i = $iStart To $iEnd
            If Number($avArray[$iMinIndex]) > Number($avArray[$i]) Then $iMinIndex = $i
        Next
    Else
        For $i = $iStart To $iEnd
            If $avArray[$iMinIndex] > $avArray[$i] Then $iMinIndex = $i
        Next
    EndIf

    Return $iMinIndex
EndFunc   ;==>_ArrayMinIndex

; #FUNCTION# ====================================================================================================================
; Author ........: Erik Pilsits
; Modified.......: Melba23 - added support for empty arrays
; ===============================================================================================================================
Func _ArrayPermute(ByRef $avArray, $sDelim = "")
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)
    If UBound($avArray, 0) <> 1 Then Return SetError(2, 0, 0)
    Local $iSize = UBound($avArray), $iFactorial = 1, $aIdx[$iSize], $aResult[1], $iCount = 1

    If UBound($avArray) Then
        For $i = 0 To $iSize - 1
            $aIdx[$i] = $i
        Next
        For $i = $iSize To 1 Step -1
            $iFactorial *= $i
        Next
        ReDim $aResult[$iFactorial + 1]
        $aResult[0] = $iFactorial
        __Array_ExeterInternal($avArray, 0, $iSize, $sDelim, $aIdx, $aResult, $iCount)
    Else
        $aResult[0] = 0
    EndIf
    Return $aResult
EndFunc   ;==>_ArrayPermute

; #FUNCTION# ====================================================================================================================
; Author ........: Cephas <cephas at clergy dot net>
; Modified.......: Ultima - code cleanup; Melba23 - added support for empty arrays
; ===============================================================================================================================
Func _ArrayPop(ByRef $avArray)
    If (Not IsArray($avArray)) Then Return SetError(1, 0, "")
    If UBound($avArray, 0) <> 1 Then Return SetError(2, 0, "")

    Local $iUBound = UBound($avArray) - 1
    If $iUBound = -1 Then Return SetError(3, 0, "")
    Local $sLastVal = $avArray[$iUBound]

    ; Remove last item
    If $iUBound > -1 Then
        ReDim $avArray[$iUBound]
    EndIf

    ; Return last item
    Return $sLastVal
EndFunc   ;==>_ArrayPop

; #FUNCTION# ====================================================================================================================
; Author ........: Helias Gerassimou(hgeras), Ultima - code cleanup/rewrite (major optimization), fixed support for $vValue as an array
; Modified.......:
; ===============================================================================================================================
Func _ArrayPush(ByRef $avArray, $vValue, $iDirection = 0)
    If (Not IsArray($avArray)) Then Return SetError(1, 0, 0)
    If UBound($avArray, 0) <> 1 Then Return SetError(3, 0, 0)
    Local $iUBound = UBound($avArray) - 1

    If IsArray($vValue) Then ; $vValue is an array
        Local $iUBoundS = UBound($vValue)
        If ($iUBoundS - 1) > $iUBound Then Return SetError(2, 0, 0)

        ; $vValue is an array smaller than $avArray
        If $iDirection Then ; slide right, add to front
            For $i = $iUBound To $iUBoundS Step -1
                $avArray[$i] = $avArray[$i - $iUBoundS]
            Next
            For $i = 0 To $iUBoundS - 1
                $avArray[$i] = $vValue[$i]
            Next
        Else ; slide left, add to end
            For $i = 0 To $iUBound - $iUBoundS
                $avArray[$i] = $avArray[$i + $iUBoundS]
            Next
            For $i = 0 To $iUBoundS - 1
                $avArray[$i + $iUBound - $iUBoundS + 1] = $vValue[$i]
            Next
        EndIf
    Else
        ; Check for empty array
        If $iUBound > -1 Then
            If $iDirection Then ; slide right, add to front
                For $i = $iUBound To 1 Step -1
                    $avArray[$i] = $avArray[$i - 1]
                Next
                $avArray[0] = $vValue
            Else ; slide left, add to end
                For $i = 0 To $iUBound - 1
                    $avArray[$i] = $avArray[$i + 1]
                Next
                $avArray[$iUBound] = $vValue
            EndIf
        EndIf
    EndIf

    Return 1
EndFunc   ;==>_ArrayPush

; #FUNCTION# ====================================================================================================================
; Author ........: Brian Keene
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> -  added $iStart parameter and logic; Tylo - added $iEnd parameter and rewrote it for speed
; ===============================================================================================================================
Func _ArrayReverse(ByRef $avArray, $iStart = 0, $iEnd = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)
    If UBound($avArray, 0) <> 1 Then Return SetError(3, 0, 0)
    If Not UBound($avArray) Then Return SetError(4, 0, 0)

    Local $vTmp, $iUBound = UBound($avArray) - 1

    ; Bounds checking
    If $iEnd < 1 Or $iEnd > $iUBound Then $iEnd = $iUBound
    If $iStart < 0 Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(2, 0, 0)

    ; Reverse
    For $i = $iStart To Int(($iStart + $iEnd - 1) / 2)
        $vTmp = $avArray[$i]
        $avArray[$i] = $avArray[$iEnd]
        $avArray[$iEnd] = $vTmp
        $iEnd -= 1
    Next

    Return 1
EndFunc   ;==>_ArrayReverse

; #FUNCTION# ====================================================================================================================
; Author ........: Michael Michta <MetalGX91 at GMail dot com>
; Modified.......: gcriaco <gcriaco at gmail dot com>; Ultima - 2D arrays supported, directional search, code cleanup, optimization; Melba23 - added support for empty arrays; BrunoJ - Added compare option 3 to use a regex pattern.
; ===============================================================================================================================
Func _ArraySearch(Const ByRef $avArray, $vValue, $iStart = 0, $iEnd = 0, $iCase = 0, $iCompare = 0, $iForward = 1, $iSubItem = -1)
    If Not IsArray($avArray) Then Return SetError(1, 0, -1)
    If UBound($avArray, 0) > 2 Or UBound($avArray, 0) < 1 Then Return SetError(2, 0, -1)

    Local $iUBound = UBound($avArray) - 1
    If $iUBound = -1 Then Return SetError(3, 0, -1)

    ; Bounds checking
    If $iEnd < 1 Or $iEnd > $iUBound Then $iEnd = $iUBound
    If $iStart < 0 Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(4, 0, -1)

    ; Direction (flip if $iForward = 0)
    Local $iStep = 1
    If Not $iForward Then
        Local $iTmp = $iStart
        $iStart = $iEnd
        $iEnd = $iTmp
        $iStep = -1
    EndIf

    ; same var Type of comparison
    Local $iCompType = False
    If $iCompare = 2 Then
        $iCompare = 0
        $iCompType = True
    EndIf

    ; Search
    Switch UBound($avArray, 0)
        Case 1 ; 1D array search
            If Not $iCompare Then
                If Not $iCase Then
                    For $i = $iStart To $iEnd Step $iStep
                        If $iCompType And VarGetType($avArray[$i]) <> VarGetType($vValue) Then ContinueLoop
                        If $avArray[$i] = $vValue Then Return $i
                    Next
                Else
                    For $i = $iStart To $iEnd Step $iStep
                        If $iCompType And VarGetType($avArray[$i]) <> VarGetType($vValue) Then ContinueLoop
                        If $avArray[$i] == $vValue Then Return $i
                    Next
                EndIf
            Else
                For $i = $iStart To $iEnd Step $iStep
                    If $iCompare = 3 Then
                        If StringRegExp($avArray[$i], $vValue) Then Return $i
                    Else
                        If StringInStr($avArray[$i], $vValue, $iCase) > 0 Then Return $i
                    EndIf
                Next
            EndIf
        Case 2 ; 2D array search
            Local $iUBoundSub = UBound($avArray, 2) - 1
            If $iSubItem > $iUBoundSub Then $iSubItem = $iUBoundSub
            If $iSubItem < 0 Then
                ; will search for all Col
                $iSubItem = 0
            Else
                $iUBoundSub = $iSubItem
            EndIf

            For $j = $iSubItem To $iUBoundSub
                If Not $iCompare Then
                    If Not $iCase Then
                        For $i = $iStart To $iEnd Step $iStep
                            If $iCompType And VarGetType($avArray[$i][$j]) <> VarGetType($vValue) Then ContinueLoop
                            If $avArray[$i][$j] = $vValue Then Return $i
                        Next
                    Else
                        For $i = $iStart To $iEnd Step $iStep
                            If $iCompType And VarGetType($avArray[$i][$j]) <> VarGetType($vValue) Then ContinueLoop
                            If $avArray[$i][$j] == $vValue Then Return $i
                        Next
                    EndIf
                Else
                    For $i = $iStart To $iEnd Step $iStep
                        If $iCompare = 3 Then
                            If StringRegExp($avArray[$i][$j], $vValue) Then Return $i
                        Else
                            If StringInStr($avArray[$i][$j], $vValue, $iCase) > 0 Then Return $i
                        EndIf
                    Next
                EndIf
            Next
        Case Else
            Return SetError(7, 0, -1)
    EndSwitch

    Return SetError(6, 0, -1)
EndFunc   ;==>_ArraySearch

; #FUNCTION# ====================================================================================================================
; Author ........: Jos van der Zande <jdeb at autoitscript dot com>
; Modified.......: LazyCoder - added $iSubItem option; Tylo - implemented stable QuickSort algo; Jos van der Zande - changed logic to correctly Sort arrays with mixed Values and Strings; Melba23 - implemented stable pivot algo
; ===============================================================================================================================
Func _ArraySort(ByRef $avArray, $iDescending = 0, $iStart = 0, $iEnd = 0, $iSubItem = 0, $iPivot = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)

    Local $iUBound = UBound($avArray) - 1
    If $iUBound = -1 Then Return SetError(5, 0, 0)

    ; Bounds checking
    If $iEnd = Default Then $iEnd = 0
    If $iEnd < 1 Or $iEnd > $iUBound Or $iEnd = Default Then $iEnd = $iUBound
    If $iStart < 0 Or $iStart = Default Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(2, 0, 0)

    If $iDescending = Default Then $iDescending = 0
    If $iPivot = Default Then $iPivot = 0
    If $iSubItem = Default Then $iSubItem = 0

    ; Sort
    Switch UBound($avArray, 0)
        Case 1
            If $iPivot Then ; Switch algorithms as required
                __ArrayDualPivotSort($avArray, $iStart, $iEnd)
            Else
                __ArrayQuickSort1D($avArray, $iStart, $iEnd)
            EndIf
            If $iDescending Then _ArrayReverse($avArray, $iStart, $iEnd)
        Case 2
            If $iPivot Then Return SetError(6, 0, 0) ; Error if 2D array and $iPivot
            Local $iSubMax = UBound($avArray, 2) - 1
            If $iSubItem > $iSubMax Then Return SetError(3, 0, 0)

            If $iDescending Then
                $iDescending = -1
            Else
                $iDescending = 1
            EndIf

            __ArrayQuickSort2D($avArray, $iDescending, $iStart, $iEnd, $iSubItem, $iSubMax)
        Case Else
            Return SetError(4, 0, 0)
    EndSwitch

    Return 1
EndFunc   ;==>_ArraySort

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __ArrayQuickSort1D
; Description ...: Helper function for sorting 1D arrays
; Syntax.........: __ArrayQuickSort1D ( ByRef $avArray, ByRef $iStart, ByRef $iEnd )
; Parameters ....: $avArray - Array to sort
;                  $iStart  - Index of array to start sorting at
;                  $iEnd    - Index of array to stop sorting at
; Return values .: None
; Author ........: Jos van der Zande, LazyCoder, Tylo, Ultima
; Modified.......:
; Remarks .......: For Internal Use Only
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __ArrayQuickSort1D(ByRef $avArray, Const ByRef $iStart, Const ByRef $iEnd)
    If $iEnd <= $iStart Then Return

    Local $vTmp

    ; InsertionSort (faster for smaller segments)
    If ($iEnd - $iStart) < 15 Then
        Local $vCur
        For $i = $iStart + 1 To $iEnd
            $vTmp = $avArray[$i]

            If IsNumber($vTmp) Then
                For $j = $i - 1 To $iStart Step -1
                    $vCur = $avArray[$j]
                    ; If $vTmp >= $vCur Then ExitLoop
                    If ($vTmp >= $vCur And IsNumber($vCur)) Or (Not IsNumber($vCur) And StringCompare($vTmp, $vCur) >= 0) Then ExitLoop
                    $avArray[$j + 1] = $vCur
                Next
            Else
                For $j = $i - 1 To $iStart Step -1
                    If (StringCompare($vTmp, $avArray[$j]) >= 0) Then ExitLoop
                    $avArray[$j + 1] = $avArray[$j]
                Next
            EndIf

            $avArray[$j + 1] = $vTmp
        Next
        Return
    EndIf

    ; QuickSort
    Local $L = $iStart, $R = $iEnd, $vPivot = $avArray[Int(($iStart + $iEnd) / 2)], $fNum = IsNumber($vPivot)
    Do
        If $fNum Then
            ; While $avArray[$L] < $vPivot
            While ($avArray[$L] < $vPivot And IsNumber($avArray[$L])) Or (Not IsNumber($avArray[$L]) And StringCompare($avArray[$L], $vPivot) < 0)
                $L += 1
            WEnd
            ; While $avArray[$R] > $vPivot
            While ($avArray[$R] > $vPivot And IsNumber($avArray[$R])) Or (Not IsNumber($avArray[$R]) And StringCompare($avArray[$R], $vPivot) > 0)
                $R -= 1
            WEnd
        Else
            While (StringCompare($avArray[$L], $vPivot) < 0)
                $L += 1
            WEnd
            While (StringCompare($avArray[$R], $vPivot) > 0)
                $R -= 1
            WEnd
        EndIf

        ; Swap
        If $L <= $R Then
            $vTmp = $avArray[$L]
            $avArray[$L] = $avArray[$R]
            $avArray[$R] = $vTmp
            $L += 1
            $R -= 1
        EndIf
    Until $L > $R

    __ArrayQuickSort1D($avArray, $iStart, $R)
    __ArrayQuickSort1D($avArray, $L, $iEnd)
EndFunc   ;==>__ArrayQuickSort1D

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __ArrayQuickSort2D
; Description ...: Helper function for sorting 2D arrays
; Syntax.........: __ArrayQuickSort2D ( ByRef $avArray, ByRef $iStep, ByRef $iStart, ByRef $iEnd, ByRef $iSubItem, ByRef $iSubMax )
; Parameters ....: $avArray  - Array to sort
;                  $iStep    - Step size (should be 1 to sort ascending, -1 to sort descending!)
;                  $iStart   - Index of array to start sorting at
;                  $iEnd     - Index of array to stop sorting at
;                  $iSubItem - Sub-index to sort on in 2D arrays
;                  $iSubMax  - Maximum sub-index that array has
; Return values .: None
; Author ........: Jos van der Zande, LazyCoder, Tylo, Ultima
; Modified.......:
; Remarks .......: For Internal Use Only
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __ArrayQuickSort2D(ByRef $avArray, Const ByRef $iStep, Const ByRef $iStart, Const ByRef $iEnd, Const ByRef $iSubItem, Const ByRef $iSubMax)
    If $iEnd <= $iStart Then Return

    ; QuickSort
    Local $vTmp, $L = $iStart, $R = $iEnd, $vPivot = $avArray[Int(($iStart + $iEnd) / 2)][$iSubItem], $fNum = IsNumber($vPivot)
    Do
        If $fNum Then
            ; While $avArray[$L][$iSubItem] < $vPivot
            While ($iStep * ($avArray[$L][$iSubItem] - $vPivot) < 0 And IsNumber($avArray[$L][$iSubItem])) Or (Not IsNumber($avArray[$L][$iSubItem]) And $iStep * StringCompare($avArray[$L][$iSubItem], $vPivot) < 0)
                $L += 1
            WEnd
            ; While $avArray[$R][$iSubItem] > $vPivot
            While ($iStep * ($avArray[$R][$iSubItem] - $vPivot) > 0 And IsNumber($avArray[$R][$iSubItem])) Or (Not IsNumber($avArray[$R][$iSubItem]) And $iStep * StringCompare($avArray[$R][$iSubItem], $vPivot) > 0)
                $R -= 1
            WEnd
        Else
            While ($iStep * StringCompare($avArray[$L][$iSubItem], $vPivot) < 0)
                $L += 1
            WEnd
            While ($iStep * StringCompare($avArray[$R][$iSubItem], $vPivot) > 0)
                $R -= 1
            WEnd
        EndIf

        ; Swap
        If $L <= $R Then
            For $i = 0 To $iSubMax
                $vTmp = $avArray[$L][$i]
                $avArray[$L][$i] = $avArray[$R][$i]
                $avArray[$R][$i] = $vTmp
            Next
            $L += 1
            $R -= 1
        EndIf
    Until $L > $R

    __ArrayQuickSort2D($avArray, $iStep, $iStart, $R, $iSubItem, $iSubMax)
    __ArrayQuickSort2D($avArray, $iStep, $L, $iEnd, $iSubItem, $iSubMax)
EndFunc   ;==>__ArrayQuickSort2D

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __ArrayDualPivotSort
; Description ...: Helper function for sorting 1D arrays
; Syntax.........: __ArrayDualPivotSort ( ByRef $aArray, $iPivot_Left, $iPivot_Right [, $fLeftMost = True ] )
; Parameters ....: $avArray  - Array to sort
;                  $iPivot_Left  - Index of the array to start sorting at
;                  $iPivot_Right - Index of the array to stop sorting at
;                  $fLeftMost    - Indicates if this part is the leftmost in the range
; Return values .: None
; Author ........: Erik Pilsits
; Modified.......: Melba23
; Remarks .......: For Internal Use Only
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __ArrayDualPivotSort(ByRef $aArray, $iPivot_Left, $iPivot_Right, $fLeftMost = True)
    If $iPivot_Left > $iPivot_Right Then Return
    Local $iLength = $iPivot_Right - $iPivot_Left + 1
    Local $i, $j, $k, $ai, $ak, $a1, $a2, $last
    If $iLength < 45 Then ; Use insertion sort for small arrays - value chosen empirically
        If $fLeftMost Then
            $i = $iPivot_Left
            While $i < $iPivot_Right
                $j = $i
                $ai = $aArray[$i + 1]
                While $ai < $aArray[$j]
                    $aArray[$j + 1] = $aArray[$j]
                    $j -= 1
                    If $j + 1 = $iPivot_Left Then ExitLoop
                WEnd
                $aArray[$j + 1] = $ai
                $i += 1
            WEnd
        Else
            While 1
                If $iPivot_Left >= $iPivot_Right Then Return 1
                $iPivot_Left += 1
                If $aArray[$iPivot_Left] < $aArray[$iPivot_Left - 1] Then ExitLoop
            WEnd
            While 1
                $k = $iPivot_Left
                $iPivot_Left += 1
                If $iPivot_Left > $iPivot_Right Then ExitLoop
                $a1 = $aArray[$k]
                $a2 = $aArray[$iPivot_Left]
                If $a1 < $a2 Then
                    $a2 = $a1
                    $a1 = $aArray[$iPivot_Left]
                EndIf
                $k -= 1
                While $a1 < $aArray[$k]
                    $aArray[$k + 2] = $aArray[$k]
                    $k -= 1
                WEnd
                $aArray[$k + 2] = $a1
                While $a2 < $aArray[$k]
                    $aArray[$k + 1] = $aArray[$k]
                    $k -= 1
                WEnd
                $aArray[$k + 1] = $a2
                $iPivot_Left += 1
            WEnd
            $last = $aArray[$iPivot_Right]
            $iPivot_Right -= 1
            While $last < $aArray[$iPivot_Right]
                $aArray[$iPivot_Right + 1] = $aArray[$iPivot_Right]
                $iPivot_Right -= 1
            WEnd
            $aArray[$iPivot_Right + 1] = $last
        EndIf
        Return 1
    EndIf
    Local $iSeventh = BitShift($iLength, 3) + BitShift($iLength, 6) + 1
    Local $e1, $e2, $e3, $e4, $e5, $t
    $e3 = Ceiling(($iPivot_Left + $iPivot_Right) / 2)
    $e2 = $e3 - $iSeventh
    $e1 = $e2 - $iSeventh
    $e4 = $e3 + $iSeventh
    $e5 = $e4 + $iSeventh
    If $aArray[$e2] < $aArray[$e1] Then
        $t = $aArray[$e2]
        $aArray[$e2] = $aArray[$e1]
        $aArray[$e1] = $t
    EndIf
    If $aArray[$e3] < $aArray[$e2] Then
        $t = $aArray[$e3]
        $aArray[$e3] = $aArray[$e2]
        $aArray[$e2] = $t
        If $t < $aArray[$e1] Then
            $aArray[$e2] = $aArray[$e1]
            $aArray[$e1] = $t
        EndIf
    EndIf
    If $aArray[$e4] < $aArray[$e3] Then
        $t = $aArray[$e4]
        $aArray[$e4] = $aArray[$e3]
        $aArray[$e3] = $t
        If $t < $aArray[$e2] Then
            $aArray[$e3] = $aArray[$e2]
            $aArray[$e2] = $t
            If $t < $aArray[$e1] Then
                $aArray[$e2] = $aArray[$e1]
                $aArray[$e1] = $t
            EndIf
        EndIf
    EndIf
    If $aArray[$e5] < $aArray[$e4] Then
        $t = $aArray[$e5]
        $aArray[$e5] = $aArray[$e4]
        $aArray[$e4] = $t
        If $t < $aArray[$e3] Then
            $aArray[$e4] = $aArray[$e3]
            $aArray[$e3] = $t
            If $t < $aArray[$e2] Then
                $aArray[$e3] = $aArray[$e2]
                $aArray[$e2] = $t
                If $t < $aArray[$e1] Then
                    $aArray[$e2] = $aArray[$e1]
                    $aArray[$e1] = $t
                EndIf
            EndIf
        EndIf
    EndIf
    Local $iLess = $iPivot_Left
    Local $iGreater = $iPivot_Right
    If (($aArray[$e1] <> $aArray[$e2]) And ($aArray[$e2] <> $aArray[$e3]) And ($aArray[$e3] <> $aArray[$e4]) And ($aArray[$e4] <> $aArray[$e5])) Then
        Local $iPivot_1 = $aArray[$e2]
        Local $iPivot_2 = $aArray[$e4]
        $aArray[$e2] = $aArray[$iPivot_Left]
        $aArray[$e4] = $aArray[$iPivot_Right]
        Do
            $iLess += 1
        Until $aArray[$iLess] >= $iPivot_1
        Do
            $iGreater -= 1
        Until $aArray[$iGreater] <= $iPivot_2
        $k = $iLess
        While $k <= $iGreater
            $ak = $aArray[$k]
            If $ak < $iPivot_1 Then
                $aArray[$k] = $aArray[$iLess]
                $aArray[$iLess] = $ak
                $iLess += 1
            ElseIf $ak > $iPivot_2 Then
                While $aArray[$iGreater] > $iPivot_2
                    $iGreater -= 1
                    If $iGreater + 1 = $k Then ExitLoop 2
                WEnd
                If $aArray[$iGreater] < $iPivot_1 Then
                    $aArray[$k] = $aArray[$iLess]
                    $aArray[$iLess] = $aArray[$iGreater]
                    $iLess += 1
                Else
                    $aArray[$k] = $aArray[$iGreater]
                EndIf
                $aArray[$iGreater] = $ak
                $iGreater -= 1
            EndIf
            $k += 1
        WEnd
        $aArray[$iPivot_Left] = $aArray[$iLess - 1]
        $aArray[$iLess - 1] = $iPivot_1
        $aArray[$iPivot_Right] = $aArray[$iGreater + 1]
        $aArray[$iGreater + 1] = $iPivot_2
        __ArrayDualPivotSort($aArray, $iPivot_Left, $iLess - 2, True)
        __ArrayDualPivotSort($aArray, $iGreater + 2, $iPivot_Right, False)
        If ($iLess < $e1) And ($e5 < $iGreater) Then
            While $aArray[$iLess] = $iPivot_1
                $iLess += 1
            WEnd
            While $aArray[$iGreater] = $iPivot_2
                $iGreater -= 1
            WEnd
            $k = $iLess
            While $k <= $iGreater
                $ak = $aArray[$k]
                If $ak = $iPivot_1 Then
                    $aArray[$k] = $aArray[$iLess]
                    $aArray[$iLess] = $ak
                    $iLess += 1
                ElseIf $ak = $iPivot_2 Then
                    While $aArray[$iGreater] = $iPivot_2
                        $iGreater -= 1
                        If $iGreater + 1 = $k Then ExitLoop 2
                    WEnd
                    If $aArray[$iGreater] = $iPivot_1 Then
                        $aArray[$k] = $aArray[$iLess]
                        $aArray[$iLess] = $iPivot_1
                        $iLess += 1
                    Else
                        $aArray[$k] = $aArray[$iGreater]
                    EndIf
                    $aArray[$iGreater] = $ak
                    $iGreater -= 1
                EndIf
                $k += 1
            WEnd
        EndIf
        __ArrayDualPivotSort($aArray, $iLess, $iGreater, False)
    Else
        Local $iPivot = $aArray[$e3]
        $k = $iLess
        While $k <= $iGreater
            If $aArray[$k] = $iPivot Then
                $k += 1
                ContinueLoop
            EndIf
            $ak = $aArray[$k]
            If $ak < $iPivot Then
                $aArray[$k] = $aArray[$iLess]
                $aArray[$iLess] = $ak
                $iLess += 1
            Else
                While $aArray[$iGreater] > $iPivot
                    $iGreater -= 1
                WEnd
                If $aArray[$iGreater] < $iPivot Then
                    $aArray[$k] = $aArray[$iLess]
                    $aArray[$iLess] = $aArray[$iGreater]
                    $iLess += 1
                Else
                    $aArray[$k] = $iPivot
                EndIf
                $aArray[$iGreater] = $ak
                $iGreater -= 1
            EndIf
            $k += 1
        WEnd
        __ArrayDualPivotSort($aArray, $iPivot_Left, $iLess - 1, True)
        __ArrayDualPivotSort($aArray, $iGreater + 1, $iPivot_Right, False)
    EndIf
EndFunc   ;==>__ArrayDualPivotSort

; #FUNCTION# ====================================================================================================================
; Author ........: David Nuttall <danuttall at rocketmail dot com>
; Modified.......: Ultima - minor optimization
; ===============================================================================================================================
Func _ArraySwap(ByRef $vItem1, ByRef $vItem2)
    Local $vTmp = $vItem1
    $vItem1 = $vItem2
    $vItem2 = $vTmp
EndFunc   ;==>_ArraySwap

; #FUNCTION# ====================================================================================================================
; Author ........: Cephas <cephas at clergy dot net>
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> - added $iStart parameter and logic, Ultima - added $iEnd parameter, make use of _ArrayToString() instead of duplicating efforts
; ===============================================================================================================================
Func _ArrayToClip(Const ByRef $avArray, $iStart = 0, $iEnd = 0)
    Local $sResult = _ArrayToString($avArray, @CR, $iStart, $iEnd)
    If @error Then Return SetError(@error, 0, 0)
    If ClipPut($sResult) Then Return 1
    Return SetError(-1, 0, 0)
EndFunc   ;==>_ArrayToClip

; #FUNCTION# ====================================================================================================================
; Author ........: Brian Keene <brian_keene at yahoo dot com>, Valik - rewritten
; Modified.......: Ultima - code cleanup; Melba23 - added support for empty arrays
; ===============================================================================================================================
Func _ArrayToString(Const ByRef $avArray, $sDelim = "|", $iStart = 0, $iEnd = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, "")
    If UBound($avArray, 0) <> 1 Then Return SetError(3, 0, "")
    If Not UBound($avArray) Then Return SetError(4, 0, "")

    Local $sResult, $iUBound = UBound($avArray) - 1

    ; Bounds checking
    If $iEnd < 1 Or $iEnd > $iUBound Then $iEnd = $iUBound
    If $iStart < 0 Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(2, 0, "")

    ; Combine
    For $i = $iStart To $iEnd
        $sResult &= $avArray[$i] & $sDelim
    Next

    Return StringTrimRight($sResult, StringLen($sDelim))
EndFunc   ;==>_ArrayToString

; #FUNCTION# ====================================================================================================================
; Author ........: jchd
; Modified.......:
; ===============================================================================================================================
Func _ArrayTranspose(ByRef $avArray)
    If UBound($avArray, 0) <> 2 Then Return SetError(1, 0, 0)

    Local $vElement = 0, $iDim_1 = UBound($avArray, 1), $iDim_2 = UBound($avArray, 2), $iDim_Max = ($iDim_1 > $iDim_2) ? $iDim_1 : $iDim_2

    If $iDim_Max <= 4096 Then
        ReDim $avArray[$iDim_Max][$iDim_Max]
        For $i = 0 To $iDim_Max - 2
            For $j = $i + 1 To $iDim_Max - 1
                $vElement = $avArray[$i][$j]
                $avArray[$i][$j] = $avArray[$j][$i]
                $avArray[$j][$i] = $vElement
            Next
        Next
        ReDim $avArray[$iDim_2][$iDim_1]
    Else
        Local $aTemp[$iDim_2][$iDim_1]
        For $i = 0 To $iDim_1 - 1
            For $j = 0 To $iDim_2 - 1
                $aTemp[$j][$i] = $avArray[$i][$j]
            Next
        Next
        ReDim $avArray[$iDim_2][$iDim_1]
        $avArray = $aTemp
    EndIf
    Return 1
EndFunc   ;==>_ArrayTranspose

; #FUNCTION# ====================================================================================================================
; Author ........: Adam Moore (redndahead)
; Modified.......: Ultima - code cleanup, optimization
; ===============================================================================================================================
Func _ArrayTrim(ByRef $avArray, $iTrimNum, $iDirection = 0, $iStart = 0, $iEnd = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)
    If UBound($avArray, 0) <> 1 Then Return SetError(2, 0, 0)
    If Not UBound($avArray) Then Return SetError(3, 0, 0)

    Local $iUBound = UBound($avArray) - 1

    ; Bounds checking
    If $iEnd < 1 Or $iEnd > $iUBound Then $iEnd = $iUBound
    If $iStart < 0 Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(5, 0, 0)

    ; Trim
    If $iDirection Then
        For $i = $iStart To $iEnd
            $avArray[$i] = StringTrimRight($avArray[$i], $iTrimNum)
        Next
    Else
        For $i = $iStart To $iEnd
            $avArray[$i] = StringTrimLeft($avArray[$i], $iTrimNum)
        Next
    EndIf

    Return 1
EndFunc   ;==>_ArrayTrim

; #FUNCTION# ====================================================================================================================
; Author ........: SmOke_N
; Modified.......: litlmike, Erik Pilsits, BrewManNH
; ===============================================================================================================================
Func _ArrayUnique(Const ByRef $aArray, $iColumn = Default, $iBase = Default, $iCase = Default, $iFlags = Default)
    If $iColumn = Default Then $iColumn = 1
    If $iBase = Default Then $iBase = 0
    If $iCase = Default Then $iCase = 0
    If $iFlags = Default Then $iFlags = 1
    ; Start bounds checking
    If UBound($aArray) = 0 Then Return SetError(1, 0, 0) ; Check if array is empty, or not an array
    ; $iBase can only be 0 or 1, if anything else, return with an error
    If $iBase < 0 Or $iBase > 1 Or (Not IsInt($iBase)) Then Return SetError(2, 0, 0)
    If $iCase < 0 Or $iCase > 1 Or (Not IsInt($iCase)) Then Return SetError(2, 0, 0)
    If $iFlags < 0 Or $iFlags > 1 Or (Not IsInt($iFlags)) Then Return SetError(4, 0, 0)
    Local $iDims = UBound($aArray, 0), $iNumColumns = UBound($aArray, 2)
    If $iDims > 2 Then Return SetError(3, 0, 0)
    ; checks the given dimension is valid
    If ($iColumn < 1) Or ($iNumColumns = 0 And ($iColumn - 1 > $iNumColumns)) Or ($iNumColumns > 0 And ($iColumn > $iNumColumns)) Then Return SetError(3, 0, 0)
    ; make $iColumn an array index, note this is ignored for 1D arrays
    $iColumn -= 1
    ; create dictionary
    Local $oD = ObjCreate("Scripting.Dictionary")
    ; compare mode for strings
    ; 0 = binary, which is case sensitive
    ; 1 = text, which is case insensitive
    ; this expression forces either 1 or 0
    $oD.CompareMode = Number(Not $iCase)
    Local $vElem
    ; walk the input array
    For $i = $iBase To UBound($aArray) - 1
        If $iDims = 1 Then
            ; 1D array
            $vElem = $aArray[$i]
        Else
            ; 2D array
            $vElem = $aArray[$i][$iColumn]
        EndIf
        ; add key to dictionary
        ; NOTE: accessing the value (.Item property) of a key that doesn't exist creates the key :)
        ; keys are guaranteed to be unique
        $oD.Item($vElem)
    Next
    ;
    ; return the array of unique keys
    If BitAND($iFlags, 1) = 1 Then
        Local $aTemp = $oD.Keys()
        _ArrayInsert($aTemp, 0, $oD.Count)
        Return $aTemp
    Else
        Return $oD.Keys()
    EndIf
EndFunc   ;==>_ArrayUnique

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __Array_ExeterInternal
; Description ...: Permute Function based on an algorithm from Exeter University.
; Syntax.........: __Array_ExeterInternal ( ByRef $avArray, $iStart, $iSize, $sDelim, ByRef $aIdx, ByRef $aResult )
; Parameters ....: $avArray - The Array to get Permutations
;                  $iStart - Starting Point for Loop
;                  $iSize - End Point for Loop
;                  $sDelim - String result separator
;                  $aIdx - Array Used in Rotations
;                  $aResult - Resulting Array
; Return values .: Success      - Computer name
; Author ........: Erik Pilsits
; Modified.......: 07/08/2008
; Remarks .......: This function is used internally. Permute Function based on an algorithm from Exeter University.
; +
;                   http://www.bearcave.com/random_hacks/permute.html
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __Array_ExeterInternal(ByRef $avArray, $iStart, $iSize, $sDelim, ByRef $aIdx, ByRef $aResult, ByRef $iCount)
    If $iStart == $iSize - 1 Then
        For $i = 0 To $iSize - 1
            $aResult[$iCount] &= $avArray[$aIdx[$i]] & $sDelim
        Next
        If $sDelim <> "" Then $aResult[$iCount] = StringTrimRight($aResult[$iCount], 1)
        $iCount += 1
    Else
        Local $iTemp
        For $i = $iStart To $iSize - 1
            $iTemp = $aIdx[$i]

            $aIdx[$i] = $aIdx[$iStart]
            $aIdx[$iStart] = $iTemp
            __Array_ExeterInternal($avArray, $iStart + 1, $iSize, $sDelim, $aIdx, $aResult, $iCount)
            $aIdx[$iStart] = $aIdx[$i]
            $aIdx[$i] = $iTemp
        Next
    EndIf
EndFunc   ;==>__Array_ExeterInternal

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __Array_Combinations
; Description ...: Creates Combination
; Syntax.........: __Array_Combinations ( $iN, $iR )
; Parameters ....: $iN - Value passed on from UBound($avArray)
;                  $iR - Size of the combinations set
; Return values .: Integer value of the number of combinations
; Author ........: Erik Pilsits
; Modified.......: 07/08/2008
; Remarks .......: This function is used internally. PBased on an algorithm by Kenneth H. Rosen.
; +
;                   http://www.bearcave.com/random_hacks/permute.html
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __Array_Combinations($iN, $iR)
    Local $i_Total = 1

    For $i = $iR To 1 Step -1
        $i_Total *= ($iN / $i)
        $iN -= 1
    Next
    Return Round($i_Total)
EndFunc   ;==>__Array_Combinations

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __Array_GetNext
; Description ...: Creates Combination
; Syntax.........: __Array_GetNext ( $iN, $iR, ByRef $iLeft, $iTotal, ByRef $aIdx )
; Parameters ....: $iN - Value passed on from UBound($avArray)
;                  $iR - Size of the combinations set
;                  $iLeft - Remaining number of combinations
;                  $iTotal - Total number of combinations
;                  $aIdx - Array containing combinations
; Return values .: Function only changes values ByRef
; Author ........: Erik Pilsits
; Modified.......: 07/08/2008
; Remarks .......: This function is used internally. PBased on an algorithm by Kenneth H. Rosen.
; +
;                   http://www.bearcave.com/random_hacks/permute.html
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __Array_GetNext($iN, $iR, ByRef $iLeft, $iTotal, ByRef $aIdx)
    If $iLeft == $iTotal Then
        $iLeft -= 1
        Return
    EndIf

    Local $i = $iR - 1
    While $aIdx[$i] == $iN - $iR + $i
        $i -= 1
    WEnd

    $aIdx[$i] += 1
    For $j = $i + 1 To $iR - 1
        $aIdx[$j] = $aIdx[$i] + $j - $i
    Next

    $iLeft -= 1
EndFunc   ;==>__Array_GetNext

Edited by boththose

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Share this post


Link to post
Share on other sites

Wouldn't it have been far easier to just create a listview and a couple of buttons to do the same thing?


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

Probably,  it was simple enough to add a right click menu item of "uninstall this software" with his solution in 2010.  But since ripdad moved on to using the builtin _ArrayDisplay, figured I would learn along as well.  Since I was balls deep in trying to get the button to work, it ended up being easier, and left ripdad's code alone, to just alter the display and paste the modified ArrayUDF to the bottom.

Edited by boththose

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

Others, were hard to make out and had various ways about them.

Some only had two entries in the subkey - the DisplayName and the Uninstall string.

Still, others had as many as 20 to 30 entries in theirs.

Another example of free-for-all standards, I guess.

 

We get lots of different machines at work, so if you need some tests running let me know and ill check against xp - 8.1 32 & 64 as they pass my bench

Just make it collect a txt file and ill send the results back to you

Edited by Chimaera

Share this post


Link to post
Share on other sites

You might also want to check out psyCodez's >Collect Uninstall Keys (App), it is very impressive! I love to use it against remote PCs.

Share this post


Link to post
Share on other sites

This link might add some more information to the first link, especially in regards to the MSI installed programs. Specifically this part of the information that wasn't clarified as to how the GUID is changed.

This post explains how the GUID is switched around.

 

Thanks for that. I have a working function to convert the GUID with now.

I did run into a location problem with the resulting string on a few of them. They were located only in HKU.

Reading on his forum, I see he went through alot of trouble to figure out how it all worked.

Also, I took some pointers from him on what to bypass, such as: SystemComponent.

Thanks for posting those links. They definitely have been very helpful.


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

Probably,  it was simple enough to add a right click menu item of "uninstall this software" with his solution in 2010.  But since ripdad moved on to using the builtin _ArrayDisplay, figured I would learn along as well.  Since I was balls deep in trying to get the button to work, it ended up being easier, and left ripdad's code alone, to just alter the display and paste the modified ArrayUDF to the bottom.

 

Hang in there - I'm not finished with it yet. This is just the engine. And I'm still working on it.

I eventually want to merge it with another program that I'm working on.

By the way, this script should work in InstalledSoftwareViewer.au3

Just replace that function with this one.

The ListView would need a modification to show the extra columns though.


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

We get lots of different machines at work, so if you need some tests running let me know and ill check against xp - 8.1 32 & 64 as they pass my bench

Just make it collect a txt file and ill send the results back to you

 

Thanks - I appreciate your offer.

I'll let you know if I need it.


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

You might also want to check out psyCodez's >Collect Uninstall Keys (App), it is very impressive! I love to use it against remote PCs.

 

Well, I don't know how I missed that one.

Reading his code, I can see he worked hard on it.


"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0