5

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.

Gordon
  • 6,257
  • 6
  • 36
  • 89
  • 1
    PowerShell.exe -windowstyle hidden ? – Sage Pourpre Sep 30 '17 at 16:14
  • Sage, so far as I can tell, that doesn't work across all versions of PowerShell. – Gordon Sep 30 '17 at 16:17
  • Well, I can't confirm through the documentation for Powershell 2.0 although I saw mentions it should work but for Powershell 3.0 to 6, it does. https://learn.microsoft.com/en-us/powershell/scripting/core-powershell/console/powershell.exe-command-line-help?view=powershell-5.1 – Sage Pourpre Sep 30 '17 at 16:31
  • Yes, I am pretty sure it was a new feature, and doesn't work in 2.0 and earlier. And I am constrained to support for Windows7/PS 2.0 unfortunately. Though maybe I should implement this differently on older vs newer OSs. – Gordon Sep 30 '17 at 16:49
  • 1
    Powershell 2.0 is pretty much deprecated in a few weeks time. So if upgrading to PS5 solves your issue, you have another reason to do so. https://redmondmag.com/articles/2017/07/24/microsoft-ending-windows-powershell-2-support.aspx – jessehouwing Sep 30 '17 at 18:08
  • I might update, but I can assure you that the 80+% of Architects who are still using Windows 7 won't, and since they are my customers, I have to support PS 2.0, and do what I can to help them move forward. I might just tell folks that they have to either update or let users seem DOSsy looking windows. Maybe that will help. ;) – Gordon Sep 30 '17 at 18:11
  • [This answer](https://stackoverflow.com/a/32302212/2165759) might be helpful. – omegastripes Sep 30 '17 at 20:12
  • So, no way to do the simpler (in my mind) version and run PS from a VBS with results piped to a file? That seems a whole lot easier than spawning a second hidden script, that still needs to gather results and return that to the originating script. – Gordon Oct 01 '17 at 12:21
  • Instead of using True at the end, have you tried using False? True is only needed if you are waiting for the return value. I see you are dumping it to a file unless your script wants to wait until the command completes to read the file. Only other way is to have it hide itself with native code but will still doing a brief pop of the console window before hiding. – Parrish Oct 01 '17 at 21:18
  • Parrish, I am waiting when I am trying to pipe results to a file, because the whole point is to get the results of the PS so I can report those results to the user. If I don't wait, I might as well just keep using .Run which works. – Gordon Oct 02 '17 at 11:17
  • So, I tried the technique omegastripes linked to, and it works as provided in that thread, running a specific command line utility, but (as revised in my OP) I can't get it to work for a PS1, even a super simplified one. – Gordon Oct 02 '17 at 11:19
  • OK, making some progress, but having issues with getting results back, as noted in the OP now. – Gordon Oct 02 '17 at 17:12
  • Also, a comment on -windowstyle hidden. it turns out this is ONLY for PS 2.0, and it doesn't work anyway. All it really does is automatically hide (or minimize, or whatever) the console window. Which I can do in code myself. And you still get a flash of console before it's minimized or hidden. So, basically a junk implementation, and probably why it got removed in subsequent version. Would have preferred it got fixed rather than removed, but oh well. – Gordon Oct 02 '17 at 17:27
  • omegastripes, I started a new thread here (https://stackoverflow.com/questions/46555113/hidden-ps-window-via-vbs-with-return-code-round-2) since refactoring your code for generic PowerShell use seems like a new topic. I wonder if you might be able to comment in that thread, on both the refactor I did, and your thoughts on making this work with powershelgl rather than a command line utility with async output. Thanks! – Gordon Oct 05 '17 at 11:19
  • I believe there's no way to return to VBS anything besides an exit code. There's a nice article about returning exit code from a PS script https://weblogs.asp.net/soever/returning-an-exit-code-from-a-powershell-script – montonero Dec 20 '18 at 10:06

1 Answers1

1

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)
Ultimate Luki
  • 224
  • 1
  • 6