使用Exec()时隐藏命令提示符窗口

时间:2015-08-30 14:39:41

标签: vbscript

我正在尝试执行这个简单的测试脚本,但是在执行脚本后出现了命令shell窗口。:

Set objShell = WScript.CreateObject("WScript.Shell")

strCommand = "cmd /C tasklist"

Set objExecObject = objShell.Exec(strCommand)

wscript.echo "Test"

如何防止它出现?

更新

我可以通过此代码更改来改进它:

strCommand = "cmd /C /Q tasklist"

现在窗口只显示一瞬间。但我不希望它出现。

8 个答案:

答案 0 :(得分:29)

您总是会使用Exec()获得一个窗口闪烁。您可以使用Run()代替在隐藏窗口中执行命令。但是您无法使用Run()直接捕获命令的输出。您必须将输出重定向到VBScript随后可以打开,读取和删除的临时文件。

例如:

With CreateObject("WScript.Shell")

    ' Pass 0 as the second parameter to hide the window...
    .Run "cmd /c tasklist.exe > c:\out.txt", 0, True

End With

' Read the output and remove the file when done...
Dim strOutput
With CreateObject("Scripting.FileSystemObject")

    strOutput = .OpenTextFile("c:\out.txt").ReadAll()
    .DeleteFile "c:\out.txt"

End With

FileSystemObject类有像GetSpecialFolder()这样的方法来检索Windows临时文件夹和GetTempName()的路径,以生成一个临时文件名,你可以使用它而不是硬编码输出文件名为I&# 39;已完成上述工作。

另请注意,您可以将/FO CSV参数与tasklist.exe一起使用来创建CSV文件,这样可以更轻松地解析它。

最后, VBScript" native"检索正在运行的进程列表的方法。例如,WMI的Win32_Process课程可以在不需要Run/Exec的情况下完成此任务。

修改

为了完整起见,我应该提一下,您的脚本可以在隐藏的控制台窗口中重新启动,您可以在其中静默运行Exec()。不幸的是,这个隐藏的控制台窗口也会隐藏WScript.Echo()等函数的输出。但是,除此之外,您可能无法注意到cscriptwscript下运行脚本的任何差异。以下是此方法的一个示例:

' If running under wscript.exe, relaunch under cscript.exe in a hidden window...
If InStr(1, WScript.FullName, "wscript.exe", vbTextCompare) > 0 Then
    With CreateObject("WScript.Shell")
        WScript.Quit .Run("cscript.exe """ & WScript.ScriptFullName & """", 0, True)
    End With
End If

' "Real" start of script. We can run Exec() hidden now...
Dim strOutput
strOutput = CreateObject("WScript.Shell").Exec("tasklist.exe").StdOut.ReadAll()

' Need to use MsgBox() since WScript.Echo() is sent to hidden console window...
MsgBox strOutput  

当然,如果您的脚本需要命令行参数,那么在重新启动脚本时也需要转发这些参数。

编辑2

另一种可能性是使用Windows剪贴板。您可以将命令的输出传递给clip.exe实用程序。然后,通过可以访问剪贴板内容的任意数量的可用COM对象检索文本。例如:

' Using a hidden window, pipe the output of the command to the CLIP.EXE utility...
CreateObject("WScript.Shell").Run "cmd /c tasklist.exe | clip", 0, True

' Now read the clipboard text...
Dim strOutput
strOutput = CreateObject("htmlfile").ParentWindow.ClipboardData.GetData("text")

答案 1 :(得分:6)

您可以使用.Exec()方法,无需控制台窗口闪存,临时文件和意外的WScript.Echo输出静音。该方法有点棘手,需要启动辅助链接脚本,所以我添加了注释:

Option Explicit

Dim objDummy, strSignature, objPrimary, objSecondary, objContainer, objWshShell, objWshShellExec, strResult

' this block is executed only in the secondary script flow, after primary script runs cscript
If WScript.Arguments.Named.Exists("signature") Then
    ' retrieve signature string from argument
    strSignature = WScript.Arguments.Named("signature")
    Do
        ' loop through all explorer windows
        For Each objContainer In CreateObject("Shell.Application").Windows
            ' check if the explorer's property with signature name contains the reference to the live script
            If ChkVBScriptTypeInfo(objContainer.getProperty(strSignature)) Then
                Exit Do
            End If
        Next
        WScript.Sleep 10
    Loop
    ' create shell object within secondary script
    Set objWshShell = CreateObject("WScript.Shell")
    ' retrieve the primary script me object reference from explorer's property with signature name
    Set objPrimary = objContainer.getProperty(strSignature)
    ' quit explorer window to release memory as it's no longer needed
    objContainer.Quit
    ' assign the secondary script me object to the primary script's variable
    Set objPrimary.objSecondary = Me
    ' emtpy loop while primary script is working
    Do While ChkVBScriptTypeInfo(objPrimary)
        WScript.Sleep 10
    Loop
    ' terminate secondary
    WScript.Quit
End If

' the code below is executed first in the primary script flow 
' create signature string
strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
' create new hidden explorer window as container to transfer a reference between script processes
Set objContainer = GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
' put this script's me object reference into explorer's property
objContainer.putProperty strSignature, Me
' launch new secondary process of the same script file via cscript.exe with hidden console window, providing signature string in named argument to identify host script
CreateObject("WScript.Shell").Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0
' wait until secondary script has been initialized and put his me object into this script variable
Do Until ChkVBScriptTypeInfo(objSecondary)
    WScript.Sleep 10
Loop

' here is your code starts...
' create exec object within hidden console window of secondary script, execute cmd instruction
Set objWshShellExec = objSecondary.objWshShell.Exec("%comspec% /c tasklist")
' read cmd output
strResult = objWshShellExec.StdOut.ReadAll()
WScript.Echo strResult
' ...


' utility check if me object is live
Function ChkVBScriptTypeInfo(objSample)
    On Error Resume Next
    If TypeName(objSample) <> "VBScriptTypeInfo" Then
        ChkVBScriptTypeInfo = False
        Exit Function
    End If
    ChkVBScriptTypeInfo = True
End Function

<强>更新

我稍微修改了代码以使其更直接:

Option Explicit

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

顺便说一句,这里是使用相同容器方法的VBScript "multithreading" implementation

答案 2 :(得分:2)

上面列出了一些很棒的建议。我想再提一个建议,这更像是一种解决方法。您可以使用Sysinternals Desktops(免费程序)在同一台计算机上的另一台桌面上运行宏。这样闪烁就可以在自己的桌面上发生,不会打断你的工作。

答案 3 :(得分:1)

我使用Sysinternals PSEXEC https://docs.microsoft.com/sv-se/sysinternals/downloads/psexec

创建一个批处理文件(与vbs和exe文件位于同一文件夹中),以文件系统用户身份运行脚本。 我无法访问用户配置文件,我需要成为本地管理员,但是当我运行脚本而不与桌面交互时,它将隐藏所有烦人的弹出窗口。

将脚本作为系统运行而不与桌面交互

"%~dp0PsExec.exe" -s wscript.exe "%~dp0MyScript.vbs"

将脚本作为与桌面交互的系统运行

"%~dp0PsExec.exe" -s -i wscript.exe "%~dp0MyScript.vbs"

答案 4 :(得分:0)

要在VBscipt中隐藏命令行窗口,请使用 Run 对象

中的 WshShell

然后要获取结果,可以将该结果发送到 %temp%

中的文本文件中

然后使用 FileSystemObject

读取此结果
Set Sh = CreateObject("WScript.Shell")
tFile=Sh.ExpandEnvironmentStrings("%Temp%")&"\t.txt"
Sh.Run "cmd.exe /C tasklist > """&tFile&""" ",0,False
Wscript.echo CreateObject("Scripting.FileSystemObject").openTextFile(tFile).readAll() 

OR

If StrComp(right(WScript.FullName,11),"wscript.exe",1) = 0 Then     '' get only wscript.exe from "c:\windows\system32\wscript.exe" to compere with wscript.exe
  WScript.Quit CreateObject("WScript.Shell").Run("cscript.exe """ & WScript.ScriptFullName & """", 0, False)
End If
MsgBox CreateObject("WScript.Shell").Exec("cmd.exe /c tasklist /v /fi ""imagename EQ explorer*"" /FO LIST | FIND ""PID:""").StdOut.ReadAll()

答案 5 :(得分:0)

这里是Windows脚本宿主的替代方法:Run a batch program(.bat) through a Visual Basic 6.0

它运行一个程序并捕获其屏幕输出。它在VB6中对我有效,但在VBA中不起作用(挂在WaitForSingleObject上,不知道为什么)。

答案 6 :(得分:0)

尝试了主要解决方案但没有成功之后,我可以使用以下代码解决问题:

With CreateObject("WScript.Shell")

    .Run "cmd /c start /b tasklist.exe > c:\out.txt", 0, True

End With

真正的问题是控制台帮助显示为“ / b”:

START ["title"] [/D path] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
      [/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
      [/NODE <NUMA node>] [/AFFINITY <hex affinity mask>] [/WAIT] [/B]
      [command/program] [parameters]

    "title"     Title to display in window title bar.
    path        Starting directory.
    B           Start application without creating a new window. The
                application has ^C handling ignored. Unless the application
                enables ^C processing, ^Break is the only way to interrupt
                the application.

答案 7 :(得分:0)

Omegastripes的答案让我着迷,并稍微改善了本已漂亮的代码。

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

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

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,i,name,objShell
Dim strCmd, strRes, objWnd, objParent, strSignature, wowError, output, exec

Set objShell = WScript.CreateObject("WScript.Shell"):wowError=False
cmdExeString="echo Hello World."
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