Скрыть окно командной строки при использовании 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