How Come You Guys Don't Use .WSF Files?
Posted by Greg Stemp. Ok, so yesterday I got a bit lackadaisical, and screwed up in my blog entry. Therefore, I vowed that today I wouldn’t say anything dumb whatsoever. And, of course, the only way for me to do that is to just not say anything at all. So, see ya tomorrow.
Nah, just kidding. Instead, I thought I’d do something potentially dumber, and address a question that we get asked every now and then, something I don’t know if we’ve ever really answered, “How come you guys never use Windows Script Files?” And that’s a good question; as one recent email pointed out, Windows Script Files (.wsf files) are the latest twist on Windows Script Host scripts. As sort of the public face of scripting, “ … shouldn’t you guys be promoting the most recent innovations?”
To tell you the truth, I don’t know; I have to admit that I’m not always a fan of the latest innovations. For example, fellow Scripting Guy Dean Tsaltas and I once toured Microsoft’s experimental home of the future, a showcase for all the latest innovations in home living and lifestyle. To be honest, I wasn’t all that impressed; although there were some admittedly cool gadgets, I’m not so sure I want my refrigerator calling the grocery store any time we’re out of milk. In fact, I’m not so sure I want my refrigerator calling anybody, regardless of how much milk we have or don’t have. I just don’t like the idea of my household appliances spending all day on the phone when their supposed to be keeping my lettuce cold or microwaving my burrito. “Hey, I’m on the phone right now. Can’t you make your own toast for once?” Scary.
So what about .wsf files: thumbs-up or thumbs-down? That’s something we Scripting Guys actually discussed a long time ago, and the verdict was split: some of us liked the idea of using .wsf files, others didn’t. In turn, that means that what you’re about to read is just my opinion, and doesn’t necessarily reflect the combined wisdom of the Microsoft Scripting Guys. (But until they start blogging, like they all promised to, then I guess my opinion is the only one that matters.)
For those of you that aren’t familiar with Windows Script Files, the .wsf format allows you to use some predefined XML tags within a script. Does that have any advantages? Maybe; that’s something we’ll talk about in a moment. But for the purposes of the Script Center and the scripting documentation we produce, it has one definite disadvantage, which is why we ultimately chose not to use them. Here, for example, is the ubiquitous Hello, World script, written as a .vbs file:
Wscript.Echo "Hello, world."
(The classics never go out of style, do they?)
Now here’s the same Hello, World script written as a .wsf file:
<job id="Job1">
<script language="VBScript">
Wscript.Echo "Hello, world."
</script>
</job>
You can see the immediate problem (at least for us): wsf files, at a minimum, add four extra lines of code to every script, lines of code that – in the case of a very simple script, such as the one above – really don’t add anything of value. We made the decision to keep our scripts as short and as bare-bones as possible; as I’ve explained earlier, that’s why we typically don’t include error-handling, that’s why we typically don’t declare variables, that’s why we typically don’t include code for parsing command-line arguments. Having adopted that philosophy, it didn’t make any sense to take all our scripts and enclose them in four lines of extraneous XML tags. One line of code or five lines of code? No contest.
On the other hand, there’s no doubt that our needs don’t exactly reflect the needs of real, live system administrators. So while the Scripting Guys might have little use for .wsf files, that doesn’t mean they aren’t useful for other people. Or does it? Let’s take a look at some of the capabilities found in Windows Script Files, and then weigh the pros and cons of each.
Include files. This probably seems like a no-brainer: after all, everybody and their dog wants the ability to use include files within a script. For newcomers to the world of scripting, an “include file,” in an admittedly simplistic definition, is a file you can reference from another file. In the case of a script, you can actually run Script A from within the Script B as if the code from Script A was part of Script B. (Eep; that last sentence doesn’t even make any sense to me.)
Let me try to explain this a little better. For example, suppose you have a script that has all the code required to connect to a SQL Server database. Using the .vbs format, you’d have to copy and paste that code into each script that needed to connect to the database (and then, of course, if that code ever changed, you’d have to change every script that uses the code). With an include file, you write then code once, then call that definitive, connect-to-SQL-Server script from within innumerable other scripts. If the code ever has to change, you just modify that lone include file, and you’re done.
So what’s wrong with that? Nothing really. (Well, it does make it a bit harder to share scripts. After all, you can’t just a copy a script and give it to someone; you have to make sure you give them all the include files as well.) For Scripting Guy purposes, however, include files pose a couple of problems. For example, here’s a .wsf file that connects to a database and then does the Hello, World thing again:
<job id="Job1">
<script language="VBScript" src="connect_to_sql.vbs">
</script>
<script language="VBScript">
WScript.Echo "Hello, world."
</script>
</job>
As you can see, from an educational point of view, we have a problem: a lot of the things that are going on in this script are “hidden” from view; that’s because they’re taking place in the include file. If our goal is to show people an example of a script that connects to a database and then performs some action, we’ve only done half the job; after all, you don’t see the code that connects to the database.
The other problem we have is logistical: you can’t just copy the script above and run it. Instead, you have to copy not only the script shown above, but also Connect_to_SQL.vbs. As much as possible, we try to create stand-alone scripts, scripts that you can just copy and run. Obviously we can’t do that if a script has an include file.
However, I’d be interested in hearing whether any of you use (or think you might use) include files for system administration scripts. What kind of things do you put in those include files? Why? Is this capability useful enough that we ought to draw more attention to it?
Access to type libraries. Many COM objects require you to specify certain values when carrying out different tasks. For example, when you use the FileSystemObject to work with text files, you need to specify whether a file is being opened for reading, for writing, or for appending. To do that, you either have to pass the name of a constant (ForReading, ForWriting, ForAppending), or the value assigned to that constant (1, 2, 8). Both the names and the values of these constants can be found in the FileSystemObject’s type library. Programming languages such as VB.NET or C# can access type libraries directly; scripting languages typically cannot. Because of that, you have to manually define constants such as ForAppending when writing a WSH script.
Unless, that is, you happen to be using the .wsf format. In that case, you can use the <reference> tag to gain direct access to the type library. Notice that, in the following script, we don’t define the constant ForWriting, However, when we run this script the correct value (2) will be echoed to the screen. Why? Because the reference tag looked at the type library and got the value of ForWriting for us:
<job id="Job1">
<reference object="Scripting.FileSystemObject">
</reference>
<script language="VBScript">
WScript.Echo ForWriting
</script>
</job>
At first glance this seems pretty handy, and it is. My only question is this: How often do you need to refer to type libraries in system administration scripts? You will occasionally need to do this with ADSI and you’ll always need to do this with the FileSystemObject. But very few WMI scripts require access to a type library. Furthermore, in a lot of cases you only need to reference a handful of constants anyway. For example, we could rewrite the above script with just two lines of code:
Const ForWriting = 2
Wscript.Echo ForWriting
Again, this is not the most realistic example, but any time you use the FileSystemObject to read and write text files you’ll use no more than three constants: ForReading, ForWriting, ForAppending. So: Is it worth including all the XML tags just to avoid defining these constants? In the case of the FileSystemObject, I’m tempted to say no.
But what about cases involving other COM objects? Well, even then I’m still not convinced. The problem is that the <reference> tag doesn’t actually tell you what the defined constants are (WSH knows what the constants are, but it’s not telling). For example, the WMI provider for System Restore uses a number of constants, and if you know what the names of those constants are then you can use the <reference> tag and have at it. But how many people know the names of the constants for System Restore? (No, I mean besides you. Oh, and that other guy over there.) You’re gonna have to look these up anyway, so I’m not sure that it’s any less work to just copy them after you do look them up.
Besides, for educational purposes I actually like seeing the constants defined right in the script:
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
I think this makes it a little bit easier for people to understand what these things are. Otherwise, and until you are conversant with the FileSystemObject, something like ForWriting seems to materialize out of the blue. But, then again, I tend to look at scripts as teaching tools rather than production tools. What do you guys who actually use scripts think about this?
Multiple jobs. Windows Script Files allow you to divide scripts into jobs. For all intents and purposes, jobs are separate scripts that run only when explicitly called upon. For example, the following sample script has two jobs (Job1 and Job2). If you just run the script as-is, with no parameters, only Job1 will run. To run Job2, you need to specify Job2 as a parameter; for example:
cscript myscript.wsh //job:job2
To run both jobs, you have to specify them both as parameters:
Cscript myscript.vbs //job:job1 //job:job2
Here’s what the code looks like:
<package>
<job id="Job1">
<script language="VBScript">
WScript.Echo "Hello, world."
</script>
</job>
<job id="Job2">
<script language="VBScript">
WScript.Echo "Hello again, world."
</script>
</job>
</package>
I have to admit that this feature has me a little stumped. I can make a case for the other capabilities of .wsf files (e.g., direct access to a type library), but I’m not sure why you would want to divide a script into multiple jobs. Yes, you can call an individual job within the script, but you could also call an individual function within a script. In addition, suppose I had one script with 50 jobs in it. How do I know what jobs are in that script, and how do I know what names have been assigned to those jobs? I’d probably find it easier to have 50 little scripts, each with a readily-identifiable file name, as opposed to one 50-job script. But I readily concede that I might be missing something here. Does anybody out there use .wsf files with multiple jobs? If so, how come?
Multiple languages. Not only do .wsf files allow you to write scripts that have multiple jobs, but each of those jobs could theoretically be written in a different language. Here, for example, is a variation on our multiple job script; notice that Job1 is written in VBScript, and Job2 is written in Jscript:
<package>
<job id="Job1">
<script language="VBScript">
WScript.Echo "Hello, world."
</script>
</job>
<job id="Job2">
<script language="JScript">
WScript.Echo("Hello again, world.");
</script>
</job>
</package>
I suppose this gives you added flexibility, but I’m not sure how realistic it is. After all, suppose you were writing a script that either started or stopped a service. Is there any reason why you’d want to start the service using VBScript and stop it using Jscript? That might not be a very good example, but – again, recognizing the fact that our primary focus is on system administration scripting – are there reasons why you might want to write half of a script using VBScript and half a script using Jscript? If so, I’d be interested in hearing them. One reason I can think of not to do this is the fact that it makes it very hard for someone else to edit/modify your script; after all, they’d have to be conversant in at least two scripting languages. A lot of system administrators have taken the time to learn one scripting language; I’m not sure how many have taken the time to learn two.
For us, of course, this was an easy one: Because we write all our scripts in VBScript, the fact that .wsf files allow you to use multiple languages in a single script wasn’t exactly a big selling point.
Usage instructions. One thing that .wsf files do provide is nice usage instructions. Usage instructions are the bits of help text that appear when you type the script name followed by /? or when you fail to enter the proper command-line arguments. Windows Script Files make it very easy to add usage instructions to a script. For example, all you have to do to have instructions displayed when a user types /? is to add the <runtime> and <description> tags:
<job id="Job1">
<runtime>
<description>
Here are usage instructions for running this script.
</description>
</runtime>
<script language="VBScript">
WScript.Echo "Hello, world."
</script>
</job>
Again, pretty cool, but is it enough to bother with? When we first looked at the XML elements for arguments, we got kind of excited. (Well, as excited you can get by looking at XML elements.) That’s because you can mark an argument as being required. Combined with the usage stuff, we thought this was going to be really cool; we thought that if you started the script without one of the required arguments that the script would automatically quit and display usage instructions. Alas, it didn’t work that way; marking an argument as required is useful for someone reading your code, but it doesn’t affect the way the script runs. In fact, although the usage stuff is pretty cool, that’s about it when it comes to argument handling: you actually have to use the same WSH code for handling arguments that you would in a .vbs file. What you get from the .wsf format is a more automated way to display usage instructions; what you don’t get (and what would arguably be more useful) is a more automated way to handle arguments. You can mark an argument as required, but it’s still up to you to write the code to see if this required argument was supplied and, if it wasn’t, to take some sort of action.
To me, the usage stuff perfectly sums up the .wsf format: it’s got some really nice features, but I’m not convinced that these features are all that useful, at least not for system administrators trying their hand at script writing. On the other hand, I have no particular dog in this hunt; if someone can convince me that the .wsf format is superior than the plain old .vbs format, well, I’m more than willing to listen. (And I know at least one Scripting Guy who’s champing at the bit to see us start using .wsf files.) I’d love to hear opposing viewpoints on this issue.
Comments
Anonymous
February 24, 2004
I used .wsf files last year :)
I really liked the usage-tags, but it would be really nice if it took care of error-handling for usage as well. (Seems like this will come in MSH (see http://microsoft.sitestream.com/PDC2003/ARC/ARC334.htm), so I'm looking forward to that one! Lot's of other features to look forward to in MSH as well :))
What I used the .wsf scripts for was to run tests of a system written in C++. So, with that background I really liked the include-feature as well :). Lot's of the tests had things in common, so for me it was natural to split those out in it's own file and include it.
For me the WSH environment is nice to create tests, since it's easy to get information from the system+network using WMI and such. Creating the tests with c++ would've taken a lot more time and effort IMO.Anonymous
February 24, 2004
I used wsf files to write scripts that generated software patches, lots of file copying, calling of regssvr32 stopping/starting services, MTS package stuff.
Then recently, I rewrote it in C# - I'm an intellisense kind of a guy! Much nicer environment to code in, even for 'scripty' tasks.
I used the 'job' facility, but found that ultimately it caused me more confusion than it helped. I had a bunch of BAT files that ran other batch files that ran jobs in the WSF file. What a mess!
Can't see myself using WSF again, but I think I am one of those people who is 'allergic' to run-time checked languages in general. I hated python too. :-)Anonymous
February 24, 2004
I've been using WSF scripts for more than a year now, and I do not make .vbs scripts anymore. Why?
I don't use the 'multiple jobs' feature.
I don't use the 'multiple languages' feature (I'm a VBScript guy)
I don't use the 'type library' feature.
...
But one simple feature alone makes it worthwhile, one you didn't mention. It's the integrated option parsing (which is related to your 'usage' feature)
I can specify my options in any order, not use them if I don't want to and simply use WScript.Arguments.Named to access them.
I've added a small wrapper library I include in all my scripts that allows me to do
sSITE =GetNamedString("site","none")
(get the /site:yyy option, or if it's not specified, give me default value 'none')
E.g: one of my scripts is a timer/stopwatch.
Usage: waTimer.wsf CMD PARAM [/total:value] [/bar:value] [/calc:value] [/unit:value] [/scale:value] [there's more but you get the picture ...]
I can call it as
waTimer STOP COPYJOB /total:1000 or
waTimer /total:1000 /scale:60 STOP COPYJOB /unit:KB/min
Trivial, but really handy.
Another thing I use in WSF is the <resource> tag:
<resource id="History">
* v1.1 - 2004-02-24 - Bugfix - enhanced performance
* v1.0 - 2004-01-13 - First public release
</resource>
I use it to have all my version/name/copyright/... information in easy editable tags in the first 20 lines of my script, and then use them as getResource("History") further on. This is a nice-to-have, I know.
If you want to see the templates I made for .wsf scripts, check out http://winadmin.sourceforge.net .
Love the blog by the way,
PeterAnonymous
February 24, 2004
I've used the .wsf format in my scripts for about 6 months now and use it mainly for the usage instructions and to include a standard set of subroutines (mainly logging and error handling related) into my scripts. Sure, I could just cut and paste the routines and have a single file, but I have a deployment solution that comprises several scripts and each needs access to the logging and error handling routines. Including a single file simply means there's less code to manage.
I take it you're not a .wsf file fan then?
So when are Dean, Bob and Ethan going to contribute to this blog? Seems a bit unfair to leave all the work to one man! Which one of the is pro- .wsf?Anonymous
February 26, 2004
I've posted some comments following up on various issues raised in this entry.
http://weblogs.asp.net/ericlippert/archive/2004/02/25/80139.aspx#80450Anonymous
March 01, 2004
Eric Lippert's comments are to the point.
However I'd like to point out that .wsf messes up the structure of the script. You really don't want to have all that extranous stuff there.
Includes and constants are nice, but it is more important that the script reads well. BTW A readable programming language is usally more verbose than the other ones.
greetings,Anonymous
March 05, 2004
The comment has been removedAnonymous
March 05, 2004
Got me!
Lost my leading whitespace. sigh Live and learn.Anonymous
March 05, 2004
The comment has been removedAnonymous
April 27, 2004
a way to avoid copying .vbs file into the .wsh file is to make .cls files instead.
Just add the following to your .vbs file and change the extension to .cls
class classname
.vbs file code
end class
and then in the .wsh file add at the top:
<script language="VBScript" src="ClassObjects ... the path name...clsname.cls" />
and there you are! No more copying huge chunks of code!Anonymous
April 27, 2004
The comment has been removedAnonymous
April 29, 2004
Well I too resisted bothering with WSF files until very recently. The change came as my company got acquired by a big lumbering multinational, whose techs had little experience with scripting.
I wrote a script to go out and force a "gpupdate /force" on all the computers in a particular OU to use in case of an emergency where we were blocking an executable using Software Restriction. I use WSH Remote Scripting, since some of the boxes seem to have configuraton pecularities that cause WMI's Win32_Process object to gag when run remotely. I like to spawn a separate Controller process for each targeted PC. That way the parent script doesn't have to wait around for a powered-down PC to time out before continuing to the next object. So I actually have three scripts: parent, a spawned controller script which in turn spawns a remote script.
Do I dare send three scripts to these guys and hope that months after my term date (yup, they're closing us down and telling us to relocate or get out) they'll have kept all three in the same folder when time comes to actually use it? Uh, I decided to wrap them all into a single WSF file, and enjoy the quick and dirty objArgs.ShowUsage() support for free.
Now that I have the boilerplate WSF set up, I'm finding myself using it for most of my administrative scripts that have to go out and connect to lots of PCs in turn, even if I don't anticipate that they'll get used by some poor Administrator Of The Day months after I'm gone :-)Anonymous
July 30, 2004
How to use wsf file with C#?Anonymous
August 02, 2004
10000Anonymous
August 02, 2004
10000Anonymous
June 02, 2005
In ASP we've always been able to store common routines/classes in a separate file and use an #include...Anonymous
September 30, 2007
PingBack from http://susanneurich.wordpress.com/2007/10/01/warum-man-fur-wsh-scripte-das-wsf-format-verwenden-sollte/Anonymous
January 21, 2009
PingBack from http://www.keyongtech.com/1941758-incude-statement-is-there-suchAnonymous
June 07, 2009
PingBack from http://besteyecreamsite.info/story.php?id=1568Anonymous
June 15, 2009
PingBack from http://workfromhomecareer.info/story.php?id=22735