Terminate vbscript after x minutes - time

I am working on a script with vbscript, and I would like it to terminate itself after x number of minutes.
I was thinking something like grabbing the time when the script starts and then keeping the whole thing in a loop until the time is x number of minutes after the start time, but I need it to keep checking in the background, and not just wait until a loop is complete.
I want a message or something that notifies the user they took too long, which I can do myself.
Is there any way to keep track of the time in the background, or will it be a bit of a drawn-out process to determine it?

Re-launching the script with //T:xx as suggested by Ekkehard.Horner is probably your best option. Another, slightly different, approach could look like this:
Const Timeout = 4 'minutes
timedOut = False
If WScript.Arguments.Named.Exists("relaunch") Then
'your code here
Else
limit = DateAdd("n", Timeout, Now)
cmd = "wscript.exe """ & WScript.ScriptFullName & """ /relaunch"
Set p = CreateObject("WScript.Shell").Exec(cmd)
Do While p.Status = 0
If Now < limit Then
WScript.Sleep 100
Else
On Error Resume Next 'to ignore "invalid window handle" errors
p.Terminate
On Error Goto 0
timedOut = True
End If
Loop
End If
If timedOut Then WScript.Echo "Script timed out."
You'd still be re-launching the script, but in this case it's your script killing the child process, not the script interpreter.

Here is another short and elegant solution which allows to terminate both the script and the external executable ran asynchronously, via WScript.Timeout
Option Explicit
Dim oSmallWrapperWshExec
WScript.Timeout = 7
Set oSmallWrapperWshExec = New cSmallWrapperWshExec
' Some code here
MsgBox "Waiting timeout" & vbCrLf & vbCrLf & "You may close notepad manually and/or press OK to finish script immediately"
Class cSmallWrapperWshExec
Private oWshShell
Private oWshExec
Private Sub Class_Initialize()
Set oWshShell = CreateObject("WSCript.Shell")
With oWshShell
Set oWshExec = .Exec("notepad")
.PopUp "Launched executable", 2, , 64
End With
End Sub
Private Sub Class_Terminate()
On Error Resume Next
With oWshShell
If oWshExec.Status <> 0 Then
.PopUp "Executable has been already terminated", 2, , 64
Else
oWshExec.Terminate
.PopUp "Terminated executable", 2, , 64
End If
End With
End Sub
End Class

I appreciate all of the answers here, but they are more complicated than I wanted to get in to.
I was very surprised to find out that there is a way to do it built into WScript.
WScript.Timeout = x_seconds

cscript
Usage: CScript scriptname.extension [option...] [arguments...]
Options:
//B Batch mode: Suppresses script errors and prompts from displaying
//D Enable Active Debugging
//E:engine Use engine for executing script
//H:CScript Changes the default script host to CScript.exe
//H:WScript Changes the default script host to WScript.exe (default)
//I Interactive mode (default, opposite of //B)
//Job:xxxx Execute a WSF job
//Logo Display logo (default)
//Nologo Prevent logo display: No banner will be shown at execution time
//S Save current command line options for this user
**//T:nn Time out in seconds: Maximum time a script is permitted to run**
//X Execute script in debugger
//U Use Unicode for redirected I/O from the console
Update:
To help people who downvote a plain (and to the point) citation of cscript.exe's usage message (how can that be wrong?) to see the light through #PanayotKarabakalov's smoke screen:
The claim:
using //T switch not guarantee real time accuracy
that all 5 Echo command executed, even if the Sleep time between them
is 1.5 second and the //T is set to 4
The evidence:
The script is restarted via:
CreateObject("WScript.Shell").Run "WScript " & _
Chr(34) & WScript.ScriptFullName & _
Chr(34) & " /T:4", 0, False
which does not contain the host-specific //T (as opposed to the script-specific /T) switch.
The (counter) argument:
Whatever way you start the first instance of the script (//T or no //T), the second/relaunched instance will never have a time out and will always run to the bitter end.
If you still have doubts, change the invocation in P.'s script to
CreateObject("WScript.Shell").Run "WScript //T:4 " & _
and try it out.

Related

Launch an external exe and set timeout VBS [duplicate]

I am working on a script with vbscript, and I would like it to terminate itself after x number of minutes.
I was thinking something like grabbing the time when the script starts and then keeping the whole thing in a loop until the time is x number of minutes after the start time, but I need it to keep checking in the background, and not just wait until a loop is complete.
I want a message or something that notifies the user they took too long, which I can do myself.
Is there any way to keep track of the time in the background, or will it be a bit of a drawn-out process to determine it?
Re-launching the script with //T:xx as suggested by Ekkehard.Horner is probably your best option. Another, slightly different, approach could look like this:
Const Timeout = 4 'minutes
timedOut = False
If WScript.Arguments.Named.Exists("relaunch") Then
'your code here
Else
limit = DateAdd("n", Timeout, Now)
cmd = "wscript.exe """ & WScript.ScriptFullName & """ /relaunch"
Set p = CreateObject("WScript.Shell").Exec(cmd)
Do While p.Status = 0
If Now < limit Then
WScript.Sleep 100
Else
On Error Resume Next 'to ignore "invalid window handle" errors
p.Terminate
On Error Goto 0
timedOut = True
End If
Loop
End If
If timedOut Then WScript.Echo "Script timed out."
You'd still be re-launching the script, but in this case it's your script killing the child process, not the script interpreter.
Here is another short and elegant solution which allows to terminate both the script and the external executable ran asynchronously, via WScript.Timeout
Option Explicit
Dim oSmallWrapperWshExec
WScript.Timeout = 7
Set oSmallWrapperWshExec = New cSmallWrapperWshExec
' Some code here
MsgBox "Waiting timeout" & vbCrLf & vbCrLf & "You may close notepad manually and/or press OK to finish script immediately"
Class cSmallWrapperWshExec
Private oWshShell
Private oWshExec
Private Sub Class_Initialize()
Set oWshShell = CreateObject("WSCript.Shell")
With oWshShell
Set oWshExec = .Exec("notepad")
.PopUp "Launched executable", 2, , 64
End With
End Sub
Private Sub Class_Terminate()
On Error Resume Next
With oWshShell
If oWshExec.Status <> 0 Then
.PopUp "Executable has been already terminated", 2, , 64
Else
oWshExec.Terminate
.PopUp "Terminated executable", 2, , 64
End If
End With
End Sub
End Class
I appreciate all of the answers here, but they are more complicated than I wanted to get in to.
I was very surprised to find out that there is a way to do it built into WScript.
WScript.Timeout = x_seconds
cscript
Usage: CScript scriptname.extension [option...] [arguments...]
Options:
//B Batch mode: Suppresses script errors and prompts from displaying
//D Enable Active Debugging
//E:engine Use engine for executing script
//H:CScript Changes the default script host to CScript.exe
//H:WScript Changes the default script host to WScript.exe (default)
//I Interactive mode (default, opposite of //B)
//Job:xxxx Execute a WSF job
//Logo Display logo (default)
//Nologo Prevent logo display: No banner will be shown at execution time
//S Save current command line options for this user
**//T:nn Time out in seconds: Maximum time a script is permitted to run**
//X Execute script in debugger
//U Use Unicode for redirected I/O from the console
Update:
To help people who downvote a plain (and to the point) citation of cscript.exe's usage message (how can that be wrong?) to see the light through #PanayotKarabakalov's smoke screen:
The claim:
using //T switch not guarantee real time accuracy
that all 5 Echo command executed, even if the Sleep time between them
is 1.5 second and the //T is set to 4
The evidence:
The script is restarted via:
CreateObject("WScript.Shell").Run "WScript " & _
Chr(34) & WScript.ScriptFullName & _
Chr(34) & " /T:4", 0, False
which does not contain the host-specific //T (as opposed to the script-specific /T) switch.
The (counter) argument:
Whatever way you start the first instance of the script (//T or no //T), the second/relaunched instance will never have a time out and will always run to the bitter end.
If you still have doubts, change the invocation in P.'s script to
CreateObject("WScript.Shell").Run "WScript //T:4 " & _
and try it out.

"Object required" when using Set in an assignment

call main()
sub main()
Dim scmd
Set scmd = "c:\windows\system32\cscript.exe //nologo c:\s.vbs"
createobject("wscript.shell").run scmd,0,false
end sub
It gives me error:
Object required: '[string: "c:\windows\system32\"]' Code 800A01A8
Update
As it's not clear feel it best to point out your Object Required issue is due to this line
Set scmd = "c:\windows\system32\cscript.exe //nologo c:\s.vbs"
This is because an Object is expected but you are assigning it a string, by removing the Set your code will work (As Ekkehard.Horner has pointed out).
Below is my interpretation of situation. First looking at your code it almost looked like it had mixed the instantiation of the WScript.Shell object with the command line for the .Run() method. It was my first stab at breaking down the code, rearranging it then putting it back together.
Original Answer
Your Set scmd should be instantiating the WScript.Shell (As Ekkehard.Horner points out you can use Server.CreateObject("WScript.Shell").Run for a one off reference but I wouldn't recommend it).
The .Run() should be executed by the instantiated scmd object and passed the command line to execute.
Here is an example I've renamed some of the variables (scmd to cmd for example).
Call main()
Sub main()
'Renamed variables to cmd is your object and cmdline is your file path.
Dim cmd, cmdline
'Instantiate WshShell object
Set cmd = Server.Createobject("WScript.Shell")
'Set cmdline variable to file path
cmdline = "c:\windows\system32\cscript.exe //nologo c:\s.vbs"
'Execute Run and return immediately
Call cmd.Run(cmdline, 0, False)
End Sub
Things to consider
When using WScript.Shell in Classic ASP to run executables there are some things to consider;
Run command will execute using the current Application Pool identity.
Run will execute the executable on the server not at the client (server side).
As
>> WScript.Echo CreateObject("WScript.Shell").CurrentDirectory
>>
E:\trials\SoTrials\answers\trials\AlgoTaBu\SuSo\wsf
proves, there is no rule or law at all that "Your Set scmd should be instantiating the WScript.Shell". Putting the command to execute in string variable scmd (or perhaps better sCmd) and not creating a variable for an only-once-used value are good practices.
The revised version (minus the stupid Set):
call main()
sub main()
Dim scmd
scmd = "c:\windows\system32\cscript.exe //nologo c:\s.vbs"
createobject("wscript.shell").run scmd,0,false
end sub
will work just as well as Lankymart's version.
To spell everything out:
The Set keyword, its semantic, and its error message are design flaws, that make VBScript harder to use correctly. "site:stackoverflow.com vbscript "object required" Set" results in 1500 hits. Even if much of those hits do not concern the "Set x = 'non-object' blunder, that's clearly too much. To explain/excuse those IEDs you have to consider that BASIC is a stone age language.
A person learning VBScript is entitled to be surprised by the "Set x = 'non-object' mistake twice. If it happens trice (or more often), he/she should be ashamed (and keep silent about it). Above all that problem should not pollute this site.
When I posted my contribution, all answers/comments - with the exception of Alex K.'s 'Just delete the Set' - emphasized/concentrated on the .Run statement; one answer called the script "topsy curvy", one answer even repeated the atrocity. So I tried to point out that there is exactly one error: The spurious Set.
I failed miserably. Evidence: John Saunders changed the title from "VBScript error" (unspecific but true) to "“Object required” when calling Run on Wscript.Shell" (specific but wrong), Lankymart engaged in psychological/philological research to save the Set at the expense of the string.
My only hope: Everybody reading this will be so disgusted by my harping on the Set, that she/he from now on will think twice when typing:
wtf
Set x = " ---- stop or be damned!!!
Set x = obj.getNumber() + 4 ---- oh no!!!
All to no avail - Same mistake again
I am not sure, try change
Set scmd = "c:\windows\system32\cscript.exe //nologo c:\s.vbs"
to
Set scmd = "c:\windows\system32\cscript.exe" //nologo "c:\s.vbs"

What does 'say' and 'ws.run task' in vbscript?

I have to work on a vbs script. I have to admit that I have only c# experience and no clue about the following, what from my point of view is more SysAdmin Powerhell VBS-scripting.
For what stands 'say' here or in general? vbcrlf seems to be some kind of constant that puts the cursor to the beginning of a new line?
say(vbcrlf)
say("Some text...")
ws.Run "C:\whatever.exe /PACK-* /SEND /Q", , True
say(vbcrlf)
What does the ws.run task here? Just starts and run the scsript.exe?
set ws = CreateObject("Wscript.Shell")
if ucase(right(wscript.fullname,11)) = "WSCRIPT.EXE" then
task = "cscript.exe " & chr(34) & wscript.scriptfullname & chr(34)
ws.run task
wscript.quit
end if
Thank you for any help on that!
Edit:
Problem is that the script runs like a charm on XP, but not on Win7. I think it must have something to do with the spaces in the path.
Here is the exact path I'm dealing with. Do I need to enclose them with additional double quotes or is chr(34) the way to go?
ws.Run "C:\Program Files (x86)\whatever.exe /PACK-* /SEND /Q", , True
Edit:
Ok, I got it->
ws.Run """C:\Program Files (x86)\whatever.exe"" /PACK-* /SEND /Q", , True
vbCrLf is a pre-defined string constant consisting of a carriage return and a linefeed:
>> WScript.Echo Asc(vbCrLf), Asc(Right(vbCrLf, 1))
>>
13 10
see String Constants
say isn't native VBScript; it must be a user-defined Sub:
>> Sub say(x) : WScript.Echo x : End Sub
>> say "pipapo"
>>
pipapo
(The param list () in your sample violate the rule: Don't use param list () when calling a Sub)
.Run is a method (function) of the WScript.Shell object; it executes/runs an external process. In your example it is used (as a Sub) to re-start the script with the *c*script.exe host (instead of *w*script.exe).
see WshShell object, .Run method
P.S.
If you use .Run (or. Exec), it is a very good idea to build the first/strCommand parameter into a variable for inspection and tests from a command prompt. The argument "Pointlessly create a variable to use extra memory, slow down the script, and to make script harder to read" became obsolete shortly after clay replaced stone for information storage.
'Connects to COM object WScript.Shell
set ws = CreateObject("Wscript.Shell")
'Testing what script engine is running the script, if the GUI one then do the following. wscript object is always available in wscript/cscript run scripts
if ucase(right(wscript.fullname,11)) = "WSCRIPT.EXE" then
'Pointlessly create a variable to use extra memory, slow down the script, and to make script harder to read
task = "cscript.exe " & chr(34) & wscript.scriptfullname & chr(34)
'Run the current script in the console version of the scripting host
ws.run task
'quits the script leaving the console version to run
wscript.quit
end if

Is the Sleep operation no longer used in VBscript?

The "Sleep" command as stated in many places over the internet (including here on this forum) DOES NOT WORK. Is it now an obsolete command?
I am writing the VBScript code like this:
sub button1_onclick()
Wscript.Sleep 1000
div1.innerHTML = textbox1.value
end sub
It should wait 1 second and then execute that simple command. This is an utterly simple statement but it does not work. Plain and simple. It comes up with an error every time saying:
Object Required: 'Wscript'
Daniel's answer is absolutely correct about context being the key here. Although you don't have the WScript method available, you do have the full browser DOM, including the window.setTimeout method. With VBScript, the semantics of passing code to setTimeout are a little bit different than JavaScript, but it's still possible:
Sub button1_onclick()
window.setTimeout GetRef("Delayed"), 1000
End Sub
Sub Delayed()
div1.innerHTML = textbox1.value
End Sub
Another option would be to (ab)use ping (if you want to avoid an additional script):
Sub Sleep(seconds)
CreateObject("WScript.Shell").Run "%COMSPEC% /c ping 127.0.0.1 -n " _
& seconds+1, 0, True
End Sub
ping sends echo requests in (roughly) 1 second intervals, so you can get an n-second delay by sending n+1 echo requests.
When run from a browser, VBScript code does not have a Wscript object. This is only for stand-alone VBS. As such, Wscript.Sleep isn't obsolete, it just doesn't work in a browser without a work around.
This thread suggests a potential work around. The rest of this post comes from the entry by mayayana on the linked page:
If your security can allow WScript.Shell to run
you can do it this way -
Script sub in webpage:
<SCRIPT LANGUAGE="VBScript">
Sub Sleep(NumberSeconds)
Dim SH, Ret
Set SH = CreateObject("WScript.Shell")
Ret = SH.Run("sleeper.vbs " & NumberSeconds, , True)
End Sub
</SCRIPT>
In the same folder as the webpage, put a file named
sleeper.vbs and put this code into it:
Dim Arg
on error resume next
Arg = WScript.Arguments(0) * 1000
WScript.sleep Arg
You can then call something like:
Sleep 5 '-- pauses 5 seconds.

How to set delay in vbscript

How to set delay in vbscript?
WScript.Sleep(100) does not work on Windows XP, Vista.
Work this end (XP).
Create a new file, call it test.vbs. Put this in it.
WScript.Sleep 1000
MsgBox "TEST"
Run it, notice the delay before the message box is shown.
Note, the number is in Milliseconds, so 1000 is 1 second.
If you're trying to simulate a sleep delay in VBScript but WScript is not available (eg: your script is called from Microsoft's BGInfo tool), then try the following approach.
The example below will delay until 10 seconds from the moment the instruction is processed:
Dim dteWait
dteWait = DateAdd("s", 10, Now())
Do Until (Now() > dteWait)
Loop
if it is VBScript, it should be
WScript.Sleep 100
If it is JavaScript
WScript.Sleep(100);
Time in milliseconds. WScript.Sleep 1000 results in a 1 second sleep.
The following line will make your script to sleep for 5 mins.
WScript.Sleep 5*60*1000
Note that the value passed to sleep call is in milli seconds.
A lot of the answers here assume that you're running your VBScript in the Windows Scripting Host (usually wscript.exe or cscript.exe). If you're getting errors like 'Variable is undefined: "WScript"' then you're probably not.
The WScript object is only available if you're running under the Windows Scripting Host, if you're running under another script host, such as Internet Explorer's (and you might be without realising it if you're in something like an HTA) it's not automatically available.
Microsoft's Hey, Scripting Guy! Blog has an article that goes into just this topic How Can I Temporarily Pause a Script in an HTA? in which they use a VBScript setTimeout to create a timer to simulate a Sleep without needing to use CPU hogging loops, etc.
The code used is this:
<script language = "VBScript">
Dim dtmStartTime
Sub Test
dtmStartTime = Now
idTimer = window.setTimeout("PausedSection", 5000, "VBScript")
End Sub
Sub PausedSection
Msgbox dtmStartTime & vbCrLf & Now
window.clearTimeout(idTimer)
End Sub
</script>
<body>
<input id=runbutton type="button" value="Run Button" onClick="Test">
</body>
See the linked blog post for the full explanation, but essentially when the button is clicked it creates a timer that fires 5,000 milliseconds from now, and when it fires runs the VBScript sub-routine called "PausedSection" which clears the timer, and runs whatever code you want it to.
Here's another alternative:
Sub subSleep(strSeconds) ' subSleep(2)
Dim objShell
Dim strCmd
set objShell = CreateObject("wscript.Shell")
'objShell.Run cmdline,1,False
strCmd = "%COMSPEC% /c ping -n " & strSeconds & " 127.0.0.1>nul"
objShell.Run strCmd,0,1
End Sub
Time of Sleep Function is in milliseconds (ms)
if you want 3 minutes, thats the way to do it:
WScript.Sleep(1000 * 60 * 3)
better use timer:
Sub wait (sec)
dim temp
temp=timer
do while timer-temp<sec
loop
end Sub
Here is my solution. Worked with script, which was ran by third party program with no WScript declared and no import allowed.
Function MySleep(milliseconds)
set WScriptShell = CreateObject("WScript.Shell")
WScriptShell.Run "Sleep -m " & milliseconds, 0, true
end Function
Update
Looks like Microsoft removed Sleep.exe from win 8, so this doesn't work in win 8 unless you put Sleep.exe in folder defined in %path%.
Here is an update to the solution provided by #user235218 that allows you to specify number of milliseconds you require.
Note: The -n option is the number of retries and the -w is the timeout in milliseconds for ping. I chose the 127.255.255.254 address because it is in the loopback range and ms windows doesn’t respond to it.
I also doubt this will provide millisecond accuracy but on another note i tried it in an application using the ms script control and whilst the built in sleep function locked up the interface this method didn't.
If somebody can provide an explanation for why this method didn't lock up the interface we could make this answer more complete. Both sleep functions where run in the user thread.
Const WshHide = 0
Const WAIT_ON_RETURN = True
Sub Sleep(ByVal ms)
Dim shell 'As WScript.Shell
If Not IsNumeric(ms) Then _
Exit Sub
Set shell = CreateObject("WScript.Shell")
Call shell.Run("%COMSPEC% /c ping -n 1 -w " & ms & " 127.255.255.254 > nul", WshHide, WAIT_ON_RETURN)
End Sub
As stated in this answer:
Application.Wait (Now + TimeValue("0:00:01"))
will wait for 1 second
WScript.Sleep 100
The following code should work properly.

Resources