====== Winst32 Preprocessor ====== In short, this preprocessor is run instead of the original opsi WINST action processor as main action processor command to //add// additional control and logging features. During its own run it will decide if WINST is allowed to run or not and therefor control the overall action processing. At the moment it is capable of: * Checking registry values before starting WINST as a action precondition * Checking list of running processes before starting WINST as a action precondition * Setting registry values before and/or after starting WINST * Logging current product configuration of the client and the action request(s) being processed **But beware:** __**It is ABSOLUTELY NOT meant as a complete replacement for the allmighty winst32.exe that everybody knows or as a new installing mechanism!**__ __**THIS IS NOT FOR THE FAINT-HEARTED AND YOU SHOULD BE ABSOLUTELY SURE OF WHAT YOU ARE DOING! I WILL NOT GIVE ANY IN-DEPTH EXPLANATION FOR THE NEWBY USERS!**__ __**USE WITH CAUTION!USE WITH CAUTION!USE WITH CAUTION!NO RISK NO FUN!USE WITH CAUTION!USE WITH CAUTION!USE WITH CAUTION!**__ Links to the files: * [[winst32_preproc.exe#winst32_preproc.au3|winst32_preproc.au3 - main program AutoIt source code]] * [[winst32_preproc.exe#winst32_preproc.ini (example)|winst32_preproc.ini - example ini file]] * [[winst32_preproc.exe#AssoArrays.au3|AssoArrays.au3 - (necessary for compilation)]] * [[winst32_preproc.exe#BinaryCall.au3|BinaryCall.au3 - (necessary for compilation)]] * [[winst32_preproc.exe#GetOpt.au3|GetOpt.au3 - (necessary for compilation)]] * [[winst32_preproc.exe#Json.au3|Json.au3 - (necessary for compilation)]] v1.0, Tested with Windows 10 x64 1809 Enterprise --- //[[holger.pandel@googlemail.com|pandel]] 2019/10/25 13:52// v1.1, Tested with Windows 10 x64 1809 Enterprise --- //[[holger.pandel@googlemail.com|pandel]] 2019/10/28 15:00// ==== Motivation ==== Let's imagine the following situation: We have the problem that our company uses two different software deployment systems and the only one that we can control ourselfs is opsi. So it is rather important to know if we can safely process our opsi tasks. Additionally, we have some clients which run more or less important scheduled tasks which should not be disturbed, so we would have to check for that, too. All these checks can be done via Windows registry queries, but not via opsi client agent directly. So I had the idea to write this preprocessor... ==== How it works ==== The preprocssor is run instead of the original winst32.exe via opsiclientd and takes control over the execution of winst32.exe based on registry checks itself. It acts as a wrapper to winst32.exe. Everything is controlled via a single configuration file. You can create an example configuration file in the same directory by running winst32_preproc.exe without any command line parameter. === Where to install === That depends on your liking. You can put it into your standard opsi-client-agent installation folder, but then you have to take care of re-deployment after updating the client. This can be done by re-packaging the original opsi-client-agent-*.opsi with the included files, ie. === Modifications to opsiclientd.conf === In opsiclientd.conf you will find a configuration entry (or multiple ones if you configured your own/different events) looking like this: [action_processor] ... # Action processor command command = "%action_processor.local_dir%\\%action_processor.filename%" /opsiservice "%service_url%" /clientid %global.host_id% /username %global.host_id% /password %global.opsi_host_key% To get the preprocessor up and running, you have to replace the above mentioned command like so (notice the "-r" at the very end of the line!): command = "%global.base_dir%\\winst32_preproc.exe" -f="%action_processor.local_dir%\\%action_processor.filename%" -s=%service_url% -c=%global.host_id% -u=%global.host_id% -p=%global.opsi_host_key% -l="%global.log_dir%\\winst32_preproc.log" -r This example expects winst32_preproc.exe and its ini file in %global.base_dir% (see opsiclientd.conf). If you put it somewhere else, modify the line accordingly. === Command line parameters === The program contains a short help regarding the command line parameters. Simply issue "winst32_preproc.exe -h" to see ------------------------------------------------------------------------------------------------------------------ winst32_preproc (MIT licensed) v1.1.0.0 Command Line Available command line options: -f --acp-command Action processor filename, ie. winst32.exe -s --service_url service URL -c --clientid client id -u --username service username -p --password password -d --debug Debug output -r --run Production mode (REALLY activate the program!) -l --log Different logfile name -h --help Show this help Default logfile: C:\Users\\AppData\Local\Temp\winst32_preproc\winst32_preproc.log === Standard mode = Test mode === For security reasons, the default mode of winst32_preproc ist test mode. That means, if you not explicitely specify production mode via command line, the program will only show you what would happen, but don't actually do anything. So, if you have configured the ini file according to your needs you can simple put both files in a directory on your test client and start the program from an administrator console to test it. Let's assume the following (see opsiclientd.conf for your current values): * your opsi host_id: testclient.opsinet.local * your opsi_host_key: 5f5efffffa26fe90473a734f1da68fed * opsi config service url: https://192.168.10.10:4447/rpc winst32_preproc.exe -d -t -f="C:\Program Files (x86)\opsi.org\opsi-client-agent\\opsi-winst\\winst32.exe" -s=https://192.168.10.10:4447/rpc -c=testclient.opsinet.local -u=testclient.opsinet.local -p=5f5efffffa26fe90473a734f1da68fed **NOTE: the path to winst32.exe must be the right one, otherwise he programm will not do anything.** ==== The configuration: winst32_preproc.ini ==== The structure of the configuration file is rather simple. It consists of five blocks: * general - logging configuration and version information * registry_check - registry value(s) that should be checked to decide if winst32.exe is being executed * registry_pre - registry value(s) that should be set BEFORE winst32.exe is being executed * registry_post - registry value(s) that should be set AFTER winst32.exe is being executed * process_check - list of processes that should be checked to decide if winst32.exe is being executed === How the blocks work === == registry_check == The keys and values you specify here will be checked for their existance AND values. They a treated as ONE single result, that means, if you specify multiple values to check for and one (ore more) values cannot be read or found, this check is considered "failed" and winst32.exe execution will be prevented. == process_check == The process names you specify here will be checked if they are running. They a treated as ONE single result, that means, if you specify multiple process names to check for and one (ore more) will found, this check is considered "failed" and winst32.exe execution will be prevented. == registry_pre / registry_post == The registry_pre and registry_post block work the same. They simply set the values you specify IF (AND ONLY IF) the registry_check block was passed successfully. == Configuration values == __Registry checks__ A configuration entry like reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0 means, that (on a 64 bit machine!) the preprocessor looks into HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV for a value FSVActive = 0 On a 64 bit machine, a line like reg1=HKLM|32|SOFTWARE\RZ\FSV|FSVActive|0 would result in a actual registry key of HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\RZ\FSV In general, a single line can be seen as: reg=||||| == Possible registry leaf values: == * HKLM (stands for: HKEY_LOCAL_MACHINE) * HKU (stands for: HKEY_USERS) * HKCR (stands for: HKEY_CLASSES_ROOT) * HKCU (stands for: HKEY_CURRENT_USER) * HKCC (stands for: HKEY_CURRENT_CONFIG) == Possible bitnesses: == * 32 * 64 == Possible value types: == * REG_BINARY ("0x..." value notation) * REG_SZ * REG_EXPAND_SZ * REG_QWORD ("0x..." value notation) * REG_DWORD * (REG_MULTI_SZ can be specified, but you are only able to read or set the first element, so it is not very useful here.) __Process checks__ Process checks are much simpler: A line like proc1=winword.exe will check, if winword.exe is found in the list of active processes. === Example === [general] version=1.1.0.0 logInstalled=1 logActions=1 [registry_check] count=1 reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0 [registry_pre] count=2 reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|1|REG_SZ reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|1|REG_SZ [registry_post] count=3 reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0|REG_SZ reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|0|REG_SZ reg3=HKLM|32|SOFTWARE\Somewhere\Else|SomeOtherValue|50|REG_DWORD [process_check] count=1 proc1=svchost.exe The syntax should be obvious. "count" is simply the number of lines in the single block, every single entry starts with "reg" and a consecutive number. To deactivate a certain block completely, simply set its "count" value to 0. The single "reg" line entries will then be ignored, you don't have to delete them. //DON'T delete a block to disable it!// What this example will do (in pseudo-lingo): if ("FSVActive" under "HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV" is "0") AND ("svchost.exe" is NOT in the list of active processes) then log currently installed products on client log current action requests set "FSVActive" under "HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV" to "1" set "Active" under "HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\Winst" to "1" execute winst32.exe (with its parameters) set "FSVActive" under "HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV" to "0" set "Active" under "HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\Winst" to "0" set "SomeOtherValue" under "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Somewhere\Else" to "50" else exit and do nothing If you don't want the preprocessor to log your installed products and/or action requests everytime it is started, simply set the configuration values to 0. ==== Files ==== * [[winst32_preproc.exe#winst32_preproc.au3|winst32_preproc.au3 - main program AutoIt source code]] * [[winst32_preproc.exe#winst32_preproc.ini (example)|winst32_preproc.ini - example ini file]] * [[winst32_preproc.exe#AssoArrays.au3|AssoArrays.au3 - (necessary for compilation)]] * [[winst32_preproc.exe#BinaryCall.au3|BinaryCall.au3 - (necessary for compilation)]] * [[winst32_preproc.exe#GetOpt.au3|GetOpt.au3 - (necessary for compilation)]] * [[winst32_preproc.exe#Json.au3|Json.au3 - (necessary for compilation)]] === winst32_preproc.ini (example) === [general] version=1.1.0.0 logInstalled=1 logActions=1 [registry_check] count=1 reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0 [registry_pre] count=2 reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|1|REG_SZ reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|1|REG_SZ [registry_post] count=3 reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0|REG_SZ reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|0|REG_SZ reg3=HKLM|32|SOFTWARE\Somewhere\Else|SomeOtherValue|50|REG_DWORD [process_check] count=1 proc1=svchost.exe === winst32_preproc.au3 === ;***************************************** ;winst32_preproc.au3 by Holger Pandel ;Erstellt mit ISN AutoIt Studio v. 1.09 ;***************************************** ;OPSI standard call to winst32 ;command = "%action_processor.local_dir%\\%action_processor.filename%" /opsiservice "%service_url%" /clientid %global.host_id% /username %global.host_id% /password %global.opsi_host_key% ; modified preprocessor command ;_getopts() ;command = "%global.base_dir%\\winst32_preproc.exe" -f="%action_processor.local_dir%\\%action_processor.filename%" -s=%service_url% -c=%global.host_id% -u=%global.host_id% -p=%global.opsi_host_key% -l="%global.log_dir%\\winst32_preproc.log" -r ; includes #include #include #include #include #include "UDFs\AssoArrays.au3" #include "UDFs\GetOpt.au3" #include "UDFs\json.au3" Global Const $HTTP_STATUS_OK = 200 ;Opt('MustDeclareVars', 1) ; appplication title and version Global $header = "winst32_preproc (MIT licensed)" Global $version = "1.1.0.0" Global $logdir = @TempDir & "\winst32_preproc" Global $log_mode = True ; write log file = true Global $log_file = $logdir & "\winst32_preproc.log" ; set default logfile name Global $opsi_command = "C:\Program Files (x86)\opsi.org\opsi-client-agent\opsi-winst\winst32.exe" Global $opsi_url = "https://server.id:4447/rpc" Global $opsi_client = "localhost.localdom.net" Global $opsi_user = "localhost.localdom.net" Global $opsi_pass = "1234567890" Global $Pid, $Handle, $ExitCode, $Cmd, $Cmdout Global $iniName = "winst32_preproc.ini" Global $isRegOk = False ; if true, it outputs messages on console, if false onbly to log file Global $debug = False ; if true, do not actually run any command Global $test = True Local $msg = '------------------------------------------------------------------------------------------------------------------' & @CRLF ; start line $msg &= $header & ' v' & $version & ' Command Line' & @CRLF & @CRLF ; Message. ; Load ini data Global $iniName = "winst32_preproc" _getIniValues($iniName) ; let's have a look at the CmdLine If $CmdLine[0] = 0 Then $msg &= "No arguments passed. See -h for help or use -d for a test run. Exiting." & @CRLF & @CRLF ConsoleWrite($msg) Exit 0 Else _debug($msg) EndIf ; get cmdline options _getopts() If not FileExists($opsi_command) Then _debug("OPSI action processor not found. Exit.") _exit(1) EndIf _debug("Running as user: " & @UserName) _debug("Profile dir : " & @UserProfileDir) _debug("Log file : " & $log_file) ; for debugging purpose ;If $test Then ; x_display() ;EndIf ; --------------------------------------------------------------------------------------------------------------------- ; ---- MAIN --------- If x($iniName & ".registry_check.count") > 0 Then Local $isRegOk = _runRegistryCheck() If $isRegOk Then _debug("All registry checks passed successfully.") Else _debug("One or more registry checks where unsuccsessful.") _exit(0) EndIf EndIf If x($iniName & ".process_check.count") > 0 Then Local $isProcOk = _runProcessCheck() If $isProcOk Then _debug("All process checks passed successfully.") Else _debug("One or more process checks where unsuccsessful.") _exit(0) EndIf EndIf if $test Then ; test run $Cmdout = '"' & $opsi_command & '" /opsiservice "' & $opsi_url & '" /clientid ' & $opsi_client & ' /username ' & $opsi_user & ' /password ***(confidential)***' _debug("") _debug("TESTMODE processing") _debug("-------------------") _debug("action processor: " & $opsi_command) _debug("url: " & $opsi_url) _debug("client: " & $opsi_client) _debug("user: " & $opsi_user) _debug("pass: (CONFIDENTIAL)") _debug("") If x($iniName & ".general.logInstalled") = 1 Then _opsi_showInstalledProducts($opsi_url, $opsi_client, $opsi_user, $opsi_pass) If x($iniName & ".general.logActions") = 1 Then _opsi_showActionRequests($opsi_url, $opsi_client, $opsi_user, $opsi_pass) _debug("") _debug("I) Registry PRE-phase:") _updateRegistry("pre") _debug("") _debug("II) Command to be launched: " & $Cmdout) _debug("") _debug("III) Registry POST-phase:") _updateRegistry("post") _debug("") _debug("Testing only, no opsi processing for now.") _debug("For production mode, use the -r switch (USE WITH CAUTION).") _exit(0) Else ; production run If x($iniName & ".general.logInstalled") = 1 Then _opsi_showInstalledProducts($opsi_url, $opsi_client, $opsi_user, $opsi_pass) If x($iniName & ".general.logActions") = 1 Then _opsi_showActionRequests($opsi_url, $opsi_client, $opsi_user, $opsi_pass) _updateRegistry("pre") $retval = _startActionProcessor($opsi_command, $opsi_url, $opsi_user, $opsi_client, $opsi_pass) _updateRegistry("post") _exit($retval) EndIf ; --------------------------------------------------------------------------------------------------------------------- ; ---- SUPPORT FUNCTIONS --------- ; check if registry values are as necessary Func _runRegistryCheck() Local $rc = False _debug("Running registry checkss...") If x($iniName & ".registry_check.count") = 0 Then _debug("--> nothing to check") return True EndIf ; Example entry: reg1=HKLM|64|SOFTWARE\FIDUCIA\Bitlocker|FSVActive|0 For $i = 1 To x($iniName & ".registry_check.count") Local $a = x($iniName & ".registry_check.reg" & $i) _debug("Test " & $i & ": " & $a[0] & "\" & $a[2] & "(" & $a[1] & "bit):" & $a[3] & "=" & $a[4]) If $a[2] = "32" Then If RegRead($a[0] & "\" & $a[2], $a[3]) = $a[4] then $rc = True _debug("--> rc=" & $rc) Else $rc = False _debug("--> rc=" & $rc) return $rc EndIf Else If RegRead($a[0] & $a[1] & "\" & $a[2], $a[3]) = $a[4] then $rc = True _debug("--> rc=" & $rc) Else $rc = False _debug("--> rc=" & $rc) return $rc EndIf EndIf Next return $rc EndFunc ; check if specified process exist Func _runProcessCheck() Local $rc = False _debug("Running process checks...") If x($iniName & ".process_check.count") = 0 Then _debug("--> nothing to check") return True EndIf ; Example entry: proc1=winword.exe For $i = 1 To x($iniName & ".process_check.count") Local $a = x($iniName & ".process_check.proc" & $i) _debug("Test if not running " & $i & ": " & $a) Local $pid = ProcessExists($a) If $pid > 0 Then $rc = False _debug("--> rc=" & $rc) _debug("--> PID=" & $pid) return $rc Else $rc = True _debug("--> rc=" & $rc) return $rc EndIf Next EndFunc ; set registry entries ; $phase can be "pre" or "post" ; RegWrite ( "keyname" [, "valuename", "type", value] ) Func _updateRegistry($phase) Local $rc = False _debug("Running registry phase: " & $phase) ; Example entry: reg1=HKLM|32|SOFTWARE\FIDUCIA\Bitlocker|FSVActive|0|REG_SZ If x($iniName & ".registry_" & $phase & ".count") = 0 Then _debug("--> nothing to do in this phase") return EndIf For $i = 1 To x($iniName & ".registry_" & $phase & ".count") Local $a = x($iniName & ".registry_" & $phase & ".reg" & $i) If not $test Then _debug("Setting " & $i & ": " & $a[0] & "\" & $a[2] & "(" & $a[1] & "bit):" & $a[3] & "=" & $a[4]) If $a[2] = "32" Then If RegWrite($a[0] & "\" & $a[2], $a[3], $a[5], $a[4]) then $rc = True _debug("--> rc=" & $rc) EndIf Else If RegWrite($a[0] & $a[1] & "\" & $a[2], $a[3], $a[5], $a[4]) then $rc = True _debug("--> rc=" & $rc) EndIf EndIf Else _debug("(TESTMODE) Setting " & $i & ": " & $a[0] & "\" & $a[2] & "(" & $a[1] & "bit):" & $a[3] & " = " & $a[5] & ":" & $a[4]) EndIf Next EndFunc ; run action processor command Func _startActionProcessor($command, $URL, $clientid, $user, $pass) _debug("Starting action...") $Cmd = '"' & $command & '" /opsiservice "' & $URL & '" /clientid ' & $clientid & ' /username ' & $user & ' /password ' & $pass $Cmdout = '"' & $command & '" /opsiservice "' & $URL & '" /clientid ' & $clientid & ' /username ' & $user & ' /password ***(confidential)***' _debug("action processor: " & $command) _debug("url: " & $URL) _debug("client: " & $clientid) _debug("user: " & $user) _debug("pass: (CONFIDENTIAL)") _debug("cmd: " & $Cmdout) $Pid = Run($Cmd, '', @SW_SHOW, $STDOUT_CHILD + $STDERR_CHILD) $Handle = _ProcessExitCode($Pid) ProcessWaitClose($pid) $ExitCode = _ProcessExitCode($Pid, $Handle) _ProcessCloseHandle($Handle) return $ExitCode EndFunc ; handle command line options Func _getopts() Local $sMsg = '------------------------------------------------------------------------------------------------------------------' & @CRLF ; start line $sMsg &= $header & ' v' & $version & ' Command Line' & @CRLF & @CRLF ; Message. Local $sOpt, $sSubOpt, $sOper Local $h_specified = False Local $failure = False ; Options array, entries have the format [short, long, default value] Local $aOpts[9][3] = [ _ ['-f', '--acp_command', $GETOPT_REQUIRED_ARGUMENT], _ ['-s', '--service_url', $GETOPT_REQUIRED_ARGUMENT], _ ['-c', '--clientid', $GETOPT_REQUIRED_ARGUMENT], _ ['-u', '--username', $GETOPT_REQUIRED_ARGUMENT], _ ['-p', '--password', $GETOPT_REQUIRED_ARGUMENT], _ ['-d', '--debug', True], _ ['-r', '--r', True], _ ['-l', '--log', $log_file], _ ['-h', '--help', True]] _GetOpt_Set($aOpts) ; Set options. If 0 < $GetOpt_Opts[0] Then ; If there are any options... ; first loop: analyse parameters and create message texts, exit program if needed While 1 ; ...loop through them one by one. ; Get the next option passing a string with valid options. $sOpt = _GetOpt('f:s:c:u:p:rdtlh') ; p: means -p option requires an argument. If Not $sOpt Then ExitLoop ; No options or end of loop. ; Check @extended above if you want better error handling. ; The current option is stored in $GetOpt_Opt, it's index (in $GetOpt_Opts) ; in $GetOpt_Ind and it's value in $GetOpt_Arg. Switch $sOpt ; What is the current option? Case '?' ; Unknown options come here. @extended is set to $E_GETOPT_UNKNOWN_OPTION $sMsg &= 'Unknown option: ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ').' & @CRLF $sMsg &= 'UNKNOWN OPTION passed.' & @CRLF $failure = True Case ':' ; Options with missing required arguments come here. @extended is set to $E_GETOPT_MISSING_ARGUMENT $sMsg &= 'MISSING REQUIRED ARGUMENT for option: ' & $GetOpt_Ind & ': ' & $GetOpt_Opt & @CRLF $failure = True Case 'l' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')' $sMsg &= '.' & @CRLF $log_file = $GetOpt_Arg Case 'd' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')' $sMsg &= '.' & @CRLF $debug = True Case 'r' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')' $sMsg &= '.' & @CRLF $test = False Case 'f' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')' $sMsg &= '.' & @CRLF $opsi_command = $GetOpt_Arg Case 's' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')' $sMsg &= '.' & @CRLF $opsi_url = $GetOpt_Arg Case 'c' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')' $sMsg &= '.' & @CRLF $opsi_client = $GetOpt_Arg Case 'u' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')' $sMsg &= '.' & @CRLF $opsi_user = $GetOpt_Arg Case 'p' $sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt $sMsg &= ' with ***(confidential)*** value supplied. ' $sMsg &= '.' & @CRLF $opsi_pass = $GetOpt_Arg Case 'h' $sMsg &= 'Available command line options:' & @CRLF & @CRLF & _ '-f' & @TAB & '--acp-command ' & @TAB & 'Action processor filename, ie. winst32.exe' & @CRLF & _ '-s' & @TAB & '--service_url ' & @TAB & 'service URL' & @CRLF & _ '-c' & @TAB & '--clientid ' & @TAB & 'client id' & @CRLF & _ '-u' & @TAB & '--username ' & @TAB & 'service username' & @CRLF & _ '-p' & @TAB & '--password ' & @TAB & 'password' & @CRLF & _ '-d' & @TAB & '--debug ' & @TAB & 'Debug output' & @CRLF & _ '-r' & @TAB & '--run ' & @TAB & 'Production mode (REALLY activate the program!)' & @CRLF & _ '-l' & @TAB & '--log ' & @TAB & 'Different logfile name' & @CRLF & _ '-h' & @TAB & '--help ' & @TAB & 'Show this help' & @CRLF & @CRLF & _ 'Default logfile: ' & $log_file & @CRLF & @CRLF $h_specified = True EndSwitch Wend If 0 < $GetOpt_Opers[0] Then ; If there are any operands... $sMsg &= 'INVALID OPERANDS PASSED. This is not allowed. Check for missing ''='' in --path or --log option!' & @CRLF While 1 ; ...loop through them one by one. $sOper = _GetOpt_Oper() ; Get the next operand. If Not $sOper Then ExitLoop ; no operands or end of loop. ; Check @extended above if you want better error handling. $sMsg &= 'Operand ' & $GetOpt_OperInd & ': ' & $sOper & @CRLF WEnd $failure = True EndIf If $failure Then $sMsg &= @CRLF & 'Please check the command line parameters!' & @CRLF If $h_specified Or $failure Then ; show help screen or wrong options ? ConsoleWrite(@crlf & $sMsg) If $failure Then _exit(1) _exit(0) EndIf EndIf ; write header to log _writeLog(_formatIOText($sMsg)) EndFunc ; part of _getIniValues Func _getLeaf($leaf, $tab) Local $str If not IsObj($leaf) Then Return $colKeys = $leaf.Keys For $strKey in $colKeys $str = "" If IsArray($leaf.Item($strKey)) Then $str = $leaf.Item($strKey)[0] For $i = 1 to Ubound($leaf.Item($strKey)) - 1 $str &= "|" & $leaf.Item($strKey)[$i] Next _debug($tab & $strKey & ' -> ' & $str) Else _debug($tab & $strKey & ' -> ' & $leaf.Item($strKey)) EndIf _getLeaf($leaf.Item($strKey), $tab & " ") Next EndFunc ; read ini values Func _getIniValues($iniName) Dim $szDrive, $szDir, $szFName, $szExt, $pathTemp, $kdSel, $op $pathTemp = _PathSplit(@ScriptFullPath, $szDrive, $szDir, $szFName, $szExt) Dim $appPath = $szDrive & $szDir Dim $iniFile = $appPath & $iniName & ".ini" Dim $rc, $cpm[3], $msg, $str, $colKeys _debug("Ini file : " & $iniFile) If FileExists($iniFile) Then ;--- global INI file found $rc = _ReadAssocFromIni2($iniFile) If @error Then _debug("Error while reading ini file. Errno: " & @error) Else _debug("Entries read: " & $rc) _debug("Logging configuration:") _debug("--------------- snip ------------------") _getLeaf($_xHashCollection, " ") _debug("--------------- snip -----------------") _debug("INI reading complete.") EndIf Else ;--- INI not existing, creating new one in script directory $msg = "INI file not found: " & $iniFile & @CRLF & _ "A new INI file with example values will be created in the script directory." & @CRLF & _ "Please customize the INI file before using it." & @CRLF & @CRLF ConsoleWrite($msg) _createIni($appPath & $iniName) EndIf EndFunc ;==>_getIniValues ; get current product status Func _REST_request($req, $clientid, $user, $pass) Local $REST = ObjCreate("WinHttp.WinHttpRequest.5.1") Local $agent ='Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36' ; https://$serverid:$serverport/rpc?{ "id": 1, "method": "getProductInstallationStatus_listOfHashes", "params": ["$clientid"] } ;ConsoleWrite(@CRLF & "Request: " & $req) $REST.Open("GET", $req, False) If (@error) Then Return SetError(1, 0, "Error on open") $REST.SetRequestHeader('Authorization','Basic ' & base64($user & ":" & $pass, True, True)) $REST.setRequestHeader ("User-Agent", $agent) $REST.Option(4) = 13056 $REST.Send() If (@error) Then Return SetError(2, 0, "Error on send") If ($REST.Status <> $HTTP_STATUS_OK) Then Return SetError(3, 0, "Error on request - HTTP status " & $REST.Status) return $REST.ResponseText EndFunc ; convert lastStateChange into more readable format Func _opsiDate($raw) Local $year = StringMid($raw,1, 4) Local $mon = StringMid($raw,5, 2) Local $day = StringMid($raw,7, 2) Local $hour = StringMid($raw,9, 2) Local $min = StringMid($raw,11, 2) return $day & "." & $mon & "." & $year & " " & $hour & ":" & $min EndFunc ; request all installed products on client Func _opsi_showInstalledProducts($URL, $clientid, $user, $pass) Local $_getProductInstallationStatus_listOfHashes = $URL & '?{ "id": 1, "method": "getProductInstallationStatus_listOfHashes", "params": ["' & $clientid & '"] }' Local $retval = _REST_request($_getProductInstallationStatus_listOfHashes, $clientid, $user, $pass) If (@error) Then _debug("Error on REST request: " & $retval) return EndIf Local $data = json_decode($retVal) ;json_dump($retval) _debug("Currently installed:") Local $i = 0 While 1 $product_id = json_get($data, '.result[' & $i & '].productId') $installationStatus = json_get($data, '.result[' & $i & '].installationStatus') $installed = json_get($data, '.result[' & $i & '].lastStateChange') If @error Then ExitLoop If $installationStatus = "installed" Then _debug("--> " & $product_id & " (lastStateChange: " & _opsiDate($installed) & ")") EndIf $i += 1 WEnd EndFunc ; request current action requests for client Func _opsi_showActionRequests($URL, $clientid, $user, $pass) Local $_getProductActionRequests_listOfHashes = $URL & '?{ "id": 1, "method": "getProductActionRequests_listOfHashes", "params": ["' & $clientid & '"] }' Local $retval = _REST_request($_getProductActionRequests_listOfHashes, $clientid, $user, $pass) If (@error) Then _debug("Error on REST request: " & $retval) return EndIf Local $data = json_decode($retVal) ;json_dump($retval) _debug("Current action requests:") Local $i = 0 While 1 $product_id = json_get($data, '.result[' & $i & '].productId') $actionRequest = json_get($data, '.result[' & $i & '].actionRequest') If @error Then ExitLoop If $actionRequest <> "none" Then _debug("--> " & $product_id & ": " & $actionRequest) EndIf $i += 1 WEnd EndFunc ; common exit Func _exit($rc) _debug("") _debug("winst32_preproc exiting.") _debug("------------------------------------------------------------------------------------------------------------------") Exit $rc EndFunc ; create default example ini file Func _createIni($ini) x($ini & ".general.version", $version) x($ini & ".general.logInstalled", 1) x($ini & ".general.logActions", 1) x($ini & ".registry_check.count", 1) Local $a = ["HKLM", "64", "SOFTWARE\RZ\FSV", "FSVActive", "0"] x($ini & ".registry_check.reg1", $a) x($ini & ".registry_pre.count", 2) Local $a = ["HKLM", "64", "SOFTWARE\RZ\FSV", "FSVActive", "1", "REG_SZ"] x($ini & ".registry_pre.reg1", $a) Local $a = ["HKLM", "64", "SOFTWARE\opsi.org\Winst", "Active", "1", "REG_SZ"] x($ini & ".registry_pre.reg2", $a) x($ini & ".registry_post.count", 3) Local $a = ["HKLM", "64", "SOFTWARE\RZ\FSV", "FSVActive", "0", "REG_SZ"] x($ini & ".registry_post.reg1", $a) Local $a = ["HKLM", "64", "SOFTWARE\opsi.org\Winst", "Active", "0", "REG_SZ"] x($ini & ".registry_post.reg2", $a) Local $a = ["HKLM", "32", "SOFTWARE\Somewhere\Else|SomeOtherValue", "50", "REG_DWORD"] x($ini & ".registry_post.reg3", $a) x($ini & ".process_check.count", 1) Local $a = ["svchost.exe"] x($ini & ".process_check.proc1", $a) _WriteAssocToIni($ini) EndFunc ; --------------------------------------------------------------------------------------------------------------------- ; ---- TOOLS --------- ; output debug messages to console Func _debug($dbg, $line = @ScriptLineNumber) Local $time = "[" & @MDAY & "." & @MON & "." & @YEAR & " " & @HOUR & ":" & @MIN & ":" & @SEC & "] ! DEBUG" If $debug Then if $line = -1 then ConsoleWrite(@CRLF & $time & ": " & $dbg) Else ConsoleWrite(@CRLF & $time & " (" & $line & "): " & $dbg) Endif EndIf _writeLog(_formatIOText($dbg)) EndFunc ;==>_debug ; format message text line for console and log ; if $mark is "?????", then special question format is used Func _formatIOText($text, $mark = "") Local $time = "[" & @MDAY & "." & @MON & "." & @YEAR & " " & @HOUR & ":" & @MIN & ":" & @SEC & "] " If $mark = "" Then $text = $time & StringReplace($text, @CRLF, @CRLF & $time) ElseIf $mark = "?????" Then $text = $time & $text & ": " Else $text = $time & "! " & $mark & ": " & StringReplace($text, @CRLF, @CRLF & $time & "! " & $mark & ": ") EndIf Return $text EndFunc ;==>_formatIOText ; log messages to disc Func _writeLog($msg, $nocrlf = False) ; write lines to logfile, even if --quiet is specified If $log_mode Then Local $file = FileOpen($log_file, 1 + 8) ; create dir and open/create file If $nocrlf Then FileWrite($file, $msg) Else FileWrite($file, @CRLF & $msg) EndIf FileFlush($file) FileClose($file) EndIf EndFunc ;==>_writeLog ;=============================================================================== ; ; Function Name: _ProcessExitCode() ; Description: Returns a handle/exitcode from use of Run(). ; Parameter(s): $i_Pid - ProcessID returned from a Run() execution ; $h_Process - Process handle ; Requirement(s): None ; Return Value(s): On Success - Returns Process handle while Run() is executing ; (use above directly after Run() line with only PID parameter) ; - Returns Process Exitcode when Process does not exist ; (use above with PID and Process Handle parameter returned from first UDF call) ; On Failure - 0 ; Author(s): MHz (Thanks to DaveF for posting these DllCalls in Support Forum) ; ;=============================================================================== ; Func _ProcessExitCode($i_Pid, $h_Process = 0) ; 0 = Return Process Handle of PID else use Handle to Return Exitcode of a PID Local $v_Placeholder If Not IsArray($h_Process) Then ; Return the process handle of a PID $h_Process = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', 0x400, 'int', 0, 'int', $i_Pid) If Not @error Then Return $h_Process Else ; Return Process Exitcode of PID $h_Process = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $h_Process[0], 'int*', $v_Placeholder) If Not @error Then Return $h_Process[2] EndIf Return 0 EndFunc ;==>_ProcessExitCode ; close handle to attached process Func _ProcessCloseHandle($h_Process) ; Close the process handle of a PID DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $h_Process) If Not @error Then Return 1 Return 0 EndFunc ;==>_ProcessCloseHandle ; Get STDOUT and ERROUT from commandline tool Func ShowStdOutErr($l_Handle, $ShowConsole = 1, $Replace = "", $ReplaceWith = "") Local $Line = "x", $Line2 = "x", $tot_out, $err1 = 0, $err2 = 0, $cnt1 = 0, $cnt2 = 0 Do Sleep(10) $Line = StdoutRead($l_Handle) $err1 = @error If $Replace <> "" Then $Line = StringReplace($Line, $Replace, $ReplaceWith) $tot_out &= $Line If $ShowConsole Then ConsoleWrite($Line) EndIf $Line2 = StderrRead($l_Handle) $err2 = @error If $Replace <> "" Then $Line2 = StringReplace($Line2, $Replace, $ReplaceWith) $tot_out &= $Line2 If $ShowConsole Then ConsoleWrite($Line2) EndIf ; end the loop also when AutoIt3 has ended but a sub process was shelled with Run() that is still active ; only do this every 50 cycles to avoid cpu hunger If $cnt1 = 50 Then $cnt1 = 0 ; loop another 50 times just to ensure the buffers emptied. If Not ProcessExists($l_Handle) Then If $cnt2 > 2 Then ExitLoop $cnt2 += 1 EndIf EndIf $cnt1 += 1 Until ($err1 And $err2) Return $tot_out EndFunc ;==>ShowStdOutErr ; Author: MilesAhead ; http://www.autoitscript.com/forum/topic/110768-itaskbarlist3/page__view__findpost__p__910631 ; read AssocArray from IniFile Section ; returns number of items read - sets @error on failure ; modified (original see AssoArray.au3): Holger Pandel, 2015: accept full path name of ini Func _ReadAssocFromIni2($myIni = 'config.ini', $mySection = '', $sSep = "|") Local $szDrive, $szDir, $szFName, $szExt, $pathTemp $pathTemp = _PathSplit(@ScriptFullPath, $szDrive, $szDir, $szFName, $szExt) $sIni = $szFName If $mySection == '' Then $aSection = IniReadSectionNames ($myIni); All sections If @error Then _debug("Error while reading section names from: " & $myIni) Return SetError(@error, 0, 0) EndIf Else Dim $aSection[2] = [1,$mySection]; specific Section EndIf For $i = 1 To UBound($aSection)-1 Local $sectionArray = IniReadSection($myIni, $aSection[$i]) If @error Then _debug("Error while reading section: " & $aSection[$i]) Return SetError(1, 0, 0) EndIf For $x = 1 To $sectionArray[0][0] If StringInStr($sectionArray[$x][1], $sSep) then $posS = _MakePosArray($sectionArray[$x][1], $sSep) Else $posS = $sectionArray[$x][1] EndIf x($sIni&"."&$aSection[$i]&"."&$sectionArray[$x][0], $posS) Next next Return $sectionArray[0][0] EndFunc ;==>_ReadAssocFromIni ;============================================================================================================================== ; Function: base64($vCode [, $bEncode = True [, $bUrl = False]]) ; ; Description: Decode or Encode $vData using Microsoft.XMLDOM to Base64Binary or Base64Url. ; IMPORTANT! Encoded base64url is without @LF after 72 lines. Some websites may require this. ; ; Parameter(s): $vData - string or integer | Data to encode or decode. ; $bEncode - boolean | True - encode, False - decode. ; $bUrl - boolean | True - output is will decoded or encoded using base64url shema. ; ; Return Value(s): On Success - Returns output data ; On Failure - Returns 1 - Failed to create object. ; ; Author (s): (Ghads on Wordpress.com), Ascer ;=============================================================================================================================== Func base64($vCode, $bEncode = True, $bUrl = False) Local $oDM = ObjCreate("Microsoft.XMLDOM") If Not IsObj($oDM) Then Return SetError(1, 0, 1) Local $oEL = $oDM.createElement("Tmp") $oEL.DataType = "bin.base64" If $bEncode then $oEL.NodeTypedValue = Binary($vCode) If Not $bUrl Then Return $oEL.Text Return StringReplace(StringReplace(StringReplace($oEL.Text, "+", "-"),"/", "_"), @LF, "") Else If $bUrl Then $vCode = StringReplace(StringReplace($vCode, "-", "+"), "_", "/") $oEL.Text = $vCode Return $oEL.NodeTypedValue EndIf EndFunc ;==>base64 === AssoArrays.au3 === #include-once #include ; #INDEX# ======================================================================================================================= ; Title .........: xHashCollection ; AutoIt Version : 3.3.4.0 ; Language ......: English ; Description ...: Create and use Multidimentional Associative arrays ; Author ........: OHB ; =============================================================================================================================== ;~ $tt = TimerInit() Global $_xHashCollection = ObjCreate( "Scripting.Dictionary" ), $_xHashCache ;~ ConsoleWrite("Asso Array Load up time: "&Round(TimerDiff($tt) ) & @CRLF) ; #FUNCTION# ==================================================================================================================== ; Name...........: x ; Description ...: Gets or sets a value in an Associative Array ; Syntax.........: SET: x( $sKey , $vValue ) ; GET: x( $key ) ; Parameters ....: $sKey - the key to set or get. Examples: ; x( 'foo' ) gets value of foo ; x( 'foo.bar' ) gets value of bar which is a key of foo ; $bar = "baz" ; x( 'foo.$bar' ) gets value of baz which is a key of foo (variables are expanded) ; Return values .: Success - When setting, return the value set. When getting, returns the requested value. ; Failure - Returns a 0 ; Author ........: OHB ; =============================================================================================================================== Func x( $sKey = '' , $vValue = '' ) $func = "get" If @NumParams <> 1 Then $func = "set" If $sKey == '' Then If $func == "get" Then Return $_xHashCollection Else $_xHashCollection.removeAll Return '' EndIf EndIf $parts = StringSplit( $sKey , "." ) $last_key = $parts[$parts[0]] $cur = $_xHashCollection For $x = 1 To $parts[0] - 1 If Not $cur.exists( $parts[$x] ) Then If $func == "get" Then Return $cur.add( $parts[$x] , ObjCreate( "Scripting.Dictionary" ) ) EndIf $cur = $cur.item( $parts[$x] ) Next If IsPtr( $vValue ) Then $vValue = String( $vValue ) If $func == "get" Then If Not $cur.exists( $last_key ) Then Return $item = $cur.item( $last_key ) Return $item ElseIf Not $cur.exists( $last_key ) Then $cur.add( $last_key , $vValue ) Else $cur.item( $last_key ) = $vValue EndIf Return $vValue EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: x_del ; Description ...: Removes a key from an Associative Array ; Syntax.........: x_del( $sKey ) ; Parameters ....: $sKey - the key to remove. ; Return values .: Success - True ; Failure - False ; Author ........: OHB ; =============================================================================================================================== Func x_del( $sKey ) If $sKey == '' Then Return x( '' , '' ) $parts = StringSplit( $sKey , "." ) $cur = $_xHashCollection For $x = 1 To $parts[0] - 1 If Not $cur.exists( $parts[$x] ) Then Return False $cur = $cur.item( $parts[$x] ) Next $cur.remove( $parts[$parts[0]] ) Return True EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: x_display ; Description ...: Displays the contents of an Associative Array ; Syntax.........: x_display( $sKey ) ; Parameters ....: $sKey - the key to display. Examples: ; x_display() displays everything ; x_display( 'foo' ) displays the contents of foo ; Author ........: OHB ; =============================================================================================================================== Func x_display( $key = '' ) $text = $key If $key <> '' Then $text &= " " $text &= StringTrimRight( _x_display( x( $key ) , '' ) , 2 ) ConsoleWrite($text & @LF) $wHnd = GUICreate( "Array " & $key , 700 , 500 ) GUISetState( @SW_SHOW , $wHnd ) $block = GUICtrlCreateEdit( $text , 5 , 5 , 690 , 490 ) GUICtrlSetFont( $block , 10 , 400 , -1 , 'Courier' ) While 1 If GUIGetMsg() == -3 Then ExitLoop WEnd GUISetState( @SW_HIDE , $wHnd ) GUIDelete( $wHnd ) EndFunc ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: _x_display ; Description ...: Itterates through an array and builds output for x_display ; Author ........: OHB ; =============================================================================================================================== Func _x_display( $item , $tab ) If IsObj( $item ) Then $text = 'Array (' & @CRLF $itemAdded = False For $i In $item $text &= $tab & " [" & $i & "] => " & _x_display( $item.item($i) , $tab & " " ) $itemAdded = True Next If Not $itemAdded Then $text &= @CRLF $text &= $tab & ')' ElseIf IsArray( $item ) Then $text = "Array" $totalItems = 1 $dimensions = UBound( $item , 0 ) For $dimension = 1 To $dimensions $size = UBound( $item , $dimension ) $totalItems *= $size $text &= "[" & $size & "]" Next $text &= " (" & @CRLF For $itemID = 0 To $totalItems - 1 $idName = '' $idNum = $itemID For $dimension = 1 To $dimensions - 1 $mul = ( $totalItems / UBound( $item , $dimension ) ) $a = Floor( $idNum / $mul ) $idName &= '[' & $a & ']' $idNum -= ( $a * $mul ) Next $idName &= '[' & $idNum & ']' $text &= $tab & " " & $idName & " => " & _x_display( Execute( "$item" & $idName ) , $tab & " " ) Next $text &= $tab & ")" Else $text = $item EndIf $text &= @CRLF Return $text EndFunc ; Name...........: x_count ; Description ...: Returns a count of items ; Syntax.........: x_count( $sKey ) ; Parameters ....: $sKey - the key ; Return values .: Success: count of items of a provided key ; x_count() returns count os keys on first level ; Failure: 0, set error to: ; -1 = the key is not an object, but an item with a value ; -2 = the key does not exist ; Author ........: shEiD (original "x" UDF by: OHB) Func x_count($sKey = '') If $sKey == '' Then Return $_xHashCollection.Count Local $cur = $_xHashCollection Local $parts = StringSplit($sKey, ".") For $x = 1 To $parts[0] If Not $cur.exists($parts[$x]) Then Return SetError(-2, 0, 0) $cur = $cur.item($parts[$x]) Next If Not IsObj($cur) Then Return SetError(-1, 0, 0) Return $cur.Count EndFunc ;==>x_count ; Author: MilesAhead ; http://www.autoitscript.com/forum/topic/110768-itaskbarlist3/page__view__findpost__p__910631 ;write AssocArray to IniFile Section ;returns 1 on success - sets @error on failure Func _WriteAssocToIni($myIni = 'config', $mySection = '', $bEraseAll = false, $sSep = "|") $myIni = $myIni&".ini" If $bEraseAll then $temp = FileOpen($myIni, 2) FileClose($temp) EndIf Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1) If Not $_xHashCollection.Exists($sIni) Then Return SetError(1, 0, 0) If $mySection == '' Then $aSection = $_xHashCollection($sIni).Keys(); All sections Else Dim $aSection[1] = [$mySection]; specific Section EndIf Local $retVal = 0 For $i = 0 To UBound($aSection)-1 $cur = x($sIni&"."&$aSection[$i]) $retVal = 0 If $cur.Count() < 1 Then Return SetError(1, 0, 0) EndIf Local $iArray[$cur.Count()][2] Local $aArray = $cur.Keys() For $x = 0 To UBound($aArray) - 1 $iArray[$x][0] = $aArray[$x] $value = x($sIni&"."&$aSection[$i]&"."&$aArray[$x]) If IsArray($value) then $iArray[$x][1] = _MakePosString($value, $sSep) Else $iArray[$x][1] = $value EndIf Next $retVal = IniWriteSection($myIni, $aSection[$i], $iArray, 0) next Return SetError(@error, 0, $retVal) EndFunc ;==>_WriteAssocToIni ;read AssocArray from IniFile Section ;returns number of items read - sets @error on failure Func _ReadAssocFromIni($myIni = 'config', $mySection = '', $sSep = "|") $myIni = $myIni&".ini" Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1) If $mySection == '' Then $aSection = IniReadSectionNames ($myIni); All sections If @error Then Return SetError(@error, 0, 0) Else Dim $aSection[2] = [1,$mySection]; specific Section EndIf For $i = 1 To UBound($aSection)-1 Local $sectionArray = IniReadSection($myIni, $aSection[$i]) If @error Then Return SetError(1, 0, 0) For $x = 1 To $sectionArray[0][0] If StringInStr($sectionArray[$x][1], $sSep) then $posS = _MakePosArray($sectionArray[$x][1], $sSep) Else $posS = $sectionArray[$x][1] EndIf x($sIni&"."&$aSection[$i]&"."&$sectionArray[$x][0], $posS) Next next Return $sectionArray[0][0] EndFunc ;==>_ReadAssocFromIni ;makes a Position string using '#' number separator Func _MakePosString($posArray, $sSep = "|") Local $str = "" For $x = 0 To UBound($posArray) - 2 $str &= String($posArray[$x]) & $sSep Next $str &= String($posArray[UBound($posArray) - 1]) Return $str EndFunc ;==>_MakePosString ;makes a Position array from a Position string Func _MakePosArray($posString, $sSep = "|") Return StringSplit($posString, $sSep, 2) EndFunc ;==>_MakePosArray === GetOpt.au3 === ; #INDEX# ====================================================================== ; Title .........: GetOpt.au3 ; Version ......: 1.3 ; Language ......: English ; Author(s) .....: dany ; Link ..........: http://www.autoitscript.com/forum/topic/143167-getoptau3-udf-to-parse-the-command-line/ ; Description ...: AutoIt3 port of getopt(). Parse a command line c-style like ; using _GetOpt in a loop. GNU style options with - (and -- for ; long options) are supported as well as Windows / style options. ; See Forum link for an example. ; See http://www.gnu.org/software/libc/manual/html_node/Getopt.html ; for more information. ; Remarks .......: TODO: ; + Optionally enforce use of one option style only, no mixing. ; * Rewrite code to use a ByRef array as return value(s) where ; possible and Local-ize the rest. This would be version 2.0 ; to mark fundamental difference in implementation. ; * More documentation in the source. ; CHANGELOG: ; Version 1.3: ; + Added support for -- (marks end of options). ; + Added support for + option modifiers e.g. +x. ; + Added support for /- option modifiers e.g. /-X. ; + Added _GetOpt_Sub to iterate through comma-separated ; suboptions like -s=a=foo,b=bar. ; * Changed $GETOPT_REQUIRED_ARGUMENT from keyword Default to ; Chr(127), keyword can now be used as an option argument. ; * Standardized comments and function headers. ; * Tidy-ed up source code. ; Version 1.2: ; + Support for required arguments with options, e.g. ; _GetOpt('ab:c') where -b=foo is valid and -b will return ; an error. ; + Added support for /C:foo (colon) when using DOS style. ; + Added optional auto-casting of command line arguments from ; Strings to AutoIt variants, e.g. -a=yes on the CLI would set ; the $GetOpt_Arg to True and not 'yes'. See __GetOpt_Cast ; * Private __GetOpt_DOSToGNU to simplify code. ; Version 1.1: ; * Initial public release. ; ============================================================================== ; #CURRENT# ==================================================================== ;_GetOpt_Set ;_GetOpt ;_GetOpt_Raw ;_GetOpt_Oper ;_GetOpt_Sub ;_GetOpt_Rewind ; ============================================================================== ; #INTERNAL_USE_ONLY# ========================================================== ;__GetOpt_StartUp ;__GetOpt_DOSToGNU ;__GetOpt_ParseOpt ;__GetOpt_SubStart ;__GetOpt_SubStop ;__GetOpt_Cast ; ============================================================================== #include-once ; #CONSTANTS# ================================================================== Global Const $GETOPT_VERSION = '1.3' ; String: Version number. Global Const $GETOPT_REQUIRED_ARGUMENT = Chr(127) ; String: Value to use for required arguments. Global Enum $GETOPT_MOD_NONE = 0, $GETOPT_MOD_PLUS, $GETOPT_MOD_MINUS ; Int: Option modifiers. ; Int: @extended error return codes. Global Enum $E_GETOPT_BAD_FUNCTION_ARGUMENT = 1, _ $E_GETOPT_INVALID_OPTIONS, $E_GETOPT_NO_OPTIONS_SET, _ $E_GETOPT_NO_COMMAND_LINE, $E_GETOPT_NO_OPTIONS, $E_GETOPT_NO_OPERANDS, _ $E_GETOPT_UNKNOWN_OPTION, $E_GETOPT_MISSING_ARGUMENT, _ $E_GETOPT_SUBOPTION_MISMATCH, $E_GETOPT_NO_SUBOPTIONS, $E_GETOPT_UNKNOWN_SUBOPTION ; #VARIABLES# ================================================================== ;Global $GetOpt_OptionStyle = 0 ; 0 = Mix (default), 1 = /Windows, 2 = --gnu ; Force style, not yet implemented. Global $GetOpt_CastArguments = True ; Boolean: Whether or not to attempt to cast arguments to AutoIt variants when quering them. Default True. Global $GetOpt_SubOptSeparator = ',' ; String: Suboption separator. Default ',' (comma). Global $GetOpt_Opt = '' ; String: Current option. Global $GetOpt_Long = '' ; String: Current long option, if any. Global $GetOpt_Arg = '' ; Mixed: Argument for current option, if any. Global $GetOpt_Ind = 0 ; Int: Option index in $GetOpt_Opts. Global $GetOpt_Mod = 0 ; Int: Indicates option modifier. See $GETOPT_MOD_* constants. Global $GetOpt_IndRaw = 0 ; Int: Raw option index in $GetOpt_Opts. Global $GetOpt_OperInd = 0 ; Int: Operand index in $GetOpt_Opers. Global $GetOpt_Opts[1] = [0] ; Array: Options. Global $GetOpt_Opers[1] = [0] ; Array: Operands (non-options). Global $GetOpt_SubOpt = '' ; String: Current suboption, if any. Global $GetOpt_SubArg = '' ; Mixed: Current argument for suboption, if any. Global $GetOpt_SubInd = 0 ; Int: Suboption index in $GetOpt_SubOpts. Global $GetOpt_SubOpts[1] = [0] ; Array: Suboptions. Global $GetOpt_ArgC = 0 ; Int: Number of arguments passed, this includes the running script/program ($CmdLine[0] + 1). Global $GetOpt_ArgV[1] = [@ScriptName] ; Array: Array of arguments passed, this includes the running script/program at index 0 (modified $CmdLine). ; #INTERNAL_USE_ONLY# ========================================================== Local $__GetOpt_fStartUp = False Local $__GetOpt_aOpts[1][3] = [[0, 0, 0]] Local $__GetOpt_fSubStart = False Local $__GetOpt_sSubOpt = '' ; #FUNCTION# =================================================================== ; Name...........: _GetOpt_Set ; Description ...: Set an array of options used by the program. ; Syntax.........: _GetOpt_Set($aOpts) ; Parameters ....: $aOpts - Array: 2-dim array with program options. ; Return values .: Success - Int: Returns 1. ; Failure - Int: Returns 0, sets @error to 1 and sets @extended: ; |1 $E_GETOPT_BAD_FUNCTION_ARGUMENT Argument is wrong variant. ; |2 $E_GETOPT_INVALID_OPTIONS The options array has a wrong ; +number of subscripts. ; |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse. ; Author ........: dany ; Modified ......: ; Remarks .......: The options array has three entries: ; |[0][0] Short option. ; |[0][1] Long option version. ; |[0][2] Default value. ; |Set the value to $GETOPT_REQUIRED_ARGUMENT if the option ; +requires an argument to be passed. ; Related .......: _GetOpt, _GetOpt_Raw, _GetOpt_Oper, _GetOpt_Rewind ;=============================================================================== Func _GetOpt_Set($aOpts) If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0) If Not IsArray($aOpts) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0) If 3 > UBound($aOpts, 2) Then Return SetError(1, $E_GETOPT_INVALID_OPTIONS, 0) Local $i, $iMax = UBound($aOpts) ReDim $__GetOpt_aOpts[$iMax + 1][3] $__GetOpt_aOpts[0][0] = $iMax For $i = 0 To $iMax - 1 $aOpts[$i][0] = __GetOpt_DOSToGNU($aOpts[$i][0]) $aOpts[$i][1] = __GetOpt_DOSToGNU($aOpts[$i][1]) $__GetOpt_aOpts[$i + 1][0] = $aOpts[$i][0] $__GetOpt_aOpts[$i + 1][1] = $aOpts[$i][1] $__GetOpt_aOpts[$i + 1][2] = $aOpts[$i][2] Next __GetOpt_StartUp() Return 1 EndFunc ;==>_GetOpt_Set ; #FUNCTION# =================================================================== ; Name...........: _GetOpt ; Description ...: Get the next available option from the command line. ; Syntax.........: _GetOpt($sOpts) ; Parameters ....: $sOpts - String: Containing all available options. ; Return values .: Success - String: The current option. ; Failure - String: '?' in case the option is not recognized. ; |- String: ':' in case the option is missing a required argument. ; |- Int: 0 if anything else went wrong or no options are left. ; |Sets @error to 1 and sets @extended: ; |1 $E_GETOPT_BAD_FUNCTION_ARGUMENT Argument is wrong variant. ; |2 $E_GETOPT_NO_OPTIONS_SET No program options set. ; |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse. ; |5 $E_GETOPT_NO_OPTIONS No options passed. ; |7 $E_GETOPT_UNKNOWN_OPTION Current option is unknown. ; |8 $E_GETOPT_MISSING_ARGUMENT The current option is missing a ; +required argument. ; Author ........: dany ; Modified ......: ; Remarks .......: $sOpts Something like 'abc' where each letter represents an ; +option as set with _GetOpt_Set. Options with a colon : require ; +an argument ('ab:c'). ; Related .......: _GetOpt_Raw, _GetOpt_Oper ;=============================================================================== Func _GetOpt($sOpts) If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0) If Not IsString($sOpts) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0) __GetOpt_StartUp() If 0 = $GetOpt_Opts[0] Then Return SetError(1, $E_GETOPT_NO_OPTIONS, 0) If $GetOpt_Ind + 1 > $GetOpt_Opts[0] Then Return 0 Local $i, $aOpt, $iLong = 0, $sLongOpt = '' $GetOpt_Opt = '' $GetOpt_Long = '' $GetOpt_Arg = '' $GetOpt_Mod = $GETOPT_MOD_NONE $aOpt = __GetOpt_ParseOpt($GetOpt_Opts[$GetOpt_Ind + 1]) If '--' = $aOpt[0] Then Return 0 If IsArray($__GetOpt_aOpts) Then If '+' = StringLeft($aOpt[0], 1) Then $aOpt[0] = StringReplace($aOpt[0], '+', '-', 1) $GetOpt_Mod = $GETOPT_MOD_PLUS ElseIf '*' = StringLeft($aOpt[0], 1) Then $aOpt[0] = StringReplace($aOpt[0], '*', '-', 1) $GetOpt_Mod = $GETOPT_MOD_MINUS EndIf If '--' = StringLeft($aOpt[0], 2) Then $iLong = 1 For $i = 1 To $__GetOpt_aOpts[0][0] If $aOpt[0] = $__GetOpt_aOpts[$i][$iLong] Then $aOpt[0] = $__GetOpt_aOpts[$i][0] $sLongOpt = $__GetOpt_aOpts[$i][1] If '' = $aOpt[1] Then $aOpt[1] = $__GetOpt_aOpts[$i][2] Else If $GetOpt_CastArguments Then $aOpt[1] = __GetOpt_Cast($aOpt[1]) EndIf ExitLoop EndIf Next Else Return SetError(1, $E_GETOPT_NO_OPTIONS_SET, 0) EndIf $aOpt[0] = StringReplace($aOpt[0], '-', '') $GetOpt_Opt = $aOpt[0] $GetOpt_Long = $sLongOpt $GetOpt_Arg = $aOpt[1] $GetOpt_Ind += 1 ; recycle $iLong = StringInStr($sOpts, $aOpt[0]) If Not $iLong Then Return SetError(1, $E_GETOPT_UNKNOWN_OPTION, '?') If ':' = StringMid($sOpts, $iLong + 1, 1) Then If $GETOPT_REQUIRED_ARGUMENT = $aOpt[1] Then Return SetError(1, $E_GETOPT_MISSING_ARGUMENT, ':') EndIf Return $GetOpt_Opt EndFunc ;==>_GetOpt ; #FUNCTION# =================================================================== ; Name...........: _GetOpt_Raw ; Description ...: Get the next available option from the command line. ; Syntax.........: _GetOpt_Raw() ; Return values .: Success - String: The current raw option as in $CmdLine. ; Failure - Int: Returns 0, sets @error to 1 and sets @extended: ; |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse. ; |5 $E_GETOPT_NO_OPTIONS No options passed. ; Author ........: dany ; Modified ......: ; Remarks .......: ; Related .......: _GetOpt, _GetOpt_Oper ;=============================================================================== Func _GetOpt_Raw() If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0) __GetOpt_StartUp() If 0 = $GetOpt_Opts[0] Then Return SetError(1, $E_GETOPT_NO_OPTIONS, 0) If $GetOpt_IndRaw + 1 > $GetOpt_Opts[0] Then Return 0 $GetOpt_IndRaw += 1 Return $GetOpt_Opts[$GetOpt_IndRaw] EndFunc ;==>_GetOpt_Raw ; #FUNCTION# =================================================================== ; Name...........: _GetOpt_Oper ; Description ...: Get the next available operand from the command line. ; Syntax.........: _GetOpt_Oper() ; Return values .: Success - String: The current operand. ; Failure - Int: Returns 0, sets @error to 1 and sets @extended: ; |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse. ; |6 $E_GETOPT_NO_OPERANDS No operands passed. ; Author ........: dany ; Modified ......: ; Remarks .......: ; Related .......: _GetOpt, _GetOpt_Raw ;=============================================================================== Func _GetOpt_Oper() If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0) __GetOpt_StartUp() If 0 = $GetOpt_Opers[0] Then Return SetError(1, $E_GETOPT_NO_OPERANDS, 0) If $GetOpt_OperInd + 1 > $GetOpt_Opers[0] Then Return 0 $GetOpt_OperInd += 1 Return $GetOpt_Opers[$GetOpt_OperInd] EndFunc ;==>_GetOpt_Oper ; #FUNCTION# =================================================================== ; Name...........: _GetOpt_Sub ; Description ...: Get the next available suboption from an option. ; Syntax.........: _GetOpt_Sub($sSubOption, $aSubOpts) ; Parameters ....: $sSubOption - String: ; $aSubOpts - Array: ; Return values .: Success - String: The current suboption. ; Failure - Int: Returns 0, sets @error to 1 and sets @extended: ; |1 $E_GETOPT_BAD_FUNCTION_ARGUMENT Argument is wrong variant. ; |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse. ; |9 $E_GETOPT_SUBOPTION_MISMATCH Passed suboption is different ; +from the one stored internally. ; |10 $E_GETOPT_NO_SUBOPTIONS No suboptions to parse. ; |11 $E_GETOPT_UNKNOWN_SUBOPTION Current suboption is unknown. ; Author ........: dany ; Modified ......: ; Remarks .......: ; Related .......: _GetOpt ;=============================================================================== Func _GetOpt_Sub($sSubOption, $aSubOpts) If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0) If Not IsString($sSubOption) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0) If Not IsArray($aSubOpts) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0) If Not $__GetOpt_fSubStart Then __GetOpt_SubStart($sSubOption) If $__GetOpt_sSubOpt <> $sSubOption Then Return SetError(1, $E_GETOPT_SUBOPTION_MISMATCH, 0) If 0 = $GetOpt_SubOpts[0] Then __GetOpt_SubStop() Return SetError(1, $E_GETOPT_NO_SUBOPTIONS, 0) EndIf If $GetOpt_SubInd + 1 > $GetOpt_SubOpts[0] Then __GetOpt_SubStop() Return 0 EndIf Local $i, $iMax = UBound($aSubOpts) - 1 Local $aParsed = __GetOpt_ParseOpt($GetOpt_SubOpts[$GetOpt_SubInd + 1]) $GetOpt_SubInd += 1 $GetOpt_SubOpt = $aParsed[0] $GetOpt_SubArg = $aParsed[1] For $i = 0 To $iMax If $aSubOpts[$i][0] = $aParsed[0] Then If '' = $GetOpt_SubArg Then $GetOpt_SubArg = $aSubOpts[$i][1] Else If $GetOpt_CastArguments Then $GetOpt_SubArg = __GetOpt_Cast($GetOpt_SubArg) EndIf Return $GetOpt_SubOpt EndIf Next Return SetError(1, $E_GETOPT_UNKNOWN_SUBOPTION, '?') EndFunc ;==>_GetOpt_Sub ; #FUNCTION# =================================================================== ; Name...........: _GetOpt_Rewind ; Description ...: Rewind all iteration functions and variables. ; Syntax.........: _GetOpt_Rewind() ; Return values .: 1, always. ; Author ........: dany ; Modified ......: ; Remarks .......: ; Related .......: _GetOpt, _GetOpt_Raw, _GetOpt_Oper ;=============================================================================== Func _GetOpt_Rewind() __GetOpt_StartUp() $GetOpt_Opt = '' $GetOpt_Long = '' $GetOpt_Arg = '' $GetOpt_Ind = 0 $GetOpt_Mod = 0 $GetOpt_IndRaw = 0 $GetOpt_OperInd = 0 __GetOpt_SubStop() Return 1 EndFunc ;==>_GetOpt_Rewind ; #INTERNAL_USE_ONLY# ========================================================== ; Name...........: __GetOpt_StartUp ; Description ...: Parses raw command line and normalizes arguments to GNU style. ; Syntax.........: __GetOpt_StartUp() ; Return values .: 1, always. ; Author ........: dany ; Modified ......: ; Remarks .......: For Internal Use Only ;=============================================================================== Func __GetOpt_StartUp() If $__GetOpt_fStartUp Then Return 1 Local $i, $fEndOfOpts = False, $aParsed = $CmdLine $GetOpt_ArgC = $CmdLine[0] + 1 ReDim $GetOpt_ArgV[$GetOpt_ArgC] For $i = 1 To $aParsed[0] $GetOpt_ArgV[$i] = $aParsed[$i] $aParsed[$i] = __GetOpt_DOSToGNU($aParsed[$i]) If Not $fEndOfOpts And StringRegExp($aParsed[$i], '^[-+*]') Then $GetOpt_Opts[0] += 1 ReDim $GetOpt_Opts[$GetOpt_Opts[0] + 1] $GetOpt_Opts[$GetOpt_Opts[0]] = $aParsed[$i] If '--' = $aParsed[$i] Then $fEndOfOpts = True EndIf Next For $i = 1 To $aParsed[0] If Not StringInStr('-+*', StringLeft($aParsed[$i], 1)) Then $GetOpt_Opers[0] += 1 ReDim $GetOpt_Opers[$GetOpt_Opers[0] + 1] $GetOpt_Opers[$GetOpt_Opers[0]] = $aParsed[$i] EndIf Next $__GetOpt_fStartUp = True Return _GetOpt_Rewind() EndFunc ;==>__GetOpt_StartUp ; #INTERNAL_USE_ONLY# ========================================================== ; Name...........: __GetOpt_DOSToGNU ; Description ...: Translates DOS style options to GNU style. ; Syntax.........: __GetOpt_DOSToGNU($sOpt) ; Parameters ....: $sOpt - String: Current raw option. ; Return values .: String: Translated GNU style option. ; Author ........: dany ; Modified ......: ; Remarks .......: For Internal Use Only ;=============================================================================== Func __GetOpt_DOSToGNU($sOpt) If StringInStr($sOpt, ':') And Not StringInStr($sOpt, '=') Then If StringRegExp($sOpt, '^[/+-]') Then $sOpt = StringReplace($sOpt, ':', '=', 1) EndIf If '/' = StringLeft($sOpt, 1) And '//' <> StringLeft($sOpt, 2) Then $sOpt = StringReplace($sOpt, '/-', '*', 1) If 0 = @extended Then $sOpt = StringReplace($sOpt, '/', '-', 1) If 2 < StringLen($sOpt) And '=' <> StringMid($sOpt, 3, 1) Then $sOpt = StringReplace($sOpt, '-', '--', 1) EndIf EndIf Return $sOpt EndFunc ;==>__GetOpt_DOSToGNU ; #INTERNAL_USE_ONLY# ========================================================== ; Name...........: __GetOpt_ParseOpt ; Description ...: Splits raw option into option and passed argument. ; Syntax.........: __GetOpt_ParseOpt($sOpt) ; Parameters ....: $sOpt - String: Current raw option. ; Return values .: Array: With index 0 = option and 1 = argument. ; Author ........: dany ; Modified ......: ; Remarks .......: For Internal Use Only ;=============================================================================== Func __GetOpt_ParseOpt($sOpt) Local $iPos, $aParsed[2] $iPos = StringInStr($sOpt, '=') If 0 = $iPos Then $iPos = StringLen($sOpt) + 1 $aParsed[0] = StringMid($sOpt, 1, $iPos - 1) $aParsed[1] = StringMid($sOpt, $iPos + 1) Return $aParsed EndFunc ;==>__GetOpt_ParseOpt ; #INTERNAL_USE_ONLY# ========================================================== ; Name...........: __GetOpt_SubStart ; Description ...: Sets variables for use with _GetOpt_Sub. ; Syntax.........: __GetOpt_SubStart($sSubOption) ; Return values .: 1, always. ; Author ........: dany ; Modified ......: ; Remarks .......: For Internal Use Only ;=============================================================================== Func __GetOpt_SubStart($sSubOption) If $__GetOpt_fSubStart Then Return 1 __GetOpt_SubStop() Local $i, $aSubOpts = StringSplit($sSubOption, $GetOpt_SubOptSeparator) $__GetOpt_sSubOpt = $sSubOption ReDim $GetOpt_SubOpts[1] $GetOpt_SubOpts[0] = 0 For $i = 1 To $aSubOpts[0] $GetOpt_SubOpts[0] += 1 ReDim $GetOpt_SubOpts[$GetOpt_SubOpts[0] + 1] $GetOpt_SubOpts[$GetOpt_SubOpts[0]] = $aSubOpts[$i] Next $__GetOpt_fSubStart = True Return 1 EndFunc ;==>__GetOpt_SubStart ; #INTERNAL_USE_ONLY# ========================================================== ; Name...........: __GetOpt_SubStop ; Description ...: Resets all variables dealing with suboptions. ; Syntax.........: __GetOpt_SubStop() ; Return values .: 1, always. ; Author ........: dany ; Modified ......: ; Remarks .......: For Internal Use Only ;=============================================================================== Func __GetOpt_SubStop() ReDim $GetOpt_SubOpts[1] $GetOpt_SubOpts[0] = 0 $GetOpt_SubOpt = '' $GetOpt_SubArg = '' $GetOpt_SubInd = 0 $__GetOpt_sSubOpt = '' $__GetOpt_fSubStart = False Return 1 EndFunc ;==>__GetOpt_SubStop ; #INTERNAL_USE_ONLY# ========================================================== ; Name...........: __GetOpt_Cast ; Description ...: Attempt to cast a String to a different AutoIt variant based ; on it's content. ; Syntax.........: __GetOpt_Cast($sArg) ; Parameters ....: $sArg - String: Current argument. ; Return values .: Mixed: Float, Int, Default, True, False or String variant. ; Author ........: dany ; Modified ......: ; Remarks .......: For Internal Use Only ;=============================================================================== Func __GetOpt_Cast($sArg) If StringIsFloat($sArg) Then Return Number($sArg) If StringIsInt($sArg) Then Return Int($sArg) If 'Default' = $sArg Then Return Default If StringRegExp($sArg, '^(?i)(true|y(es)?)$') Then Return True If StringRegExp($sArg, '^(?i)(false|no?)$') Then Return False Return String($sArg) EndFunc ;==>__GetOpt_Cast === BinaryCall.au3 === ; ============================================================================= ; AutoIt BinaryCall UDF (2014.7.24) ; Purpose: Allocate, Decompress, And Prepare Binary Machine Code ; Author: Ward ; ============================================================================= #Include-once Global $__BinaryCall_Kernel32dll = DllOpen('kernel32.dll') Global $__BinaryCall_Msvcrtdll = DllOpen('msvcrt.dll') Global $__BinaryCall_LastError = "" Func _BinaryCall_GetProcAddress($Module, $Proc) Local $Ret = DllCall($__BinaryCall_Kernel32dll, 'ptr', 'GetProcAddress', 'ptr', $Module, 'str', $Proc) If @Error Or Not $Ret[0] Then Return SetError(1, @Error, 0) Return $Ret[0] EndFunc Func _BinaryCall_LoadLibrary($Filename) Local $Ret = DllCall($__BinaryCall_Kernel32dll, "handle", "LoadLibraryW", "wstr", $Filename) If @Error Then Return SetError(1, @Error, 0) Return $Ret[0] EndFunc Func _BinaryCall_lstrlenA($Ptr) Local $Ret = DllCall($__BinaryCall_Kernel32dll, "int", "lstrlenA", "ptr", $Ptr) If @Error Then Return SetError(1, @Error, 0) Return $Ret[0] EndFunc Func _BinaryCall_Alloc($Code, $Padding = 0) Local $Length = BinaryLen($Code) + $Padding Local $Ret = DllCall($__BinaryCall_Kernel32dll, "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", $Length, "dword", 0x1000, "dword", 0x40) If @Error Or Not $Ret[0] Then Return SetError(1, @Error, 0) If BinaryLen($Code) Then Local $Buffer = DllStructCreate("byte[" & $Length & "]", $Ret[0]) DllStructSetData($Buffer, 1, $Code) EndIf Return $Ret[0] EndFunc Func _BinaryCall_RegionSize($Ptr) Local $Buffer = DllStructCreate("ptr;ptr;dword;uint_ptr;dword;dword;dword") Local $Ret = DllCall($__BinaryCall_Kernel32dll, "int", "VirtualQuery", "ptr", $Ptr, "ptr", DllStructGetPtr($Buffer), "uint_ptr", DllStructGetSize($Buffer)) If @Error Or $Ret[0] = 0 Then Return SetError(1, @Error, 0) Return DllStructGetData($Buffer, 4) EndFunc Func _BinaryCall_Free($Ptr) Local $Ret = DllCall($__BinaryCall_Kernel32dll, "bool", "VirtualFree", "ptr", $Ptr, "ulong_ptr", 0, "dword", 0x8000) If @Error Or $Ret[0] = 0 Then $Ret = DllCall($__BinaryCall_Kernel32dll, "bool", "GlobalFree", "ptr", $Ptr) If @Error Or $Ret[0] <> 0 Then Return SetError(1, @Error, False) EndIf Return True EndFunc Func _BinaryCall_Release($CodeBase) Local $Ret = _BinaryCall_Free($CodeBase) Return SetError(@Error, @Extended, $Ret) EndFunc Func _BinaryCall_MemorySearch($Ptr, $Length, $Binary) Static $CodeBase If Not $CodeBase Then If @AutoItX64 Then $CodeBase = _BinaryCall_Create('0x4883EC084D85C94889C8742C4C39CA72254C29CA488D141131C9EB0848FFC14C39C97414448A1408453A140874EE48FFC04839D076E231C05AC3', '', 0, True, False) Else $CodeBase = _BinaryCall_Create('0x5589E58B4D14578B4508568B550C538B7D1085C9742139CA721B29CA8D341031D2EB054239CA740F8A1C17381C1074F34039F076EA31C05B5E5F5DC3', '', 0, True, False) EndIf If Not $CodeBase Then Return SetError(1, 0, 0) EndIf $Binary = Binary($Binary) Local $Buffer = DllStructCreate("byte[" & BinaryLen($Binary) & "]") DllStructSetData($Buffer, 1, $Binary) Local $Ret = DllCallAddress("ptr:cdecl", $CodeBase, "ptr", $Ptr, "uint", $Length, "ptr", DllStructGetPtr($Buffer), "uint", DllStructGetSize($Buffer)) Return $Ret[0] EndFunc Func _BinaryCall_Base64Decode($Src) Static $CodeBase If Not $CodeBase Then If @AutoItX64 Then $CodeBase = _BinaryCall_Create('0x41544989CAB9FF000000555756E8BE000000534881EC000100004889E7F3A44C89D6E98A0000004439C87E0731C0E98D0000000FB66E01440FB626FFC00FB65E020FB62C2C460FB62424408A3C1C0FB65E034189EB41C1E4024183E3308A1C1C41C1FB044509E34080FF634189CC45881C08744C440FB6DFC1E5044489DF4088E883E73CC1FF0209C7418D44240241887C08014883C10380FB63742488D841C1E3064883C60483E03F4409D841884408FF89F389C84429D339D30F8C67FFFFFF4881C4000100005B5E5F5D415CC35EC3E8F9FFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000003E0000003F3435363738393A3B3C3D00000063000000000102030405060708090A0B0C0D0E0F101112131415161718190000000000001A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233', '', 132, True, False) Else $CodeBase = _BinaryCall_Create('0x55B9FF00000089E531C05756E8F10000005381EC0C0100008B55088DBDF5FEFFFFF3A4E9C00000003B45140F8FC20000000FB65C0A028A9C1DF5FEFFFF889DF3FEFFFF0FB65C0A038A9C1DF5FEFFFF889DF2FEFFFF0FB65C0A018985E8FEFFFF0FB69C1DF5FEFFFF899DECFEFFFF0FB63C0A89DE83E630C1FE040FB6BC3DF5FEFFFFC1E70209FE8B7D1089F3881C074080BDF3FEFFFF63745C0FB6B5F3FEFFFF8BBDECFEFFFF8B9DE8FEFFFF89F083E03CC1E704C1F80209F88B7D1088441F0189D883C00280BDF2FEFFFF6374278A85F2FEFFFFC1E60683C10483E03F09F088441F0289D883C0033B4D0C0F8C37FFFFFFEB0231C081C40C0100005B5E5F5DC35EC3E8F9FFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000003E0000003F3435363738393A3B3C3D00000063000000000102030405060708090A0B0C0D0E0F101112131415161718190000000000001A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233', '', 132, True, False) EndIf If Not $CodeBase Then Return SetError(1, 0, Binary("")) EndIf $Src = String($Src) Local $SrcLen = StringLen($Src) Local $SrcBuf = DllStructCreate("char[" & $SrcLen & "]") DllStructSetData($SrcBuf, 1, $Src) Local $DstLen = Int(($SrcLen + 2) / 4) * 3 + 1 Local $DstBuf = DllStructCreate("byte[" & $DstLen & "]") Local $Ret = DllCallAddress("uint:cdecl", $CodeBase, "ptr", DllStructGetPtr($SrcBuf), "uint", $SrcLen, "ptr", DllStructGetPtr($DstBuf), "uint", $DstLen) If $Ret[0] = 0 Then Return SetError(2, 0, Binary("")) Return BinaryMid(DllStructGetData($DstBuf, 1), 1, $Ret[0]) EndFunc Func _BinaryCall_Base64Encode($Src) Static $CodeBase If Not $CodeBase Then If @AutoItX64 Then $CodeBase = _BinaryCall_Create('AwAAAARiAQAAAAAAAAArkuFQDAlvIp0qAgbDnjr76UDZs1EPNIP2K18t9s6SNTbd43IB7HfdyPM8VfD/o36z4AmSW2m2AIsC6Af3fKNsHU4BdQKGd0PQXHxPSX0iNqp1YAKovksqQna06NeKMoOYqryTUX4WgpHjokhp6zY2sEFSIjcL7dW3FDoNVz4bGPyZHRvjFwmqvr7YGlNYKwNoh+SYCXmIgVPVZ63Vz1fbT33/QFpWmWOeBRqs4J+c8Qp6zJFsK345Pjw0I8kMSsnho4F4oNzQ2OsAbmIioaQ6Ma2ziw5NH+M+t4SpEeHDnBdUTTL20sxWZ0yKruFAsBIRoHvM7LYcid2eBV2d5roSjnkwMG0g69LNjs1fHjbI/9iU/hJwpSsgl4fltXdZG659/li13UFY89M7UfckiZ9XOeBM0zadgNsy8r8M3rEAAA==') Else $CodeBase = _BinaryCall_Create('AwAAAARVAQAAAAAAAAAqr7blBndrIGnmhhfXD7R1fkOTKhicg1W6MCtStbz+CsneBEg0bbHH1sqTLmLfY7A6LqZl6LYWT5ULVj6MXgugPbBn9wKsSU2ZCcBBPNkx09HVPdUaKnbqghDGj/C5SHoF+A/5g+UgE1C5zJZORjJ8ljs5lt2Y9lA4BsY7jVKX2vmDvHK1NnSR6nVwh7Pb+Po/UpNcy5sObVWDKkYSCCtCIjKIYqOe3c6k8Xsp4eritCUprXEVvCFi7K5Z6HFXdm3nZsFcE+eSJ1WkRnVQbWcmpjGMGka61C68+CI7tsQ13UnCFWNSpDrCbzUejMZh8HdPgEc5vCg3pKMKin/NavNpB6+87Y9y7HIxmKsPdjDT30u9hUKWnYiRe3nrwKyVDsiYpKU/Nse368jHag5B5or3UKA+nb2+eY8JwzgA') EndIf If Not $CodeBase Then Return SetError(1, 0, Binary("")) EndIf $Src = Binary($Src) Local $SrcLen = BinaryLen($Src) Local $SrcBuf = DllStructCreate("byte[" & $SrcLen & "]") DllStructSetData($SrcBuf, 1, $Src) Local $DstLen = Int(($SrcLen + 2) / 3) * 4 + 1 Local $DstBuf = DllStructCreate("char[" & $DstLen & "]") Local $Ret = DllCallAddress("uint:cdecl", $CodeBase, "ptr", DllStructGetPtr($SrcBuf), "uint", $SrcLen, "ptr", DllStructGetPtr($DstBuf), "uint", $DstLen) If $Ret[0] = 0 Then Return Binary("") Return StringMid(DllStructGetData($DstBuf, 1), 1, $Ret[0]) EndFunc Func _BinaryCall_LzmaDecompress($Src) Static $CodeBase If Not $CodeBase Then If @AutoItX64 Then $CodeBase = _BinaryCall_Create(_BinaryCall_Base64Decode('QVcxwEFWQVVBVFVXSInXVkiJzlMx20iB7OgAAABEiiFBgPzgdgnpyQAAAEGD7C1BiMf/wEGA/Cx38THA6wRBg+wJQYjG/8BBgPwId/GLRglEi24FQQ+2zkyJRCQoRQ+2/0HB5xBBiQFBD7bEAcG4AAMAANPgjYQAcA4AAEhjyOjIBAAATInpSInF6L0EAABIicMxwEyJ8kSI4EyLRCQoiNQl//8A/0QJ+EiF24lFAHQoTYXtdCNIjVfzSI1MJDhIg8YNTIkEJE2J6UmJ2EiJ7+g2AAAAicbrBb4BAAAASInp6IQEAACF9nQKSInZMdvodgQAAEiJ2EiBxOgAAABbXl9dQVxBXUFeQV/DVVNBV0FWQVVBVEFQTQHBQVFNicVRVkgB8lJIieX8SYn0iwdMjX8Eik8Cg8r/0+L30olV6Ijhg8r/0+L30olV5ADBiUXsuAEAAACJReCJRdyJRdhIiUXQRSnJKfaDy/8A0bgAAwAA0+BIjYg2BwAAuAAEAARMif/R6fOrvwUAAADoUAMAAP/PdfdEie9EicgrfSDB4ARBifpEI1XoRAHQTY0cR+hAAwAAD4WTAAAAik3sI33k0+eA6Qj22dPuAfe4AQAAAEiNPH++AAEAAMHnCEGD+QdNjbR/bA4AAHI0TInvSCt90A+2P9HnQYnzIf5BAfNPjRxe6O8CAACJwcHuCIPhATnOvgABAAB1DjnGd9jrDE2J8+jQAgAAOfBy9EyJ76pEiclBg/kEcg65AwAAAEGD+QpyA4PBA0EpyelDAgAAT42cT4ABAADomgIAAHUsi0XcQYP5B4lF4BnAi1XY99CLTdCD4AOJVdxBicGJTdhNjbdkBgAA6akAAABPjZxPmAEAAOhfAgAAdUZEicjB4AREAdBNjZxH4AEAAOhHAgAAdWpBg/kHuQkAAAByA4PBAkGJyUyJ70grfdBIO30gD4L9AQAAigdIA33QqumzAQAAT42cT7ABAADoCgIAAIt12HQhT42cT8gBAADo+AEAAIt13HQJi03ci3XgiU3gi03YiU3ci03QiU3YiXXQQYP5B7kIAAAAcgODwQNBiclNjbdoCgAATYnz6LsBAAB1FESJ0CnJweADvggAAABJjXxGBOs2TY1eAuicAQAAdRpEidC5CAAAAMHgA74IAAAASY28RgQBAADrEUmNvgQCAAC5EAAAAL4AAQAAiU3MuAEAAABJifvoYQEAAInCKfJy8gNVzEGD+QSJVcwPg7kAAABBg8EHuQMAAAA50XICidHB4Qa4AQAAAEmNvE9gAwAAvkAAAABJifvoHwEAAEGJwkEp8nLwQYP6BHJ4RInWRIlV0NHug2XQAf/Og03QAkGD+g5zFYnx0mXQi0XQRCnQTY20R14FAADrLIPuBOi6AAAA0evRZdBBOdhyBv9F0EEp2P/OdedNjbdEBgAAwWXQBL4EAAAAvwEAAACJ+E2J8+ioAAAAqAF0Awl90NHn/8516+sERIlV0P9F0EyJ74tNzEiJ+IPBAkgrRSBIOUXQd1RIif5IK3XQSItVGKyqSDnXcwT/yXX1SYn9D7bwTDttGA+C9fz//+gwAAAAKcBIi1UQTCtlCESJIkiLVWBMK20gRIkqSIPEKEFcQV1BXUFfW13DXli4AQAAAOvSgfsAAAABcgHDweMITDtlAHPmQcHgCEWKBCRJg8QBwynATY0cQ4H7AAAAAXMVweMITDtlAHPBQcHgCEWKBCRJg8QBidlBD7cTwekLD6/KQTnIcxOJy7kACAAAKdHB6QVmQQELAcDDKcvB6gVBKchmQSkTAcCDwAHDSLj////////////gbXN2Y3J0LmRsbHxtYWxsb2MASLj////////////gZnJlZQA=')) Else $CodeBase = _BinaryCall_Create(_BinaryCall_Base64Decode('VYnlVzH/VlOD7EyLXQiKC4D54A+HxQAAADHA6wWD6S2I0ID5LI1QAXfziEXmMcDrBYPpCYjQgPkIjVABd/OIReWLRRSITeSLUwkPtsmLcwWJEA+2ReUBwbgAAwAA0+CNhABwDgAAiQQk6EcEAACJNCSJRdToPAQAAItV1InHi0Xkhf+JArgBAAAAdDaF9nQyi0UQg8MNiRQkiXQkFIl8JBCJRCQYjUXgiUQkDItFDIlcJASD6A2JRCQI6CkAAACLVdSJRdSJFCToAQQAAItF1IXAdAqJPCQx/+jwAwAAg8RMifhbXl9dw1dWU1WJ5YtFJAFFKFD8i3UYAXUcVot1FK2SUopO/oPI/9Pg99BQiPGDyP/T4PfQUADRifeD7AwpwEBQUFBQUFcp9laDy/+4AAMAANPgjYg2BwAAuAAEAATR6fOragVZ6MoCAADi+Yt9/ItF8Ct9JCH4iUXosADoywIAAA+FhQAAAIpN9CN97NPngOkI9tnT7lgB916NPH/B5wg8B1qNjH5sDgAAUVa+AAEAAFCwAXI0i338K33cD7Y/i23M0eeJ8SH+AfGNbE0A6JgCAACJwcHuCIPhATnOvgABAAB1DjnwctfrDIttzOh5AgAAOfBy9FqD+gSJ0XIJg/oKsQNyArEGKcpS60mwwOhJAgAAdRRYX1pZWln/NCRRUrpkBgAAsQDrb7DM6CwCAAB1LLDw6BMCAAB1U1g8B7AJcgKwC1CLdfwrddw7dSQPgs8BAACsi338qumOAQAAsNjo9wEAAIt12HQbsOTo6wEAAIt11HQJi3XQi03UiU3Qi03YiU3Ui03ciU3YiXXcWF9ZumgKAACxCAH6Ulc8B4jIcgIEA1CLbczovAEAAHUUi0Xoi33MweADKclqCF6NfEcE6zWLbcyDxQLomwEAAHUYi0Xoi33MweADaghZaghejbxHBAEAAOsQvwQCAAADfcxqEFm+AAEAAIlN5CnAQIn96GYBAACJwSnxcvMBTeSDfcQED4OwAAAAg0XEB4tN5IP5BHIDagNZi33IweEGKcBAakBejbxPYAMAAIn96CoBAACJwSnxcvOJTeiJTdyD+QRyc4nOg2XcAdHug03cAk6D+Q5zGbivAgAAKciJ8dJl3ANF3NHgA0XIiUXM6y2D7gToowAAANHr0WXcOV3gcgb/RdwpXeBOdei4RAYAAANFyIlFzMFl3ARqBF4p/0eJ+IttzOi0AAAAqAF0Awl93NHnTnXs6wD/RdyLTeSDwQKLffyJ+CtFJDlF3HdIif4rddyLVSisqjnXcwNJdfeJffwPtvA7fSgPgnH9///oKAAAACnAjWwkPItVIIt1+Ct1GIkyi1Usi338K30kiTrJW15fw15YKcBA69qB+wAAAAFyAcPB4whWi3X4O3Ucc+SLReDB4AisiUXgiXX4XsOLTcQPtsDB4QQDRegByOsGD7bAA0XEi23IjWxFACnAjWxFAIH7AAAAAXMci0wkOMFkJCAIO0wkXHOcihH/RCQ4weMIiFQkIInZD7dVAMHpCw+vyjlMJCBzF4nLuQAIAAAp0cHpBWYBTQABwI1sJEDDweoFKUwkICnLZilVAAHAg8ABjWwkQMO4///////gbXN2Y3J0LmRsbHxtYWxsb2MAuP//////4GZyZWUA')) EndIf If Not $CodeBase Then Return SetError(1, 0, Binary("")) EndIf $Src = Binary($Src) Local $SrcLen = BinaryLen($Src) Local $SrcBuf = DllStructCreate("byte[" & $SrcLen & "]") DllStructSetData($SrcBuf, 1, $Src) Local $Ret = DllCallAddress("ptr:cdecl", $CodeBase, "ptr", DllStructGetPtr($SrcBuf), "uint_ptr", $SrcLen, "uint_ptr*", 0, "uint*", 0) If $Ret[0] Then Local $DstBuf = DllStructCreate("byte[" & $Ret[3] & "]", $Ret[0]) Local $Output = DllStructGetData($DstBuf, 1) DllCall($__BinaryCall_Msvcrtdll, "none:cdecl", "free", "ptr", $Ret[0]) Return $Output EndIf Return SetError(2, 0, Binary("")) EndFunc Func _BinaryCall_Relocation($Base, $Reloc) Local $Size = Int(BinaryMid($Reloc, 1, 2)) For $i = 3 To BinaryLen($Reloc) Step $Size Local $Offset = Int(BinaryMid($Reloc, $i, $Size)) Local $Ptr = $Base + $Offset DllStructSetData(DllStructCreate("ptr", $Ptr), 1, DllStructGetData(DllStructCreate("ptr", $Ptr), 1) + $Base) Next EndFunc Func _BinaryCall_ImportLibrary($Base, $Length) Local $JmpBin, $JmpOff, $JmpLen, $DllName, $ProcName If @AutoItX64 Then $JmpBin = Binary("0x48B8FFFFFFFFFFFFFFFFFFE0") $JmpOff = 2 Else $JmpBin = Binary("0xB8FFFFFFFFFFE0") $JmpOff = 1 EndIf $JmpLen = BinaryLen($JmpBin) Do Local $Ptr = _BinaryCall_MemorySearch($Base, $Length, $JmpBin) If $Ptr = 0 Then ExitLoop Local $StringPtr = $Ptr + $JmpLen Local $StringLen = _BinaryCall_lstrlenA($StringPtr) Local $String = DllStructGetData(DllStructCreate("char[" & $StringLen & "]", $StringPtr), 1) Local $Split = StringSplit($String, "|") If $Split[0] = 1 Then $ProcName = $Split[1] ElseIf $Split[0] = 2 Then If $Split[1] Then $DllName = $Split[1] $ProcName = $Split[2] EndIf If $DllName And $ProcName Then Local $Handle = _BinaryCall_LoadLibrary($DllName) If Not $Handle Then $__BinaryCall_LastError = "LoadLibrary fail on " & $DllName Return SetError(1, 0, False) EndIf Local $Proc = _BinaryCall_GetProcAddress($Handle, $ProcName) If Not $Proc Then $__BinaryCall_LastError = "GetProcAddress failed on " & $ProcName Return SetError(2, 0, False) EndIf DllStructSetData(DllStructCreate("ptr", $Ptr + $JmpOff), 1, $Proc) EndIf Local $Diff = Int($Ptr - $Base + $JmpLen + $StringLen + 1) $Base += $Diff $Length -= $Diff Until $Length <= $JmpLen Return True EndFunc Func _BinaryCall_CodePrepare($Code) If Not $Code Then Return "" If IsBinary($Code) Then Return $Code $Code = String($Code) If StringLeft($Code, 2) = "0x" Then Return Binary($Code) If StringIsXDigit($Code) Then Return Binary("0x" & $Code) Return _BinaryCall_LzmaDecompress(_BinaryCall_Base64Decode($Code)) EndFunc Func _BinaryCall_SymbolFind($CodeBase, $Identify, $Length = Default) $Identify = Binary($Identify) If IsKeyword($Length) Then $Length = _BinaryCall_RegionSize($CodeBase) EndIf Local $Ptr = _BinaryCall_MemorySearch($CodeBase, $Length, $Identify) If $Ptr = 0 Then Return SetError(1, 0, 0) Return $Ptr + BinaryLen($Identify) EndFunc Func _BinaryCall_SymbolList($CodeBase, $Symbol) If Not IsArray($Symbol) Or $CodeBase = 0 Then Return SetError(1, 0, 0) Local $Tag = "" For $i = 0 To UBound($Symbol) - 1 $Tag &= "ptr " & $Symbol[$i] & ";" Next Local $SymbolList = DllStructCreate($Tag) If @Error Then Return SetError(1, 0, 0) For $i = 0 To UBound($Symbol) - 1 $CodeBase = _BinaryCall_SymbolFind($CodeBase, $Symbol[$i]) DllStructSetData($SymbolList, $Symbol[$i], $CodeBase) Next Return $SymbolList EndFunc Func _BinaryCall_Create($Code, $Reloc = '', $Padding = 0, $ReleaseOnExit = True, $LibraryImport = True) Local $BinaryCode = _BinaryCall_CodePrepare($Code) If Not $BinaryCode Then Return SetError(1, 0, 0) Local $BinaryCodeLen = BinaryLen($BinaryCode) Local $TotalCodeLen = $BinaryCodeLen + $Padding Local $CodeBase = _BinaryCall_Alloc($BinaryCode, $Padding) If Not $CodeBase Then Return SetError(2, 0, 0) If $Reloc Then $Reloc = _BinaryCall_CodePrepare($Reloc) If Not $Reloc Then Return SetError(3, 0, 0) _BinaryCall_Relocation($CodeBase, $Reloc) EndIf If $LibraryImport Then If Not _BinaryCall_ImportLibrary($CodeBase, $BinaryCodeLen) Then _BinaryCall_Free($CodeBase) Return SetError(4, 0, 0) EndIf EndIf If $ReleaseOnExit Then _BinaryCall_ReleaseOnExit($CodeBase) EndIf Return SetError(0, $TotalCodeLen, $CodeBase) EndFunc Func _BinaryCall_CommandLineToArgv($CommandLine, ByRef $Argc, $IsUnicode = False) Static $SymbolList If Not IsDllStruct($SymbolList) Then Local $Code If @AutoItX64 Then $Code = 'AwAAAASuAgAAAAAAAAAkL48ClEB9jTEOeYv4yYTosNjFNgf81Ag4vS2VP4y4wxFa+4yMI7GDB7CG+xn4JE3cdEVvk8cMp4oIuS3DgTxlcKHGVIg94tvzG/256bizZfGtAETQUCPQjW5+JSx2C/Y4C0VNJMKTlSCHiV5AzXRZ5gw3WFghbtkCCFxWOX+RDSI2oH/vROEOnqc0jfKTo17EBjqX+dW3QxrUe45xsbyYTZ9ccIGySgcOAxetbRiSxQnz8BOMbJyfrbZbuVJyGpKrXFLh/5MlBZ09Cim9qgflbGzmkrGStT9QL1f+O2krzyOzgaWWqhWL6S+y0G32RWVi0uMLR/JOGLEW/+Yg/4bzkeC0lKELT+RmWAatNa38BRfaitROMN12moRDHM6LYD1lzPLnaiefSQRVti561sxni/AFkYoCb5Lkuyw4RIn/r/flRiUg5w48YkqBBd9rXkaXrEoKwPg6rmOvOCZadu//B6HN4+Ipq5aYNuZMxSJXmxwXVRSQZVpSfLS2ATZMd9/Y7kLqrKy1H4V76SgI/d9OKApfKSbQ8ZaKIHBCsoluEip3UDOB82Z21zd933UH5l0laGWLIrTz7xVGkecjo0NQzR7LyhhoV3xszlIuw2v8q0Q/S9LxB5G6tYbOXo7lLjNIZc0derZz7DNeeeJ9dQE9hp8unubaTBpulPxTNtRjog==' Else $Code = 'AwAAAAR6AgAAAAAAAABcQfD553vjya/3DmalU0BKqABevUb/60GZ55rMwmzpQfPSRUlIl04lEiS8RDrXpS0EoBUe+uzDgZd37nVu9wsJ4fykqYvLoMz3ApxQbTBKleOIRSla6I0V8dNP3P7rHeUfjH0jCho0RvhhVpf0o4ht/iZptauxaoy1zQ19TkPZ/vf5Im8ecY6qEdHNzjo2H60jVwiOJ+1J47TmQRwxJ+yKLakq8QNxtKkRIB9B9ugfo3NAL0QslDxbyU0dSgw2aOPxV+uttLzYNnWbLBZVQbchcKgLRjC/32U3Op576sOYFolB1Nj4/33c7MRgtGLjlZfTB/4yNvd4/E+u3U6/Q4MYApCfWF4R/d9CAdiwgIjCYUkGDExKjFtHbAWXfWh9kQ7Q/GWUjsfF9BtHO6924Cy1Ou+BUKksqsxmIKP4dBjvvmz9OHc1FdtR9I63XKyYtlUnqVRtKwlNrYAZVCSFsyAefMbteq1ihU33sCsLkAnp1LRZ2wofgT1/JtT8+GO2s/n52D18wM70RH2n5uJJv8tlxQc1lwbmo4XQvcbcE91U2j9glvt2wC1pkP0hF23Nr/iiIEZHIPAOAHvhervlHE830LSHyUx8yh5Tjojr0gdLvQ==' EndIf Local $CodeBase = _BinaryCall_Create($Code) If @Error Then Return SetError(1, 0, 0) Local $Symbol[] = ["ToArgvW","ToArgvA"] $SymbolList = _BinaryCall_SymbolList($CodeBase, $Symbol) If @Error Then Return SetError(1, 0, 0) EndIf Local $Ret If $IsUnicode Then $Ret = DllCallAddress("ptr:cdecl", DllStructGetData($SymbolList, "ToArgvW"), "wstr", $CommandLine, "int*", 0) Else $Ret = DllCallAddress("ptr:cdecl", DllStructGetData($SymbolList, "ToArgvA"), "str", $CommandLine, "int*", 0) EndIf If Not @Error And $Ret[0] <> 0 Then _BinaryCall_ReleaseOnExit($Ret[0]) $Argc = $Ret[2] Return $Ret[0] Else Return SetError(2, 0, 0) EndIf EndFunc Func _BinaryCall_StdioRedirect($Filename = "CON", $Flag = 1 + 2 + 4) Static $SymbolList If Not IsDllStruct($SymbolList) Then Local $Code, $Reloc If @AutoItX64 Then $Code = 'AwAAAASjAQAAAAAAAAAkL48ClEB9jTEOeYv4yYTosNjFM1rLNdMULriZUDxTj+ZdkQ01F5zKL+WDCScfQKKLn66EDmcA+gXIkPcZV4lyz8VPw8BPZlNB5KymydM15kCA+uqvmBc1V0NJfzgsF0Amhn0JhM/ZIguYCHxywMQ1SgKxUb05dxDg8WlX/2aPfSolcX47+4/72lPDNTeT7d7XRdm0ND+eCauuQcRH2YOahare9ASxuU4IMHCh2rbZYHwmTNRiQUB/8dLGtph93yhmwdHtyMPLX2x5n6sdA1mxua9htLsLTulE05LLmXbRYXylDz0A' $Reloc = 'AwAAAAQIAAAAAAAAAAABAB7T+CzGn9ScQAC=' Else $Code = 'AwAAAASVAQAAAAAAAABcQfD553vjya/3DmalU0BKqABaUcndypZ3mTYUkHxlLV/lKZPrXYWXgNATjyiowkUQGDVYUy5THQwK4zYdU7xuGf7qfVDELc1SNbiW3NgD4D6N6ZM7auI1jPaThsPfA/ouBcx2aVQX36fjmViTZ8ZLzafjJeR7d5OG5s9sAoIzFLTZsqrFlkIJedqDAOfhA/0mMrkavTWnsio6yTbic1dER0DcEsXpLn0vBNErKHoagLzAgofHNLeFRw5yHWz5owR5CYL7rgiv2k51neHBWGx97A==' $Reloc = 'AwAAAAQgAAAAAAAAAAABABfyHS/VRkdjBBzbtGPD6vtmVH/IsGHYvPsTv2lGuqJxGlAA' EndIf Local $CodeBase = _BinaryCall_Create($Code, $Reloc) If @Error Then Return SetError(1, 0, 0) Local $Symbol[] = ["StdinRedirect","StdoutRedirect","StderrRedirect"] $SymbolList = _BinaryCall_SymbolList($CodeBase, $Symbol) If @Error Then Return SetError(1, 0, 0) EndIf If BitAND($Flag, 1) Then DllCallAddress("none:cdecl", DllStructGetData($SymbolList, "StdinRedirect"), "str", $Filename) If BitAND($Flag, 2) Then DllCallAddress("none:cdecl", DllStructGetData($SymbolList, "StdoutRedirect"), "str", $Filename) If BitAND($Flag, 4) Then DllCallAddress("none:cdecl", DllStructGetData($SymbolList, "StderrRedirect"), "str", $Filename) EndFunc Func _BinaryCall_StdinRedirect($Filename = "CON") Local $Ret = _BinaryCall_StdioRedirect($Filename, 1) Return SetError(@Error, @Extended, $Ret) EndFunc Func _BinaryCall_StdoutRedirect($Filename = "CON") Local $Ret = _BinaryCall_StdioRedirect($Filename, 2) Return SetError(@Error, @Extended, $Ret) EndFunc Func _BinaryCall_StderrRedirect($Filename = "CON") Local $Ret = _BinaryCall_StdioRedirect($Filename, 4) Return SetError(@Error, @Extended, $Ret) EndFunc Func _BinaryCall_ReleaseOnExit($Ptr) OnAutoItExitRegister('__BinaryCall_DoRelease') __BinaryCall_ReleaseOnExit_Handle($Ptr) EndFunc Func __BinaryCall_DoRelease() __BinaryCall_ReleaseOnExit_Handle() EndFunc Func __BinaryCall_ReleaseOnExit_Handle($Ptr = Default) Static $PtrList If @NumParams = 0 Then If IsArray($PtrList) Then For $i = 1 To $PtrList[0] _BinaryCall_Free($PtrList[$i]) Next EndIf Else If Not IsArray($PtrList) Then Local $InitArray[1] = [0] $PtrList = $InitArray EndIf If IsPtr($Ptr) Then Local $Array = $PtrList Local $Size = UBound($Array) ReDim $Array[$Size + 1] $Array[$Size] = $Ptr $Array[0] += 1 $PtrList = $Array EndIf EndIf EndFunc === Json.au3 === ; ============================================================================================================================ ; File : Json.au3 (2018.12.29) ; Purpose : A Non-Strict JavaScript Object Notation (JSON) Parser UDF ; Author : Ward ; Dependency: BinaryCall.au3 ; Website : http://www.json.org/index.html ; ; Source : jsmn.c ; Author : zserge ; Website : http://zserge.com/jsmn.html ; ; Source : json_string_encode.c, json_string_decode.c ; Author : Ward ; Jos - Added Json_Dump() ; TheXMan - Json_ObjGetItems and some Json_Dump Fixes. ; Jos - Changed Json_ObjGet() and Json_ObjExists() to allow for multilevel object in string. ; ============================================================================================================================ ; ============================================================================================================================ ; Public Functions: ; Json_StringEncode($String, $Option = 0) ; Json_StringDecode($String) ; Json_IsObject(ByRef $Object) ; Json_IsNull(ByRef $Null) ; Json_Encode($Data, $Option = 0, $Indent = Default, $ArraySep = Default, $ObjectSep = Default, $ColonSep = Default) ; Json_Decode($Json, $InitTokenCount = 1000) ; Json_ObjCreate() ; Json_ObjPut(ByRef $Object, $Key, $Value) ; Json_ObjGet(ByRef $Object, $Key) ; Json_ObjDelete(ByRef $Object, $Key) ; Json_ObjExists(ByRef $Object, $Key) ; Json_ObjGetCount(ByRef $Object) ; Json_ObjGetKeys(ByRef $Object) ; Json_ObjGetItems(ByRef $Object) ; Json_ObjClear(ByRef $Object) ; Json_Put(ByRef $Var, $Notation, $Data, $CheckExists = False) ; Json_Get(ByRef $Var, $Notation) ; Json_Dump($String) ; ============================================================================================================================ #include-once #include "BinaryCall.au3" ; The following constants can be combined to form options for Json_Encode() Global Const $JSON_UNESCAPED_UNICODE = 1 ; Encode multibyte Unicode characters literally Global Const $JSON_UNESCAPED_SLASHES = 2 ; Don't escape / Global Const $JSON_HEX_TAG = 4 ; All < and > are converted to \u003C and \u003E Global Const $JSON_HEX_AMP = 8 ; All &s are converted to \u0026 Global Const $JSON_HEX_APOS = 16 ; All ' are converted to \u0027 Global Const $JSON_HEX_QUOT = 32 ; All " are converted to \u0022 Global Const $JSON_UNESCAPED_ASCII = 64 ; Don't escape ascii charcters between chr(1) ~ chr(0x1f) Global Const $JSON_PRETTY_PRINT = 128 ; Use whitespace in returned data to format it Global Const $JSON_STRICT_PRINT = 256 ; Make sure returned JSON string is RFC4627 compliant Global Const $JSON_UNQUOTED_STRING = 512 ; Output unquoted string if possible (conflicting with $Json_STRICT_PRINT) ; Error value returnd by Json_Decode() Global Const $JSMN_ERROR_NOMEM = -1 ; Not enough tokens were provided Global Const $JSMN_ERROR_INVAL = -2 ; Invalid character inside JSON string Global Const $JSMN_ERROR_PART = -3 ; The string is not a full JSON packet, more bytes expected Global $Total_JSON_DUMP_Output = "" Func __Jsmn_RuntimeLoader($ProcName = "") Static $SymbolList If Not IsDllStruct($SymbolList) Then Local $Code If @AutoItX64 Then $Code = 'AwAAAAQfCAAAAAAAAAA1HbEvgTNrvX54gCiWSTVmt5v7RCdoFJ/zhkKmwcm8yVqZPjJBoVhNHHAIzrHWKbZh1J0QAUaHB5zyQTilTmWa9O0OKeLrk/Jg+o7CmMzjEk74uPongdHv37nwYXvg97fiHvjP2bBzI9gxSkKq9Cqh/GxSHIlZPYyW76pXUt//25Aqs2Icfpyay/NFd50rW7eMliH5ynkrp16HM1afithVrO+LpSaz/IojowApmXnBHUncHliDqbkx6/AODUkyDm1hj+AiEZ9Me1Jy+hBQ1/wC/YnuuYSJvNAKp6XDnyc8Nwr54Uqx5SbUW2CezwQQ7aXX/HFiHSKpQcFW/gi8oSx5nsoxUXVjxeNI/L7z6GF2mfu3Tnpt7hliWEdA2r2VB+TIM7Pgwl9X3Ge0T3KJQUaRtLJZcPvVtOuKXr2Q9wy7hl80hVRrt9zYrbjBHXLrRx/HeIMkZwxhmKo/dD/vvaNgE+BdU8eeJqFBJK2alrK2rh2WkRynftyepm1WrdKrz/5KhQPp/4PqH+9IADDjoGBbfvJQXdT+yiO8DtfrVnd+JOEKsKEsdgeM3UXx5r6tEHO9rYWbzbnyEiX7WozZemry+vBZMMtHn1aA63+RcDQED73xOsnj00/9E5Z6hszM5Hi8vi6Hw3iOgf3cHwcXG44aau0JpuA2DlrUvnJOYkNnY+bECeSdAR1UQkFNyqRoH2xm4Y7gYMCPsFtPBlwwleEKI27SsUq1ZHVQvFCoef7DXgf/GwPCAvwDMIQfb3hJtIVubOkASRQZVNIJ/y4KPrn/gcASV7fvMjE34loltTVlyqprUWxpI51tN6vhTOLAp+CHseKxWaf9g1wdbVs0e/5xAiqgJbmKNi9OYbhV/blpp3SL63XKxGiHdxhK1aR+4rUY4eckNbaHfW7ob+q7aBoHSs6LVX9lWakb/xWxwQdwcX/7/C+TcQSOOg6rLoWZ8wur9qp+QwzoCbXkf04OYpvD5kqgEiwQnB90kLtcA+2XSbDRu+aq02eNNCzgkZujeL/HjVISjf2EuQKSsZkBhS15eiXoRgPaUoQ5586VS7t7rhM8ng5LiVzoUQIZ0pNKxWWqD+gXRBvOMIXY2yd0Ei4sE5KFIEhbs3u8vwP7nFLIpZ/RembPTuc0ZlguGJgJ2F5iApfia+C2tRYRNjVCqECCveWw6P2Btfaq9gw7cWWmJflIQbjxtccDqsn52cftLqXSna9zk05mYdJSV8z2W7vM1YJ5Rd82v0j3kau710A/kQrN41bdaxmKjL+gvSRlOLB1bpvkCtf9+h+eVA4XIkIXKFydr1OjMZ8wq2FIxPJXskAe4YMgwQmeWZXMK1KBbLB3yQR1YOYaaHk1fNea9KsXgs5YLbiP/noAusz76oEDo/DJh1aw7cUwdhboVPg1bNq88mRb5RGa13KDK9uEET7OA02KbSL+Q4HOtyasLUoVrZzVyd8iZPoGrV36vHnj+yvG4fq6F/fkug/sBRp186yVZQVmdAgFd+WiRLnUjxHUKJ6xBbpt4FTP42E/PzPw3JlDb0UQtXTDnIL0CWqbns2E7rZ5PBwrwQYwvBn/gaEeLVGDSh84DfW4zknIneGnYDXdVEHC+ITzejAnNxb1duB+w2aVTk64iXsKHETq53GMH6DuFi0oUeEFb/xp0HsRyNC8vBjOq3Kk7NZHxCQLh7UATFttG7sH+VIqGjjNwmraGJ0C92XhpQwSgfAb3KHucCHGTTti0sn6cgS3vb36BkjGKsRhXVuoQCFH96bvTYtl8paQQW9ufRfvxPqmU0sALdR0fIvZwd7Z8z0UoEec6b1Sul4e60REj/H4scb6N2ryHBR9ua5N1YxJu1uwgoLXUL2wT9ZPBjPjySUzeqXikUIKKYgNlWy+VlNIiWWTPtKpCTr508logA==' Else $Code = 'AwAAAASFBwAAAAAAAAA1HbEvgTNrvX54gCiqsa1mt5v7RCdoAFjCfVE40DZbE5UfabA9UKuHrjqOMbvjSoB2zBJTEYEQejBREnPrXL3VwpVOW+L9SSfo0rTfA8U2W+Veqo1uy0dOsPhl7vAHbBHrvJNfEUe8TT0q2eaTX2LeWpyrFEm4I3mhDJY/E9cpWf0A78e+y4c7NxewvcVvAakIHE8Xb8fgtqCTVQj3Q1eso7n1fKQj5YsQ20A86Gy9fz8dky78raeZnhYayn0b1riSUKxGVnWja2i02OvAVM3tCCvXwcbSkHTRjuIAbMu2mXF1UpKci3i/GzPmbxo9n/3aX/jpR6UvxMZuaEDEij4yzfZv7EyK9WCNBXxMmtTp3Uv6MZsK+nopXO3C0xFzZA/zQObwP3zhJ4sdatzMhFi9GAM70R4kgMzsxQDNArueXj+UFzbCCFZ89zXs22F7Ixi0FyFTk3jhH56dBaN65S+gtPztNGzEUmtk4M8IanhQSw8xCXr0x0MPDpDFDZs3aN5TtTPYmyk3psk7OrmofCQGG5cRcqEt9902qtxQDOHumfuCPMvU+oMjzLzBVEDnBbj+tY3y1jvgGbmEJguAgfB04tSeAt/2618ksnJJK+dbBkDLxjB4xrFr3uIFFadJQWUckl5vfh4MVXbsFA1hG49lqWDa7uSuPCnOhv8Yql376I4U4gfcF8LcgorkxS+64urv2nMUq6AkBEMQ8bdkI64oKLFfO7fGxh5iMNZuLoutDn2ll3nq4rPi4kOyAtfhW0UPyjvqNtXJ/h0Wik5Mi8z7BVxaURTDk81TP8y9+tzjySB/uGfHFAzjF8DUY1vqJCgn0GQ8ANtiiElX/+Wnc9HWi2bEEXItbm4yv97QrEPvJG9nPRBKWGiAQsIA5J+WryX5NrfEfRPk0QQwyl16lpHlw6l0UMuk7S21xjQgyWo0MywfzoBWW7+t4HH9sqavvP4dYAw81BxXqVHQhefUOS23en4bFUPWE98pAN6bul+kS767vDK34yTC3lA2a8wLrBEilmFhdB74fxbAl+db91PivhwF/CR4Igxr35uLdof7+jAYyACopQzmsbHpvAAwT2lapLix8H03nztAC3fBqFSPBVdIv12lsrrDw4dfhJEzq7AbL/Y7L/nIcBsQ/3UyVnZk4kZP1KzyPCBLLIQNpCVgOLJzQuyaQ6k2QCBy0eJ0ppUyfp54LjwVg0X7bwncYbAomG4ZcFwTQnC2AX3oYG5n6Bz4SLLjxrFsY+v/SVa+GqH8uePBh1TPkHVNmzjXXymEf5jROlnd+EjfQdRyitkjPrg2HiQxxDcVhCh5J2L5+6CY9eIaYgrbd8zJnzAD8KnowHwh2bi4JLgmt7ktJ1XGizox7cWf3/Dod56KAcaIrSVw9XzYybdJCf0YRA6yrwPWXbwnzc/4+UDkmegi+AoCEMoue+cC7vnYVdmlbq/YLE/DWJX383oz2Ryq8anFrZ8jYvdoh8WI+dIugYL2SwRjmBoSwn56XIaot/QpMo3pYJIa4o8aZIZrjvB7BXO5aCDeMuZdUMT6AXGAGF1AeAWxFd2XIo1coR+OplMNDuYia8YAtnSTJ9JwGYWi2dJz3xrxsTQpBONf3yn8LVf8eH+o5eXc7lzCtHlDB+YyI8V9PyMsUPOeyvpB3rr9fDfNy263Zx33zTi5jldgP2OetUqGfbwl+0+zNYnrg64bluyIN/Awt1doDCQkCKpKXxuPaem/SyCHrKjg' EndIf Local $Symbol[] = ["jsmn_parse", "jsmn_init", "json_string_decode", "json_string_encode"] Local $CodeBase = _BinaryCall_Create($Code) If @error Then Exit MsgBox(16, "Json", "Startup Failure!") $SymbolList = _BinaryCall_SymbolList($CodeBase, $Symbol) If @error Then Exit MsgBox(16, "Json", "Startup Failure!") EndIf If $ProcName Then Return DllStructGetData($SymbolList, $ProcName) EndFunc ;==>__Jsmn_RuntimeLoader Func Json_StringEncode($String, $Option = 0) Static $Json_StringEncode = __Jsmn_RuntimeLoader("json_string_encode") Local $Length = StringLen($String) * 6 + 1 Local $Buffer = DllStructCreate("wchar[" & $Length & "]") Local $Ret = DllCallAddress("int:cdecl", $Json_StringEncode, "wstr", $String, "ptr", DllStructGetPtr($Buffer), "uint", $Length, "int", $Option) Return SetError($Ret[0], 0, DllStructGetData($Buffer, 1)) EndFunc ;==>Json_StringEncode Func Json_StringDecode($String) Static $Json_StringDecode = __Jsmn_RuntimeLoader("json_string_decode") Local $Length = StringLen($String) + 1 Local $Buffer = DllStructCreate("wchar[" & $Length & "]") Local $Ret = DllCallAddress("int:cdecl", $Json_StringDecode, "wstr", $String, "ptr", DllStructGetPtr($Buffer), "uint", $Length) Return SetError($Ret[0], 0, DllStructGetData($Buffer, 1)) EndFunc ;==>Json_StringDecode Func Json_Decode($Json, $InitTokenCount = 1000) Static $Jsmn_Init = __Jsmn_RuntimeLoader("jsmn_init"), $Jsmn_Parse = __Jsmn_RuntimeLoader("jsmn_parse") If $Json = "" Then $Json = '""' Local $TokenList, $Ret Local $Parser = DllStructCreate("uint pos;int toknext;int toksuper") Do DllCallAddress("none:cdecl", $Jsmn_Init, "ptr", DllStructGetPtr($Parser)) $TokenList = DllStructCreate("byte[" & ($InitTokenCount * 20) & "]") $Ret = DllCallAddress("int:cdecl", $Jsmn_Parse, "ptr", DllStructGetPtr($Parser), "wstr", $Json, "ptr", DllStructGetPtr($TokenList), "uint", $InitTokenCount) $InitTokenCount *= 2 Until $Ret[0] <> $JSMN_ERROR_NOMEM Local $Next = 0 Return SetError($Ret[0], 0, _Json_Token($Json, DllStructGetPtr($TokenList), $Next)) EndFunc ;==>Json_Decode Func _Json_Token(ByRef $Json, $Ptr, ByRef $Next) If $Next = -1 Then Return Null Local $Token = DllStructCreate("int;int;int;int", $Ptr + ($Next * 20)) Local $Type = DllStructGetData($Token, 1) Local $Start = DllStructGetData($Token, 2) Local $End = DllStructGetData($Token, 3) Local $Size = DllStructGetData($Token, 4) $Next += 1 If $Type = 0 And $Start = 0 And $End = 0 And $Size = 0 Then ; Null Item $Next = -1 Return Null EndIf Switch $Type Case 0 ; Json_PRIMITIVE Local $Primitive = StringMid($Json, $Start + 1, $End - $Start) Switch $Primitive Case "true" Return True Case "false" Return False Case "null" Return Null Case Else If StringRegExp($Primitive, "^[+\-0-9]") Then Return Number($Primitive) Else Return Json_StringDecode($Primitive) EndIf EndSwitch Case 1 ; Json_OBJECT Local $Object = Json_ObjCreate() For $i = 0 To $Size - 1 Step 2 Local $Key = _Json_Token($Json, $Ptr, $Next) Local $Value = _Json_Token($Json, $Ptr, $Next) If Not IsString($Key) Then $Key = Json_Encode($Key) If $Object.Exists($Key) Then $Object.Remove($Key) $Object.Add($Key, $Value) Next Return $Object Case 2 ; Json_ARRAY Local $Array[$Size] For $i = 0 To $Size - 1 $Array[$i] = _Json_Token($Json, $Ptr, $Next) Next Return $Array Case 3 ; Json_STRING Return Json_StringDecode(StringMid($Json, $Start + 1, $End - $Start)) EndSwitch EndFunc ;==>_Json_Token Func Json_IsObject(ByRef $Object) Return (IsObj($Object) And ObjName($Object) = "Dictionary") EndFunc ;==>Json_IsObject Func Json_IsNull(ByRef $Null) Return IsKeyword($Null) Or (Not IsObj($Null) And VarGetType($Null) = "Object") EndFunc ;==>Json_IsNull Func Json_Encode_Compact($Data, $Option = 0) Local $Json = "" Select Case IsString($Data) Return '"' & Json_StringEncode($Data, $Option) & '"' Case IsNumber($Data) Return $Data Case IsArray($Data) And UBound($Data, 0) = 1 $Json = "[" For $i = 0 To UBound($Data) - 1 $Json &= Json_Encode_Compact($Data[$i], $Option) & "," Next If StringRight($Json, 1) = "," Then $Json = StringTrimRight($Json, 1) Return $Json & "]" Case Json_IsObject($Data) $Json = "{" Local $Keys = $Data.Keys() For $i = 0 To UBound($Keys) - 1 $Json &= '"' & Json_StringEncode($Keys[$i], $Option) & '":' & Json_Encode_Compact($Data.Item($Keys[$i]), $Option) & "," Next If StringRight($Json, 1) = "," Then $Json = StringTrimRight($Json, 1) Return $Json & "}" Case IsBool($Data) Return StringLower($Data) Case IsPtr($Data) Return Number($Data) Case IsBinary($Data) Return '"' & Json_StringEncode(BinaryToString($Data, 4), $Option) & '"' Case Else ; Keyword, DllStruct, Object Return "null" EndSelect EndFunc ;==>Json_Encode_Compact Func Json_Encode_Pretty($Data, $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep, $ArrayCRLF = Default, $ObjectCRLF = Default, $NextIdent = "") Local $ThisIdent = $NextIdent, $Json = "", $String = "", $Match = "", $Keys = "" Local $Length = 0 Select Case IsString($Data) $String = Json_StringEncode($Data, $Option) If BitAND($Option, $JSON_UNQUOTED_STRING) And Not BitAND($Option, $JSON_STRICT_PRINT) And Not StringRegExp($String, "[\s,:]") And Not StringRegExp($String, "^[+\-0-9]") Then Return $String Else Return '"' & $String & '"' EndIf Case IsArray($Data) And UBound($Data, 0) = 1 If UBound($Data) = 0 Then Return "[]" If IsKeyword($ArrayCRLF) Then $ArrayCRLF = "" $Match = StringRegExp($ArraySep, "[\r\n]+$", 3) If IsArray($Match) Then $ArrayCRLF = $Match[0] EndIf If $ArrayCRLF Then $NextIdent &= $Indent $Length = UBound($Data) - 1 For $i = 0 To $Length If $ArrayCRLF Then $Json &= $NextIdent $Json &= Json_Encode_Pretty($Data[$i], $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep, $ArrayCRLF, $ObjectCRLF, $NextIdent) If $i < $Length Then $Json &= $ArraySep Next If $ArrayCRLF Then Return "[" & $ArrayCRLF & $Json & $ArrayCRLF & $ThisIdent & "]" Return "[" & $Json & "]" Case Json_IsObject($Data) If $Data.Count = 0 Then Return "{}" If IsKeyword($ObjectCRLF) Then $ObjectCRLF = "" $Match = StringRegExp($ObjectSep, "[\r\n]+$", 3) If IsArray($Match) Then $ObjectCRLF = $Match[0] EndIf If $ObjectCRLF Then $NextIdent &= $Indent $Keys = $Data.Keys() $Length = UBound($Keys) - 1 For $i = 0 To $Length If $ObjectCRLF Then $Json &= $NextIdent $Json &= Json_Encode_Pretty(String($Keys[$i]), $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep) & $ColonSep _ & Json_Encode_Pretty($Data.Item($Keys[$i]), $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep, $ArrayCRLF, $ObjectCRLF, $NextIdent) If $i < $Length Then $Json &= $ObjectSep Next If $ObjectCRLF Then Return "{" & $ObjectCRLF & $Json & $ObjectCRLF & $ThisIdent & "}" Return "{" & $Json & "}" Case Else Return Json_Encode_Compact($Data, $Option) EndSelect EndFunc ;==>Json_Encode_Pretty Func Json_Encode($Data, $Option = 0, $Indent = Default, $ArraySep = Default, $ObjectSep = Default, $ColonSep = Default) If BitAND($Option, $JSON_PRETTY_PRINT) Then Local $Strict = BitAND($Option, $JSON_STRICT_PRINT) If IsKeyword($Indent) Then $Indent = @TAB Else $Indent = Json_StringDecode($Indent) If StringRegExp($Indent, "[^\t ]") Then $Indent = @TAB EndIf If IsKeyword($ArraySep) Then $ArraySep = "," & @CRLF Else $ArraySep = Json_StringDecode($ArraySep) If $ArraySep = "" Or StringRegExp($ArraySep, "[^\s,]|,.*,") Or ($Strict And Not StringRegExp($ArraySep, ",")) Then $ArraySep = "," & @CRLF EndIf If IsKeyword($ObjectSep) Then $ObjectSep = "," & @CRLF Else $ObjectSep = Json_StringDecode($ObjectSep) If $ObjectSep = "" Or StringRegExp($ObjectSep, "[^\s,]|,.*,") Or ($Strict And Not StringRegExp($ObjectSep, ",")) Then $ObjectSep = "," & @CRLF EndIf If IsKeyword($ColonSep) Then $ColonSep = ": " Else $ColonSep = Json_StringDecode($ColonSep) If $ColonSep = "" Or StringRegExp($ColonSep, "[^\s,:]|[,:].*[,:]") Or ($Strict And (StringRegExp($ColonSep, ",") Or Not StringRegExp($ColonSep, ":"))) Then $ColonSep = ": " EndIf Return Json_Encode_Pretty($Data, $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep) ElseIf BitAND($Option, $JSON_UNQUOTED_STRING) Then Return Json_Encode_Pretty($Data, $Option, "", ",", ",", ":") Else Return Json_Encode_Compact($Data, $Option) EndIf EndFunc ;==>Json_Encode Func Json_ObjCreate() Local $Object = ObjCreate('Scripting.Dictionary') $Object.CompareMode = 0 Return $Object EndFunc ;==>Json_ObjCreate Func Json_ObjPut(ByRef $Object, $Key, $Value) $Key = String($Key) If $Object.Exists($Key) Then $Object.Remove($Key) $Object.Add($Key, $Value) EndFunc ;==>Json_ObjPut Func Json_ObjGet(ByRef $Object, $Key) Local $DynObject = $Object Local $Keys = StringSplit($Key, ".") For $x = 1 To $Keys[0] If $DynObject.Exists($Keys[$x]) Then If $x = $Keys[0] Then Return $DynObject.Item($Keys[$x]) Else $DynObject = Json_ObjGet($DynObject, $Keys[$x]) EndIf EndIf Next Return SetError(1, 0, '') EndFunc ;==>Json_ObjGet Func Json_ObjDelete(ByRef $Object, $Key) $Key = String($Key) If $Object.Exists($Key) Then $Object.Remove($Key) EndFunc ;==>Json_ObjDelete Func Json_ObjExists(ByRef $Object, $Key) Local $DynObject = $Object Local $Keys = StringSplit($Key, ".") For $x = 1 To $Keys[0] If $DynObject.Exists($Keys[$x]) Then If $x = $Keys[0] Then Return True Else $DynObject = Json_ObjGet($DynObject, $Keys[$x]) EndIf Else Return False EndIf Next Return False EndFunc ;==>Json_ObjExists Func Json_ObjGetCount(ByRef $Object) Return $Object.Count EndFunc ;==>Json_ObjGetCount Func Json_ObjGetKeys(ByRef $Object) Return $Object.Keys() EndFunc ;==>Json_ObjGetKeys Func Json_ObjGetItems(ByRef $Object) Return $Object.Items() EndFunc ;==>Json_ObjGetItems Func Json_ObjClear(ByRef $Object) Return $Object.RemoveAll() EndFunc ;==>Json_ObjClear ; Both dot notation and square bracket notation can be supported Func Json_Put(ByRef $Var, $Notation, $Data, $CheckExists = False) Local $Ret = 0, $Item = "", $Error = 0 Local $Match = "" $Match = StringRegExp($Notation, "(^\[([^\]]+)\])|(^\.([^\.\[]+))", 3) If IsArray($Match) Then Local $Index If UBound($Match) = 4 Then $Index = String(Json_Decode($Match[3])) ; only string using dot notation $Notation = StringTrimLeft($Notation, StringLen($Match[2])) Else $Index = Json_Decode($Match[1]) $Notation = StringTrimLeft($Notation, StringLen($Match[0])) EndIf If IsString($Index) Then If $CheckExists And (Not Json_IsObject($Var) Or Not Json_ObjExists($Var, $Index)) Then Return SetError(1, 0, False) ; no specific object EndIf If Not Json_IsObject($Var) Then $Var = Json_ObjCreate() If $Notation Then $Item = Json_ObjGet($Var, $Index) $Ret = Json_Put($Item, $Notation, $Data, $CheckExists) $Error = @error If Not $Error Then Json_ObjPut($Var, $Index, $Item) Return SetError($Error, 0, $Ret) Else Json_ObjPut($Var, $Index, $Data) Return True EndIf ElseIf IsInt($Index) Then If $Index < 0 Or ($CheckExists And (Not IsArray($Var) Or UBound($Var, 0) <> 1 Or $Index >= UBound($Var))) Then Return SetError(1, 0, False) ; no specific object EndIf If Not IsArray($Var) Or UBound($Var, 0) <> 1 Then Dim $Var[$Index + 1] ElseIf $Index >= UBound($Var) Then ReDim $Var[$Index + 1] EndIf If $Notation Then $Ret = Json_Put($Var[$Index], $Notation, $Data, $CheckExists) Return SetError(@error, 0, $Ret) Else $Var[$Index] = $Data Return True EndIf EndIf EndIf Return SetError(2, 0, False) ; invalid notation EndFunc ;==>Json_Put ; Both dot notation and square bracket notation can be supported Func Json_Get(ByRef $Var, $Notation) Local $Match = StringRegExp($Notation, "(^\[([^\]]+)\])|(^\.([^\.\[]+))", 3) If IsArray($Match) Then Local $Index If UBound($Match) = 4 Then $Index = String(Json_Decode($Match[3])) ; only string using dot notation $Notation = StringTrimLeft($Notation, StringLen($Match[2])) Else $Index = Json_Decode($Match[1]) $Notation = StringTrimLeft($Notation, StringLen($Match[0])) EndIf Local $Item If IsString($Index) And Json_IsObject($Var) And Json_ObjExists($Var, $Index) Then $Item = Json_ObjGet($Var, $Index) ElseIf IsInt($Index) And IsArray($Var) And UBound($Var, 0) = 1 And $Index >= 0 And $Index < UBound($Var) Then $Item = $Var[$Index] Else Return SetError(1, 0, "") ; no specific object EndIf If Not $Notation Then Return $Item Local $Ret = Json_Get($Item, $Notation) Return SetError(@error, 0, $Ret) EndIf Return SetError(2, 0, "") ; invalid notation EndFunc ;==>Json_Get ; List all JSON keys and their value to the Console Func Json_Dump($Json, $InitTokenCount = 1000) Static $Jsmn_Init = __Jsmn_RuntimeLoader("jsmn_init"), $Jsmn_Parse = __Jsmn_RuntimeLoader("jsmn_parse") If $Json = "" Then $Json = '""' Local $TokenList, $Ret $Total_JSON_DUMP_Output = "" ; reset totaldump variable at the start of the dump process (Use for testing) Local $Parser = DllStructCreate("uint pos;int toknext;int toksuper") Do DllCallAddress("none:cdecl", $Jsmn_Init, "ptr", DllStructGetPtr($Parser)) $TokenList = DllStructCreate("byte[" & ($InitTokenCount * 20) & "]") $Ret = DllCallAddress("int:cdecl", $Jsmn_Parse, "ptr", DllStructGetPtr($Parser), "wstr", $Json, "ptr", DllStructGetPtr($TokenList), "uint", $InitTokenCount) $InitTokenCount *= 2 Until $Ret[0] <> $JSMN_ERROR_NOMEM Local $Next = 0 _Json_TokenDump($Json, DllStructGetPtr($TokenList), $Next) EndFunc ;==>Json_Dump Func _Json_TokenDump(ByRef $Json, $Ptr, ByRef $Next, $ObjPath = "") If $Next = -1 Then Return Null Local $Token = DllStructCreate("int;int;int;int", $Ptr + ($Next * 20)) Local $Type = DllStructGetData($Token, 1) Local $Start = DllStructGetData($Token, 2) Local $End = DllStructGetData($Token, 3) Local $Size = DllStructGetData($Token, 4) Local $Value $Next += 1 If $Type = 0 And $Start = 0 And $End = 0 And $Size = 0 Then ; Null Item $Next = -1 Return Null EndIf Switch $Type Case 0 ; Json_PRIMITIVE Local $Primitive = StringMid($Json, $Start + 1, $End - $Start) Switch $Primitive Case "true" Return "True" Case "false" Return "False" Case "null" Return "Null" Case Else If StringRegExp($Primitive, "^[+\-0-9]") Then Return Number($Primitive) Else Return Json_StringDecode($Primitive) EndIf EndSwitch Case 1 ; Json_OBJECT For $i = 0 To $Size - 1 Step 2 Local $Key = _Json_TokenDump($Json, $Ptr, $Next) Local $cObjPath = $ObjPath & "." & $Key $Value = _Json_TokenDump($Json, $Ptr, $Next, $ObjPath & "." & $Key) If Not (IsBool($Value) And $Value = False) Then If Not IsString($Key) Then $Key = Json_Encode($Key) EndIf ; show the key and its value ConsoleWrite("+-> " & $cObjPath & ' =' & $Value & @CRLF) $Total_JSON_DUMP_Output &= "+-> " & $cObjPath & ' =' & $Value & @CRLF EndIf Next Return False Case 2 ; Json_ARRAY Local $sObjPath = $ObjPath For $i = 0 To $Size - 1 $sObjPath = $ObjPath & "[" & $i & "]" $Value = _Json_TokenDump($Json, $Ptr, $Next, $sObjPath) If Not (IsBool($Value) And $Value = False) Then ;XC - Changed line ; show the key and its value ConsoleWrite("+=> " & $sObjPath & "=>" & $Value & @CRLF) $Total_JSON_DUMP_Output &= "+=> " & $sObjPath & "=>" & $Value & @CRLF EndIf Next $ObjPath = $sObjPath Return False Case 3 ; Json_STRING Local $LastKey = Json_StringDecode(StringMid($Json, $Start + 1, $End - $Start)) Return $LastKey EndSwitch EndFunc ;==>_Json_TokenDump