VBScript Data Validation - Numeric 1 Results in Infinite Loop - vbscript

DISCLAIMER: I'm still an uber-n00b with programming concepts and know just enough VBS to hurt myself, so I'm likely to slur/slaughter some of the terms/concepts/ideas that are entailed with this program I'm trying to write. You, the vastly superior programmer who has every right to flame me, have been warned.
I've been trying to write a VBScript to validate data, specifically Dates. Since my users are kind of poor with keyboards, I figured I'd make it easy by separating the entry of each part of the date (Month & Day only, I've got the Year set statically).
Previously, I was having problems with validating the numbers with only 1 "Do...Loop", as I was trying to verify if it was Numeric Input and checking at the same time if it was within the specified range (1 - 12, for the 12 months of the year).
This is what the previous code roughly looked like:
Do
' If...Then Statements Here
Loop Until (dtFirstMonth > 0) _
And (dtFirstMonth < 13) _
And IsNumeric(dtFirstMonth) _
And (dtFirstMonth <> "") _
And (dtFirstMonth <> vbNull)
This often resulted in "Data Type Mismatch" errors, so I had to split the Validation Critera to two separate "Do...Loop" statements, as you can see in the current code I have below:
Sub srTest()
Do
dtFirstMonth = InputBox("Please Enter a Numeric Month for the Starting Range", _
"Starting Range Month")
If (dtFirstMonth = vbNull) _
Or (dtFirstMonth = "") _
Or Not IsNumeric(dtFirstMonth) Then
MsgBox "Please Enter a Valid Numeric Month",, "Enter Month Number"
ElseIf (dtFirstMonth <> vbNull) _
And (dtFirstMonth <> "") _
And IsNumeric(dtFirstMonth) Then
Do
dtFirstMonth = Round(dtFirstMonth)
Wscript.Echo dtFirstMonth ' Infinite Loop Here (Basically, As Soon As We Get Into Loop with a Value of 1, We're Stuck)
dtFirstMonth = CInt(dtFirstMonth)
' Must Convert User Input to Integer to
' Prevent Data Mismatch Errors In
' Following "If" Statement; Besides,
' It Passed the First Test to be a
' Numeric Value in the First Place
If (dtFirstMonth < 1) Or (dtFirstMonth > 12) Then
MsgBox "Please Enter a Valid Numeric Month",, "Enter Month Number"
Exit Do
' Drop Out of 2nd Level Loop to
' Enter Into 1st Level Loop
End If
Loop Until (dtFirstMonth > 0) _
And (dtFirstMonth < 13) _
And IsNumeric(dtFirstMonth) _
And (dtFirstMonth <> "") _
And (dtFirstMonth <> vbNull)
If (dtFirstMonth < 1) Or (dtFirstMonth > 12) Then
dtFirstMonth = ""
End If
' dtFirstMonth Was Converted to Integer Earlier
' This is to Meet the Value that Didn't Pass
' the Nested Do & If Statement (Level 2 Do Loop)
' Sets dtFirstMonth to "Empty String" to Continue
' Looping in the Level 1 "Do...Loop" Statement;
' If Omitted, Level 1 "Do...Loop" is Satisfied,
' Thus Ending the Subroutine (Since the Value
' of dtFirstMonth is Still a Numeric Value)
End If
Loop Until IsNumeric(dtFirstMonth) _
And (dtFirstMonth <> "") _
And (dtFirstMonth <> vbNull)
Wscript.Echo dtFirstMonth
End Sub
srTest
I had to set up the 1st "Do...Loop" to check that the User Input (dtFirstMonth) was a indeed a Numeric Value and not a Null Value nor an Empty String. The Nested "Do...Loop", or 2nd "Do...Loop", statement is where I have the same Criteria plus the extra Criteria defining the desired ranges (any number between 1 and 12).
This is working perfectly for number 2-12, but when the script parses the number 1, I enter into an Infinite Loop.
I've checked to make sure that the Infinite Loop is occurring in the 2nd "Do...Loop" by replacing the entire 2nd "Do...Loop" section with "Wscript.Echo dtFirstMonth". By doing this, I get the expected results: a single Echo, not an infinite number of them (technically, I get 2, as I do have another "Wscript.Echo dtFirstMonth" string at the bottom of the Subroutine for the purpose of debugging, but either way, it's not an Infinite Loop).
I've also changed the criterion for the lower range to be like this, yet this doesn't remediate the error:
Do
' If...Then Statements Here
Loop Until (dtFirstMonth >= 1)
I've also tried this, with no resulting success:
Do
' If...Then Statements Here
Loop Until (dtFirstMonth >= CInt(1))
In all reality, there really is no need for this segment, since I converted the User's Input to an integer anyway.
Since this was starting to get confusing, I decided to add the "Round" statement before the script passed the User's Input to the "CInt" function, hoping that it would make sure that it wasn't getting caught as a 0 value or decimal value somehow; yes, this is irrational thought on my part, but I still wanted to explore all avenues (there's also the fact that I have some users with "Fat Finger Syndrome" and some others with "Abuse The Program" mentality, so I figured I'd make sure the script accounted for decimal entries). I added the "Round" string before and after the nested "Do...Loop" and I still had the Infinite Loop issue.
This is as far as I've been able to get on this and now I'm stuck.
I realize that there are likely better ways to do Date/Time Validation in VBScript, and I'm certainly open to any new suggestions, but I'd love to solve this for the pure sake of edification.

Way too much code for a simple input of a number. Just try to keep it short and simple. Example:
Do
dtm = InputBox("Please Enter a Numeric Month for the Starting Range", _
"Starting Range Month")
Select Case True
Case isNull(dtm), (not isNumeric(dtm)), dtm = "", dtm = empty, (dtm < 1 OR dtm > 12)
' too exhaustive, but just for the sake of the example.
MsgBox "Please enter an amount between 1 and 12"
Case else
' Hey, this seems to be a valid amount!
Exit do
End Select
Loop While True
' Do something with dtm
Just showed you some creative Select Casing, this supports lazy exit, so if a value is Null, it escapes before getting evaluated where evaluating could throw an error.

The problem is with your attempt to check for null values:
(dtFirstMonth = vbNull)
The proper way to check for nulls, as demonstrated in AutomatedChaos' answer, is with the IsNull function. vbNull is actually a constant that's used with the VarType function. The value of vbNull is 1, which is why that particular value behaved differently from other entries. That's the fundamental problem, and if you replace every dtFirstMonth = vbNull with IsNull(dtFirstMonth), you won't get an infinite loop when entering 1.
Now, the actual place where your code infinitely loops is interesting. I would expect the first conditional If (dtFirstMonth = vbNull) to evalute true for an entry of "1", and you would get the message box "Please enter a valid numeric month". However, the Else condition is triggered. This is weird because normally, when you compare a string to a number, VBScript will attempt to convert the number to a string or vice versa, so if dtFirstMonth is "1", it should be equal to vbNull (which is 1). However, there appears to be a special case when you compare a string variable to an integer variable. See this example:
' vbNull = 1, built-in constant
dtFirstMonth = "1"
MsgBox (dtFirstMonth = vbNull) ' False
MsgBox ("1" = vbNull) ' True
MsgBox (dtFirstMonth = 1) ' True
MsgBox (CInt(dtFirstMonth) = vbNull) ' True
I don't know if this is a bug or just an obscure detail about VBScript's implicit conversion, but it does illustrate that implicit conversion can be unpredictable in VBScript.
As far as alternative methods go, you may be interested in the IsDate function, which returns True if the given expression can be converted into a date. It may not be ideal for your users and their keyboard skills, but it would reduce your code to:
Do
str = InputBox("Enter a date (MM/DD)")
If IsDate(str) Then
Exit Do
Else
WScript.Echo "Please enter a valid date"
End If
Loop
dt = CDate(str)
' Put your static year here; CDate will default to the current year
dtActual = DateSerial(2007, Month(dt), Day(dt))
WScript.Echo (dtActual) & " - Thanks!"
Note that IsDate should return False for the typical edge cases ("", Null, Empty, etc.), so there's no need for a separate check.

I found a simple program to generate Date and time from http://rindovincent.blogspot.com/p/vbscript-programs.html. I am pasting the same program with permission.
<html>
<body>
<center>
<script type="text/vbscript">
d=CDate("October 22, 2010")
document.write(d&"<br/>")
document.write("Current system date is:"&date&"<br/>")
document.write("Current system time is:"&time&"<br/>")
document.write(DatePart("m",Now())&"<br/>")
document.write(DateAdd("yyyy",1,"31-Jan-10") & "<br />")
document.write(MonthName(10,true)& "<br />")
fromDate="22-sep-10 00:00:00"
toDate="21-oct-10 23:59:00"
document.write(DateDiff("m",fromDate,toDate)&"<br />")
document.write(DateDiff("y",fromDate,toDate) & "<br />")
document.write(DateDiff("w",fromDate,toDate) & "<br />")
document.write(DateDiff("h",fromDate,toDate) & "<br />")
</script>
</center>
</body>
</html>

Related

Access 2010 string issue with letters "AJ"

in Access 2010 (x64) I have a method declared in a class module as below:
Public Sub addFieldAndValueToXML(fieldName As String, value As String)
xmlStr = xmlStr & "<field name=""" & fieldName & """><value>" & value & "</value></field>"
End Sub
I call it from a method on a form as below:
For i = 0 To 4
If Not .EOF Then
Dim n As Integer
n = i + 1
'Stop
builder.addFieldAndValueToXML "FINDINGS Include applicable referencesRow" & n, Nz(!FIndings, "")
Select Case !RatingID
Case 1
builder.addFieldAndValueToXML "MAJORRow" & n, "X"
builder.addFieldAndValueToXML "MINORRow" & n, ""
Case 2
builder.addFieldAndValueToXML "MAJORRow" & n, ""
builder.addFieldAndValueToXML "MINORRow" & n, "X"
Case Else
builder.addFieldAndValueToXML "MAJORRow" & n, ""
builder.addFieldAndValueToXML "MINORRow" & n, ""
End Select
builder.addFieldAndValueToXML "FOCUS AREARow" & _
n, Left(DLookup("Discrepancy_Type", "DiscrepancyType_Tbl", "DiscrepancyTypeID =" & !DiscrepancyTypeID), 1)
.MoveNext
End If
Next i
The purpose of the code is to build an xml string that is output to a .xfdf file, and it appears to be working fine except for the lines where fieldName is given the value "MAJORRow".
When I step through the code I can see that the lines prior to, and after the offending lines call the method properly, and I can see the fieldName and value arguments being populated properly.
However on the offending lines, fieldName is not being populated at all, whereas value is.
I've tried a number of different values for fieldName ("majorrow", "maj_row", "MAJ_Row", etc.) and when the combination "AJ" (has to be capitalized) is in the string, it fails to populate fieldName.
I've checked for reserved words, and done a fair bit of searching but can find nothing relating to this at all. Has anyone else come across this?
I'm running Windows 10 (x64).
Cheers.

Multiple condition loop

I need a VBScript loop asking to entered an integer between 1 to 10 including, if wrong symbol or number entered then asking again until the desired number is retrieved from user.
This is what I tried:
Option Explicit
Dim Num
Num=inputbox("Please enter integer number between 1 to 10")
'Checking if entered value is numeric
Do while not isnumeric(Num)
Num=inputbox("Please enter integer number between 1 to 10", "INCORRECT SYMBOL")
Loop
Do while (Num<1 or Num>10)
Num=inputbox("Please enter integer number between 1 to 10 ", "Number is NOT IN RANGE")
Loop
Do while not int(Num)
Num=inputbox("Please enter integer number between 1 to 10 ", "Number is NOT INTEGER")
Loop
does not work: when I enter 3 for example I am getting inputbox saying "Number is NOT INTEGER", when entering a letter I receive error message Type mismatch string, error code 800A00D.
You need one Loop. For each (variant) input you need to check:
is it Empty (User pressed Cancel or X) - Abort
is it a blank (zero length or sequence of spaces) string
is it numeric
is it an integer
is it in range - Accept
As in:
Option Explicit
Dim vNum, sNum, nNum
Do
vNum = InputBox("Please enter an integer beween 1 and 10 (inclusive)")
If IsEmpty(vNum) Then
WScript.Echo "Aborted"
Exit Do
Else
sNum = Trim(vNum)
If "" = sNum Then
WScript.Echo "Empty string"
Else
If IsNumeric(sNum) Then
nNum = CDbl(sNum)
If nNum <> Fix(nNum) Then
WScript.Echo "Not an Integer"
Else
If nNum < 1 Or nNum > 10 Then
WScript.Echo "Not in range"
Else
WScript.Echo nNum, "is ok"
Exit Do
End If
End If
Else
WScript.Echo "Not a number"
End If
End If
End If
Loop
WScript.Echo "Done"
Using different variables for the different data types may be pedantic, but should show why you had type problems.
Your
Do while not int(Num)
does not work as expected because here Num is a number between 1 and 10; rounding off the (not existing) fractional part gives Num again; Num evaluated in a boolean context/as a bool gives (always) True.
Update wrt comment:
Trim removes space from the head or tail of a string; WScript.Echo sends output to the console (cscript) or a dialog box (wscript).
Update:
As this question shows, I didn't make clear that pressing Cancel or X (Close) sets vNum to an empty variant, which is different from an empty/zero length string. So it should be treated as indication of the users intention to abort.
BTW: You need to read the Docs, but you can't believe them always (cf. here).

Round function not working properly

I tried to round off value using VBscript
here is my code
sql = "select lastcode from " & session("gasbook") & ".acccode where account='" _
& Trim(rsgrn.fields("stockcode")) & "'"
response.write sql
rs.Open sql, cn
if err.number<>0 then
cn.rollbacktrans
call HandleError(err.number,err.description,err.source)
Response.End
else
if (rs.EOF and rs.BOF) then
cn.RollbackTrans
Response.Write "GAS Code doesn't exist in " & session("gasbook")
Response.End
else
If clng(rs.Fields("lastcode")) <> 0 Then
cn.RollbackTrans
Response.Write "Stock Account must be control account in " _
& session ("gasbook")
Response.End
End If
end if
end if
dim tmp
tmp = rsgrn.Fields("amount")
response.write tmp
response.write round(tmp)
tmp has value 2984.5, but when I apply round on tmp it convert into 2984 instead of 2984.5.
The number is rounded to 2948 because the .Net framework follows the Bankers Rounding algorithm off of the IEEE 754 standard. It is not a question of if the number was rounded, it is rounded to the nearest even number due to standards.
Evidence: Here. and Here.
So, the round function is working perfectly to the IEEE Standards.
When in doubt, read the documentation. Without the optional second parameter numdecimalplaces, the Round function returns an integer:
numdecimalplaces
Optional. Number indicating how many places to the right of the decimal are included in the rounding. If omitted, integers are returned by the Round function.
Change the line
response.write round(tmp)
to
response.write round(tmp, 1)
if you want the value rounded to one decimal.

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

Issue with variable set to variable in VBS

In a VBS I have which I use in conjunction with SecureCRT to automate some processes on Cisco devices, I have (very much pared down) the following code:
Sub prConnectToHost(strConnectHost)
'If no host is passed into subroutine then we need to prompt for one.
If strConnectHost = "" Then strConnectHost = LCase(crt.Dialog.Prompt("Enter hostname or IP address:", "Connect to a host", strHost, False))
strHost = strConnectHost
'If user hits Cancel or hits Ok with no hostname entered then exit.
If strHost = "" Then
booReconnect = False
Exit Sub
End If
'Write to connection log
Call prWriteToConnectionLog
'Run command capture subroutine.
Call prCommandLoop
Set intWaitString = Nothing: Set strScreenGet = Nothing
Set strLatestScriptVersion = Nothing: Set strConnectHost = Nothing
End Sub
Sub Main has a section like this:
Do While booReconnect = True
Call prConnectToHost("")
Loop
crt.Dialog.Prompt is the same as MsgBox, only it centres on the window and not the screen, so it's a little neater. The variable strHost is the actual hostname string which is global in the script and contains the hostname we want to connect to. It is used in the Prompt line as a default text, the idea being that if you disconnect and the booReconnect flag is set, this Sub is called again, and next time you're prompted for a hostname the old one is there - useful if you spelled it wrong first time, or you're connecting to a bunch of devices with a similar name.
You can see where we call prCommandLoop at the end of this Sub, which is a loop which uses a crt Function called WaitForStrings which puts the script on hold until it finds a particular string sequence. When it does, it fires off some stuff, then loops back around until it sits waiting again.
One of the automation commands detects for the presence of the connection menu (so therefore we have quit the router session) and prompts the user for another hostname to connect to.
The important bit is in the variable clearup at the end - Set strConnectHost = Nothing. If I leave this in and immediately exit prCommandLoop with booReconnect set, as soon as Set strConnectHost = Nothing is applied, strHost dies - if I try to reference it I get an error Object Variable not set. I experimented with putting a MsgBox strHost line right at the end of the Sub, which proved this.
The bizarre thing is that if I choose a different automation command in prCommandLoop first and then quit the session, the Set strConnectHost = Nothing doesn't seem to bother anyone.
Can anyone help me explain why this is a problem, as it is baffling me. I can easily work around it (by not issuing Set strConnectHost = Nothing at the end of the prConnectToHost Sub), but I just want to understand what the problem is.
Set is used to assign objects to variables. Think of Nothing as a very special object
>> WScript.Echo IsObject(Nothing)
>>
-1
which is useful only to indicate the emptiness of the variable. Your
Set strConnectHost = Nothing
assigns this Nothing to strConnectHost. After that, the variable is good for nothing - it holds the empty object that can't be printed or used in computations or asked to do methods.
The type prefix fraud (*str*ConnectHost) should alert you that this is fishy. You work with strings (and numbers?); to clear/reset them use (simple) assignment with Empty:
>> strConnectHost = Empty
>>
>> WScript.Echo IsEmpty(strConnection)
>>
-1
or with a suitable value:
intWaitString = -1 ' or 0 ...
(assuming intWaitString isn't another type prefix fraud).
SECOND ATTEMPT:
I assume you call your sub like this:
strHost = "SomeHost"
prConnectToHost strHost
The relevant digest of your sub is:
Sub prConnectToHost( [ByRef] strConnectHost)
...
Set strConnectHost = Nothing
End Sub
As VBScript uses by reference passing of parameters as default, your modification
changes the caller variable strHost. This happens to non-object variables too:
Dim sVar : sVar = "String 0"
WScript.Echo 0, sVar
changeString sVar
WScript.Echo 1, sVar
Sub changeString( sByRefVar )
sByRefVar = "String 1: changed by changeString( ByRef sByRefVar )"
End Sub
output:
0 String 0
1 String 1: changed by changeString( ByRef sVar )
In your case the modification assigns Nothing to the variable that is called
strConnectHost in the Sub and strHost on the caller level. As I said before,
that makes the variable useless (except of testing for Is Nothing).
I hope that explains the clobbering of strHost.
WRT 'memory management': Except for very special cases, you don't need to
clear/reset/SetToNothing variables in VBScript. Using local variable in your
Subs/Functions is all that is necessary. If you decide to use global variables
and manage their state yourself, you must pay attention to the variable types:
Changing the type from object (including Nothing) <=> non-object and lying
about types by misleading type prefixes is dangerous/a sure way to desaster.
If you think you must clear strHost, assign Empty or "" to strConnectHost.
NEXT ADDITION
All VBScript variables are Variants, but not all Variants are created equal:
>> s0 = "string"
>> s1 = CStr( 12.35 )
>> WScript.Echo TypeName( s0 ), TypeName( s1 )
>>
String String
>> n0 = 1
>> n1 = CByte( n0 )
>> WScript.Echo TypeName( n0 ), TypeName( n1 )
>>
Integer Byte
TypeName() and VarType() show the sub-types and a progammer can use a set of
C[hange/onvertTo]<Type>() functions to enforce them - to a degree, as assignments
may change types 'under the hood'.
>> WScript.Echo TypeName( n0 ), TypeName( n1 )
>>
Integer Byte
>> n0 = 1.1
>> n1 = 2 ^ 20
>> WScript.Echo TypeName( n0 ), TypeName( n1 )
>>
Double Double
There are even Type Mismatch Errors:
>> WScript.Echo Nothing
>>
Error Number: 13
Error Description: Type mismatch
>>
>> WScript.Echo s0 Is Nothing
>>
Error Number: 424
Error Description: Object required
So sub-types matter. Some people think type prefixes are uncool, but others see
them as valuable help in weakly typed languages. If you decide to use them, you
should use them correctly -
Set strWhatEver = objWhatever
objWhatever = intWhatever
intWhatever = objWhatever
If strWhatEver = intWhatever Then
all smell (of not paying attention to types and hard to pin down errors in later code).

Resources