Подтвердить что ты не робот

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

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

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

strCommand = "cmd /C tasklist"

Set objExecObject = objShell.Exec(strCommand)

wscript.echo "Test"

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

Обновление

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

strCommand = "cmd /C /Q tasklist"

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

4b9b3361

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

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

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


Edit

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

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


Изменить 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")

Ответ 2

Вы можете использовать .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 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 property with signature name
    Set objPrimary = objContainer.getProperty(strSignature)
    ' quit explorer window to release memory as it no longer needed
    objContainer.Quit
    ' assign the secondary script me object to the primary script 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 me object reference into explorer 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 "многопоточность", которая использует тот же контейнерный подход.

Ответ 3

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

Ответ 4

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

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

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

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

Запустите script как систему со взаимодействием с рабочим столом

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

Ответ 5

Чтобы скрыть окна командной строки в VBscipt, используйте Run in WshShell Object

Затем, чтобы получить результат, вы можете отправить этот результат в текстовый файл в %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()