Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 04/16/2025 in all areas

  1. Yes, the escaping of the quotation marks within the commands can be quite annoying. So you have to escape the quotation marks in the command. This is a bit easier if you swap the double quotation marks with the single ones. Something like this: Local $command_r = 'PowerShell -Command "' & StringReplace($powerShellCommand, '"', '\"') & '"' Why you are using @ComSpec (i.e. the cmd.exe) although you want to call the powershell instead is not clear to me. I have written the following function myself. Maybe it will help you here: #include <WinAPIConv.au3> ; Get the list of running processes and filter for processes named "notepad" Local $commandToRun = "Get-Process | Where-Object {$_.ProcessName -eq 'notepad'} | Format-List Name, Id, MainWindowTitle" Local $powerShellResult = _prc_runPS($commandToRun) If @error Then MsgBox(16, "Error", "Could not get results from PowerShell.") Else If $powerShellResult Then MsgBox(64, "PowerShell Result", "Result returned from PowerShell: " & $powerShellResult) Else MsgBox(64, "Information", "No processes match the criteria.") EndIf EndIf ; Another example: Get the PowerShell version Local $versionCommand = "$PSVersionTable.PSVersion" Local $powerShellVersion = _prc_runPS($versionCommand) If Not @error Then MsgBox(64, "PowerShell Version", "PowerShell Version: " & $powerShellVersion) EndIf ; #FUNCTION# ====================================================================================== ; Name ..........: _prc_runPS() ; Description ...: Executes a Powershell command and returns its output as a string ; Syntax ........: _prc_runPS($sCmd, [$nFlags = 0x8, [$sWorkDir = '', [$sOptions = '-NoProfile -ExecutionPolicy Bypass', [$nTimeOut = 0, $bLoopRead = False]]]]]) ; Parameters ....: $sCmd - the powershell command to be executed as you would use it directly in the powershell ; you can also use line breaks ; $nFlags - [optional] flags that control the handling of the two streams stdout and stderr: ; 0x2: [Default] Return a string with the content of stdout ; 0x4: [Default] Return a string with the content of stderr ; 0x6: Return a array with the content of stdout in $Array[0] and stderr in $Array[1] ; 0x8: Return a string with the combined content of stdout and stderr ; $sWorkDir - [optional] the working directory like in Run() ; $sOptions - [String] additional parameters to be passed to powershell.exe ; $nTimeOut - [optional] the maximum time to wait for the process to be completed (see @error return) ; default = 0: infinite; every other number: wait time in seconds ; $bLoopRead - if true: stdout and stderr are read in a loop; if false: they are read in one go ; Return values .: Success: String with powershell output or if $nFlags = 0x6: array with cmdline outputs and set ; @extended = return code of the process ; Failure: "" and set @error to: ; | 1: error during run; @extended = @error of Run() ; | 2: ProcessWaitClose reaches timeout before completion ; | 3: content written in stderr - indicates error messages of the program (not a real error) ; Author ........: AspirinJunkie ; Modified ......: 2025-03-10 ; Related .......: _WinAPI_OemToChar() ; Example .......: Yes ; $x = _prc_runPS('$obj = New-Object -ComObject "Shell.Application"' & @CRLF & _ ; '$obj | Get-Member') ; ConsoleWrite($x & @CRLF) ; ================================================================================================= Func _prc_runPS($sCmd, $nFlags = 0x8, $sWorkDir = '', $sOptions = '-NoProfile -ExecutionPolicy Bypass', $nTimeOut = 0, $bLoopRead = False) ; handling Default keyword word for the parameters If IsKeyWord($nFlags) = 1 Then $nFlags = 0x8 If IsKeyWord($sWorkDir) = 1 Then $sWorkDir = "" If IsKeyWord($sOptions) = 1 Then $sOptions = '-NoProfile -ExecutionPolicy Bypass' ; format command as call parameter, passed to powershell.exe $sCmd = StringFormat('powershell.exe %s -Command "%s"', $sOptions, StringReplace($sCmd, '"', '\"',0,1)) ; start the cmd/process Local $iPID = Run($sCmd, $sWorkDir, @SW_Hide, $nFlags) If @error Then Return SetError(1, @error, "") Local $iExit, $sStdOut = "", $sStdErr = "" If $bLoopRead Then ; fill stdout and/or stderr over a loop Do If BitAND($nFlags, 0x4) Then $sStdErr &= StderrRead($iPID) $sStdOut &= StdoutRead($iPID) If @error Then ExitLoop Until 0 ; determine the exit code $iExit = ProcessWaitClose($iPID, 0) ElseIf ProcessWaitClose($iPID, $nTimeOut) = 0 Then ; wait until process ends Return SetError(2, 0, "") Else ; read out the process results in one go $iExit = @extended $sStdOut = StdoutRead($iPID) $sStdErr = BitAND($nFlags, 0x4) ? StderrRead($iPID) : "" EndIf ; return only stderr If $nFlags = 0x4 Then Return SetExtended($iExit, _WinAPI_OemToChar($sStdErr)) ; return array if stdout and stderr should be read separately ElseIf $nFlags = 0x6 Then Local $aRet[2] = [_WinAPI_OemToChar($sStdOut), _WinAPI_OemToChar($sStdErr)] Return SetError($sStdErr = "" ? 0 : 3, $iExit, $aRet) EndIf ; return a string Return SetExtended($iExit, _WinAPI_OemToChar($sStdOut)) EndFunc
    3 points
  2. Exactly - it is not escaping for Powershell code. But we're not at that level yet (as I said before: escaping with such nested interpreters is quite annoying...) Escaping per \" is not intended for powershell.exe but for the Windows API function CreateProcessA/W (which is the basis for the AutoIt function Run()). This function must ensure that the parameters are passed correctly to powershell.exe and the syntax is such that double quotation marks are escaped via \". A little clearer with the example: If you execute the following via Run (or from the command line): powershell.exe -Command "Write-Host \"This is a \"\"short\"\" test with multiple \"\"quotes\"\".\"" Then powershell.exe receives the following code as a parameter: Write-Host "This is a ""short"" test with multiple ""quotes""." And your double quotation marks for the powershell are correct again! This is exactly what the _prc_runPS() function does here.
    2 points
  3. Here are the results with jq added to the mix. This was run on a 10 year old Dell XPS 8700 running Windows 7 64bit. That explains the slower speeds overall. speed comparison.zip
    2 points
  4. TheSaint

    Kobo Cover Fixer

    Latest update now available, see the first post. I did a stack of work today on the program .... then did a stack more. I started off with some improvements that had occurred to me since yesterday, then discovered a couple of bugs, then thought of some more improvements. Then I discovered my Kobo device was almost flat, so a good time to work with it and test a bunch of things. That led to me finding things weren't as good as I wanted them to be, and that I hadn't completed some stuff that needed to be. So implemented a bunch of things and kept testing, and decided I needed to do a few more, and before you knew it most of the day had disappeared. Eventually my Kobo device was fully charged and so kept connecting and disconnecting, so no more testing, though I still had a bunch of things to code and test without needing my device. Latest Changes While not a huge list, it was a lot of work. I'm pretty sure I still need to do more, especially as I haven't looked at the FIX COVERS code again, yet ... not for 10 months at least. I imagine the process still works okay, just needs some additional Log file recording (perhaps). and some visual updating to the list etc, and perhaps some other records. Anyway, I am pleased to say that the many covers created on my Kobo device today, did the job and worked well. Only a few hundred left to do. Those Fuschia colored rows are successful images copied to my Kobo device. Mostly it was three images per each ebook, but sometimes only one was required. If you look back at a previous screenshot, you can see what has changed.
    1 point
  5. I created this UDF for JSON using JSON-C because I needed high performance parsing of JSON data in my script. I needed to query large data arrays of several thousand entries and other UDFs were taking many seconds to do so. With this UDF a query of 5,000 array entries takes me about 600ms (see Example2.au3). This UDF executes JSON functions through a JSON-C DLL to achieve better performance. The JSON-C project is https://github.com/json-c/json-c. To download this UDF please visit https://github.com/seanhaydongriffin/JsonC-UDF. Two examples are provided (Example1.au3 and Example2.au3) that demonstrate all the functions.
    1 point
  6. TheXman

    JSON UDF using JSON-C

    Are your tests being run on virtual machines or physical machines?
    1 point
  7. First I must thank @MattyD for showing me how to perform such a task in this thread. I felt I needed to play with it to fully understand the intrinsic. So I ended up making a small UDF that is accomplishing the creation of interface objects and the deletion of those made by the UDF. The example included in the zip file is largely inspired on the example made by the original author of the Media Engine topic. I believe the UDF is quite straightforward to use but, in any cases, post here your questions, I will be glad to answer in the scope of my knowledge. Version 2025-04-16 * Solved an issue with the tag transformation * Code optimization * Added tag validation ObjFromTag UDF.zip
    1 point
  8. Understand. I think I will leave it like that, feel it is less prone to bug if user misspelled by error the method-function. However, I could make it clearer to the user if a method-function does not exist. DllCallbackRegister will return an error but it is not obvious what is the reason.
    1 point
  9. @Gianni Apparently if you read through the documentation there are more dependencies needed ... https://www.vbforums.com/showthread.php?899415-OrdoWebview2-ActiveX-WebView2-Browser-Control-(Replacement-of-the-MS-browser-control) They have built a custom vbRichClient based on the RC6, that is built with VB6 and has a few DLL's to download first https://www.vbrichclient.com/#/en/Downloads.htm Next you need to register a few COM components from the downloaded DLL's. It also has to the WebView2Loader.dll in there that is needed... For more info best go to the VB forum to see some example scripts to do some trial and error...
    1 point
  10. New version available.
    1 point
  11. Understood, thanks for the explanation @AspirinJunkie 🤝 . Usually, when I run PS1 snippets or scripts from AutoIt, I simply call powershell with the *.ps1 script file as argument. Then all of these struggles aren't relevant. A very old example, but as showcase okay, can be found here. Best regards Sven
    1 point
  12. Hi @AspirinJunkie 👋 , I believe I understood your statement and also the code, but I am a bit confused by the escaping character \ (backslash) which is not a escaping character for powershell - at least as I know so far. In my understanding you have to escape by the same char which you want to escape, in general, or for special chars you have to use ` (backtick). Am I am wrong by this 🤔 ? I personally did always usw "" or `" in the last years which works great. I am sure you will open my eyes with an explanation 🤭 . Best regards Sven
    1 point
  13. Hey mate - great work! just a suggestion - take or leave it This *should* add a placeholder for methods we don't care about, and don't want to wrap. (probably requires some more testing) #include <WinAPIConv.au3> #include <WinAPIConstants.au3> ; #ObjFromTag# ================================================================================================================= ; Name ..........: ObjFromTag UDF.au3 ; Description ...: Create COM interface object based on tag instead of CLSID ; Author ........: Nine ; Created .......: 2020-04-14 ; Modified ......: ; Remark ........: Inspired by MattyD, trancexx ; Links .........: https://www.autoitscript.com/forum/topic/212828-byo-com-object/ ; : https://www.autoitscript.com/forum/topic/153520-iuiautomation-ms-framework-automate-chrome-ff-ie/page/6/#findComment-1143566 ; Example .......: Yes ; =============================================================================================================================== ; #Functions# =================================================================================================================== ; ObjCreateFromTag($sPrefix, $tagInterface, ByRef $tInterface, $sIID = Default, $bIsUnknown = Default) ; _QueryInterface($pSelf, $pRIID, $pObj) ; _AddRef($pSelf) ; _Release($pSelf) ; ObjDeleteFromTag(ByRef $tInterface) ; =============================================================================================================================== Global Const $sIID_IUNKNOWN = "{00000000-0000-0000-C000-000000000046}" Global Const $tagIUNKNOWN = _ "QueryInterface hresult(ptr; ptr*);" & _ "AddRef ulong();" & _ "Release ulong();" Global Const $tagIHEADER = "align 4;ptr pObject;long iRefCnt;char sIID[" & StringLen($sIID_IUNKNOWN) + 1 & "];" Global Const $tagIINTERFACE = $tagIHEADER & "int iSize;ptr pVTable[%i];ptr pCallback[%i];" Global $hQueryInterface = DllCallbackRegister("_QueryInterface", "long", "ptr;ptr;ptr*") Global $hAddRef = DllCallbackRegister("_AddRef", "int", "ptr") Global $hRelease = DllCallbackRegister("_Release", "int", "ptr") Global $hNoMethod = DllCallbackRegister("_NoMthd", "int64", "ptr;int64;int64;int64;int64;int64;int64;int64;int64;int64;int64;int64;int64") Func ObjCreateFromTag($sPrefix, $tagInterface, ByRef $tInterface, $sIID = Default, $bIsUnknown = Default) If $sIID = Default Then $sIID = $sIID_IUNKNOWN If $bIsUnknown = Default Then $bIsUnknown = True Local $sInterface = ($bIsUnknown ? $tagIUNKNOWN : "") & $tagInterface Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($sInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3) Local $iUbound = UBound($aMethods), $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback $tInterface = DllStructCreate(StringFormat($tagIINTERFACE, $iUbound, $iUbound)) For $i = 0 To $iUbound - 1 $aSplit = StringSplit($aMethods[$i], "|", 2) If UBound($aSplit) <> 2 Then ReDim $aSplit[2] $sNamePart = $aSplit[0] $sTagPart = $aSplit[1] $sMethod = $sPrefix & $sNamePart $aTagPart = StringSplit($sTagPart, ";", 2) $sRet = $aTagPart[0] $sParams = StringStripWS("ptr" & StringReplace($sTagPart, $sRet, "", 1), $STR_STRIPALL) $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams) If @error Then ConsoleWrite( $sMethod & "/" & $sRet & "/" & $sParams & " = " & @error & @CRLF) $hCallback = $hNoMethod If $bIsUnknown Then Switch $i Case 0 $hCallback = $hQueryInterface Case 1 $hCallback = $hAddRef Case 2 $hCallback = $hRelease EndSwitch EndIf EndIf DllStructSetData($tInterface, "pVTable", DllCallbackGetPtr($hCallback), $i + 1) DllStructSetData($tInterface, "pCallback", $hCallback, $i + 1) Next $tInterface.iRefCnt = 1 ; start ref at 1 since creation is completed $tInterface.iSize = $iUbound $tInterface.pObject = DllStructGetPtr($tInterface, "pVTable") $tInterface.sIID = $sIID Return ObjCreateInterface(DllStructGetPtr($tInterface), $sIID, $tagInterface, $bIsUnknown) EndFunc ;==>ObjFromTag Func _QueryInterface($pSelf, $pRIID, $pObj) If Not $pObj Then Return $E_POINTER Local $sIID = _WinAPI_StringFromGUID($pRIID) Local $tInter = DllStructCreate($tagIHEADER, $pSelf) If $sIID = $tInter.sIID Or $sIID = $sIID_IUNKNOWN Then DllStructSetData(DllStructCreate("ptr", $pObj), 1, $pSelf) _AddRef($pSelf) Return $S_OK EndIf Return $E_NOINTERFACE EndFunc ;==>_QueryInterface Func _AddRef($pSelf) Local $tInter = DllStructCreate($tagIHEADER, $pSelf) $tInter.iRefCnt += 1 Return $tInter.iRefCnt EndFunc ;==>_AddRef Func _Release($pSelf) Local $tInter = DllStructCreate($tagIHEADER, $pSelf) $tInter.iRefCnt -= 1 Return $tInter.iRefCnt EndFunc ;==>_Release Func _NoMthd($pThis, $vP1, $vP2, $vP3, $vP4, $vP5, $vP6, $vP7, $vP8, $vP9, $vP10, $vP11, $vP12) EndFunc
    1 point
  14. argumentum

    JSON UDF using JSON-C

    ..I posted these many pics because I couldn't believe that JSON-C was slower than others. Edit: oops, did run in 32bit hence the JSON-C did not do anything. It does look faster doing nothing
    1 point
  15. TheXman

    JSON UDF using JSON-C

    jq is very fast and it is very powerful too. It can do much more than parse JSON. jq is a JSON processor. Meaning, a lot of the post-processing of JSON data that you would normally do using AutoIt functions can be accomplished using jq, in a single jq filter, and be done much quicker too. The jq UDF has a few examples included in the zip file and jqPlayground has many more (20+) example jq filters to help you get started learning what jq can do. jqPlayground can be very helpful as a learning tool because it is interactive and allows you to save and restore sessions.
    1 point
  16. I am very excited about the jq UDF now. It looks so fast. Thanks @TheXman. I'll definitely check it out.
    1 point
  17. UPDATE: Scrollbar working as intended now! New buttons added. New code (uploaded + new zip file) ready for you to test. Optimizations and details like handling double clicking on controls are comming soon.
    1 point
  18. I didn't try the Example3, because I have Windows Defender disabled Example1, Example2, Example4 is ok #RequireAdmin #include <AutoItConstants.au3> ; Choose the example to run ;~ Example1() ;~ Example2() ;~ Example3() Example4() ;--------------------------------------------------------------------------------------- Func Example1() ; Get the list of running processes and filter for processes named "notepad" Local $commandToRun = "Get-Process | Where-Object {$_.ProcessName -eq 'notepad'} | Format-List Name, Id, MainWindowTitle" Local $powerShellResult = RunPowerShellCommand($commandToRun) If @error Then MsgBox(16, "Error", "Could not get results from PowerShell.") Else If $powerShellResult Then MsgBox(64, "PowerShell Result", "Result returned from PowerShell: " & $powerShellResult) Else MsgBox(64, "Information", "No processes match the criteria.") EndIf EndIf EndFunc ;--------------------------------------------------------------------------------------- Func Example2() ; Get the PowerShell version Local $versionCommand = "$PSVersionTable.PSVersion" Local $powerShellVersion = RunPowerShellCommand($versionCommand) If Not @error Then MsgBox(64, "PowerShell Version", "PowerShell Version: " & $powerShellVersion) EndIf EndFunc ;--------------------------------------------------------------------------------------- Func Example3() ; Add to Windows Defender Exclusions using PowerShell Local $programToExclude = @Compiled ? @ScriptFullPath : @AutoItExe If AddToDefenderExclusions_PS($programToExclude) Then MsgBox(64, "Success", "Successfully added '" & $programToExclude & "' to Windows Defender Exclusions.") Else MsgBox(16, "Failure", "Failed to add to Defender Exclusions.") EndIf EndFunc ;--------------------------------------------------------------------------------------- Func Example4() ; Add to Windows Firewall Exclusions using PowerShell Local $programToExclude = @Compiled ? @ScriptFullPath : @AutoItExe Local $firewallRuleName = "AutoIt3" If AddToFirewallExclusions_PS($programToExclude, $firewallRuleName) Then MsgBox(64, "Success", "Successfully added Firewall rule for '" & $programToExclude & "'.") Else MsgBox(16, "Failure", "Failed to add Firewall rule.") EndIf EndFunc ;--------------------------------------------------------------------------------------- Func RunPowerShellCommand($powerShellCommand) Local $output = "" Local $error = "" Local $command_r = 'PowerShell -Command "' & $powerShellCommand & '"' ConsoleWrite('-> ' & $command_r & @CRLF) Local $pid = Run(@ComSpec & ' /c ' & $command_r, "", @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD) If Not $pid Then MsgBox(16, "Error", "Could not run PowerShell.") Return SetError(-1, 0, False) EndIf While 1 Local $line = StdoutRead($pid) If @error Then ExitLoop $output &= $line & @CRLF WEnd While 1 Local $line = StderrRead($pid) If @error Then ExitLoop $error &= $line & @CRLF WEnd ProcessClose($pid) $error = StringStripWS($error, 7) $output = StringStripWS($output, 7) If StringLen(StringStripWS($error, 8)) > 0 Then ConsoleWrite($output & @CRLF & $error & @CRLF) MsgBox(16, "PowerShell Error", "Error returned from PowerShell: " & $error) Return SetError(1, 0, $error) Else ConsoleWrite($output & @CRLF) Return SetError(0, 0, $output) EndIf EndFunc ;--------------------------------------------------------------------------------------- Func AddToDefenderExclusions_PS($programPath) Local $command = "Add-MpPreference -ExclusionPath '" & $programPath & "'" Local $result = RunPowerShellCommand($command) If @error Then MsgBox(16, "Defender Error", "Error running PowerShell command to add to Defender Exclusion." & @CRLF & $result) Return False Else Return True EndIf EndFunc ;--------------------------------------------------------------------------------------- Func AddToFirewallExclusions_PS($programPath, $ruleName) Return AddToFirewall_InboundExclusions_PS($programPath, $ruleName) And AddToFirewall_OutboundExclusions_PS($programPath, $ruleName) EndFunc ;==>AddToFirewallExclusions_PS ;--------------------------------------------------------------------------------------- Func AddToFirewall_InboundExclusions_PS($programPath, $ruleName) Local $inboundCommand = "New-NetFirewallRule -DisplayName '" & $ruleName & " (Inbound)' -Direction Inbound -Action Allow -Program '" & $programPath & "' -Enabled True" Local $inboundResult = RunPowerShellCommand($inboundCommand) If @error Then MsgBox(16, "Firewall Error", "Error adding Inbound Firewall rule." & @CRLF & $inboundResult) Return False EndIf Return True EndFunc ;--------------------------------------------------------------------------------------- Func AddToFirewall_OutboundExclusions_PS($programPath, $ruleName) Local $outboundCommand = "New-NetFirewallRule -DisplayName '" & $ruleName & " (Outbound)' -Direction Outbound -Action Allow -Program '" & $programPath & "' -Enabled True" Local $outboundResult = RunPowerShellCommand($outboundCommand) If @error Then MsgBox(16, "Firewall Error", "Error adding Outbound Firewall rule." & @CRLF & $outboundResult) Return False EndIf Return True EndFunc ;---------------------------------------------------------------------------------------
    1 point
  19. Hi @Trong 👋 , I can not test it right now, but try the following please: ; replace this line in you "RunPowerShellCommand" function: Local $command_r = "PowerShell -Command '" & '"' & $powerShellCommand & '"' & "'" ; by this: Local $command_r = StringFormat('powershell.exe -Command "%s"', $powerShellCommand) As far as I read your code this should be all. In case this is not successful for all your cases, because you use double and single quotes not consistent in the powershell commands, escape the double quotes by this: Local $command_r = StringFormat('powershell.exe -Command "%s"', StringReplace($powerShellCommand, '"', '""')) Best regards Sven
    1 point
  20. [New Release] - 06 April 2019 Added: Error-checking for sensible column numbers in the $aSortData array, with an additional error status. ------------------------------------------------------------------------------------------------------------------------ While answering a recent question about sorting a ListView on several columns, I developed this function to sort a 2D array on several columns and I though I might give it a wider audience. Here is the function: #include-once ;#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w- 7 ; #INCLUDES# ========================================================================================================= #include <Array.au3> ; =============================================================================================================================== ; #INDEX# ======================================================================================================================= ; Title .........: ArrayMultiColSort ; AutoIt Version : v3.3.8.1 or higher ; Language ......: English ; Description ...: Sorts 2D arrays on several columns ; Note ..........: ; Author(s) .....: Melba23 ; Remarks .......: ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _ArrayMultiColSort : Sort 2D arrays on several columns ; =============================================================================================================================== ; #INTERNAL_USE_ONLY#================================================================================================= ; __AMCS_SortChunk : Sorts array section ; =============================================================================================================================== ; #FUNCTION# ==================================================================================================================== ; Name...........: _ArrayMultiColSort ; Description ...: Sort 2D arrays on several columns ; Syntax.........: _ArrayMultiColSort(ByRef $aArray, $aSortData[, $iStart = 0[, $iEnd = 0]]) ; Parameters ....: $aArray - The 2D array to be sorted ; $aSortData - 2D array holding details of the sort format ; Format: [Column to be sorted, Sort order] ; Sort order can be either numeric (0/1 = ascending/descending) or a ordered string of items ; Any elements not matched in string are left unsorted after all sorted elements ; $iStart - Element of array at which sort starts (default = 0) ; $iEnd - Element of array at which sort endd (default = 0 - converted to end of array) ; Requirement(s).: v3.3.8.1 or higher ; Return values .: Success: No error ; Failure: @error set as follows ; @error = 1 with @extended set as follows (all refer to $sIn_Date): ; 1 = Array to be sorted not 2D ; 2 = Sort data array not 2D ; 3 = More data rows in $aSortData than columns in $aArray ; 4 = Start beyond end of array ; 5 = Start beyond End ; @error = 2 with @extended set as follows: ; 1 = Invalid string parameter in $aSortData ; 2 = Invalid sort direction parameter in $aSortData ; 3 = Invalid column index in $aSortData ; Author ........: Melba23 ; Remarks .......: Columns can be sorted in any order ; Example .......; Yes ; =============================================================================================================================== Func _ArrayMultiColSort(ByRef $aArray, $aSortData, $iStart = 0, $iEnd = 0) ; Errorchecking ; 2D array to be sorted If UBound($aArray, 2) = 0 Then Return SetError(1, 1, "") EndIf ; 2D sort data If UBound($aSortData, 2) <> 2 Then Return SetError(1, 2, "") EndIf If UBound($aSortData) > UBound($aArray) Then Return SetError(1, 3) EndIf For $i = 0 To UBound($aSortData) - 1 If $aSortData[$i][0] < 0 Or $aSortData[$i][0] > UBound($aArray, 2) -1 Then Return SetError(2, 3, "") EndIf Next ; Start element If $iStart < 0 Then $iStart = 0 EndIf If $iStart >= UBound($aArray) - 1 Then Return SetError(1, 4, "") EndIf ; End element If $iEnd <= 0 Or $iEnd >= UBound($aArray) - 1 Then $iEnd = UBound($aArray) - 1 EndIf ; Sanity check If $iEnd <= $iStart Then Return SetError(1, 5, "") EndIf Local $iCurrCol, $iChunk_Start, $iMatchCol ; Sort first column __AMCS_SortChunk($aArray, $aSortData, 0, $aSortData[0][0], $iStart, $iEnd) If @error Then Return SetError(2, @extended, "") EndIf ; Now sort within other columns For $iSortData_Row = 1 To UBound($aSortData) - 1 ; Determine column to sort $iCurrCol = $aSortData[$iSortData_Row][0] ; Create arrays to hold data from previous columns Local $aBaseValue[$iSortData_Row] ; Set base values For $i = 0 To $iSortData_Row - 1 $aBaseValue[$i] = $aArray[$iStart][$aSortData[$i][0]] Next ; Set start of this chunk $iChunk_Start = $iStart ; Now work down through array For $iRow = $iStart + 1 To $iEnd ; Match each column For $k = 0 To $iSortData_Row - 1 $iMatchCol = $aSortData[$k][0] ; See if value in each has changed If $aArray[$iRow][$iMatchCol] <> $aBaseValue[$k] Then ; If so and row has advanced If $iChunk_Start < $iRow - 1 Then ; Sort this chunk __AMCS_SortChunk($aArray, $aSortData, $iSortData_Row, $iCurrCol, $iChunk_Start, $iRow - 1) If @error Then Return SetError(2, @extended, "") EndIf EndIf ; Set new base value $aBaseValue[$k] = $aArray[$iRow][$iMatchCol] ; Set new chunk start $iChunk_Start = $iRow EndIf Next Next ; Sort final section If $iChunk_Start < $iRow - 1 Then __AMCS_SortChunk($aArray, $aSortData, $iSortData_Row, $iCurrCol, $iChunk_Start, $iRow - 1) If @error Then Return SetError(2, @extended, "") EndIf EndIf Next EndFunc ;==>_ArrayMultiColSort ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: __AMCS_SortChunk ; Description ...: Sorts array section ; Author ........: Melba23 ; Remarks .......: ; =============================================================================================================================== Func __AMCS_SortChunk(ByRef $aArray, $aSortData, $iRow, $iColumn, $iChunkStart, $iChunkEnd) Local $aSortOrder ; Set default sort direction Local $iSortDirn = 1 ; Need to prefix elements? If IsString($aSortData[$iRow][1]) Then ; Split elements $aSortOrder = StringSplit($aSortData[$iRow][1], ",") If @error Then Return SetError(1, 1, "") EndIf ; Add prefix to each element For $i = $iChunkStart To $iChunkEnd For $j = 1 To $aSortOrder[0] If $aArray[$i][$iColumn] = $aSortOrder[$j] Then $aArray[$i][$iColumn] = StringFormat("%02i-", $j) & $aArray[$i][$iColumn] ExitLoop EndIf Next ; Deal with anything that does not match If $j > $aSortOrder[0] Then $aArray[$i][$iColumn] = StringFormat("%02i-", $j) & $aArray[$i][$iColumn] EndIf Next Else Switch $aSortData[$iRow][1] Case 0, 1 ; Set required sort direction if no list If $aSortData[$iRow][1] Then $iSortDirn = -1 Else $iSortDirn = 1 EndIf Case Else Return SetError(1, 2, "") EndSwitch EndIf ; Sort the chunk Local $iSubMax = UBound($aArray, 2) - 1 __ArrayQuickSort2D($aArray, $iSortDirn, $iChunkStart, $iChunkEnd, $iColumn, $iSubMax) ; Remove any prefixes If IsString($aSortData[$iRow][1]) Then For $i = $iChunkStart To $iChunkEnd $aArray[$i][$iColumn] = StringTrimLeft($aArray[$i][$iColumn], 3) Next EndIf EndFunc ;==>__AMCS_SortChunk And here is an example to show it working: #include "ArrayMultiColSort.au3" #include <String.au3> ; Only used to fill array ; Create and display array Global $aArray[100][4] For $i = 0 To 99 $aArray[$i][0] = _StringRepeat(Chr(Random(65, 68, 1)), 5) $aArray[$i][1] = _StringRepeat(Chr(Random(74, 77, 1)), 5) $aArray[$i][2] = _StringRepeat(Chr(Random(80, 83, 1)), 5) $aArray[$i][3] = _StringRepeat(Chr(Random(87, 90, 1)), 5) Next _ArrayDisplay($aArray, "Unsorted") ; Copy arrays for separate examples below $aArray_1 = $aArray $aArray_2 = $aArray ; This sorts columns in ascending order - probably the most common requirement ; Sort requirement: ; Col 0 = Decending ; Col 1 = Ascending ; Col 2 = Required order of elements (note not alphabetic PQRS nor reverse SRQP) ; Col 3 = Ascending Global $aSortData[][] = [ _ [0, 1], _ [1, 0], _ [2, "SSSSS,QQQQQ,PPPPP,RRRRR"], _ [3, 0]] ; Sort and display array _ArrayMultiColSort($aArray_1, $aSortData) ; Display any errors encountered If @error Then ConsoleWrite("Oops: " & @error & " - " & @extended & @CRLF) _ArrayDisplay($aArray_1, "Sorted in order 0-1-2-3") ; But the UDF can sort columns in any order ; Sort requirement: ; Col 2 = Decending ; Col 0 = Ascending Global $aSortData[][] = [ _ [2, 1], _ [0, 0]] ; Sort and display array _ArrayMultiColSort($aArray_2, $aSortData) ; Display any errors encountered If @error Then ConsoleWrite("Oops: " & @error & " - " & @extended & @CRLF) _ArrayDisplay($aArray_2, "Sorted in order 2-0") And here are both in zip form: ArrayMultiColSort.zip As usual all comments welcome. M23
    1 point
×
×
  • Create New...