Catch powershell exception in VBScript - powershell

I'm running PS scripts from VBScript and would like to throw exception in VBScript if there were errors in PS. This is the VBScript code I use to run PowerShell:
Option Explicit
Dim oShell, appCmd
Set oShell = CreateObject("WScript.Shell")
appCmd = "powershell set-executionpolicy unrestricted -force; c:\3.ps1"
oShell.Run appCmd, 4, true
This is my PS script:
throw "I'm exception"
I've tried general VBScript catch:
if( Err.number <> 0 ) then
Err.raise()
end if
but it doesn't seem to work. I would love to use only PS and get rid of VBScript altogether, but it's another application that is running VBs and it only supports VBs. Any ideas?
I could write exception in file in PS and then from VBs check if file exist and throw exception, but I hope there's a better way.
Thanks

In the Powershell script use
exit $MyErrorLevel
Where $MyErrorLevel is your own code you will detect with VBS.
In VBS the WShell object Run method will return the exit code from PowerShell.
Example VBS script:
Option Explicit
Dim oShell, appCmd
Dim intReturnCode
Set oShell = CreateObject("WScript.Shell")
appCmd = "powershell.exe -File c:\Test.ps1"
intReturnCode = oShell.Run(appCmd, 4, true)
MsgBox intReturnCode
Example PS script (C:\Test.ps1):
try {
1 / $null
} catch {
exit 1
}
exit 0

Related

SSIS and Powershell File Lock

I'm using SSIS and Powershell to check if a file is locked or not.
I have the below Expression in a variable called 'Cmd':
"-NoProfile -ExecutionPolicy ByPass -Command \"try { [IO.File]::OpenWrite(‘” + #[User::TestFilePath] + “‘).close();0 } catch {999}"
Which evaluates to this:
-NoProfile -ExecutionPolicy ByPass -Command "try { [IO.File]::OpenWrite(‘” + #[User::TestFilePath] + “‘).close();0 } catch {999}
Using the Execute Process Task I then call the Cmd variable above and have Success and Failure constraints after it. The Process always reports a Success, even I open the file in question, rename or even delete it.
If I then amend to the below, the Task will always fail, even if its not open:
"-NoProfile -ExecutionPolicy ByPass -Command \"try { [IO.File]::OpenWrite(‘” + #[User::TestFilePath] + “‘).close();exit 0} catch {exit 999}"
What am I missing?
If it helps anyone, I found a resolution. Instead of calling the PS script via an expression, I just called the actual PS file via a Process Task with the below in in the PS file:
$file = "\\xxxx\xxxxx\xxxxxxxx\test.log"
try { [IO.File]::OpenWrite($file).close();exit 0 } catch { exit 999}

Why does a PowerShell script not end when there is a non-zero exit code using the call operator?

Why does a PowerShell script not end when there is a non-zero exit code when using the call operator and $ErrorActionPerference = "Stop"?
Using the following example, I get the result managed to get here with exit code 1:
$ErrorActionPreference = "Stop"
& cmd.exe /c "exit 1"
Write-Host "managed to get here with exit code $LASTEXITCODE"
The Microsoft documentation for the call operator does not discuss what should happen when using call operator, it only states the following:
Runs a command, script, or script block. The call operator, also known as the "invocation operator," lets you run commands that are stored in variables and represented by strings. Because the call operator does not parse the command, it cannot interpret command parameters.
Additionally, if this is expected behaviour, is there any other way to have the call operator cause an error rather than let it continue?
The return code is not a PowerShell error - it's seen the same way as any other variable.
You need to then act on the variable and throw an error using PowerShell for you script to see it as a terminating error:
$ErrorActionPreference = "Stop"
& cmd.exe /c "exit 1"
if ($LASTEXITCODE -ne 0) { throw "Exit code is $LASTEXITCODE" }
In almost all my PowerShell scripts, I prefer to "fail fast," so I almost always have a small function that looks something like this:
function Invoke-NativeCommand() {
# A handy way to run a command, and automatically throw an error if the
# exit code is non-zero.
if ($args.Count -eq 0) {
throw "Must supply some arguments."
}
$command = $args[0]
$commandArgs = #()
if ($args.Count -gt 1) {
$commandArgs = $args[1..($args.Count - 1)]
}
& $command $commandArgs
$result = $LASTEXITCODE
if ($result -ne 0) {
throw "$command $commandArgs exited with code $result."
}
}
So for your example I'd do this:
Invoke-NativeCommand cmd.exe /c "exit 1"
... and this would give me a nice PowerShell error that looks like:
cmd /c exit 1 exited with code 1.
At line:16 char:9
+ throw "$command $commandArgs exited with code $result."
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (cmd /c exit 1 exited with code 1.:String) [], RuntimeException
+ FullyQualifiedErrorId : cmd /c exit 1 exited with code 1.

How to return PowerShell variable to VBScript

I have a vbscript to call a PowerShell script in hopes of returning the PowerShell output to an HTA (HTML Application) GUI. Right now I just want to see if I can return the PowerShell output into a MsgBox in the vbscript. I am not having much luck with this.
VBScript Code:
Set shell = CreateObject("WScript.Shell")
return = shell.Run("powershell.exe -executionpolicy bypass -noprofile -file pathToScript\PowerShellToVBA.ps1", , true)
MsgBox return
PowerShell Code:
Clear-Host
return 50
I am trying to keep the return value extremely simple until it works. With this, I would expect the MsgBox to return '50', however it is returning '0' instead. Any ideas what im doing wrong?
I think you just want the exit command to get the return value:
VBScript
pscommand = ".\myscript.ps1; exit $LASTEXITCODE"
cmd = "powershell.exe -noprofile -command " & pscommand
Set shell = CreateObject("WScript.Shell")
rv = shell.Run(cmd, , True)
MsgBox "PowerShell returned: " & rv, vbSystemModal
Powershell
exit 50;
EDIT #1
Or if you want to grab a return string:
VBScript
pscommand = ".\myscript2.ps1"
cmd = "powershell.exe -noprofile -command " & pscommand
Set shell = CreateObject("WScript.Shell")
Set executor = shell.Exec(cmd)
executor.StdIn.Close
MsgBox executor.StdOut.ReadAll
Powershell
Write-Output '***SUCCESS***';
As a followup, using the VBScript above to output a string. In your PS script if you use eg
write-host "Error: some specific thing happened"
to set various error or success messages the
MsgBox executor.StdOut.ReadAll
will display all of what you write via write-host.
A helpful way to display the outcome of the PS script to your VBA user.
Just FYI.

How to set the exit code when throwing an exception

MyScript.ps1:
exit 1
MyThrow.ps1:
throw "test"
Execution in PowerShell:
& ".\MyScript.ps1"
Write-Host $LastExitCode # Outputs 1
Clear-Variable LastExitCode
& ".\MyThrow.ps1"
Write-Host $LastExitCode # Outputs nothing
How do I set a proper exit code when throwing an exception?
You don't. When you throw an exception you expect someone to handle it. That someone would be the one to terminate execution and set an exit code. For instance:
try {
& ".\MyThrow.ps1"
} catch {
exit 1
}
If there is nothing to catch your exception you shouldn't be throwing it in the first place, but exit right away (with a proper exit code).
Becareful:
With Powershell 4 and less:
When an exception is thrown, exit code remains at 0 (unfortunately)
With Powershell 5 and up:
When an exception is thrown, exit code defaults to 1
Actually you can set the code also with throw, just put it before throw.
Contents of the script: Throw.ps1
exit 222
throw "test"
Output:
PS C:\> .\Throw.ps1
PS C:\> $LASTEXITCODE
222
If you run it like this:
powershell .\Throw.ps1
The output will be as follows:
PS C:\> powershell .\Throw.ps1
PS C:\> $LASTEXITCODE
1
But here is the thing, powershell exit codes should be either 0 or 1,
anything else, finally will give you result of 1.
Another interesting thing to mention, if to try $? after you run the script,
if true or false, the result depends of what you want to put in there.
exit 0 --> true, exit 1 --> false
Here it is an example:
Content of the script: Throw_1.ps1
exit 1
throw "test"
Output:
PS C:\> .\Throw_1.ps1
PS C:\> $?
False
Content of the script: Throw_0.ps1
exit 0
throw "test"
Output:
PS C:\> .\Throw_0.ps1
PS C:\> $?
True
As you can see it is just what you need or want to achieve.
Running the mythrow.ps1 inside powershell will set $? to false, and will add to the $error object array. Running it with another powershell process will set $lastexitcode to 1.
PS C:\> powershell mythrow.ps1
PS C:\> $lastexitcode
1

how vbscript read the result file generated by powershell script (called by the vbscript itself) ?

I have one vbscript to execute a powershell script.
The powershell script simply append the result to a file.
Then after the powershell script execution complete and return to vbscript.
The vbscript will read content of the file and display it on console.
Unfortunately the powershell cannot generate the result file to be read by the vbscript.
If I change the line to ErrCode = objShell.run("powershell.exe -file writeFile.Ps1", 0),
The powershell script can generate the result file.
But the vbscript can never display the content of the result file correctly or it is corrupted.
If I open the result file from Notepad, I can view the content correctly.
Do you have any idea what is the problem behind?
Here are the source code:
Source code for readFile.vbs:\
resultFile = "writeFile.Ps1.txt"
set objShell = CreateObject("WScript.Shell")
ErrCode = objShell.run("powershell.exe -nologo -command writeFile.Ps1", 0)
Set objReadFile = CreateObject("Scripting.FileSystemObject")
Set strLines = objReadFile.OpenTextFile(resultFile, 1)
Do While Not strLines.AtEndOfStream
strLine = strLines.ReadLine()
WScript.Echo strLine
Loop
Set objReadFile = Nothing
Source code for writeFile.Ps1:
$ResultFilePath = "writeFile.Ps1.txt"
If (Test-Path $ResultFilePath) {
Remove-Item $ResultFilePath
}
Get-Date | out-file $ResultFilePath
Exit 0
Thanks.
On the Out-File, try using the parameter "-Encoding ASCII". By default Out-File outputs in Unicode.
Either make PowerShell create the file with ASCII encoding as Keith Hill suggested:
Get-Date | Out-File $ResultFilePath -Encoding ASCII
or open it as Unicode in VBScript:
Set strLines = objReadFile.OpenTextFile(resultFile, 1, False, -1)