Skip navigation

Today we are going to examine VBScript. Now, a bit of fair warning before we start on this.  This scripting language is a departure from our standard shell scripting.  VBScript is an automation scripting language for the Windows Script Host (WSH)/Internet Explorer (IE)/Internet Information Services (IIS), in short, this is the primary Windows scripting language.

Not to get too carried away on the subject, but this language uses a Component Object Model that allows direct (or indirect) action on just about every object within the windows environment and does so natively via the windows script host interpreter ((which is extensible) (wscript.exe or cscript.exe)).  There are nuances to that statement, and I encourage you to investigate it more thoroughly, however I’m going to jump right into some of the basics of using this language, then discuss the code below as we’ve done in the previous 2 posts.

First off, what is a component object model?  Put as simply as possible, they are exposed objects that you can manipulate in the environment by invoking their methods and properties.  This scripting language brings you dangerously close to windows development at times, but at it’s heart, it’s still very much a script.  Controlling environment objects and properties, performing sequenced tasks, and no compile.

Now, I’m going to introduce 4 new things with this script, I will name them now and explain them after we’ve reviewed the code:

Arrays

WMI

Subroutines & Functions (They can be the same, but in WSH they are different)

Loops

Function VBscript (blog)

The overall activity of this script is the same as our drivemounter batch script we made.  Lets take a look at the code.


'||========================================|| 
'||Author Daniel Belcher                   || 
'||Function drivemounter                   || 
'||Date 5/4/2011                           || 
'||========================================|| 
'|Objects, Variables, and Arrays *********** 
'==========================================/ 
'|Set number of items in array             '| 
'|Example: 3 drives would be (3,1) and 4   '| 
Dim ArrDrive(6,1)                          '| 
DriveCount = 7                             '| 
’|Define the Array to read                 ‘| 
    arrDrive(0,0) = "h:"                   '| 
    arrDrive(0,1) = “\uncpath1”            '| 
    arrDrive(1,0) = "i:"                   '| 
    arrDrive(1,1) = ”\uncpath2”            '| 
    arrDrive(2,0) = "j:"                   '| 
    arrDrive(2,1) = ”\uncpath3”            '| 
    arrDrive(3,0) = "k:"                   '| 
    arrDrive(3,1) = ”\uncpath4”            '| 
    arrDrive(4,0) = "l:"                   '| 
    arrDrive(4,1) = ”\uncpath5”            '| 
    arrDrive(5,0) = "m:"                   '| 
    arrDrive(5,1) = ”\uncpath6”            '| 
    arrDrive(6,0) = "n:"                   ‘| 
    arrDrive(6,1) = "\uncpath7"            ‘| 
‘|Define the Server to Ping for Net Check  ‘| 
Dim strSvr                                 '| 
    strSvr = "servertoping"                '| 
'========================================================================== 
Const DEBUGMSG = True    'True = Debug Messages, False = No Messages 
Dim oWShell 
    Set oWShell = CreateObject("WScript.Shell") 
Dim oWNet 
    Set oWNet = CreateObject("WScript.Network") 
Dim Target 
    Target = "." 
Dim oWMISvc 
    Set oWMISvc = GetObject _ 
("winmgmts:{impersonationLevel=impersonate}!\"&Target&"rootcimv2") 
Dim oPing 
    Set oPing = oWMISvc.ExecQuery _ 
("Select StatusCode From Win32_PingStatus where Address = '"&strSvr&"'") 
'|Main Run ================================================================= 
DriveMount        'Perform Function DriveMount 
Report            'Perform Function Report 
Wscript.Quit(0)   'Close Script 
'SubRoutines and Procedures ================================================ 
Sub Mount(letter,path) 
    oWNet.MapNetworkDrive letter,path 
End Sub 
'***************************************************** 
Sub Umount(letter) 
    oWNet.RemoveNetworkDrive letter 
End Sub 
'|Functions ================================================================ 
Function DriveMount 
'http://msdn.microsoft.com/en-us/library/aa394350(v=vs.85).aspx 
    For Each item in oPing 
        If IsNull(item.StatusCode) or item.StatusCode<>0 Then 
'**********Domain not reachable, unmount drives in arrDrive 
        x = 0 
          do until x = DriveCount 
             On Error Resume Next 
                Umount arrDrive(x,0) 
                       If debugmsg then msgbox "Umount"& vbcrlf _ 
                       &"Drive Letter: "&arrDrive(x,0)& vbcrlf _ 
                       &     "Error Number: "&Err.Number& vbcrlf _ 
                       &     "Error: "&Err.Description 
              Err.Clear 
        x = x + 1 
          loop 
        else 
'**********Domain exists, mount drives from arrDrive 

        x = 0 
          do until x = DriveCount 
             On Error Resume Next 
                Mount arrDrive(x,0), arrDrive(x,1)  
                       If debugmsg then msgbox "Mount"& vbcrlf _ 
                       &"Drive Letter: "&arrDrive(x,0)& vbcrlf _ 
                       &     "Error Number: "&Err.Number& vbcrlf _ 
                       &     "Error: "&Err.Description 
              Err.Clear 
         x = x + 1 
          loop 

        End if 
   Next 
End Function 
'***************************************************** 
Function Report 
      If Debugmsg then msgbox "Script completed" 
End Function 
'End ======================================================================
 

Copy and paste the code and save as a .vbs file (unless you have a specific editor, I recommend notepad to insure formatting is properly translated.)

Lets start with our 4 areas of interest we’ve focused on with the previous posts.

  1. Sequence
  2. Command usage
  3. Output usage
  4. Conditional logic

Sequence:

Sequencing and Command usage will be the most interesting topics this time around.  Unless wscript is pointed towards a specific function or sub, it reads everything in line.  This point is rather large when you consider BASH and POWERSHELL and a lot of other script languages which will error out if something is called that hasn’t been previously declared (speaking about functions and subroutines, variables must always be declared before usage).  However, the rule remains that everything is read one after another, or AS the interpreter is directed to read.

As a habit when I write my vbscripts I will almost always write them in this format. Variables, Run sequence, subs, functions.  There is no real reason for this outside of the fact I prefer to immediately see variables and prescribed run order.

Now lets look at our variable declaration and focus on what’s new.  An array, and a multidimensional one at that (more than two columns of data).  An array is an assortment of data stored in memory, think of it like a spreadsheet.  Row 0,1,2,3,4,5,6 and Column 0,1,2,3,4,5,6.  This would be a multidimensional array.  A simple array would be 1 Column (column 0) and however many rows as you wanted.  In this script I declare this array to be limited to 7 rows and 2 columns.  That sounds weird right, cause the declare clearly says (6,1) ?  Remember that 0 counts so 6 = 7 and 1 = 2, but you declare it by the integer value of the last entry.

Dim ArrDrive(6,1)

I then declare my Array entries by defining ArrayName(row,column) = data

arrDrive(0,0) = "h:"          '| 
arrDrive(0,1) = “\uncpath1”   '| 
arrDrive(1,0) = "i:"          '| 
arrDrive(1,1) = ”\uncpath2”   '|

...

From here on out when I need to utilize any of these pieces of data I can simply call them using their array name and position.  Another way to think of this is a shared variable name perhaps?

Now lets discuss our run sequence:

'|Main Run ================================================================= 
DriveMount      'Perform Function DriveMount 
Report          'Perform Function Report 
Wscript.Quit(0) 'Close Script 

This block of code dictates the run order of my script.  The first 2 are Functions I have defined, and the last is a wscript method to insure a clean exit (more on this in command usage).

Sequence is important. For organizations sake, I will take a quick and easy VBScript and build it in this fashion.  For me, and for others, it allows for simpler review and additions to be made to the script that could and will improve it’s functionality.  It’s also far simpler to isolate bad sections of your script and to target a specific function for testing.

Now let’s discuss the iteration statements that are introduced in the DriveMount function.  I will touch on the code more specifically when we break down what the script is doing, but an iteration statement is just a loop that continually performs a task until a certain condition has been met.  That could be reaching a certain number, that could be reaching the end of a file, that could be reaching the end of an array, etc.  In this script (although not how I would naturally do it) I’ve offered 2 concrete examples of vbscript loop statements for your review.

Finally, subroutines and functions.  Similar to labels, identical to the function I showed with BASH.  They are, put succinctly, sections of code that can be called by name to perform a fixed task.  In VBScript specifically a sub routine passes back no data, a function does.  So if I need to perform mathematical operations and reuse it as a variable elsewhere, I’d use a function*.  If I just need to move a file from one location to the next, a sub.  As a safe bet inside of VBScript, declare them all as functions, there is really no benefit I’ve found one way or the other.  I declare them as Sub and Function only to offer them as an example. Syntax for this is:

 Function Name                              Sub Mount                          

        Code to use                             Code to use 

End Function                                End Sub

If you wish to use arguments with these functions/subs you would use:

Function Name (arguement1, arguement2, etc)
 
                Code using arguments provided
 
End Function 

Command Usage:

Unlike our other scripting languages so far, the WSH doesn’t really utilize commands per say.  Command usage is actually called through a COM.  So when you build a vbscript, you spend the first portion of the script defining what COMs will be utilized in the script.  This is all part of my initial variable declaration (DIM statements or Declare Into Memory)

Dim oWNet 
    Set oWNet = CreateObject("WScript.Network") 

This is essentially creating the callable object or “commandoWNet for me to use moving forward.  What’s actually happening is I am using the WScript object to instantiate (create an instance of) the WshNetwork object for later method or property usage (exposing the object for manipulation).

This is how an object based language works, call the object, use the object, (and ideally) release the object.  When I later use these objects methods I will do so by invoking them.  Syntax comes in to play in these cases.

oWNet.MapNetworkDrive letter,path

That is the invocation of the WshNetwork MapNetworkDrive method which requires drive letter then path to map.  Hopefully that makes sense, it’s an extra step but allows for some amazing control and it remains rather light weight as it depends on existing objects for it’s control base as opposed to having them already built in.  However that’s not to say there aren’t commands built into the WSH but they are for conditional and iteration statements as well as integer, string, and date functions (w3schools.com has a nice breakdown as well) etc.

Output Usage and Conditional Logic:

For this script the output usage is in reading a ping response, mapping drives, and in exception handling (error catching).  We read the status code from the WMI Win32_PingStatus class object (list of WMI Classes) then use it as our condition.  We then loop through the array and perform the appropriate action while catching and clearing any potential errors.  We also redirect that error output to a popup message if DEBUGMSG Boolean is set to TRUE in our initial variable declaration.

Our conditional logic as stated before is in the form of IF statements.  I will demonstrate case statements in my PowerShell write up.  Case statements look for something (usually a specific string type) and respond in a defined way.  However a case statement is strictly defined where an IF statement is generally this or that, it will make more sense in the future I promise.


So what does this vbscript do?

In this script we open with our declarations.  We define our Array ArrDrive as a 7 row and 2 column array (0 counts, remember though declaration will be the last integer value so in this case: 6,1) and supply it with values.  We set a Boolean for debugging messages and we set our object instance for WScript.Network to oWNet.

We also start an instance of the WMI Service, and define a WQL that reads the ping status to a targeted server (strSvr) for us (Target=”.” means the local machine, it can be changed for remote PC name, more on wmi scripting).  (WMI is Windows Management Instrumentation, similar to the WSH objects it contains a series of namespaces, classes, methods, and properties to control the windows platform.  Essentially if you can do it on the Windows machine, there is a WMI namespace/class/method involved in it.)

Dim Target 
    Target = "."
Dim oWMISvc 
    Set oWMISvc = GetObject _
("winmgmts:{impersonationLevel=impersonate}!\"&Target&"rootcimv2")
Dim oPing 
    Set oPing = oWMISvc.ExecQuery _
("Select StatusCode From Win32_PingStatus where Address = ‘"&strSvr&"’")

Now that we have all that declared we move into our run sequence.  The Function DriveMount is first and it immediately performs a For Each statement through our oPing WQL and grabs the statuscode value and compares it with an IF statement.

For Each item in oPing 
         If IsNull(item.StatusCode) or item.StatusCode<>0 Then 

If there is no value, or the status code is anything but 0 (0 is a clean exit code, success, that’s what the status code is in its simplest form) then we aren’t in our appropriate network so we will begin a Dismount loop.  If the return value is 0 and is NOT null then we will perform a Mount loop.

Lets stop here for a second so I can explain this loop line for line, first the code sample:

‘**********Domain exists, mount drives from arrDrive
 

x = 0
  do until x = DriveCount 
     On Error Resume Next
        Mount arrDrive(x,0), arrDrive(x,1)   
              If debugmsg then msgbox "Mount"& vbcrlf _
              &"Drive Letter: "&arrDrive(x,0)& vbcrlf _ 
              & "Error Number: "&Err.Number& vbcrlf _
              & "Error: "&Err.Description 
     Err.Clear
x = x + 1
   loop 

We declare x as 0, we will enumerate this integer value with each pass of our iteration (loop).  Word of warning here, if using this method make doubly sure that you set your enumerate value outside of the loop or it will be re-declared each loop and become an infinite loop.

We do until x = DriveCount, we declared DriveCount as 7 at the start of the script.  This loop will loop 7 times.

On Error Resume Next, we will not let error outputs break our function, we will perform the next in line.

We run the Mount subroutine passing the letter and path arguments which we pull from our arrDrive array.  We use x to select our row (remember we are enumerating x) and select the appropriate column for the data to pass.

Now as an extra step, we check for the DEBUGMSG Boolean, if it’s TRUE, we generate a popup window that tells us what Subroutine is being used, drive letter, error number, and error message. (if the drives are already mounted it WILL error, the error is that the drives are already mounted, so this is helpful if we are chasing something down)

We clear the errors that may or may not have been generated.

Now we take x and we add 1 to it, so 0 becomes 1, 1 becomes 2, 2 becomes 3, etc on each successive pass.

Finally we terminate the statement with loop so that the WSH knows to return to the start of the loop statement.

Easy huh?  There is a lot of power to learning how to use loops and for each statements effectively.

I would normally have just made another for each statement against the array which would read it to the end, but I felt this serves as a great learning opportunity.

At this point our DriveMount function is complete so we move to the next Function in the run sequence which is Report.  Report runs a debugmsg check and tells us the script completed if debugmsg is true.

Now back to our run sequence where we close our script with

Wscript.Quit(0) 

This insures an exit code of 0 which is a universal “everything ran great, nothing to see here” output.

End Function

Next up we will be discussing PowerShell.  A very robust shell scripting language for the newer windows platforms (xp sp3+) that utilizes commands, .net, com, wmi, dcom, and the list goes on.  I’ll try to keep it lite.


Now for some bonus material, lets take a look at the Mount loop if I had used a For Each instead of a conditional loop:

x = 0
For Each object in arrDrive
    On Error Resume Next
       Mount arrDrive(x,0), arrDrive(x,1)
            If debugmsg then msgbox "Mount"& vbcrlf _
            &"Drive Letter: "&arrDrive(x,0)& vbcrlf _ 
            & "Error Number: "&Err.Number& vbcrlf _
            & "Error: "&Err.Description 
     Err.Clear
x = x + 1  
Next

You are probably thinking “this looks the same, why is this better?”.

When you perform a conditional loop statement it needs a stop point which is another factor you have to manage.  In this case we had to declare a maximum loop count (our DriveCount variable which was 7).  A for each statement will go through a list till the end and then move on.  You can think of a for each statement as “For Each item in whatever until the end.”

If you have any questions or corrections, please leave a comment and I’ll try and get back with you about it. For an unaltered version of this script to copy and paste, go here.


Stick to the Script Parts 1 – 4:

  1. Stick to the Script…
  2. Stick to the script #!/BIN/BASH…
  3. Stick to the script CreateObject(“Wscript.Shell”)…
  4. Stick to the Script PS C:> PowerShell …

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.