Just a link, but well done.
http://martinvalasek.com/blog/pictures-from-a-developers-life
Animated gifs to describe the day to day of a developer; or most personnel in an engineering level role of IT.
Just a link, but well done.
http://martinvalasek.com/blog/pictures-from-a-developers-life
Animated gifs to describe the day to day of a developer; or most personnel in an engineering level role of IT.
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:
Subroutines & Functions (They can be the same, but in WSH they are different)
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.
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 “command” oWNet 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: