web-dev-qa-db-fra.com

Masquer la fenêtre d'invite de commande lors de l'utilisation de Exec ()

J'essaie d'exécuter ce script de test simple, mais une fenêtre de commande apparaît après l'exécution du script:

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

strCommand = "cmd /C tasklist"

Set objExecObject = objShell.Exec(strCommand)

wscript.echo "Test"

Comment puis-je l'empêcher d'apparaître?

Mettre à jour

J'ai pu l'améliorer avec ce changement de code:

strCommand = "cmd /C /Q tasklist"

Maintenant, la fenêtre ne s'affiche que pendant une fraction de seconde. Mais je ne veux pas que ça apparaisse du tout.

11
Black

Vous aurez toujours un flash de fenêtre avec Exec(). Vous pouvez utiliser Run() à la place pour exécuter la commande dans une fenêtre masquée. Mais vous ne pouvez pas capturer directement le résultat de la commande avec Run(). Vous devez rediriger la sortie vers un fichier temporaire que votre VBScript pourrait ensuite ouvrir, lire et supprimer.

Par exemple:

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 a des méthodes telles que GetSpecialFolder() pour récupérer le chemin du dossier temporaire Windows et GetTempName() pour générer un nom de fichier temporaire que vous pouvez utiliser au lieu de coder en dur un nom de fichier de sortie, comme je l’ai fait plus haut.

Notez également que vous pouvez utiliser l'argument /FO CSV avec tasklist.exe pour créer un fichier CSV, ce qui devrait faciliter son analyse.

Enfin, il existe _ are _ méthodes VBScript "natives" permettant de récupérer la liste des processus en cours d'exécution. WMI's Win32_Process class, par exemple, peut le faire sans avoir besoin de Run/Exec.


Modifier:

Par souci d'exhaustivité, je dois mentionner que votre script peut se relance dans une fenêtre de console masquée où vous pouvez exécuter Exec() en mode silencieux. Malheureusement, cette fenêtre de console masquée masquera également votre sortie de fonctions telles que WScript.Echo(). En dehors de cela, cependant, vous ne remarquerez probablement aucune différence dans l'exécution de votre script sous cscript vs wscript. Voici un exemple de cette méthode:

' 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  

Bien entendu, si votre script attend des paramètres de ligne de commande, ceux-ci devront également être transférés lors de la relance du script.


Edit 2:

Une autre possibilité consiste à utiliser le Presse-papiers de Windows. Vous pouvez diriger la sortie de votre commande vers l'utilitaire clip.exe. Ensuite, récupérez le texte via un nombre quelconque d'objets COM disponibles pouvant accéder au contenu du Presse-papiers. Par exemple:

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

Vous pouvez utiliser la méthode .Exec(), sans mémoire flash de la fenêtre de console, fichiers temporaires et désactivation inattendue de la sortie WScript.Echo. La méthode est légèrement délicate et nécessite de lancer un script lié secondaire. J'ai donc ajouté les commentaires:

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

METTRE &AGRAVE; JOUR

J'ai légèrement retravaillé le code pour le rendre plus simple:

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, voici Implémentation "multithreading" de VBScript qui utilise la même approche conteneur.

6
omegastripes

Quelques bonnes suggestions sont énumérées ci-dessus. Je voudrais faire une autre suggestion qui est plus une solution de contournement. Vous pouvez utiliser Sysinternals Desktops (programme gratuit) pour exécuter votre macro sur un autre bureau de votre même ordinateur. De cette façon, le clignotement peut se produire sur son propre bureau et n'interrompra pas votre travail.

2
Fidel

J'utilise Sysinternals PSEXEC https://docs.Microsoft.com/sv-se/sysinternals/downloads/psexec

Créé un fichier de commandes (dans le même dossier que vbs et exe) qui exécute le script en tant qu'utilisateur système . Je ne peux pas accéder au profil de l'utilisateur et je dois être administrateur local, mais lorsque j'exécute le script sans interaction avec le bureau, il va cacher tous les popups ennuyeux.

Exécuter le script en tant que système sans interaction avec le bureau

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

Exécuter le script en tant que système avec interaction avec le bureau

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

1
Henrik Andersen

Pour masquer les fenêtres de ligne de commande dans VBscipt, utilisezRuninWshShellObject 

Ensuite, pour obtenir le résultat, vous pouvez envoyer ce résultat au fichier texte dans%temp%

Puis lisez ce résultat avecFileSystemObject 

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() 

OR

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()
0
hollopost