Tuesday, August 12, 2008

ReDim

I have to admit that the way vbScript handles arrays is abysmal at best and hideous at worst. First of all, what is an array? It is a list. Pure and simple. vbScript expose things like multidimensional arrays, but that is just a list of lists. (or if there are three dimensions, a list of lists of lists).

OK let's declare an array


Dim arrFred(2)


So far so good. now some entries for the array.


arrFred(0) = 1
arrFred(1) = 54


OK so arrays use zero counting. That is the first element of an array is designated 0 and the last is length - 1. This is something to keep in mind as vbScript spends a lot of time flicking between 0 based and 1 based counting for various functions.

Now let's look at what the rant is really about. Say you want to make the list longer by one. Frequently done by people with say, shopping lists. Whoops forgot the eggs. I'll just write it on the end of the list. Lucky for us, there is the command


ReDim arrFred(3)
arrFred(2) = 42


So far so painless. Let's get back to calling that first value now we are at the shops and looking for the flour.


Wscript.Echo CStr(arrFred(0)) 'Outputs ""


That's right my chickadees, nothing. No output. And why? because when we did the ReDim. When you ReDim, your shopping list is torn up and thrown out. All values are returned to Null. Not very useful. So what options do we have? We could make the list impossibly long to begin with,


Dim arrFred(1000)


But my experience is that even if we make this really big, some user puts in more things than can really be dealt with by a really big number. I had a problem with a script that was designed to deal with around 20 servers. Someone then adapted my script to deal with 150 servers. If I had have used the really big number thought, then there would have been a failure there, as "really big" would have been 100 due to other constraints. So where is the answer? vbScript gives us the command


ReDim Preserve arrFred(arrFred.Length + 1)


This grows the array and keeps it's contents intact. Although why Preserve is not presumed for all arrays is quite beyond me and one of the dumbest things I have found in vbScript. If I ruled the world the code would be along the lines of.


arrFred.ClearAll
ReDim arrFred(3)


But I'm not. So I'll just highlight the issue.

Thursday, July 31, 2008

Chr(34)

Chr(34). What kind of drugs am I on with this rant? One of th emost frustrating things I have come across with vbScript is it's handling of odd characters. Say I want something that looks like

"Hello,
World."

In Perl this is a matter of typing in

print "\"Hello,\nWorld.\"";

The backslash works as an incoming special character next alert. Double quotes are special characters. It's almost self explanatory that way. The \n means new line. Let's write the same peice of text in vbScript.

Wscript.Echo Chr(34) & "Hello," & vbCRLF & "World." & Chr(34)

It's twice as long, contains an outside object call, two functions and a built in constant. People who say Perl is line noise should have a look at this first. And it isn't as if the scripty people at Microsoft didn't know about \n as a substitute for Carriage Return Line Feed. (new line for anybody who doesn't use dot matrix printers as their text out interface.) They use it for matching regular expressions.

So how does writing things to the screen get to be so messy? I think it's probably down to a battle of wills. For instance, in my job I get to code any way I like to. The decisions I make are my own. The style I use is used by one person on a team of 8. And the down side of this is that I have to know exactly what is going on and document it all to keep up our quality certification.

Because Microsoft has lots of coders and (probably more importantly) managers, we see a battle of wills with their flagship language. If a coder has a new method of making coding more consistent across a range of areas, he has to convince his manager, who has to convince his manager who has to care enough to inform two downstream managers that this should go ahead. That is a lot of convincing to make something go ahead. Not gonna happen.

Whereas Larry Wall when he designed perl started out making the decisions himself. A coder who wished to change the direction of perl needs only convince Larry (or whoever is pump king these days) that it is a good idea and the change will be made. And it doesn't even have to be that bad.

print '"Hello,
World."'


will also work. So we see the difference between the One True Way and TIMTOWTDI.

On a side note, I would suggest that making pop up dialog boxes with more than one line of information in them is probably a bad thing because most users don't read even that one line. The ones that do, probably won't bother with the second.

So what do we do with this horror? I suppose we could all give up and code in javascript or perl, but if you work in establishments such as mine, vbScript is mandated as "The" scripting language. The first problem is the double quote. vbScript refuses to handle it as anything besides the beginning or the end of a quote. Chr(34) is the only way to print this. It is built in. if you feel like it you could include a line such as

Const vbQuot = Chr(34)

which at least has the advantage of readability in the example.

Wscript.Echo vbQuot & "Hello," & vbCRLF & "World." & vbQuot

The other thing that bugs me is the vb prefix to internal constants. Yes it is nice to flag such things, but it puts me in mind of those applications that install under Windows that stick a shortcut in every conceivable place to give the application screen presence. Even when it is not needed. You get a desktop icon, a start menu icon, a quick launch icon, a toll tray icon that does nothing more than chew up memory and slow down start times. They then pin the icon onto your right click wherever possible.

The same thing can be said of the vb. I've paid for the product. Why are you trying to sell it to me over and over when I write a constant?

Enough rants. Must go to bed.

Monday, July 28, 2008

WhereFor art thou loop?

So there I was learning this vbScript thingy. I have a problem that requires a for loop. So I start applying logic rather than reading the manual.


For intFoo = 0 To intEndCount Step 1
Wscript.Echo CStr(intFoo) & ". Hello World"
Next
. . .


I fully expected never to greet the world. I am a cave dweller. Why would I? Logic dictates that any lazy parser would read the first line and say unto itself one of two things. Either "intFoo is already equal to intEndCount, therefore I can jump over this redundant code block and get on with my life." This would have been acceptable to me as I really only wanted the for loop to run if intEndCount was bigger than 0.

The other behaviour which would be less acceptable but at least logical would have been "Error 0xFOAD Dumb use of a for loop. Try RTFM and come back to me." (RTFM = Read that Fine Manual) I'd be quite happy to see such a message as this informs the user as to how clever the parser isn't and allows me to dumb down my code appropriately or pick another language.

What I wasn't expecting was this

0. Hello World


I sat there in wonderment. staring at my scratch pad for a full 30 seconds in disbelief. Why loop through once? Your condition has already been met. I tried some variations. Maybe upping the count to 1 would work

For intFoo = 1 To 1 Step 1


Almost the same result. Only the prefixing digit changes. So I go back to TFM. No mention of this strange behaviour. I asked 5 of my colleagues what they would expect. the range of answers went from zero loops, infinite loops, to error state. Nobody guessed that you get one loop out of it. I even went to the Microsoft Fanboi. The guy who likes Vista. He could not explain it. So rather than trying to bust my head on it, my code now features the following statement.

If intEndCount > 0 Then
For intFoo = 0 To intEndCount Step 1
. . .
Next
End If


The trouble is that all the looping functionality in vbScript WORKS THE SAME WAY. I would suggest that having multiple looping statements should give you subtley different functionality.

Tuesday, July 22, 2008

Option Implicit

To introduce myself, I'm a scripter for a reasonably large organisation. I came from a perl background doing php to interface with web pages. I now find myself writing in vbScript and missing a few things from the other side of the house.

Learning a new language you will find yourself scraping the net looking for any number of resources that will aid you in writing good code, but one of the frustrations I found was that Microsoft Scripting is not particularly consistent in their code management. There are any number of other forums that will give you band aid solitions for the "How do I..." code. However it gets very tempting to copy and paste from these sites without actually working out what you have done.

So todays lesson will be on two words. The first two words you should write in a vbScript without a comment in front of them.

Option Explicit


What does this do? It forces you to declare variables, it forces you to think about whether the routine you are writing is a Subroutine or a Function. In short, it forces you to stop writing spaghetticode and start thinking about what you are writing.

When faced with a new peice of code that needs to be written, my first step will be to write it out something like this

Option Explicit

' What do we want to do here?
' Open an ini file and check that it contains all the info we want
' grab the latest data from central server
' copy this data out using patching routines
' log all of the above

All of these steps break down quite nicely later on. I only have to fill in the blanks now with useful code. But now I can start using the limitations placed on me by Option Explicit to think about variable names.

At the moment I am coding a fancy "Copy Files" routine that should automate periodic data upgrades once out of testing. I found that this needed a tie in from four scripts to do the job. One to set up the "Golden Source", one to control how many servers to replicate to at a given time. (I'm lucky the system I work on tends to have more servers on it than a lot of organisations have workstations.). One to do the actual copying, and one to launch the product on a workstation at the end of it all. 4 scripts written by 4 people. Each with their own idea about how it should go.

My boss (Who incidentally wrote and maintained the scripts before me) told me that looking after this should be my job. Great. I open each one up and see the pain before me. Spaghetti code and undelcared variables everywhere. Job One to work out this mess? Our favorite friend, Option Explicit. Running the script now errors horribly, but starts to produce a list of undeclared variables and the scope they cover off on. I now have a list of variables and have tidied up some of the subroutines.

What I find is that accross all four scripts are variables that contain the same information, but are called three or four different names. So as part of the rewrite, all global variables are renamed the same thing so it is easy to flick from script to script and see where the information is going and what it is being used for.

The other thing that it does is allows me to reduce memory footprint. Not particularly important to me as I am working on servers and power workstations where any less than 4GB of memory is considered obsolete. But as I go hunting through the subroutines I meet up with some friends.

  • objFSO
  • objFS
  • objFileSys
  • filesys
  • fs

All prefixing

= CreateObject("Scripting.FileSystemObject")

Hmm. So what would happen if we have one name right through one script called one thing. We would be able to borrow this File System Object any time we want to do file operations. It's a handy class by the way. Does cool stuff. Not nessecarily predictably, but more on that in another rant.

So, because of Option Explcit, accross my four scripts, I only have to open the class once. Less of a memory footprint, and therefore more space for servers to do important stuff, like cope with an SQL call to push out a report to an executive.

Next time? why not have a look at things For a While Until we are Done.