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.
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")
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.
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.
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"
Pour masquer les fenêtres de ligne de commande dans VBscipt, utilisezRun
inWshShell
Object
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()