In VBScript, I can not stop my loop from repeating - vbscript

I have looked hard to resolve this issue, but I am coming up short.
Here is my code:
'attempts and completions loop
'attempts
do
do
Wscript.StdOut.WriteLine "How many attempts did " & QB & " throw: "
attempts = Wscript.StdIn.ReadLine
if IsNumeric(attempts) then
attempts = CInt(attempts)
else
Wscript.StdOut.Write "You did not enter a number. Please try again."
end if
loop while IsNumeric(attempts) = false
'completions
do
Wscript.StdOut.WriteLine "How many completed passes did " & QB & " throw for: "
completions = Wscript.StdIn.ReadLine
if IsNumeric(completions) then
attempts = CInt(completions)
else
Wscript.StdOut.Write "You did not enter a number. Please try again."
end if
loop while (completions) = false
if attempts > completions then
exit do
else
Wscript.StdOut.Writeline "Completions can not be more that attempts please try again."
end if
loop while attempts < completions
When I enter more completions than attempts, I get my desire result. But when I enter more attempts than completes it will still loop through this code. Can I please get some assistance.

Nevermind my comment...I see it now. In the loop for Completions, you are setting the attempts variable when you should be setting the completions variable.
if IsNumeric(completions) then
attempts = CInt(completions)
So when you're comparing, attempts is always blank or whatever you've initialized it to.
Standard copy/paste error :)

Related

vbscript X and Cancel not doing what they should

im writing a code in vbscript where it will ask the user for input and then run certain files according to the input and i have the else so that it will redo the if else sequence when you type something that isnt an option but when i try to press cancel or the red 'X' it acts as if i have put in an invalid input and goes over the else sequence.
Dim sInput
sInput = InputBox("input")
If sInput = "input1" or sInput = "input2" Then
set shell=createobject("wscript.shell")
shell.run "file.bat"
elseif sInput = "exit" or sInput = "Exit" Then
WScript.Quit
else
name=msgbox (" That is not a valid response",0+16,"ERROR")
set shell=createobject("wscript.shell")
shell.run "input.vbs"
end if
Don't try to restart the script.
Use a loop instead. End the loop when the user entered a valid option, or quit the entire program if requested.
Option Explicit
Dim Shell, input, button
Set Shell = CreateObject("WScript.Shell")
Do
input = InputBox("input")
If IsEmpty(input) Or LCase(input) = "exit" Then WScript.Quit
input = LCase(Trim(input))
If input = "input1" Or input = "input2" Then
Shell.Run "file.bat"
Exit Do
Else
button = MsgBox("That is not a valid response.", vbExclamation + vbRetryCancel, "ERROR")
If button = vbCancel Then Exit Do
End If
Loop
Notes:
Option Explicit makes variable declaration mandatory. It's a good idea to always have this enabled.
IsEmpty() is true when the user pressed the Cancel button (or the Esc key) in the InputBox - but this will work only before the response is manipulated in any way, such as LCase or Trim. Supporting the Cancel button is more intuitive than having a special "exit" keyword, so maybe you should get rid of that.
The various constants you can use with MsgBox are described on ss64.com and in more detal in the official VBScript language reference.
You can change what Enter and Esc do in each MsgBox by using the vbDefaultButton1 or vbDefaultButton2 constants.
The Do loop without any conditions (Do/Loop While ... or Do/Loop Until ...) will run forever - be sure not to forget using Exit Do or WScript.Quit(). (If you do, killing the Script with the Task Manager will get you out of it.)

VBScript Error "Permission Denied" But Works Anyway

I have a VBScript setup as a logon script in my GPO. I'm having an issue where every time it runs at logon, I get Permission Denied on the line:
set lf = fso.opentextfile(lfp, 2, true)
Points of interest:
Despite the error, the script completes successfully.
The script executes without error if I run it manually.
The lfp variable points to c:\folder\folder\file.log. The file is created (when necessary) and populated appropriately (overwritten, as expected when it does exist).
If the file is created, the fso is closed before trying to opentextfile.
The user logging in does have modify permission to the path and to the file being replaced when it exists via inherited Authenticated Users permission (from c:\folder1 - see below).
If I throw in a wscript.sleep 30000 just before that line, it just waits 30 seconds to throw permissions denied.
If user is a member of local administrators group on PC, I get no errors. Again, when user is not local admin, it errors, but completes successfully.
I see the same behavior under both Windows 7 and 10.
I'm at a loss here. Here's the pertinent section of code (please excuse any poor coding practice):
'notify robocopy log file location
function seelog(log)
lf.writeline "[" & now & "] " & "See log file for details: " & log
end function
'process robocopy exit codes and write log
function writeerrors(items)
docs = items(0)
retcode = items(1)
logfile = items(2)
if docs = "c" then
name = "some stuff"
else
name = "some other stuff"
end if
If retcode = 0 Then
lf.writeline "[" & now & "] " & name & " folder was already up to date."
elseif retcode = 1 then
lf.writeline "[" & now & "] " & name & " folder was updated."
seelog(logfile)
else
lf.writeline "[" & now & "] " & name & " folder update exited with robocopy error code: " & retcode
seelog(logfile)
End If
end function
'get logged in user
un = CreateObject("WScript.Network").UserName
'check for logfile, and if not exist, create
'folder1 always exists, no need to check or create
lfp = "c:\folder1\folder2\folder3\logfile1.log"
ld1 = "c:\folder1\folder2"
ld2 = "c:\folder1\folder2\folder3"
set fso = createobject("scripting.filesystemobject")
if not fso.fileexists(lfp) then
if not fso.folderexists(ld1) then
fso.createfolder(ld1)
end if
if not fso.folderexists(ld2) then
fso.createfolder(ld2)
end if
set cf = fso.createtextfile(lfp)
cf.close
end if
'open logfile (lfp variable)
'for writing (2)
'overwrite if already exists (true)
wscript.sleep 30000
'************permission denied happens on next line on lfp var*************
Set lf = fso.OpenTextFile(lfp, 2, True)
lf.writeline "[" & now & "] " & "Script started."
lf.writeline "[" & now & "] " & "Logged in user: " & un
lf.writeline "[" & now & "] " & "========================================================="
more code writing to log file and executing robocopy....
I suppose suppressing all errors is an option, but 1) I'd rather not, and 2) I'm not clear on how to accomplish that in VBScript.
ETA:
On Error Resume Next
Set lf = fso.OpenTextFile(lfp, 2, True)
On Error Goto 0
I tested this and it does break the script. lf is not set due to the error so the following lines error out with 'object required "lf"' code 800a01a8 as expected.
I don't like doing this, but my work around was to launch the .vbs from a .bat (personal preference - I like to keep everything from one job in one script so in the future I don't have to go chasing files around).
I placed a logon.bat file in my GPO as the logon script.
#echo off
echo Launching update script...
cscript c:\folder1\script.vbs
This seems to work around the (apparently false) permissions issue I was having. I'm still curious if anyone can tell me exactly why I was seeing the behavior I saw. Does the script engine write to a temp location maybe, when calling OpenTextFile (when launching directly from the GPO) that only admin users would have access to?
On Error Resume Next will ignore the error then On Error Goto 0 will turn normal error handling back on

VBS Freezes When Waiting on Cmd Prompt

Hard to make a concise title, but basically, I have started an instance of command prompt from VBS to run an exe, everything works great and I verify my feedback with msgboxes of output line. When the command prompt gets to a part that is loading and says Verifying File (XX%), the VBS does not run anymore. It does not crash, it simply never moves on from its line. I even have noticed that if I don't constantly Writeline, it will pause before that. So while I wait, I constantly write a 1. I dont see anywhere it could be in an infinite loop without showing me a messagebox.
Please help.
set shell = WScript.CreateObject("WScript.Shell")
set oExec = Shell.exec("cmd.exe")
do while Not oExec.StdOut.AtEndOfStream
junkChar = oExec.stdOut.Read(1)
message = message & junkChar
'errormsg = oExec.stderr.readline
if asc(junkChar) = 13 and message <> junkChar then
msgbox message '& len(message)
message = ""
end if
if right(message,1) = ">" and not bool1stCmd then
'msgbox(message)
msgbox("Command" & cmdArchive)
oExec.stdIn.Writeline cmdArchive
bool1stCmd = True
'oExec.StdIn.Write VbCrLf
elseif bool1stCmd then
if InStr(1,message,"Enter a command>")>0 then
msgbox "Enter a command!"
end if
oExec.stdIn.writeline "1"
end if
msgbox "Loop again!"
Loop
msgbox "exiting loop"

How to extract context informationm the equivalent of __LINE__, __FILE__,etc., in VBSCRIPT

I'd like to know how to get the line number of a line in vbscript programmaticly either at the point of the code like __LINE__ or more ideally a way to get the line number of where the current function was called like python's stack module so I can write a reusable debugging function(and the file the code is located in) and no I don't want to know how to turn on line numbers in my editor.
Also I'd like to now any similar useful information that can be extracted such as calling function, variable type as string, etc.
Unfortunatly that doesn't work the way like in Ruby and Python. The next best thing i worked out is putting a call to a errorhandling function everywhere where things could go wrong. The numbers in the parameter of this function are adapted each time i execute a macro in my editor (i use textpad, the \i is autonumbering in a Regular Expression). If your editor doesn't support this you could write a script that does this. So when an error occurs, it is logged with the number the errorhandling function was called and you can easily find it back in the source by looking for #number#.
This is usable for both asp and vbs but for vbs there is an easier way.
Some editors like textpad or sublimle text let you execute a vbs script, show the output in a tab and if an error is produced let you double click the line with the errormessage which opens the script at that line. This is also done by a regular expression. Let me know if you need the one for textpad.
on error resume next
'initialize constants DEBUGLEVEL and LOGFILE
'initialize strHostName
'some code
oConn.execute(sql)
if not LogError("#1#") then
'do the things if successfull, otherwise log error with number
end if
'again some code
if not LogError("#2#") then
'do the things if successfull, otherwise log error with number
end if
'the debug and log functions
function LogError(errornumber)
'LogError\(\"#[0-9]+#\"\) replace by LogError("#\i#")
if err.number <> 0 then
call debug("<name of script>/Logerror","","","Errornumber:" _
& errornumber & " " & err.number & " " & err.description & " " _
& err.source)
LogError = True
err.clear
errors = errors+1
else
LogError = False
end if
end function
function Debug (pagina, lijn, varnaam, varinhoud)
if DEBUGLEVEL > 0 then
const forReading = 1, forWriting = 2, forAppending = 8, CreateFile = True
dim fs,f, var, strHostName
set fs=CreateObject("Scripting.FileSystemObject")
strHostName = fs.GetFileName(WScript.FullName)
if fs.FileExists(LOGFILE) then
set f=fs.OpenTextFile(LOGFILE, forAppending)
else
set f=fs.OpenTextFile(LOGFILE, forWriting,true)
end if
var = now & " " & pagina & ":" & lijn & ":" & varnaam & ":" & varinhoud
f.WriteLine var
if LCase(strHostName) = "cscript.exe" then 'debugging
if DEBUGLEVEL > 1 then
wscript.echo var
end if
end if
f.Close
set f=Nothing
set fs=Nothing
end if
debug = true
end function
VBScript doesn't expose that information, so you can't access it programmatically from within the script (edge cases notwithstanding). You're going to need a debugger for extracting this kind of information. Or you could have another script interpret the first one and keep track of line numbers (like this). I wouldn't recommend the latter for any kind of production environment, though.
As long as it's happening outside of a function, the following works.
Automatic error-handling is turned off at the start of the script by On Error Resume Next, so that the script doesn't just exit before you can do anything. BUT, you can then turn error-handling back on using On Error GoTo 0 and Raise an exception yourself. That will output the line number in addition to any of your debugging messages.
For example:
On Error Resume Next
server = WScript.Arguments(0)
If Err.Number <> 0 Then
WScript.Echo("Need to pass in an argument!")
On Error GoTo 0
Err.Raise(1)
End if
If you run this without any arguments, you get the following output:
Need to pass in an argument!
C:\script.vbs(6, 5) Microsoft VBScript runtime error: Unknown runtime error
The "6" refers to the line number where the exception was raised.
This way you can print custom output, and also you'll know what line the error happened at.
Yes!
There is a way to get the exact error line number, but it's HUGLY, as we are talking about an ancient programming tool....
And yes, it is worth it, especially if your code is going to run in front of many users. That way you can get past isolating and reproducing the bug, right to solving it.
Take a close look at the last variable "Erl" in the line of code below. It is an undocumented global variable the VB script processor holds.
Dim sErrorMsg as String
sErrorMsg = Err.Description & "(" & Err.Number & ")" & vbNewLine & "Source: " & Err.Source & vbNewLine & "At line number: " & Erl
In order to get anything from that global "Erl" variable you need to (manually)** set its value at the beginning of each line of code as shown below. Beware, you set the line number, if you forget to set the number for a specific line, Erl will report the last set value. See the division by zero error line, it reports the line number set above because I did not set a line number value at the beginning of the line that caused the error.
I have not figured out the inbuilt call stack, though I know there is one. Please let me know if you figure that one out, for now I use a module level variable to build the stack.
More tips at the very end, below this code sample
Sub WhatEverSub ()
2 Const iColIdxPageNbr As Integer = 2
3 Const iColIdxDefinition As Integer = 3
5 Dim oDoc_Source As Document
6 Dim oDoc_Target As Document
10 Dim oTable As Table
11 Dim oRange As Range
12 Dim n As Long
13 Dim strAllFound As String
14 Dim Title As String
15 Dim Msg As String
On Error GoTo PrepErrorHandler
Dim xyz As Long
xyz = Rnd(3) / 0
16
17 Title = "Evil Finder - This program is about doing something important for the world"
18
19 'Show msg - stop if user does not click Yes
20 Msg = "This macro finds all evil things consisting of 2 or more " & _
"uppercase letters and extracts the hex representation to a table " & _
"in a new document." & vbNewLine & vbNewLine & _
"Do you want to continue?"
21 If MsgBox(Msg, vbYesNo + vbQuestion, Title) <> vbYes Then
22 Exit Sub
23 End If
(... whatever code ...)
820 Application.ScreenUpdating = True
830 If n = 1 Then
840 Msg = "No evil things were found. Need to find better detection tool"
850 oDoc_Target.Close savechanges:=wdDoNotSaveChanges
860 Else
870 Msg = "Finished extracting " & n - 1 & " evil thing(s) to a new document."
880 End If
PrepErrorResumeLine:
890 MsgBox Msg, vbOKOnly, Title
'Clean up
1000 Set oRange = Nothing
1010 Set oDoc_Source = Nothing
1020 Set oDoc_Target = Nothing
1030 Set oTable = Nothing
Exit Sub
PrepErrorHandler:
Msg = Err.Description & "(" & Err.Number & ")" & vbNewLine & "Source: " & Err.Source & vbNewLine & "At line number: " & Erl
Resume PrepErrorResumeLine
End Sub
**Some more tips:
1)
As for setting the error line number values manually, I wrote a utility (more than a decade ago) to automate the addition or removal or renumbering of all lines in a module by working directly on the VB project files (or standalone .vbs files), but the below will take care of the basic, with a few manual adjustsments remaining...
Set up VB code line #s using MS Excel
a) paste code in column C
b) set column A's first cell value to 10, and second to 20 and drag copy down to auto increment until you reach the last line/row of code in column B
c) in column B paste in the following formula and drag copy down =A1 & REPT(" ", 8 - LEN(A1))
d) copy columns B and C back into the VB code pane et voila!
Strip out the line numbers to do major edits using Word
Paste the code in,
Hit CTRL + H and make sure wildcards is checked (click the "more" button)
Fill in the following settings
FIND
[^13][0-9 ]{4}
REPLACE
^p
Done!
2)
number each line in increments of 10 at least so you can wedge in a few lines at the last minute without having to renumber each line below your change
3) On Error Resume Next is evil and will cost you a lot of debugging hours!
At least 90% of the time, one should use a specific handler, or nothing. If you do not already know how to recover from an error, do not use RESUME NEXT to silence it, instead, log all the details (using Erl) and learn from the run time logs and use GoTo 0 'Zero, not the letter O to let the rest of the errors bubble up the call stack.
On Error GoTo MyErrorHandlerSection
(... write your risky code here ...)
On Error GoTo 0
'the line immediately above disables all error handling in the current function, if any error happens, it will be passed to the calling function
Nothing prevents you from adding another handling section in the same function if you have another chunk of risky code using
On Error GoTo MySecondErrorHandlerSection

Help Continue VBS String?

I'm VERY new to VBScript and general computer programing in general.
IF I were to do this:
a=inputbox("Password:")
if a="Drop Zero" then
msgbox"Loging On"
else
msgbox"Invalid"
end if
a=inputbox("Hello, what would you like to know:")
I can't continue with the if and else after the second input box. Any help would be highly appreciated for a nOOb such as myself.!!
I'm in trouble understanding your question.
Anyway, if you need to quit when password is wrong, you can try this:
a=inputbox("Password:")
if a="Drop Zero" then
msgbox"Logging On"
else
msgbox"Invalid"
WScript.Quit
end if
a=inputbox("Hello, what would you like to know:")
UPDATE:
Look at this code taken from here:
Function ValidInteger(sNumber,iMin,iMax)
If IsNumeric(sNumber) = 0 Then
If InStr(sNumber,".") Then
If CLng(sNumber)>= iMin And CLng(sNumber)<= iMax Then
ValidInteger=""
Else
ValidInteger = "You must enter a number between " & iMin & " and " & iMax
End If
Else
ValidInteger = "You must enter a whole number"
End If
Else
ValidInteger = "You must enter a number value"
End If
End Function

Resources