2015-08-30 5 views
7

Я пытаюсь выполнить этот простой тестовый скрипт, но окно командной оболочки появляется после того, как я выполнить сценарий .:Скрыть при использовании Exec()

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

strCommand = "cmd /C tasklist" 

Set objExecObject = objShell.Exec(strCommand) 

wscript.echo "Test" 

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

Update

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

strCommand = "cmd /C /Q tasklist" 

Теперь окно показывает только на долю секунды. Но я не хочу, чтобы он вообще появлялся.

ответ

19

Вы всегда будете получать вспышку окна с 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 

The FileSystemObject класс имеет такие методы, как GetSpecialFolder(), чтобы получить путь к папке Windows Temp и GetTempName() для создания временного файла, который можно использовать вместо жесткого кодирования выходного файла, как я «Сделано выше.

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

И, наконец, : VBScript «родные» способы получить список запущенных процессов. Класс WMI Win32_Process, например, может сделать это без необходимости Run/Exec.


Edit:

Для полноты картины следует упомянуть, что ваш сценарий может Перезапуск себя в скрытом окне консоли, где вы можете запустить Exec() молча. К сожалению, это скрытое окно консоли также скроет ваш результат от таких функций, как WScript.Echo(). Кроме того, однако, вы, вероятно, не заметите различий, выполняющих ваш скрипт под cscript против 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 

Конечно, если ваш скрипт ожидает параметры командной строки, те должны были бы быть направлены при перезапуске сценария, а также.


Edit 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") 
+0

Подсказка: Для меня он работал только с 'cmd.exe/C ... 'вместо' cmd/C ... ' – Black

+0

Трубопровод к команде CLIP отлично работает, спасибо. –

5

Вы можете использовать .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 

UPDATE

Я слегка переработанный код, чтобы сделать его более простым:

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 

BTW, здесь VBScript "multithreading" implementation, который использует тот же подход к контейнерам.

+1

Это интересное решение. Вы также можете просто использовать 'Run 'cscript" "" & WScript.ScriptFullName & "" "", 0', чтобы перезапустить скрипт в скрытом окне консоли, где вы можете сделать скрытый 'Exec()', но есть разветвления (например, , 'WScript.Echo' отправит вывод на скрытую консоль вместо окна сообщения). Кроме того, необходимо перенаправить параметры сценария и т. Д. – Bond

+0

Благодарим вас за это решение и за весь комментарий, очень полезно! – Black

2

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

1

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

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

Run Script, как системы без взаимодействия с рабочего стола

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

Run Script, как системы с взаимодействием с рабочего стола

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

+1

Пожалуйста, следуйте за форматированием Markdown во время ответа. [Это] (https://stackoverflow.com/editing-help#comment-formatting) поможет вам. – Yankee