通过VBS隐藏PS窗口,返回代码 - 第2轮

时间:2017-10-03 23:56:44

标签: powershell vbscript

我正在尝试使用更新的技术omegastripes提供here来创建帮助程序脚本,以便静默启动PowerShell并获取结果/状态。我做的第一件事是重构,以帮助我理解逻辑。基本上我摆脱了两个函数,一个强制退出,并将它们移动到If / Then / Else。在搜索已识别的窗口时,我还将所有内容都移动到条件中。所以我离开了这个:

Dim strCmd, strRes, objWnd, objParent, strSignature

If WScript.Arguments.Named.Exists("signature") Then WshShellExecCmd
strCmd = "%comspec% /c tasklist"
RunCScriptHidden
WScript.Echo strRes

Sub RunCScriptHidden()
    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
End Sub

Sub WshShellExecCmd()
    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()
    WScript.Quit
End Sub

对此:

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 
            Set objParent = objWnd.getProperty(WScript.Arguments.Named("signature"))
            objWnd.Quit
            objParent.strRes = CreateObject("WScript.Shell").Exec(objParent.strCmd).StdOut.ReadAll()
            Exit For
        End If
    Next
Else
    strCmd = "%comspec% /c tasklist"
    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

这很有效,而我唯一的问题只是对重构的批评。有什么可以改进的吗?事实上,首先是否存在反对重构的争论?

从那时起,我将strCmd修改为

strCmd = "powershell.exe -noLogo -noProfile -executionPolicy bypass -file ""\\Mac\Px\Support\Px Tools\Dev 3.3.#\_Spikes\TestMessage.ps1"" -message:""It's ALIVE!"""

哪个调用此PowerShell

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

到目前为止,这么好。现在从PowerShell获取状态。在这里我应该清楚,最终目标是一个通用启动器,我希望能够捕获任何错误,将它们传递回VBS,并从那里写入错误日志或事件。一些潜在的错误包括传递目标PS1中不存在的参数等。 所以,回到返回的信息,我试过了:

Write-Output "Write-Output"
Write-Host "Write-Host"
Out-Host "Out-Host"

这些实际上都没有被返回。那么,我如何输出到标准输出,以便VBS可以访问该信息?我 THINK 一个方面是原始strCmd在处理时发出标准输出,而PowerShell最后会输出。但这只是对这个问题的一个猜测,所以我会问更多的实验,而不是混乱这是否可能,如果是这样,需要做哪些改变?

1 个答案:

答案 0 :(得分:0)

这是您链接到的代码的改进版本,从vbs调用cmd.exe。我今天做了。将来自cmd.exe的混乱响应拆分为一个数组,而不是将所有内容都放入一个难以解析的字符串中。

此外,如果在执行cmd.exe的过程中发生错误,则有关该错误的消息将在vbs中变为已知。

好吧... Powershell:

Option Explicit
Sub RunCScriptHidden()
    strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
    GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty strSignature, Me
    objShell.Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0, True
End Sub
Sub WshShellExecCmd()
    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() 'simple solution
    Set exec = CreateObject("WScript.Shell").Exec(objParent.strCmd)
    While exec.Status = WshRunning
        WScript.Sleep 20
    Wend
    Dim err
    If exec.ExitCode = WshFailed Then
        err = exec.StdErr.ReadAll
    Else
        output = Split(exec.StdOut.ReadAll,Chr(10))
    End If
    If err="" Then
        objParent.strRes = output(UBound(output)-1) 'array of results, you can: output(0) Join(output) - Usually needed is the last
    Else
        objParent.wowError = err
    End If
WScript.Quit
End Sub
Const WshRunning = 0,WshFailed = 1:Dim cmdExeString,objShell
Dim strCmd, strRes, objWnd, objParent, strSignature, wowError, output, exec

Set objShell = WScript.CreateObject("WScript.Shell"):wowError=False
Dim psCode:psCode="Write-Host 'Hello PS'"
cmdExeString="for /f ""usebackq delims="" %a in (`C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command """ & psCode & """`) do echo %a"
strCmd = "%comspec% /c " & cmdExeString
If WScript.Arguments.Named.Exists("signature") Then WshShellExecCmd
RunCScriptHidden
If wowError=False Then
    objShell.popup(strRes)
Else
    objShell.popup("Error=" & wowError)
End If