May 2nd, 2007

How Can I Ensure That a Script Runs Only Once on a Computer?

Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I ensure that a script runs only once on a computer?

— ML

SpacerHey, Scripting Guy! AnswerScript Center

Hey, ML. You know, we were going to start off today’s column by saying, “Show us your bobbleheads!” However, we didn’t want to have to explain to Microsoft people that there was nothing risqué or offensive in that statement; instead, all we wanted is to see what people have done with their Dr. Scripto Bobblehead dolls. (Hard as this is to believe, not everyone who works at Microsoft is a fun-loving, carefree type who enjoys a good laugh every now and then.) So while we do want people to show us their bobbleheads, we’re not going to ask people to show us their bobbleheads. After all, the last thing the Scripting Guys want to do is get themselves into trouble.

In other words, whatever you do, don’t show us your bobbleheads. Thank you.

Instead, if you were one of the lucky 250 people who won a bobblehead during the 2007 Winter Scripting Games we’d love to know what you’ve done with it. Just snap a digital photo and send it to scripter@microsoft.com (in English, if possible). We’ll publish your photo in the Script Center, right alongside the cool picture we received from Switzerland’s very own Ben Simkins:

Spacer

 

Note. Yes, that is Dr. Scripto. How are you supposed to tell the difference between Dr. Scripto and Scripting Guy Jean Ross? Here’s a hint: Dr. Scripto doesn’t have any staples in his head.

OK, let’s get down to business. To tell you the truth, we don’t know of a foolproof way to ensure that a script runs only once on a computer; there might very well be such a way, but it’s probably too complicated to configure and even more complicated to enforce. Therefore, we’ve come up with a simpler solution, one that might not be 100% foolproof, but at least is easy to implement.

What we’re going to do is this: each time the script in question runs, the first thing it’s going to do is check a value in the registry. If that value is No that means the script has never run on this computer. As a result, the script will go ahead and run, and then, upon completion, change the registry value to Yes. Logically enough, if the registry value is Yes that means that script has run on the computer. So what happens if you try to run the script a second time? That’s easy: the script checks the registry, sees that it has already run, and then automatically terminates itself. Not the fanciest approach in the world, but it should work.

Of course, before we can actually get the script to work we need to create a registry value. To accomplish that task we need to do two things. First, we need to create a new registry key (in the HKEY_LOCAL_MACHINE hive) named AdminScripts. Once that’s done we’ll then need to create a new registry value named Script 1 inside the AdminScripts key. Here’s what the script that accomplishes both of those tasks looks like:

Const HKEY_LOCAL_MACHINE = &H80000002

strComputer = “.”

Set objRegistry = GetObject(“winmgmts:\\” & strComputer & “\root\default:StdRegProv”)

strKeyPath = “SOFTWARE\AdminScripts” objRegistry.CreateKey HKEY_LOCAL_MACHINE, strKeyPath

strValueName = “Script 1” strValue = “No” objRegistry.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

Note: If you run this script on Windows Vista, you need to open the command prompt window as an administrator. Right-click Command Prompt in the Start menu and select Run As Administrator. We remind you of this because if you don’t Run As Administrator, the script will run just fine with no errors. This seems fine, until you discover that you don’t have a new registry key.

As you can see, we start out by defining a constant named HKEY_LOCAL_MACHINE and setting the value to &H80000002; this constant tells the script which registry hive we want to work with. We then connect to the WMI service on the local computer, binding directly to the StdRegProv class, which just happens to live in the root\default namespace:

Set objRegistry = GetObject(“winmgmts:\\” & strComputer & “\root\default:StdRegProv”)

And sure, you can run this script against a remote computer; just assign the name of that remote computer to the variable strComputer. For example, this line of code causes the script to run against the remote machine atl-fs-01:

strComputer = “atl-fs-01”

Once we’ve gotten this far, creating the new registry key is a piece of cake: all we have to do is assign the registry path to a variable named strKeyPath and then call the CreateKey method. That’s what we do with these two lines of code:

strKeyPath = “SOFTWARE\AdminScripts”
objRegistry.CreateKey HKEY_LOCAL_MACHINE, strKeyPath

As soon as the registry key exists we can then add a new value to that key. To do that we first assign the value name to a variable named strValueName; after that we assign the default value (No) to a variable named strValue. That’s what these two lines of code are for:

strValueName = “Script 1”
strValue = “No”

And then we simply call the SetStringValue method, which not only creates the Script 1 registry value but – as an added bonus – assigns it the value No to boot:

objRegistry.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

That was easy, wasn’t it? We might add that we opted to approach today’s problem by splitting the task into two parts: first we created the registry key and value (that’s the part we just showed you), then we created (or, to be more accurate, are about to create) a second script, the script that we want to run only once per computer. Could we have combined these two scripts into a single script, one that checks for the presence of the registry value and, if it doesn’t exist, goes ahead and creates it? Sure. For information on verifying whether or not a registry value exists see this column from our oldies-but-goodies section.

Note. Why didn’t we just combine these two scripts in the first place? Mainly because we wanted to keep things simple: in our experience, it’s typically easier for people to read and understand two 10-line scripts than it is to read and understand one 20-line script. And trust us, when it comes to being simple no one is simpler than the Scripting Guys.

So what about that other script, the one we want to run only once on a computer? Well, here’s a quick-and-easy version that demonstrates the concept:

Const HKEY_LOCAL_MACHINE = &H80000002

strComputer = “.”

Set objRegistry = GetObject(“winmgmts:\\” & strComputer & “\root\default:StdRegProv”)

strKeyPath = “SOFTWARE\AdminScripts” strValueName = “Script 1” objRegistry.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

If strValue = “No” Then Wscript.Echo “This script will now run.” strValue = “Yes” objRegistry.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue Else Wscript.Quit End If

Note: Same thing here about Windows Vista: Run As Administrator if you actually want this script to work.

As you probably noticed, there are quite a few similarities between this script and the one we just showed you. As we did before, we define a constant named HKEY_LOCAL_MACHINE and then connect to the WMI service. Once that’s done we assign values to the variables strKeyPath (the path within the registry hive) and strValueName (the name of the registry value we want to read). That brings us to this line of code:

objRegistry.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

Here we’re using the GetStringValue method to read the registry value Script 1. As you can see, we pass this method four parameters:

HKEY_LOCAL_MACHINE, representing the registry hive.

strKeyPath, representing the path with that hive.

strValueName, representing the registry value we want to read.

strValue, an “out” parameter for storing the value read from strValueName.

And you’re right: we didn’t assign a value to strValue, did we? That’s the beauty of out parameters: we don’t have to do anything. Instead, we merely provide a variable name to the GetStringValue method, and GetStringValue then goes ahead and assigns the appropriate value to the variable for us.

Just WMI’s way of saying thanks.

Needless to say, our next step is to determine what value did get assigned to strValue:

If strValue = “No” Then

If strValue is No that means that the script hasn’t been run on this machine yet. Therefore, we go ahead and run this script. In our sample script, that simply means echoing a value back to the screen:

Wscript.Echo “This script will now run.”

All you have to do is replace the preceding line of code with the line or lines that you want your script to execute.

Now that the script has run we need to change the value in the registry from No to Yes. That’s what this block of code is for:

strValue = “Yes”
objRegistry.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

Nothing too fancy here: we assign the value Yes to the variable strValue, then call the SetStringValue method to write the new value to the registry.

Now, suppose strValue didn’t equal No. In that case, we’re assuming two things: that strValue equals Yes, and that the script has already run on this computer. What do we do in that case? We do what the Scripting Guys do best: nothing. Instead, we just call the Quit method and terminate the script:

Wscript.Quit

And that should do it. Again, this isn’t foolproof: obviously someone could go into the registry and manually change the value of Script 1 from Yes to No. If that happens then the script can be fooled into thinking it’s never run on this particular machine. But it’s generally safe to assume that won’t happen.

Note. If that is a concern you could set up auditing on the key in question or monitor that key for changes. That won’t prevent people from making the change, but at least you will be notified if and when they do.

As for sending us pictures, well, we realize that most people didn’t win a Dr. Scripto bobblehead doll. What can we do for those of you who didn’t win? Nothing. Next time try a little harder, huh?

No, hey, just kidding. As it turns out, the Scripting Guys have a few bobbleheads leftover, bobbleheads which are currently being held under lock-and-key at Fort Knox. We’ll be giving away a bunch of those at TechEd 2007; if you’re planning on going to TechEd be sure and swing by the TechNet Magazine booth, say hi to the Scripting Guys, and sign up for the drawing. And if you’re not going to the conference, well, don’t despair; in conjunction with TechEd we’ll be holding a little contest which will give everyone a chance to win a bobblehead. We’ll provide more details on that in a few weeks.

Just remember, if you do win, don’t show us your bobblehead. Just send us a picture of your prize instead.

Author

0 comments

Discussion are closed.