Hidden PS window via VBS with return code

Viewed 1014

I am trying to use a VBScript helper script to launch a PowerShell script in a hidden window. I have been doing this successfully for some time, but now I want to add getting a return code, and that makes things more complicated. The usual approach for getting a return code is to use .Exec rather than .Run, but then you lose the ability to hide the command window. I found this thread, and based on that and a known working strCMD, I tried

strCMD = strCMD & " > c:\out.txt"
objShell.Run strCMD, 0, True

But that doesn't work. Any suggestions where I am going wrong? Or suggestions on a better way to get a hidden Powershell script with return code? FWIW, I have to target PS 2.0. :(

For completeness, the value of strCMD is

powershell -noLogo -noProfile -file "C:\Program Files\PragmaticPraxis\Resources\TestMessage.ps1" -message:"I'M ALIVE!!!" -message2:"Me too!"

And TestMessage.ps1 is simply

[CmdletBinding()]
param (
    [string]$message,
    [string]$message2
)

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > $null
[System.Windows.Forms.MessageBox]::Show($message)
[System.Windows.Forms.MessageBox]::Show($message2)

EDIT: Thanks to omegastripes' link, I am trying to implement a more general and PowerShell focused version of this implementation, with some code refactor to eliminate the one time calls to functions and awkward forced exits. What I have is this:

Option Explicit
Dim strCmd, strRes, objWnd, objParent, strSignature

If WScript.Arguments.Named.Exists("signature") Then 
    For Each objWnd In CreateObject("Shell.Application").Windows
        If IsObject(objWnd.getProperty(WScript.Arguments.Named("signature"))) Then Exit For
    Next
    Set objParent = objWnd.getProperty(WScript.Arguments.Named("signature"))

    objWnd.Quit
    objParent.strRes = CreateObject("WScript.Shell").Exec(objParent.strCmd).StdOut.ReadAll()
Else
    strCmd = "powershell.exe -noLogo -noProfile -executionPolicy bypass -file ""\\Mac\Px\Support\Px Tools\Dev 3.3.#\_Spikes\TestMessage.ps1"""
    strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
    GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty strSignature, Me
    CreateObject("WScript.Shell").Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0, True

    WScript.Echo strRes
End If

Which in turn calls this PS1

try {
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > $null
    [System.Windows.Forms.MessageBox]::Show("Message test!") > $null
    Write-Host "Success"
} catch {
    Write-Host "Failure"
}

This works in that it does run the PS1, and it is hidden, but I am having trouble getting results back. I have tried Return, Write-Output and Write-Host and none sends the results from the PS1 back to the VBS. I am also having some issues when passing arguments, and I really don't like the For Each loop with a forced Exit internally, but that is polish. The bigger issue is how to consistently get results back from the PS1.

1 Answers

Your powershell script needs to return an exit code through

Exit <int>

Your WScript.Shell's Run method in VBS will then return this code so you need to save it.

Set WshShell = WScript.CreateObject("WScript.Shell")
Result = WshShell.Run(...

So Result now contains the Exit code so you can now either use it or return it again and exit the process.

WScript.Quit(Result)
Related