May 31st, 2007

How Can I Wait Until One Application Has Quit Before Starting a Second Application?

Hey, Scripting Guy! Question

Hey, Scripting Guy! I have a script that starts two applications; however, the second application can not start until the first program has finished running. How can I ensure that my second program doesn’t start until the first one ends?

— OR

SpacerHey, Scripting Guy! AnswerScript Center

Hey, OR. Not too long ago the Scripting Guy who writes this column read an article about Walter Mossberg, who happens to write a technology column for the Wall Street Journal. This article speculated that Mossberg, who writes a column twice a week and then performs a few other duties, makes around $1 million a year. If any of you read this same article it probably made you think: “Hmmm, Walter Mossberg writes two columns a week and performs a few other duties, and he makes $1 million a year. The Scripting Guy who writes that other column writes 5 columns a week and performs a few other duties. Does that mean he makes $2.5 milliona year?”

To tell you the truth, Microsoft doesn’t like us to divulge salary information. However, we can tell you this: no, the Scripting Guy who writes this column does not make $2.5 million a year. Instead, Microsoft pays him exactly what he’s worth.

That’s right: ouch!

But that’s OK. Sure, Walter Mossberg might get the big bucks, but he never gets the satisfaction of writing a script that runs an application and then waits until that program has finished before it runs a second application:

strComputer = “.”

Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)

Set objProcess = GetObject(“winmgmts:root\cimv2:Win32_Process”)

errReturn = objProcess.Create(“Notepad.exe”, null, null, intProcessID)

Set colMonitoredProcesses = objWMIService. _ ExecNotificationQuery(“select * From __InstanceDeletionEvent ” _ & ” within 1 where TargetInstance isa ‘Win32_Process'”)

Do While True Set objLatestProcess = colMonitoredProcesses.NextEvent If objLatestProcess.TargetInstance.ProcessID = intProcessID Then Exit Do End If Loop

errReturn = objProcess.Create(“Calc.exe”, null, null, intProcessID)

Now, some of you – maybe even Walter Mossberg – might take exception with this script. Why not use a far simpler script, one that looks like this:

Set objShell = CreateObject(“Wscript.Shell”)

objShell.Run “Notepad.exe”, ,True objShell.Run “Calc.exe”

We actually have two reasons for that. For one, OR told us that he tried a script exactly like the one above, but, for some reason, it didn’t work: when the first process terminated the second process failed to start. We’re not sure why that might be the case, so we decided to try a different, albeit somewhat more complicated, route.

Second, and more important, our script can be used against a remote computer. You just need to do a couple things first. For one, assign the name of the remote computer to the variable strComputer. For another, get rid of the applications Notepad.exe and Calc.exe, and replace them with applications that: 1) don’t require a visible window; and, 2) will run and then automatically terminate themselves. (Scripts are a good candidate for this; so are batch files.)

Why those stipulations? That’s easy: for security reasons you cannot create a visible process on a remote computer. We can start Notepad on a remote machine, but that instance of Notepad will run in a hidden window and the user will never see it. And if the user never sees it it’s going to be awfully hard for him or her to use the application and then terminate it.

Make sense? Well, sadly, that doesn’t matter. Like it or not, that’s just the way it works.

Note. What if you do need to start a GUI application on a remote machine, and what if you do need that application to run in a visible window? For one approach to that problem see this Hey, Scripting Guy! column.

As for the script itself, we begin by connecting to the WMI service on the local computer. We then use this line of code to bind directly to the Win32_Process class:

Set objProcess = GetObject(“winmgmts:root\cimv2:Win32_Process”)

Once we’re bound to the Win32_Process class we can start an instance of Notepad by using this line of code:

errReturn = objProcess.Create(“Notepad.exe”, null, null, intProcessID)

As you can see, we simply call the Create method followed by four parameters:

•

Notepad.exe, the name of the executable file (or script) that we want to start. If this file or script is not in your Windows path then make sure you specify the full path name here.

•

Null, an optional parameter for specifying a working directory. Because the default directory for Notepad is fine for our purposes we pass a Null parameter, which is the same thing as saying “use the default value.”

•

Null, another optional parameter which enables us to specify startup options for the process. Again, the default values are fine for our purposes, so we pass a Null parameter here, too.

•

intProcessID, an out parameter. When the script runs and Notepad starts up, the process ID will automatically be assigned to this out parameter. That’s how we can keep track of our copy of Notepad, even if there are multiple copies of Notepad running on the computer.

As soon as Notepad is up and running we set up an event subscription to monitor the __InstanceDeletionEvent class:

Set colMonitoredProcesses = objWMIService. _        
    ExecNotificationQuery(“Select * From __InstanceDeletionEvent ” _ 
        & ” Within 1 Where TargetInstance ISA ‘Win32_Process'”)

As the name implies, instances of this class are created any time an object is deleted. Of course, we’re not interested in all objects; we’re only interested in process objects. That’s why we add the Where clause that limits our event subscription to objects (the TargetInstance) that are members of (ISA) the Win32_Process class.

Note. OK, so maybe that is a lot of information packed into a single paragraph. For a kindler, gentler introduction to WMI events, see our Scripting Week 2 webcast on this very subject.

That brings us to this block of code:

Do While True
    Set objLatestProcess = colMonitoredProcesses.NextEvent
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
        Exit Do
    End If
Loop

What we’ve done here is set up an “endless” loop that will pause the script until a process is deleted; that’s what the first two lines of code are for:

Do While True
    Set objLatestProcess = colMonitoredProcesses.NextEvent

When line 2 executes, the script will “block” (pause) until a process object gets deleted (that is, until a process is terminated). When a process does get deleted we then check to see if the process ID of the terminated process is equal to the process ID of the process we created:

If objLatestProcess.TargetInstance.ProcessID = intProcessID Then

Because process IDs must be unique at any given time, if the IDs match that can only mean one thing: the instance of Notepad we created has been terminated. In turn, we then call the Exit Do statement and exit the “endless” loop.

And what if the process IDs don’t match? In that case, that must mean our instance of Notepad is still running; it was some other process that was terminated. Thus we loop around and wait for the next event notification (the next process to be deleted).

When we finally do exit the loop we simply call the Create method and start up our second application, in this case, Calc.exe:

errReturn = objProcess.Create(“Calc.exe”, null, null, intProcessID)

And then we’re done.

You know, that sounds like a column that’s worth $2.5 million dollars, doesn’t it? Especially when you consider how much other useful tasks the Scripting Guys perform. For example, next week the Scripting Guys will be manning a booth at TechEd 2007. If you’re attending TechEd (and you are attending TechEd, aren’t you?) then swing by the Partners Expo Hall and look for the CMP Media Booth (booth 1301): the Scripting Guys will be there, handing out copies of Dr. Scripto’s Fun Book and giving away Dr. Scripto bobblehead dolls. In addition, the Scripting Guy who writes this column will be counting his money … which means he’ll have plenty of time to chat with anyone who drops by.

Note. So will Walter Mossberg be manning a booth at TechEd 2007? Not as far as we know. $1 million a year and he can’t even man a booth at TechEd. Interesting ….

Author

0 comments

Discussion are closed.