Can I add a Timeout to a Function, VBscript
-
Wednesday, August 08, 2012 2:16 PM
Hey guys. I have a script that connects to remote PC's and checks for running processes via WMI. The problem is, every once in a while there is a system out there that seems to hang my script at the WMI check. There is no error produced that I can seem to catch. I am wondering if there is a way to make the "connect to WMI" function" in my script timeout after 30 seconds and then continue with the rest of the script? Here is my code:
Function ConnectWMI(CompName) ProcessList=Null OSNameList=Null OSArchList=Null oLogFile.Write Now()&" - "& "Connecting to WMI on "&CompName&"..." Set oWMIService=GetObject("winmgmts:"_ & "{impersonationLevel=impersonate}!\\" & CompName & "\root\cimv2") If Err.Number <> 0 Then oLogFile.WriteLine "WMI Connection error #"&Err.Number&" on "&CompName&": "&Err.Description WMIconn=False ErrorExists=True oLogFile.WriteLine Now()&" - "&"ERROR CONDITION EXISTS...WMI FAILED" ConnectWMI=False Err.Clear Else oLogFile.WriteLine "WMI connection to "&CompName&" was successfull. Querying WMI for running processes." Set ProcessList=GetObject("winmgmts://"&CompName).InstancesOf("win32_process") WMIconn=True ConnectWMI=True End If End FunctionIt seems to get stuck at the Set ProcessList=GetObject("winmgmts://"&CompName).InstancesOf("win32_process") statement every once in a while on the odd computer.
Any ideas? thanks in advance.
All Replies
-
Wednesday, August 08, 2012 2:26 PM
That means it i s time to fix your WMI on those computers. The call will timeout in 60 secods for any computer where WMI is working correctly or for any computer that does not respond.
¯\_(ツ)_/¯
-
Wednesday, August 08, 2012 2:31 PM
Yes, I am aware that WMI needs to be fixed on the computer that causes my script to hang. But I need the script to move on from the problem computer and continue checking the other computers in the list.
I'm not sure what you mean by "the call will timeout in 60 seconds"...obviously that is not happening with my script, hence my original question.
-
Wednesday, August 08, 2012 3:45 PM
Sorry but that is a well know deadlock. There is no way to timeout a deadlock.
In many cases it is just extremly slow response for a very large query.
If you succeed on teh conenct then the query can lock. The connect will always timeout.
YOU can try adding a query timeout but that has not been successful when the remote or local WMI are corrupt.
With PowerShell this is easy:
$wmi = [wmisearcher]''
#Set timeout for 10 seconds
$wmi.options.timeout = "0:0:10"In VBS we can do this:
server = "localhost"
namespace = "root\cimv2"
UserName = ""
Password = ""
'iSecFlag 0 '= forever
iSecFlag = 128 '= (2 minutes) wbemConnectFlagUseMaxWaitset loc = CreateObject("WbemScripting.SWbemLocator" )
Set WBemServices = loc.ConnectServer(Server, Namespace,UserName,password,,,iSecFlag)You can also change your query from a Get to an ExecQuery:
Set ProcessList = WBemServices.ExecQuery("select * from win32_process",,48)
Note the 48 in the flags field.
¯\_(ツ)_/¯
- Marked As Answer by IamMredMicrosoft Employee, Owner Tuesday, August 14, 2012 4:03 AM
-
Thursday, August 30, 2012 7:50 PMThanks jrv! sorry it took me so long to get back to you. I will try out the code you suggested...but I'm wondering: Is there a way to 'catch' the 2 minute timeout event if it happens?
-
Thursday, August 30, 2012 8:25 PM
Thanks jrv! sorry it took me so long to get back to you. I will try out the code you suggested...but I'm wondering: Is there a way to 'catch' the 2 minute timeout event if it happens?
It throws an exception. Use try/catch.
¯\_(ツ)_/¯
-
Thursday, August 30, 2012 9:31 PM
So I have tried out your code as suggested, unfortunately the script still hangs indefinitely at the line:
Set ProcessList= WbemServices.ExecQuery("select * from win32_process",,16)
It seems I am able to connect to the WMI namespace on the PC no problem, but as soon as I try to query WMI it hangs the script.
Is there any other way you can think of to catch a hung WMI query and allow the script to continue? Maybe I have to learn some PowerShell.
thanks again for your help.
-
Thursday, August 30, 2012 9:42 PM
So I have tried out your code as suggested, unfortunately the script still hangs indefinitely at the line:
Set ProcessList= WbemServices.ExecQuery("select * from win32_process",,16)
It seems I am able to connect to the WMI namespace on the PC no problem, but as soon as I try to query WMI it hangs the script.
Is there any other way you can think of to catch a hung WMI query and allow the script to continue? Maybe I have to learn some PowerShell.
thanks again for your help.
Which is whty I posted this:
Sorry but that is a well know deadlock. There is no way to timeout a deadlock. In many cases it is just extremly slow response for a very large query. If you succeed on the conenct then the query can lock. The connect will always timeout. You can try adding a query timeout but that has not been successful when the remote or local WMI are corrupt.
Please read that carefully. You have a corrupt WMI system that needs to be fixed. That is not a scripting issue.
I have never seen a query issue with Win32_process except when WMI was corrupt or teh system was corrupt.
Fix your problem and the issue will go away.
Running sepearate queries as separate jobs wil allow you to monitor the job and time it out. This is not WMI that is doing this but is you and your script. WMI will still hang.
So process the call to the function as a job with start-job or other call
¯\_(ツ)_/¯
-
Thursday, August 30, 2012 10:01 PM
Sorry jrv, I thought the code you provided was meant as a possible solution to the 'well known deadlock'.
I understand I need to fix WMI on the system the script is hanging on. I can fix WMI on the system in question, but eventually another system in our company will have a similar issue in the future. Also, fixing WMI would be handled by our desktop support team, but they won't know they have anything to fix unless I can catch the problem and alert them about it. But this script is ran as a non-interactive scheduled task from a server. So if the script just hangs, there is no way to know something is wrong unless I manually go check on the status of the script, thus defeating the purpose of my automated task!
Anyways...so, your suggestion of "process the call to the function as a job with start-job or other call" could potentially work around the 'known deadlock' issue? If so, could you elaborate?
thanks again for your help and patience!
-
Thursday, August 30, 2012 10:11 PM
Nothing to el;aborate omn. Just run the function as a jopb/ Sen the copmputername as the argument and use the computer name to name the job. You then just need to monitor the jobs. If one hangs just kill it and report teh failure.
¯\_(ツ)_/¯
-
Thursday, August 30, 2012 10:39 PM
Thats exactly what I need some more info on..."just run the function as a job". I am not sure how to do this in vbscript? I have been googling and "run function as job" doesn't not return much help.
I am already calling my ConnectWMI(ComputerName) function from another sub/function in the script and passing the computer name. Are you saying I need to put my WMIQuery in another .vbs file and call it from the first vbs? Like:
QueryResult = Wscript.Shell.Run "cscript.exe QueryWMI.vbs",True Wscript.sleep 6000 If QueryResult<>0 Then 'QueryWMI.vbs has not finished yet 'Kill the QueryWMI.vbs process, somehow(!?) Else 'QueryWMI.vbs finished in 60 seconds End If
Thanks again for your help and sorry for being such a noob!
-
Thursday, August 30, 2012 10:49 PM
Use PowerSHell. YOU can't do it with VBScript.
¯\_(ツ)_/¯
-
Thursday, August 30, 2012 11:35 PM
Thats exactly what I need some more info on..."just run the function as a job". I am not sure how to do this in vbscript? I have been googling and "run function as job" doesn't not return much help.
I am already calling my ConnectWMI(ComputerName) function from another sub/function in the script and passing the computer name. Are you saying I need to put my WMIQuery in another .vbs file and call it from the first vbs? Like:
QueryResult = Wscript.Shell.Run "cscript.exe QueryWMI.vbs",True Wscript.sleep 6000 If QueryResult<>0 Then 'QueryWMI.vbs has not finished yet 'Kill the QueryWMI.vbs process, somehow(!?) Else 'QueryWMI.vbs finished in 60 seconds End If
Thanks again for your help and sorry for being such a noob!
You can do it in VB Script by creating an auxiliary WMI process and monitoring it for x seconds. If it terminates successfully, do the real thing. If it hangs, get the main process to kill it, then move on to the next machine.
Set oWshShell = CreateObject("WScript.Shell")
Set oFSO = CreateObject("Scripting.FileSystemObject")
sSemaphore = oWshShell.ExpandEnvironmentStrings("%temp%\Semaphore.txt")
iTimeout = 10 'seconds
'If the parameter count is 0, process all PCs
'If it is > 0, check if the PC can be accessed with WMI
If WScript.Arguments.count = 0 Then
Set oFile = oFSO.OpenTextFile("d:\PCs.txt", 1, False)
aPCs = Split(oFile.ReadAll, VbCrLf)
oFile.Close
For Each sPC In aPCs
Process(sPC)
Next
Else
TestWMI(WScript.Arguments(0))
WScript.Quit
End If
'Process one PC
'1. Create a semaphore file.
'2. Invoke this script again with two parameters: The PC name plus a bar (|).
' Use the Run method but do not wait for it to end.
'3. Check during the timeout period if the semaphore file still exists.
' If it does not, continue with the WMI code.
'4. If the timeout expires and the file still exists, kill the WMI test
' process, then exit to continue with the next PC.
Function Process(CompName)
WScript.Echo "Checking", CompName
Set oFile = oFSO.CreateTextFile(sSemaphore, True)
oFile.close
oWshShell.Run "wscript.exe """ & WScript.ScriptFullName & """ " & CompName & " |", 0, False
i = 0
s = Second(Time())
Do
if not ofso.FileExists(sSemaphore) then Exit Do
If s <> Second(Time()) Then
i = i + 1
if i > iTimeout then Exit Do
s = Second(Time())
End If
Loop
If oFSO.FileExists(sSemaphore) Then
Set oWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = oWMIService.ExecQuery("Select * From Win32_Process")
For Each oItem In colItems
If InStr(oItem.CommandLine, "|") > 0 Then
WScript.Echo "Killing process", oItem.ProcessId
oItem.Terminate
Exit Function
End If
Next
Else
ConnectWMI(CompName)
End If
End Function
Function ConnectWMI(CompName)
WScript.Echo "Processing", CompName
Set oLogFile = oFSO.CreateTextFile("d:\Log.txt", True)
ProcessList=Null
OSNameList=Null
OSArchList=Null
oLogFile.Write Now()&" - "& "Connecting to WMI on "&CompName&"..."
Set oWMIService=GetObject("winmgmts:"_
& "{impersonationLevel=impersonate}!\\" & CompName & "\root\cimv2")
If Err.Number <> 0 Then
oLogFile.WriteLine "WMI Connection error #"&Err.Number&" on "&CompName&": "&Err.Description
WMIconn=False
ErrorExists=True
oLogFile.WriteLine Now()&" - "&"ERROR CONDITION EXISTS...WMI FAILED"
ConnectWMI=False
Err.Clear
Else
oLogFile.WriteLine "WMI connection to "&CompName&" was successfull. Querying WMI for running processes."
Set ProcessList=GetObject("winmgmts://"&CompName).InstancesOf("win32_process")
WMIconn=True
ConnectWMI=True
End If
End Function
Function TestWMI(CompName)
Set oWMIService=GetObject("winmgmts:"_
& "{impersonationLevel=impersonate}!\\" & CompName & "\root\cimv2")
Set ProcessList = GetObject("winmgmts://" & CompName).InstancesOf("win32_process")
oFSO.DeleteFile(sSemaphore)
End Function
-
Friday, August 31, 2012 12:00 AM
Powershell does it much more easily but go ahead.
¯\_(ツ)_/¯
-
Friday, August 31, 2012 5:49 AM
Agreed. On the other hand it CAN be done with VB Script, contrary to what you thought initially.Powershell does it much more easily but go ahead.
¯\_(ツ)_/¯
-
Friday, August 31, 2012 9:49 AM
Agreed. On the other hand it CAN be done with VB Script, contrary to what you thought initially.Powershell does it much more easily but go ahead.
¯\_(ツ)_/¯
It makes more sense to fix WMI.
I used to have that issue on more than onenetork, Since fixing WMI and properly configuring the networks I have never seen that problem again.
¯\_(ツ)_/¯

