Скрыть окно командной строки при использовании Exec()

Я пытаюсь выполнить этот простой тестовый скрипт, но после выполнения скрипта появляется окно командной оболочки.:

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

strCommand = "cmd /C tasklist"

Set objExecObject = objShell.Exec(strCommand)

wscript.echo "Test"

Как я могу предотвратить его появление?

обновление

я смог улучшить его с помощью этого изменения кода:

strCommand = "cmd /C /Q tasklist"

теперь окно отображается только на долю секунды. Но я вовсе не хочу, чтобы это обнаружилось.

5 ответов


вы всегда будете получать вспышку окна с Exec(). Вы можете использовать выполнить команду в скрытом окне. Но вы не можете напрямую захватить выходные данные команды с помощью 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 temp и GetTempName() чтобы создать временное имя файла, которое вы можете использовать вместо жесткого кодирования выходного имени файла, как я сделал выше.

также обратите внимание, что вы можете использовать с tasklist.exe чтобы создать CSV-файл, который должен сделать его разбор намного проще.

наконец, are VBScript "native" способы получения списка запущенных процессов. В WMI это Win32_Process класс, например, может сделать это без необходимости Run/Exec.


редактировать:

для полноты, я должен упомянуть, что ваш сценарий can перезапустить себя в скрытом окне консоли, где вы можете запустить Exec() молча. К сожалению, это скрытое окно консоли также скроет ваш вывод из таких функций, как WScript.Echo(). Помимо этого, однако, вы, вероятно, не заметите никаких различий в работе вашего скрипта под cscript vs wscript. Вот пример этого метод:

' 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")

можно использовать .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 "многопоточность" который использует тот же контейнер подход.


некоторые большие предложения перечислены выше. Я хотел бы сделать еще одно предложение, которое является скорее обходным путем. Вы можете использовать Рабочие Столы Sysinternals (бесплатная программа) для запуска макроса на другом рабочем столе на том же компьютере. Таким образом, мигание может происходить на собственном рабочем столе и не прерывать вашу работу.


Я использую Sysinternals PSEXEC https://docs.microsoft.com/sv-se/sysinternals/downloads/psexec

создал пакетный файл (в той же папке, что и vbs и exe-файл), который запускает скрипт как системный пользователь. Я не могу получить доступ к профилю пользователя, и мне нужно быть локальным администратором, но когда я запускаю скрипт без взаимодействия с рабочим столом, он скроет все раздражающие всплывающие окна.

запустить скрипт как систему без взаимодействия с столе

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

запустить скрипт как систему с взаимодействием с desktop

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


чтобы скрыть окна командной строки в 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() 

или

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 //nologo """ & 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() 'get the Explorer process ID