
Today we’re going to tell a story of creation. While our story contains no religious significance (unless maybe you happen to worship some sort of scripting deity), we’ve borrowed from just one of many tales of creation to tell our story.
It is not good that the man should be alone; I will make him a help meet for him.
-- Genesis 2:18
Whether you happen to be a Creator (with an uppercase C) or just a creator (with a lowercase c) you need to be interested in and concerned about the welfare of your creations. In the preceding quote, God, the Creator, was concerned that Adam would be sad having to live by himself, and so he created Eve. (Granted, there are times when men wonder whether creating women was such a good idea, and vice-versa, but …. Besides, it’s the thought that counts, right?)
And what about you, the creator of scripts that use Microsoft Agent? Are you concerned about your creations, the little characters you display on screen? Sure, your agents look happy, always smiling and talking and doing cute little tricks. But, deep down inside, are they really happy? Sneak a peek some time when they think you aren’t looking. Why does Genie sigh like that all the time? Could that be a tear in Merlin the Magician’s eye? Why is Peedy the Parrot sticking his head in the oven? Peedy, no!
The truth is, Microsoft Agent characters are no different than other people (OK, except for the fact that they aren’t exactly people in the first place). Agents need companionship, they need someone to talk to, they need “a help meet.” (Or at least we assume they do. We’re not totally sure what that is.) And as the creator (with a lowercase c) it’s your job – nay, your obligation – to provide your creations with everything they need.
That’s what this article is all about: in this article we’re going to show you how you can display multiple agents on screen, as well as how you can get those agents to interact and converse with each other. We should also note that this is not an introductory article on writing Microsoft Agent scripts. If this is your first experience in scripting with Microsoft Agent, then we recommend you read Microsoft Agent: Teach Your Scripts to Talk before doing anything else. That article will make it easier to understand the code we’re going to show you, and will also help you download and install the Agent characters (just in case you haven’t already done so).
That said, let’s get on with the show. After all, it is not good that Robby the Robot should be alone.
| I’m Sorry; Did You Say Something? | |
| I Hear You, Man | |
| I’m Sure My Boss Will Be Thrilled to See Me Doing This |
Now, we know what you’re thinking. You’re thinking, “What’s the big deal here? If I want to use two agents in my script then I’ll just put two agents in my script. What’s wrong with that?”
Actually there’s only one thing wrong with that; as you’ll see, however, that one thing is a big thing, one that can cause your script (and thus your characters) to act unpredictably. To better explain what we mean, let’s show you a sample script that creates and displays two different characters: Peter Costantini (played by Merlin the Magician) and Dean Tsaltas (played by Peedy the Parrot). Here’s what the script looks like:
On Error Resume Next
strAgentName = "Peedy"
strAgentPath = "C:\Windows\Msagent\Chars\" & strAgentName & ".acs"
Set objAgent = CreateObject("Agent.Control.2")
objAgent.Connected = TRUE
objAgent.Characters.Load strAgentName, strAgentPath
Set objDean = objAgent.Characters.Character(strAgentName)
strAgentName2 = "Merlin"
strAgentPath2 = "C:\Windows\Msagent\Chars\" & strAgentName2 & ".acs"
Set objAgent2 = CreateObject("Agent.Control.2")
objAgent2.Connected = TRUE
objAgent2.Characters.Load strAgentName2, strAgentPath2
Set objPeter = objAgent2.Characters.Character(strAgentName2)
objPeter.MoveTo 700,300
objPeter.Show
objPeter.Play "GetAttention"
objPeter.Play "GetAttentionReturn"
objPeter.Speak("Hi, I'm Peter Costantini. Oh, and here comes my friend Dean Tsaltas.")
objPeter.Play "LookRight"
objDean.MoveTo 100, 300
objDean.Show
objDean.Play "Wave"
objDean.Play "GetAttentionReturn"
objDean.Speak("Hi, Peter; sorry I'm late. How are you?")
objPeter.Play "LookRightReturn"
objPeter.Speak("I'm fine, Dean; thanks for asking.")
objDean.Speak("Well, gotta go, Peter. Bye.")
objPeter.Speak("Bye.")
Wscript.Sleep 10000
Set objAction= objPeter.Hide
Set objAction= objDean.Hide
Do While objPeter.Visible = True
Wscript.Sleep 250
Loop
What happens when we run this script? This happens:

To get the full effect you should run the sample code we just showed you. If you do, then you’ll see that the timing of our script is way off. Peter appears onscreen and begins talking; however, he barely gets started before Dean shows up and starts talking. In the screenshot above, you can see that the two are just jabbering away, without waiting for or paying any attention to the other.
Just like they do in real life.
Is this a problem? In this scenario, yes: after all, the two characters are supposed to be having a conversation. In a conversation you’re supposed to take turns: while person A talks, person B listens. (Of course, these rules rarely apply to conversations between spouses or significant others.) When we run our script, however, Dean shows up, says his piece, and then leaves; meanwhile Peter is just chattering away, completely oblivious to everything that is happening around him.
You know, you’re right: just like he does in real life.
To be fair, the problem isn’t with Peter: it’s with Microsoft Agent. For obvious reasons Microsoft Agent runs much slower than the script itself runs; if it didn’t, the characters would zip by so quickly and speak so rapidly that no one could understand them. (Just like – well, you know the rest by now.) For example, take this block of code:
objPeter.Play "GetAttention"
objPeter.Play "GetAttentionReturn"
objPeter.Speak("Hi, I'm Peter Costantini. Oh, and here comes my friend Dean Tsaltas.")
objPeter.Play "LookRight"
objDean.MoveTo 100, 300
objDean.Show
If you just glance at the code you’d probably expect this to happen:
1. | Peter knocks on the monitor in order to get your attention. |
2. | Peter returns to his normal position. |
3. | Peter says, "Hi, I'm Peter Costantini. Oh, and here comes my friend Dean Tsaltas." |
4. | Peter looks off to the right. |
5. | Dean comes flying in. |
So why doesn’t it work like that? Well, what happens is that VBScript sees the first command (GetAttention) and dutifully instructs the Peter character to perform that animation. But while Peter is knocking on the screen – a behavior which takes a second or two to complete – the script is still chugging along: VBScript does not wait for the first command to finish before moving on and executing the next line of code. While Peter is still knocking the next command (GetAttentionReturn) is put into his command queue; that is rapidly followed by commands to speak and to look right. Like we said, those commands are queued up and sooner or later Peter will perform all of them. But they take time.
In the meantime, the script continues on its merry way, eventually ordering Dean to execute the MoveTo command. Because Dean has no commands in his queue (this is the first time we’ve asked him to do anything) he executes this command immediately: that’s why he shows up before Peter is ready for him. The script continues to run asynchronously, with Peter dutifully carrying out his commands in complete isolation from Dean, who’s busy carrying out his commands. The net result is – well, we aren’t totally sure what it is. But it’s definitely not a conversation. As you’ll see for yourself when you run the script.
So does this mean that our little agents are doomed to a life of loneliness? Of course not. If you’ve ever written a Microsoft Agent script you know that the characters will do whatever you tell them to: they’ll wave, they’ll speak, they’ll move around on screen – your wish is their command. If we want Dean to hold off on making his appearance until Peter is through talking, well, all we have to do is tell him that.
And here’s how we tell him:
Set objAction = objPeter.Speak("Hi, I'm Peter Costantini. Oh, and here comes my friend Dean Tsaltas.")
objDean.Wait objAction
Notice that we don’t just let Peter start talking. (Never a good idea under any circumstances!) Instead, we create an instance of the Request object (objAction) and then assign a command – in this case, the Speak method – to this object. Now, to tell you the truth, we’re not totally sure why they call this the Request object; there are probably better names for it than that. Regardless, though, the Request object provides a mechanism for keeping track of a command and its progress. By assigning the Speak command to the Request object that object will know when Peter starts talking. More important, it will also know when Peter stops talking. (Now if only we had something that would tell us when the real Peter stops talking ….)
This is the key to creating characters that seem to interact with one another. We want Dean to wait his turn before doing anything on his own. The problem is this: how is Dean supposed to know when Peter is finished speaking? Well, that’s the job of the Request object: it knows when Peter finishes speaking, and as soon as that happens it will pass the message on to Dean. To make that happen we simply tell Dean to wait until he hears from the Request object; we do that by calling the Wait method and passing as the sole parameter the object reference to the Request object. That’s what we do here:
objDean.Wait objAction
Keep in mind that this doesn’t pause the script; on the contrary, the script is still humming along as fast as ever. What this does do is tell Dean not to process his commands the instant they come in; instead, those commands are to be placed in the command queue and not acted on until the Request object tells Dean that Peter is through speaking.
Make sense? Let’s take a look at trimmed-down version of our initial script, a version that actually works as expected:
On Error Resume Next
strAgentName = "Peedy"
strAgentPath = "C:\Windows\Msagent\Chars\" & strAgentName & ".acs"
Set objAgent = CreateObject("Agent.Control.2")
objAgent.Connected = TRUE
objAgent.Characters.Load strAgentName, strAgentPath
Set objDean = objAgent.Characters.Character(strAgentName)
strAgentName2 = "Merlin"
strAgentPath2 = "C:\Windows\Msagent\Chars\" & strAgentName2 & ".acs"
Set objAgent2 = CreateObject("Agent.Control.2")
objAgent2.Connected = TRUE
objAgent2.Characters.Load strAgentName2, strAgentPath2
Set objPeter = objAgent2.Characters.Character(strAgentName2)
objPeter.MoveTo 700,300
objPeter.Show
Set objAction = objPeter.Speak("Hi, I'm Peter Costantini. Oh, and here comes my friend Dean Tsaltas.")
objDean.Wait objAction
objDean.MoveTo 100, 300
objDean.Show
Set objAction2 = objDean.Speak("Hi, Peter; sorry I'm late. How are you?")
objPeter.Wait objAction2
Wscript.Sleep 10000
Set objAction= objPeter.Hide
Set objAction= objDean.Hide
Do While objPeter.Visible = True
Wscript.Sleep 250
Loop
So what do you think will happen when we run this script? Well, for one thing, it’s actually going to work: Peter will show up and speak. When he’s done speaking – and only when he’s done speaking – Dean will show up. Dean will speak, and Peter will politely stand there and listen to him. And then when Dean is done both of them will disappear. On screen the conversation will have the look and feel of a true conversation:

That’s more like it.
So how did we get these two characters to play nicely together? The secret is to just use the Request object any time we need to pause one of our agents. You’ve already seen this code, which tells Dean to wait until Peter is through speaking:
Set objAction = objPeter.Speak("Hi, I'm Peter Costantini. Oh, and here comes my friend Dean Tsaltas.")
objDean.Wait objAction
What happens when Peter is finished? Well, then it’s Dean’s turn, which means we need to tell Peter to sit still and wait for Dean to finish. Take a look at this block of code, which kicks off after Peter finishes talking:
objDean.MoveTo 100, 300
objDean.Show
Set objAction2 = objDean.Speak("Hi, Peter; sorry I'm late. How are you?")
objPeter.Wait objAction2
You can see what happens here. First, Dean flies in off the screen. After he arrives, we create a second Request object that tracks Dean’s little speech:
Set objAction2 = objDean.Speak("Hi, Peter; sorry I'm late. How are you?")
And then notice the line of code that comes next:
objPeter.Wait objAction2
Here we’re telling Peter to wait until a particular method has finished. In other words, now it’s Peter’s turn to wait while Dean talks. (Note that the object reference is objAction2; to help keep things straight, we’re using separate object references, one for Peter and another for Dean.) To create a nice, amiable conversation we just tell Dean to wait until Peter is done talking; then we tell Peter to wait until Dean is done talking. (If only we’d thought of that years ago!)
Let’s go back to our original script, insert a few Request objects, and see what happens:
On Error Resume Next
strAgentName = "Peedy"
strAgentPath = "C:\Windows\Msagent\Chars\" & strAgentName & ".acs"
Set objAgent = CreateObject("Agent.Control.2")
objAgent.Connected = TRUE
objAgent.Characters.Load strAgentName, strAgentPath
Set objDean = objAgent.Characters.Character(strAgentName)
strAgentName2 = "Merlin"
strAgentPath2 = "C:\Windows\Msagent\Chars\" & strAgentName2 & ".acs"
Set objAgent2 = CreateObject("Agent.Control.2")
objAgent2.Connected = TRUE
objAgent2.Characters.Load strAgentName2, strAgentPath2
Set objPeter = objAgent2.Characters.Character(strAgentName2)
objPeter.MoveTo 700,300
objPeter.Show
objPeter.Play "GetAttention"
objPeter.Play "GetAttentionReturn"
Set objAction = objPeter.Speak("Hi, I'm Peter Costantini. Oh, and here comes my friend Dean Tsaltas.")
objDean.Wait objAction
objPeter.Play "LookRight"
objDean.MoveTo 100, 300
objDean.Show
objDean.Play "Wave"
objDean.Play "GetAttentionReturn"
Set objAction2 = objDean.Speak("Hi, Peter; sorry I'm late. How are you?")
objPeter.Wait objAction2
objPeter.Play "LookRightReturn"
Set objAction = objPeter.Speak("I'm fine, Dean; thanks for asking.")
objDean.Wait objAction
Set objAction2 = objDean.Speak("Well, gotta go, Peter. Bye.")
objPeter.Wait objAction2
Set objAction = objPeter.Speak("Bye.")
objDean.Wait objAction
Wscript.Sleep 10000
Set objAction= objPeter.Hide
Set objAction= objDean.Hide
Do While objPeter.Visible = True
Wscript.Sleep 250
Loop
It’s a little confusing until you get the hang of it, but hopefully you can follow the logic. If Peter’s doing something we create a Request object and tell Dean to wait until the Request object informs him that Peter is done. And then we do the same thing with Peter’s character, creating a Request object and telling Peter to wait until Dean finishes doing whatever Dean does. Etc., etc.
And, yes, there’s no reason why you can’t use more than two characters: you just have to make sure that while one character is speaking the other characters are all paused and waiting on notification from a Request object. We have a sample script (see below) available for download that uses three agents at the same time.
Now, it’s possible that you work for a company that would prefer you write useful system administration scripts rather than scripts that have Peedy the Parrot chatting with Merlin the Magician. (Yes, it’s hard to believe, but some companies are like that.) Writing a script that causes two characters to interact is definitely fun, but is it useful in any way?
Maybe. For example, lots of people write to the Scripting Guys and say, “As part of our logon script we pop up a daily announcement in a message box. Is there any way we can make an educated guess as to whether people are actually reading that message or just clicking OK and getting rid of it?” We don’t know. But we do know that users are more inclined to pay attention to a pair of interacting Microsoft Agent characters than they are to read a message in a message box. Likewise, Microsoft Agent is a cheap and easy way to create little training vignettes, and also provides an interesting way to spice up presentations. We don’t know if it’s enough to convince your boss, but ….
Maybe this will help. A couple months ago Peter and Dean were doing a pair of presentations in the Midwest. Looking for something a little different, we wrote a script that would cause Microsoft Agent to do their presentation (or at least part of it) for them. In the end Peter and Dean decided not to use the script (which is why we decided to go ahead and poke a little fun at them in this article). Granted, it might not be 100% relevant, but it does illustrate how you can use the Request object to manage multiple agents. If you’d like to see what a 5- or 6-minute “video” built around Microsoft Agent might look like, download the script and give it a try. To run the script you’ll need to:
| • | Have the Merlin, Peedy, and Robby characters installed on your computer. |
| • | Have Microsoft Excel and Microsoft PowerPoint installed on your computer. |
| • | Picture yourself sitting in the audience waiting for Peter and Dean to do a talk on system administration scripting. |
Next time out we’ll explore ways to make it rain for 40 days and 40 nights. Just one more thing a creator really should know how to do.