2015-08-30 20 views
7

Sto cercando di eseguire questo semplice script di prova, ma una finestra di shell dei comandi viene visualizzato dopo aver eseguire lo script .:Nascondi quando si utilizza Exec()

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

strCommand = "cmd /C tasklist" 

Set objExecObject = objShell.Exec(strCommand) 

wscript.echo "Test" 

Come posso evitare che si rivelando?

Aggiornamento

sono stato in grado di migliorare con questa modifica del codice:

strCommand = "cmd /C /Q tasklist" 

Ora la finestra mostra solo fino a una frazione di secondo. Ma io non voglio che si mostri affatto.

risposta

19

Hai sempre intenzione di ottenere una finestra con Exec(). È possibile utilizzare Run() per eseguire il comando in una finestra nascosta. Ma non è possibile acquisire direttamente l'output del comando con Run(). Dovresti reindirizzare l'output su un file temporaneo che il tuo VBScript potrebbe quindi aprire, leggere ed eliminare.

Ad esempio:

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 

La classe FileSystemObject ha metodi come GetSpecialFolder() per recuperare il percorso della cartella temporanea di Windows e GetTempName() per generare un nome di file temporaneo che è possibile utilizzare invece di hardcoding un nome di file di output come ho ho fatto sopra.

Si noti inoltre che è possibile utilizzare l'argomento /FO CSV con tasklist.exe per creare un file CSV che dovrebbe semplificare l'analisi.

Infine, ci sono VBScript "nativo" modi per recuperare l'elenco dei processi in esecuzione. La classe Win32_Process di WMI, ad esempio, può eseguire questa operazione senza la necessità di Run/Exec.


Edit:

Per ragioni di completezza, vorrei ricordare che lo script può stesso rilancio in una finestra di console nascosta in cui è possibile eseguire Exec() silenzio. Sfortunatamente, questa finestra della console nascosta nasconderà il tuo output da funzioni come WScript.Echo(). A parte questo, tuttavia, probabilmente non noterai alcuna differenza nell'esecuzione del tuo script sotto cscript rispetto a wscript. Ecco un esempio di questo metodo:

' 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 

Naturalmente, se il vostro script si aspetta che i parametri della riga di comando, chi avrebbe bisogno di essere inoltrato quando rilanciare lo script come bene.


Edit 2:

Ancora un'altra possibilità è quella di utilizzare la clipboard di Windows. È possibile reindirizzare l'output del comando all'utilità clip.exe. Quindi, recuperare il testo tramite qualsiasi numero di oggetti COM disponibili che possono accedere ai contenuti degli Appunti.Per esempio:

' 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

Suggerimento: per me ha funzionato solo con 'cmd.exe/C ... 'invece di' cmd/C ... ' – Black

+0

La connessione al comando CLIP funziona meravigliosamente, grazie. –

5

È possibile utilizzare .Exec() metodo, senza finestra console flash, file temporanei e inaspettato muting WScript.Echo uscita. Il metodo è un po 'complicato, e richiede di lanciare lo script collegato secondaria, così ho aggiunto i commenti:

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

ho leggermente rielaborato il codice per renderlo più semplice:

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, ecco VBScript "multithreading" implementation che utilizza lo stesso approccio contenitore.

+1

È una soluzione interessante. Puoi anche usare "Esegui" cscript "" "& WScript.ScriptFullName e" "" ", 0' per riavviare lo script in una finestra della console nascosta dove puoi eseguire un' Exec() nascosto ma ci sono delle ramificazioni (es. , 'WScript.Echo' invierà l'output a una console nascosta anziché a una finestra di messaggio). Inoltre, i parametri dello script devono essere inoltrati, ecc. – Bond

+0

Grazie per questa soluzione e per tutti i commenti, molto utile! – Black

2

Alcuni ottimi suggerimenti sono elencati sopra. Mi piacerebbe fare un altro suggerimento che è più di una soluzione. È possibile utilizzare Sysinternals Desktops (un programma gratuito) per eseguire la macro su un altro desktop sulla stessa macchina. In questo modo il flashing può accadere sul proprio desktop e non interromperà il tuo lavoro.

1

Io uso Sysinternals PSEXEC https://docs.microsoft.com/sv-se/sysinternals/downloads/psexec

Creato un file batch (nella stessa cartella dei VBS e exe-file) che esegue lo script come utente del sistema. Non riesco ad accedere al profilo utente e devo essere amministratore locale ma quando eseguo lo script senza interazione con il desktop nasconderà tutti i fastidiosi popup.

Esegui script come il sistema senza l'interazione con il desktop

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

Esegui script come il sistema con l'interazione con il desktop

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

+1

Seguire la formattazione Markdown mentre si risponde. [Questa] (https://stackoverflow.com/editing-help#comment-formatting) guida dovrebbe aiutarti. – Yankee