COM Programming in zMUD

Updated: January 31, 2002 by Mike Potter (Zugg)

Back to Introduction

COM Scripting in zMUD

Steps for Scripting COM

NOTE: This document refers to zMUD versions 6.26 and later.  Earlier versions of zMUD did not support some of the simpler COM syntax used in these examples.

NOTE: The examples given in this document are for Outlook 2000.  Previous versions of Outlook will work a bit differently.

The best way to get started with understanding COM is to go through an example.  When you want to access a COM Server, there are several steps to take:

  1. Get some documentation on the COM Server!
  2. Using the Name of the COM Server, create a link to that server.
  3. Using the Link to the COM Server, either: a) read data from the server, b) send data to the server, or c) execute a method on the server.

In our example, we want to query the Microsoft Outlook 2000 application and find out how many messages are currently in our InBox.  So, for the first step, we need some documentation on Microsoft Outlook's COM Server.  You can search the Microsoft web site and find the article "Automating Outlook from a Visual Basic Application" which has a good beginning, or get the book "Outlook 2000 VBA" which goes into greater detail.

Here's the catch with COM: it is as simple or as hard as the application vendor wants to make it.  And you guessed it:  since Microsoft invented COM, it's applications tend to have rather complex COM implementations.  Finding out how to do something with one of their applications usually takes the most time.  Once you find some good documentation or examples, the rest of it is easy.

Creating a Link to a COM Server

OK, now that we have some documentation, the next step is to create a link to the Microsoft Outlook 2000 COM Server.  According to the Visual Basic documentation (and most documentation you find, *will* be in Visual Basic), you start by doing something like this:

Dim Outlook as Object
Set Outlook = CreateObject("Outlook.Application")

As we can see from this, the Name of the COM Server is: "Outlook.Application".  This is the most important piece of information to start with.  Without the name of the server, you can't do anything.  Now, in Visual Basic, they are using a method called "CreateObject" to return a link to the COM server.  In zMUD, you use the %comcreate function instead, and assign the result to a zMUD variable using #VAR, like this:

#VAR Outlook %comcreate("Outlook.Application")

Easy!  So, anytime you learn the name of a COM Server, now you know how to get a link to it in zMUD.  In zMUD, if you then do a #VAR command to look at the contents of a variable, you'll see this:

#VAR
Outlook <OLE object: Outlook.Application>

zMUD is telling you that the @Outlook variable doesn't contain a normal value, but instead contains a link to an OLE object.  OLE is basically the same thing as COM.  OLE stands for "Object Linking and Embedding" and is the most common application of COM in Windows.  I won't go into the technical differences here.  But what we are doing with COM is "OLE Automation" of another application, like Microsoft Outlook in this case.

If Outlook was already running, you might want to link to the running program instead of creating a second copy of it in memory.  To do this, use the %comactive function instead of %comcreate:

#VAR Outlook %comactive("Outlook.Application")

Retrieving a Property value from a COM Object

Now that we have a link to a COM object, it's time to do something with it.  This is where life can get complicated.  Microsoft loves to nest layers and layers of COM objects.  Sometimes it takes several steps to get what you want.  In this case, we want to get down to the Inbox folder in the Mail part of Outlook.  What we learn from the Microsoft documentation is that everything in Outlook is stored in the "MAPI Namespace".  The example in Visual Basic does this:

Set NameSpace = Outlook.GetNameSpace("MAPI")

What this is doing is taking the previous COM object link "Outlook" and retrieving the property called "GetNameSpace".  This property takes an argument, which is "MAPI".  Properties can take as many arguments as they need.  So, the general Visual Basic COM call is:

Set VariableName = COMObjectLink.PropertyName(Arguments)

In zMUD, it looks like this:

#VAR VariableName @ComObjectLink.PropertyName(Arguments)

almost just like Visual Basic!  Versions of zMUD prior to 6.26 required the more complex syntax:

#VAR VariableName %comget( COMObjectLink, PropertyName, Arguments)

Either will work, but the first syntax is easier to use and closer to other programming languages.  So, in order to get the MAPI Namespace from Outlook, we would do:

#VAR NameSpace @Outlook.GetNameSpace("MAPI")

Now, if we do a #VAR to look at the variables, we see:

Outlook 
<OLE object: Outlook.Application>
NameSpace 
<OLE object: Outlook.Application.GetNameSpace(MAPI)>

Notice that zMUD tells us that NameSpace is still a COM Object link.  In addition, it tells us what it is linking to.  The output of the #VAR command can be a great help in doing COM Scripting in zMUD.

OK, so now we have the MAPI NameSpace, so now what?  After digging through the documentation, we discover that the NameSpace has a property called Folders.  The "Personal Folders" is the top folder within the Folders property.  So, in Visual Basic, you would do this:

Set Folders = NameSpace.Folders("Personal Folders")

The corresponding zMUD code is:

#VAR Folders @NameSpace.Folders("Personal Folders")

Now, in Outlook you'll notice your "Personal Folders" is really a whole collection of folders, including your Calendar, Contacts, etc.  So, to get the actual Inbox Mail folder, we need to go one level deeper:

#VAR InBox @Folders.Folders("InBox")

Whew, are we there yet?  Almost.  If we do another #VAR, we now have:

Outlook 
<OLE object: Outlook.Application>
NameSpace 
<OLE object: Outlook.Application.GetNameSpace(MAPI)>
Folders 
<OLE object: Outlook.Application.GetNameSpace(MAPI).Folders(Personal Folders)>
InBox 
<OLE object: Outlook.Application.GetNameSpace(MAPI).Folders(Personal Folders).Folders(InBox)>

From this we see that InBox is *still* a COM Object link.  Digging further into the documentation, we discover that an email folder is a collection of messages.  This collection of messages is stored in the property "Items".  In order to determine the number of messages in the collection, we need the "Count" property of the "Items".

Nested property calls

OK, you should be getting the hang of this.  To get the Count property of the Items property, you can do this:

#VAR Items @InBox.Items
#VAR Count @Items.Count

however, instead of creating all of these intermediate variables, zMUD let's you nest property calls.  So we can simplify this code by using:

#VAR Count @InBox.Items.Count

This is the same as the Visual Basic code:

Count = InBox.Items.Count

Yes, the Visual Basic code is a bit easier to read, but hey, this is zMUD, not Visual Basic.  If you do a #VAR command now, you will see:

Outlook 
<OLE object: Outlook.Application>
NameSpace 
<OLE object: Outlook.Application.GetNameSpace(MAPI)>
Folders 
<OLE object: Outlook.Application.GetNameSpace(MAPI).Folders(Personal Folders)>
InBox 
<OLE object: Outlook.Application.GetNameSpace(MAPI).Folders(Personal Folders).Folders(InBox)>
Count 
47

Notice that Count is no longer an OLE object but is a normal, everyday zMUD variable.  In this case, with a value of 47 (the number of messages currently in my InBox).

Accessing COM Properties from within a Loop

OK, so now we have read data from the COM Server.  In this case we have read the InBox and determined how many messages are in it.  Let's get even fancier.  Let's print out the Subject lines for each email message.  Think about it for a second...it's actually easy:

#LOOP 1,@Count {#ECHO @InBox.Items(%i).Subject}

What we are doing is using %i in the argument field to return the ith message from the "Items" collection.  Once we have this message, we access it's "Subject" property.  This is the same as the Visual Basic code:

for i = 1 to Count
  print InBox.Items(I).Subject
next

Setting the Value of a COM Object Property

Now let's send some data back to the COM Server.  Let's change the subjects of one of the email messages.  We will change the subject of the first email message to "Test Message".  First, let's go ahead and get a link to the first message:

#VAR Msg @InBox.Items(1)

To display the current subject, we just do:

#ECHO @Msg.Subject

In fact, let's save this result:

#VAR Subject @Msg.Subject

Now, to change the subject, we use normal #VAR syntax:

#VAR ComObjectLink.Property NewValue

Older versions of zMUD required the %comset function.  The format for %comset is:

%comset( COMObjectLink, PropertyName, NewValue, OptionalArguments)

so we can do this:

#VAR Msg.Subject "Test Message"

or

Msg.Subject = "Test Message"

to change the subject of the message to "Test Message".

Executing a COM Object Method

But if you go to your Outlook application and look in your InBox, you'll see that the subject of your first message has not been changed!  How can this be?  Well, we have changed the subject within memory, but we haven't told Outlook to SAVE the results.  To Save a message, you need to call the Save "method" for the message.  In Visual Basic, you'd just do this:

Msg.Save

But in zMUD, you need to use a command to execute something, so you use the #COM command.  The format of the #COM command is:

#COM COMObjectLink MethodName OptionalArguments

so the result is:

#COM Msg Save

You can also use the #CALL command and execute the COM method as a function:

#CALL @Msg.Save

Now you'll see that the subject line of the first message in your InBox has really been changed.  I hope you didn't need that message.  Never fear, if you have truly been following along, then you'll remember that we saved the original subject.  So let's change it back:

#VAR Msg.Subject @Subject
#CALL @Msg.Save

There, back to normal.

So now we have learned how to get data from another application, send data back to that application, and tell the application to execute a method.  As you have seen, the hardest part is getting the documentation about the server and how it works.  You'll spend far more time trying to learn about all of the properties and methods of COM objects then you will actually writing code.  But once you figure out the correct property and object names, then you can do some amazing things with it.

Freeing up a COM Object

Let's finish by talking a bit about memory usage.  If you do a #VAR, you'll still see all of our COM Object links sitting there.  All of these take up memory.  In fact, as long as you have any of these sitting in memory, you've also got an entire copy of the COM Application (in this case, Outlook 2000) in memory.  To get rid of these COM links, just assign something else (like "") to the variables:

#VAR Msg ""
#VAR InBox ""
#VAR Folders ""
#VAR NameSpace ""
#VAR Outlook ""

Now if you look at #VAR, all of your OLE Objects should be gone.  And if you check your Task Manager, you'll see that Outlook is no longer running in the background.  This is how you free up COM memory in zMUD.

Using Arrays in COM

Some object methods might require an argument that is an array.  For example, the zMUDCommand method of the zMUDSession COM object requires an array of parameters to pass to a zMUD command.  In Visual Basic, you would use an array like this:

call session.zMUDCommand( 1, array("param1","param2"))

In Delphi, you use the VarArrayOf routine and put the arguments in brackets:

session.zMUDCommand( 1, VarArrayOf( ['param1','param2']);

In zMUD, you use the %array function:

#CALL @session.zMUDCommand( 1, %array("param1","param2"))

For more examples, see: ADO Programming in zMUD, zMUD COM using zMUD