Trong Posted Tuesday at 04:36 AM Posted Tuesday at 04:36 AM I have written a function to run the ps command and get the result back, but the semicolon problem has been driving me crazy, as the command sent to ps quotes seems to have problems and cannot run the command correctly! no matter I try to add 2 double quotes or use single quotes, it still does not work properly. expandcollapse popup; *** Add a program to Windows Defender Exclusions using PowerShell *** #RequireAdmin #include <AutoItConstants.au3> ; *** Example Usage *** ; Get the list of running processes and filter for processes named "notepad" Local $commandToRun = "Get-Process | Where-Object {$_.ProcessName -eq 'notepad'} | Format-List Name, Id, MainWindowTitle" Local $powerShellResult = RunPowerShellCommand($commandToRun) If @error Then MsgBox(16, "Error", "Could not get results from PowerShell.") Else If $powerShellResult Then MsgBox(64, "PowerShell Result", "Result returned from PowerShell: " & $powerShellResult) Else MsgBox(64, "Information", "No processes match the criteria.") EndIf EndIf ; Another example: Get the PowerShell version Local $versionCommand = "$PSVersionTable.PSVersion" Local $powerShellVersion = RunPowerShellCommand($versionCommand) If Not @error Then MsgBox(64, "PowerShell Version", "PowerShell Version: " & $powerShellVersion) EndIf ;~ ; *** Example Usage *** ; **Change the program path and rule name according to your needs** Local $programToExclude = @Compiled ? @ScriptFullPath : @AutoItExe Local $firewallRuleName = "AutoIt3" ; Add to Windows Defender Exclusions using PowerShell If AddToDefenderExclusions_PS($programToExclude) Then MsgBox(64, "Success", "Successfully added '" & $programToExclude & "' to Windows Defender Exclusions (PowerShell).") Else MsgBox(16, "Failure", "Failed to add '" & $programToExclude & "' to Windows Defender Exclusions (PowerShell). Please check the path or run the script with administrator privileges.") EndIf ; Add to Windows Firewall Exclusions using PowerShell If AddToFirewallExclusions_PS($programToExclude, $firewallRuleName) Then MsgBox(64, "Success", "Successfully added Firewall rule for '" & $programToExclude & "' (PowerShell).") Else MsgBox(16, "Failure", "Failed to add Firewall rule for '" & $programToExclude & "' (PowerShell). Please check the path or run the script with administrator privileges.") EndIf Func RunPowerShellCommand($powerShellCommand) Local $output = "" Local $error = "" Local $command_r = "PowerShell -Command '" & '"' & $powerShellCommand & '"' & "'" ConsoleWrite('-> ' & $command_r & @CRLF) Local $pid = Run(@ComSpec & ' /c ' & $command_r, "", @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD) If Not $pid Then MsgBox(16, "Error", "Could not run PowerShell.") Return SetError(-1, 0, False) EndIf While 1 $line = StdoutRead($pid) If @error Then ExitLoop $output &= $line & @CRLF WEnd While 1 $line = StderrRead($pid) If @error Then ExitLoop $error &= $line & @CRLF WEnd ProcessClose($pid) $error = StringStripWS($error, 7) $output = StringStripWS($output, 7) If StringLen(StringStripWS($error, 8)) > 0 Then ConsoleWrite($output & @CRLF & $error & @CRLF) MsgBox(16, "PowerShell Error", "Error returned from PowerShell: " & $error) Return SetError(1, 0, $error) Else ConsoleWrite($output & @CRLF) Return SetError(0, 0, $output) EndIf EndFunc ;==>RunPowerShellCommand Func AddToDefenderExclusions_PS($programPath) Local $command = 'Add-MpPreference -ExclusionPath "' & $programPath & '"' Local $result = RunPowerShellCommand($command) If @error Then MsgBox(16, "Defender Error", "Error running PowerShell command to add to Defender Exclusion." & @CRLF & "" & $result & "") Return False Else If StringLen($result) > 0 Then MsgBox(16, "Defender return", "" & $result & "") Return True Else Return False EndIf EndIf EndFunc ;==>AddToDefenderExclusions_PS ; *** Add a program to Windows Firewall Exclusions using PowerShell *** Func AddToFirewallExclusions_PS($programPath, $ruleName) Return AddToFirewall_InboundExclusions_PS($programPath, $ruleName) And AddToFirewall_OutboundExclusions_PS($programPath, $ruleName) EndFunc ;==>AddToFirewallExclusions_PS Func AddToFirewall_InboundExclusions_PS($programPath, $ruleName) ; Create Inbound rule Local $inboundCommand = 'New-NetFirewallRule -DisplayName "' & $ruleName & ' (Inbound)" -Direction Inbound -Action Allow -Program "' & $programPath & '" -Enabled True' Local $inboundResult = RunPowerShellCommand($inboundCommand) If @error Then MsgBox(16, "Firewall Error", "Error running PowerShell command to add Inbound Firewall rule." & @CRLF & "" & $inboundResult & "") Return False Else If StringLen($inboundResult) > 0 Then MsgBox(16, "Defender return", "" & $inboundResult & "") Return True Else Return False EndIf EndIf EndFunc ;==>AddToFirewall_InboundExclusions_PS Func AddToFirewall_OutboundExclusions_PS($programPath, $ruleName) ; Create Outbound rule Local $outboundCommand = 'New-NetFirewallRule -DisplayName "' & $ruleName & ' (Outbound)" -Direction Outbound -Action Allow -Program "' & $programPath & '" -Enabled True' Local $outboundResult = RunPowerShellCommand($outboundCommand) If @error Then MsgBox(16, "Firewall Error", "Error running PowerShell command to add OutboundFirewall rule." & @CRLF & "" & $outboundResult & "") Return False Else If StringLen($outboundResult) > 0 Then MsgBox(16, "Defender return", "" & $outboundResult & "") Return True Else Return False EndIf EndIf Return True EndFunc ;==>AddToFirewall_OutboundExclusions_PS Regards,
SOLVE-SMART Posted Tuesday at 05:50 AM Posted Tuesday at 05:50 AM (edited) Hi @Trong 👋 , I can not test it right now, but try the following please: ; replace this line in you "RunPowerShellCommand" function: Local $command_r = "PowerShell -Command '" & '"' & $powerShellCommand & '"' & "'" ; by this: Local $command_r = StringFormat('powershell.exe -Command "%s"', $powerShellCommand) As far as I read your code this should be all. In case this is not successful for all your cases, because you use double and single quotes not consistent in the powershell commands, escape the double quotes by this: Local $command_r = StringFormat('powershell.exe -Command "%s"', StringReplace($powerShellCommand, '"', '""')) Best regards Sven Edited Tuesday at 06:09 AM by SOLVE-SMART Trong 1 ==> AutoIt related: 🔗 GitHub, 🔗 Discord Server Spoiler 🌍 Au3Forums 🎲 AutoIt (en) Cheat Sheet 📊 AutoIt limits/defaults 💎 Code Katas: [...] (comming soon) 🎭 Collection of GitHub users with AutoIt projects 🐞 False-Positives 🔮 Me on GitHub 💬 Opinion about new forum sub category 📑 UDF wiki list ✂ VSCode-AutoItSnippets 📑 WebDriver FAQs 👨🏫 WebDriver Tutorial (coming soon)
Solution ioa747 Posted Tuesday at 04:36 PM Solution Posted Tuesday at 04:36 PM I didn't try the Example3, because I have Windows Defender disabled Example1, Example2, Example4 is ok expandcollapse popup#RequireAdmin #include <AutoItConstants.au3> ; Choose the example to run ;~ Example1() ;~ Example2() ;~ Example3() Example4() ;--------------------------------------------------------------------------------------- Func Example1() ; Get the list of running processes and filter for processes named "notepad" Local $commandToRun = "Get-Process | Where-Object {$_.ProcessName -eq 'notepad'} | Format-List Name, Id, MainWindowTitle" Local $powerShellResult = RunPowerShellCommand($commandToRun) If @error Then MsgBox(16, "Error", "Could not get results from PowerShell.") Else If $powerShellResult Then MsgBox(64, "PowerShell Result", "Result returned from PowerShell: " & $powerShellResult) Else MsgBox(64, "Information", "No processes match the criteria.") EndIf EndIf EndFunc ;--------------------------------------------------------------------------------------- Func Example2() ; Get the PowerShell version Local $versionCommand = "$PSVersionTable.PSVersion" Local $powerShellVersion = RunPowerShellCommand($versionCommand) If Not @error Then MsgBox(64, "PowerShell Version", "PowerShell Version: " & $powerShellVersion) EndIf EndFunc ;--------------------------------------------------------------------------------------- Func Example3() ; Add to Windows Defender Exclusions using PowerShell Local $programToExclude = @Compiled ? @ScriptFullPath : @AutoItExe If AddToDefenderExclusions_PS($programToExclude) Then MsgBox(64, "Success", "Successfully added '" & $programToExclude & "' to Windows Defender Exclusions.") Else MsgBox(16, "Failure", "Failed to add to Defender Exclusions.") EndIf EndFunc ;--------------------------------------------------------------------------------------- Func Example4() ; Add to Windows Firewall Exclusions using PowerShell Local $programToExclude = @Compiled ? @ScriptFullPath : @AutoItExe Local $firewallRuleName = "AutoIt3" If AddToFirewallExclusions_PS($programToExclude, $firewallRuleName) Then MsgBox(64, "Success", "Successfully added Firewall rule for '" & $programToExclude & "'.") Else MsgBox(16, "Failure", "Failed to add Firewall rule.") EndIf EndFunc ;--------------------------------------------------------------------------------------- Func RunPowerShellCommand($powerShellCommand) Local $output = "" Local $error = "" Local $command_r = 'PowerShell -Command "' & $powerShellCommand & '"' ConsoleWrite('-> ' & $command_r & @CRLF) Local $pid = Run(@ComSpec & ' /c ' & $command_r, "", @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD) If Not $pid Then MsgBox(16, "Error", "Could not run PowerShell.") Return SetError(-1, 0, False) EndIf While 1 Local $line = StdoutRead($pid) If @error Then ExitLoop $output &= $line & @CRLF WEnd While 1 Local $line = StderrRead($pid) If @error Then ExitLoop $error &= $line & @CRLF WEnd ProcessClose($pid) $error = StringStripWS($error, 7) $output = StringStripWS($output, 7) If StringLen(StringStripWS($error, 8)) > 0 Then ConsoleWrite($output & @CRLF & $error & @CRLF) MsgBox(16, "PowerShell Error", "Error returned from PowerShell: " & $error) Return SetError(1, 0, $error) Else ConsoleWrite($output & @CRLF) Return SetError(0, 0, $output) EndIf EndFunc ;--------------------------------------------------------------------------------------- Func AddToDefenderExclusions_PS($programPath) Local $command = "Add-MpPreference -ExclusionPath '" & $programPath & "'" Local $result = RunPowerShellCommand($command) If @error Then MsgBox(16, "Defender Error", "Error running PowerShell command to add to Defender Exclusion." & @CRLF & $result) Return False Else Return True EndIf EndFunc ;--------------------------------------------------------------------------------------- Func AddToFirewallExclusions_PS($programPath, $ruleName) Return AddToFirewall_InboundExclusions_PS($programPath, $ruleName) And AddToFirewall_OutboundExclusions_PS($programPath, $ruleName) EndFunc ;==>AddToFirewallExclusions_PS ;--------------------------------------------------------------------------------------- Func AddToFirewall_InboundExclusions_PS($programPath, $ruleName) Local $inboundCommand = "New-NetFirewallRule -DisplayName '" & $ruleName & " (Inbound)' -Direction Inbound -Action Allow -Program '" & $programPath & "' -Enabled True" Local $inboundResult = RunPowerShellCommand($inboundCommand) If @error Then MsgBox(16, "Firewall Error", "Error adding Inbound Firewall rule." & @CRLF & $inboundResult) Return False EndIf Return True EndFunc ;--------------------------------------------------------------------------------------- Func AddToFirewall_OutboundExclusions_PS($programPath, $ruleName) Local $outboundCommand = "New-NetFirewallRule -DisplayName '" & $ruleName & " (Outbound)' -Direction Outbound -Action Allow -Program '" & $programPath & "' -Enabled True" Local $outboundResult = RunPowerShellCommand($outboundCommand) If @error Then MsgBox(16, "Firewall Error", "Error adding Outbound Firewall rule." & @CRLF & $outboundResult) Return False EndIf Return True EndFunc ;--------------------------------------------------------------------------------------- Trong 1 I know that I know nothing
AspirinJunkie Posted Wednesday at 07:31 AM Posted Wednesday at 07:31 AM Yes, the escaping of the quotation marks within the commands can be quite annoying. So you have to escape the quotation marks in the command. This is a bit easier if you swap the double quotation marks with the single ones. Something like this: Local $command_r = 'PowerShell -Command "' & StringReplace($powerShellCommand, '"', '\"') & '"' Why you are using @ComSpec (i.e. the cmd.exe) although you want to call the powershell instead is not clear to me. I have written the following function myself. Maybe it will help you here: expandcollapse popup#include <WinAPIConv.au3> ; Get the list of running processes and filter for processes named "notepad" Local $commandToRun = "Get-Process | Where-Object {$_.ProcessName -eq 'notepad'} | Format-List Name, Id, MainWindowTitle" Local $powerShellResult = _prc_runPS($commandToRun) If @error Then MsgBox(16, "Error", "Could not get results from PowerShell.") Else If $powerShellResult Then MsgBox(64, "PowerShell Result", "Result returned from PowerShell: " & $powerShellResult) Else MsgBox(64, "Information", "No processes match the criteria.") EndIf EndIf ; Another example: Get the PowerShell version Local $versionCommand = "$PSVersionTable.PSVersion" Local $powerShellVersion = _prc_runPS($versionCommand) If Not @error Then MsgBox(64, "PowerShell Version", "PowerShell Version: " & $powerShellVersion) EndIf ; #FUNCTION# ====================================================================================== ; Name ..........: _prc_runPS() ; Description ...: Executes a Powershell command and returns its output as a string ; Syntax ........: _prc_runPS($sCmd, [$nFlags = 0x8, [$sWorkDir = '', [$sOptions = '-NoProfile -ExecutionPolicy Bypass', [$nTimeOut = 0, $bLoopRead = False]]]]]) ; Parameters ....: $sCmd - the powershell command to be executed as you would use it directly in the powershell ; you can also use line breaks ; $nFlags - [optional] flags that control the handling of the two streams stdout and stderr: ; 0x2: [Default] Return a string with the content of stdout ; 0x4: [Default] Return a string with the content of stderr ; 0x6: Return a array with the content of stdout in $Array[0] and stderr in $Array[1] ; 0x8: Return a string with the combined content of stdout and stderr ; $sWorkDir - [optional] the working directory like in Run() ; $sOptions - [String] additional parameters to be passed to powershell.exe ; $nTimeOut - [optional] the maximum time to wait for the process to be completed (see @error return) ; default = 0: infinite; every other number: wait time in seconds ; $bLoopRead - if true: stdout and stderr are read in a loop; if false: they are read in one go ; Return values .: Success: String with powershell output or if $nFlags = 0x6: array with cmdline outputs and set ; @extended = return code of the process ; Failure: "" and set @error to: ; | 1: error during run; @extended = @error of Run() ; | 2: ProcessWaitClose reaches timeout before completion ; | 3: content written in stderr - indicates error messages of the program (not a real error) ; Author ........: AspirinJunkie ; Modified ......: 2025-03-10 ; Related .......: _WinAPI_OemToChar() ; Example .......: Yes ; $x = _prc_runPS('$obj = New-Object -ComObject "Shell.Application"' & @CRLF & _ ; '$obj | Get-Member') ; ConsoleWrite($x & @CRLF) ; ================================================================================================= Func _prc_runPS($sCmd, $nFlags = 0x8, $sWorkDir = '', $sOptions = '-NoProfile -ExecutionPolicy Bypass', $nTimeOut = 0, $bLoopRead = False) ; handling Default keyword word for the parameters If IsKeyWord($nFlags) = 1 Then $nFlags = 0x8 If IsKeyWord($sWorkDir) = 1 Then $sWorkDir = "" If IsKeyWord($sOptions) = 1 Then $sOptions = '-NoProfile -ExecutionPolicy Bypass' ; format command as call parameter, passed to powershell.exe $sCmd = StringFormat('powershell.exe %s -Command "%s"', $sOptions, StringReplace($sCmd, '"', '\"',0,1)) ; start the cmd/process Local $iPID = Run($sCmd, $sWorkDir, @SW_Hide, $nFlags) If @error Then Return SetError(1, @error, "") Local $iExit, $sStdOut = "", $sStdErr = "" If $bLoopRead Then ; fill stdout and/or stderr over a loop Do If BitAND($nFlags, 0x4) Then $sStdErr &= StderrRead($iPID) $sStdOut &= StdoutRead($iPID) If @error Then ExitLoop Until 0 ; determine the exit code $iExit = ProcessWaitClose($iPID, 0) ElseIf ProcessWaitClose($iPID, $nTimeOut) = 0 Then ; wait until process ends Return SetError(2, 0, "") Else ; read out the process results in one go $iExit = @extended $sStdOut = StdoutRead($iPID) $sStdErr = BitAND($nFlags, 0x4) ? StderrRead($iPID) : "" EndIf ; return only stderr If $nFlags = 0x4 Then Return SetExtended($iExit, _WinAPI_OemToChar($sStdErr)) ; return array if stdout and stderr should be read separately ElseIf $nFlags = 0x6 Then Local $aRet[2] = [_WinAPI_OemToChar($sStdOut), _WinAPI_OemToChar($sStdErr)] Return SetError($sStdErr = "" ? 0 : 3, $iExit, $aRet) EndIf ; return a string Return SetExtended($iExit, _WinAPI_OemToChar($sStdOut)) EndFunc SOLVE-SMART, Trong and ioa747 3
SOLVE-SMART Posted Wednesday at 08:22 AM Posted Wednesday at 08:22 AM (edited) Hi @AspirinJunkie 👋 , I believe I understood your statement and also the code, but I am a bit confused by the escaping character \ (backslash) which is not a escaping character for powershell - at least as I know so far. In my understanding you have to escape by the same char which you want to escape, in general, or for special chars you have to use ` (backtick). Am I am wrong by this 🤔 ? I personally did always usw "" or `" in the last years which works great. I am sure you will open my eyes with an explanation 🤭 . Best regards Sven Edited Wednesday at 08:22 AM by SOLVE-SMART Trong 1 ==> AutoIt related: 🔗 GitHub, 🔗 Discord Server Spoiler 🌍 Au3Forums 🎲 AutoIt (en) Cheat Sheet 📊 AutoIt limits/defaults 💎 Code Katas: [...] (comming soon) 🎭 Collection of GitHub users with AutoIt projects 🐞 False-Positives 🔮 Me on GitHub 💬 Opinion about new forum sub category 📑 UDF wiki list ✂ VSCode-AutoItSnippets 📑 WebDriver FAQs 👨🏫 WebDriver Tutorial (coming soon)
AspirinJunkie Posted Wednesday at 08:45 AM Posted Wednesday at 08:45 AM 14 minutes ago, SOLVE-SMART said: I am a bit confused by the escaping character \ (backslash) which is not a escaping character for powershell Exactly - it is not escaping for Powershell code. But we're not at that level yet (as I said before: escaping with such nested interpreters is quite annoying...) Escaping per \" is not intended for powershell.exe but for the Windows API function CreateProcessA/W (which is the basis for the AutoIt function Run()). This function must ensure that the parameters are passed correctly to powershell.exe and the syntax is such that double quotation marks are escaped via \". A little clearer with the example: If you execute the following via Run (or from the command line): powershell.exe -Command "Write-Host \"This is a \"\"short\"\" test with multiple \"\"quotes\"\".\"" Then powershell.exe receives the following code as a parameter: Write-Host "This is a ""short"" test with multiple ""quotes""." And your double quotation marks for the powershell are correct again! This is exactly what the _prc_runPS() function does here. SOLVE-SMART and ioa747 2
SOLVE-SMART Posted Wednesday at 09:53 AM Posted Wednesday at 09:53 AM Understood, thanks for the explanation @AspirinJunkie 🤝 . Usually, when I run PS1 snippets or scripts from AutoIt, I simply call powershell with the *.ps1 script file as argument. Then all of these struggles aren't relevant. A very old example, but as showcase okay, can be found here. Best regards Sven Trong 1 ==> AutoIt related: 🔗 GitHub, 🔗 Discord Server Spoiler 🌍 Au3Forums 🎲 AutoIt (en) Cheat Sheet 📊 AutoIt limits/defaults 💎 Code Katas: [...] (comming soon) 🎭 Collection of GitHub users with AutoIt projects 🐞 False-Positives 🔮 Me on GitHub 💬 Opinion about new forum sub category 📑 UDF wiki list ✂ VSCode-AutoItSnippets 📑 WebDriver FAQs 👨🏫 WebDriver Tutorial (coming soon)
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now