【问题标题】:Hide command prompt window when using Exec()使用 Exec() 时隐藏命令提示符窗口
【发布时间】:2015-08-30 14:39:41
【问题描述】:

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

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

strCommand = "cmd /C tasklist"

Set objExecObject = objShell.Exec(strCommand)

wscript.echo "Test"

如何防止它出现?

更新

我能够通过此代码更改对其进行改进:

strCommand = "cmd /C /Q tasklist"

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

【问题讨论】:

    标签: vbscript


    【解决方案1】:

    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() 来生成可以使用的临时文件名,而不是像我在上面所做的那样对输出文件名进行硬编码。

    还请注意,您可以将/FO CSV 参数与tasklist.exe 一起使用来创建一个CSV 文件,这将使解析变得更加容易。

    最后, VBScript“本机”方法来检索正在运行的进程列表。例如,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")
    

    【讨论】:

    • 提示:对我来说,它只适用于 cmd.exe /C ... 而不是 cmd /C ...
    【解决方案2】:

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

    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,它使用相同的容器方法。

    【讨论】:

    • 这是一个有趣的解决方案。您也可以只使用Run "cscript """ &amp; WScript.ScriptFullName &amp; """", 0 在隐藏的控制台窗口中重新启动您的脚本,您可以在其中执行隐藏的Exec(),但会有一些后果(例如,WScript.Echo 会将输出发送到隐藏的控制台而不是消息框)。还有,脚本参数需要转发等等。
    • 感谢您提供此解决方案和所有评论,非常有帮助!
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我使用 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"

      【讨论】:

      • 请按照 Markdown 格式回答。 This 指南应该可以帮助您。
      【解决方案5】:

      在 VBscipt 中隐藏命令行窗口是在 WshShell 对象中使用 Run

      然后要获得结果,您可以将此结果发送到 %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() 
      

      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()
      

      【讨论】:

        【解决方案6】:

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

        它运行一个程序并捕获它的屏幕输出。它适用于 VB6,但不适用于 VBA(挂在 WaitForSingleObject,不知道为什么)。

        【讨论】:

          【解决方案7】:

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

          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.
          

          【讨论】:

            猜你喜欢
            • 2017-07-18
            • 2015-12-11
            • 2015-09-13
            • 2022-11-23
            • 2015-12-27
            • 1970-01-01
            • 2012-11-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多