0

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.

JMax
  • 26,109
  • 12
  • 69
  • 88
Tom Bell
  • 23
  • 1
  • 2
  • 4

1 Answers1

0

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).

Ekkehard.Horner
  • 38,498
  • 2
  • 45
  • 96
  • I don't know if I'm missing the point, but making the variable good for nothing until the next time the routine is called is exactly what I want - I'm trying to exercise memory management, or is this the wrong way to do it. The real issue is... Why is a variable (strHost) which got its value from another variable (strConnect) made useless when the variable it got its value from (strConnect) is set to nothing? That doesn't seem to make sense. strHost is not cleared, so why is it affected? – Tom Bell Aug 23 '11 at 13:05
  • Can you explain what you mean about "type prefix fraud?" VBS doesn't have a method to select a variable type, all variables are variants. With that in mind I have used prefixes to distinguish what I am functionally using the variables for. I'm confuses as to what problems this has the potential to cause. – Tom Bell Aug 23 '11 at 15:21