Search code examples
vbscriptwmi-service

vbscript kill a process with a PID WMIService


I have a folder filled with few vbscripts (say 10) which has to be run sequentially, Instead of running each by clicking on them i want to automate this process. Below is my master.vbs script which runs each vbscript in that folder one by one.

strComputer = "."

Set objFSO = CreateObject("Scripting.FileSystemObject")
currentdirectory = objFSO.GetAbsolutePathName(".")

filedirectory = currentdirectory 

Set objFolder = objFSO.GetFolder(filedirectory)
Dim filestring

Set colFiles = objFolder.Files
For Each objFile in colFiles
    'Wscript.Echo objFile.Name
    filestring = filestring & objFile.Name & ","
Next

'filestring = filestring.Trim().Substring(0, filestring.Length - 1)
filestring = Left(filestring,Len(filestring)-1)


Dim files 
 files = Split(filestring,",")


For Each f In files
'WScript.Echo f

chk = Split(f,".")
If chk(UBound(chk)) = "vbs" then


pat = filedirectory & "\" & f
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
proc = "cmd.exe /c"& Chr(32) &"cscript.exe " & Chr(32) & chr(34) & pat & Chr(34) 
objWMIService.Create proc , null, null, intProcessID

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
    ("Select * From __InstanceDeletionEvent Within 1 Where TargetInstance ISA 'Win32_Process'")

Do Until i = 1
    Set objLatestProcess = colMonitoredProcesses.NextEvent
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
        i = 1
    End If
Loop

'Wscript.Echo "Process has been terminated."
End If
Next

The issue i have here is that the scripts are running perfectly by since the process name is going to be the same for all the scripts (cscript) It doesnt wait for a process to terminate before starting another .

I understand there is just minute tweaking required in my code but im not sure how to go about it. Can some one point out what im doing wrong here ?..


Solution

  • Option Explicit
    
    ' Styles for the window of the started process
    Const SW_NORMAL = 1
    Const SW_HIDE = 0
    
    ' Wait or not for the started process to end
    Const RUN_WAIT = True 
    
    ' Access to the shell
    Dim shell
        Set shell = WScript.CreateObject("WScript.Shell")
    
    Dim oFile
        With WScript.CreateObject("Scripting.FileSystemObject")
            ' Enumerate files in current active directory
            For Each oFile In .GetFolder( shell.CurrentDirectory ).Files 
                ' If it is a VBS script
                If LCase(.GetExtensionName( oFile.Name )) = "vbs" Then 
                    ' But not the current script
                    If oFile.Path <> WScript.ScriptFullName Then 
                        ' Execute it and wait for the process to end
                        shell.Run "cscript.exe """ & oFile.Path & """", SW_NORMAL, RUN_WAIT
                    End If 
                End If 
            Next 
        End With 
    

    For a WMI solution,

    Option Explicit
    
    ' Determine current directory for file retrieval
    Dim currentDirectory 
        currentDirectory = WScript.CreateObject("WScript.Shell").CurrentDirectory
    
    Dim oFile
        With WScript.CreateObject("Scripting.FileSystemObject")
            ' For each file in the current directory
            For Each oFile In .GetFolder( currentDirectory ).Files 
                ' If it is a VBS file
                If LCase(.GetExtensionName( oFile.Name )) = "vbs" Then 
                    ' But not the current script
                    If oFile.Path <> WScript.ScriptFullName Then 
                        ' Execute it via WMI
                        WMIExecute "cscript.exe """ & oFile.Path & """"
                    End If 
                End If 
            Next 
        End With 
    
    Sub WMIExecute( command )
        ' Styles for the window of the started process
        Const SW_NORMAL = 1
        Const SW_HIDE = 0
    
        ' Get a reference to WMI
        Dim wmi
            Set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
    
        ' Configure how the process will be started 
        Dim processConfig    
            Set processConfig = wmi.Get("Win32_ProcessStartup").SpawnInstance_
            processConfig.ShowWindow = SW_NORMAL
    
        ' Prepare a monitor to wait for process termination
        Dim monitor
            Set monitor = wmi.ExecNotificationQuery ( _ 
                "Select * From __InstanceDeletionEvent Within 1 " & _ 
                " Where TargetInstance ISA 'Win32_Process'" _ 
            )
    
        ' Start the indicated process
        Dim processID, retCode
            retCode = wmi.Get("Win32_Process").Create( command, Null, processConfig, processID )
    
            ' The process is started if the return code is 0, else there is a error
            ' see https://msdn.microsoft.com/en-us/library/aa389388%28v=vs.85%29.aspx
            If Not retCode = 0 Then 
                WScript.Echo "ERROR code ["& Hex(retCode) &"] starting ["& command &"]"
                Exit Sub
            End If
    
        ' Wait for process termination, AND, every X seconds (10 in the code) also check
        ' if the process is still running just in case we miss the deletion event. 
        Dim oProcess, keepLooping, endTime, colProcesses
            endTime = Now
            keepLooping = True
    
            Do While keepLooping
                ' Wait for the next process deletion event
                Set oProcess = monitor.NextEvent
    
                ' If it is our event, no need to loop any more
                If oProcess.TargetInstance.ProcessID = processID Then
                    keepLooping = False
                End If
    
                ' If we are still looping, and the timeout has been reached
                ' check if the process still exists
                If keepLooping And (Now >= endTime) Then 
                    Set colProcesses = wmi.ExecQuery ( _ 
                        "Select ProcessID from Win32_Process Where ProcessID = " & processID _ 
                    )
                    ' If no process meets the condition, leave the loop, else repeat
                    ' this check in X seconds
                    If colProcesses.Count < 1 Then 
                        keepLooping = False 
                    Else 
                        endTime = DateAdd("s", 10, Now )
                    End If
                End If 
            Loop
    
    End Sub