Updated: January 31, 2002 by Mike Potter (Zugg)
Creating a COM-based plugin for zMUD is fairly easy. With a bit of practice and some basic understanding of COM and OLE-automation, you'll soon be creating zMUD plugins that add all sorts of functionality to zMUD. You can create COM-based plugins for zMUD in many different programming languages, including Visual Basic, Delphi, C, C++, etc. This tutorial contains examples for both Delphi and Visual Basic.
zMUD Plugins can be written using two different methods: COM-based, and API-based. API-based are traditional DLL files that export certain procedures required by zMUD. This is the traditional method for creating zMUD plugins and is described in more detail in the zMUD Developer's Kit.
This article covers COM-based plugins for zMUD. However, this terminology is a bit misleading, since zMUD plugins are actually OLE Automation Servers, which themselves are COM objects. Think of COM as a very general framework, with OLE Automation Servers as a subset of that framework.
Specifically, all OLE Servers implement the IDispatch interface, rather than the bare-bones IUnknown interface of regular COM objects. Any programming language that you will create plugins with will handle the details of the IDispatch interface, so you don't need to understand the details. However, one important consequence of using OLE Automation Servers is that your plugin will require a Type Library.
A Type Library is a set of information that describes your plugin and allows it to be registered within Windows. The Type Library is typically linked in with your DLL or EXE plugin file. Most programming languages allow you to import and view type libraries, either installed on the system, or residing in a DLL or EXE file.
zMUD itself has a Type Library called the "Zmud Library". Simply load ZMUD.EXE into a Type Library viewer, or look for the Zmud Library in the system registry.
To help with your plugin debugging, I've compiled a simple Type Library viewer called TIViewer.exe. This utility was written by Eric Harmon as part of his book "Delphi COM Programming". Click on the filename above to download the utility. It allows you to open a DLL or EXE file and view it's associated type library.
To create a zMUD Plugin, you need to create an OLE Automation Server that implements the IzMUDPlugin interface. The IzMUDPlugin interface is defined in the Zmud Type Library contained in ZMUD.EXE. I'll document all of the methods and properties of this interface at the end of this document. The IzMUDPlugin is simply an interface that describes the methods and properties that you can implement in your plugin. Any OLE Automation Server that implements this interface can be loaded into zMUD as a plugin. Some of the methods and properties are required, many are optional.
So, pick whatever programming language you want to use, and learn how to create an OLE Automation Server. I'm going to show a detailed example using Delphi, and then in Visual Basic, but you should be able to create OLE Automation Servers in most any language. It might take some practice to master the detailed steps involved in creating an automation server.
Delphi 5 contains several wizards that make COM and OLE Automation very easy. There are two types of automation servers that you can create: in-process, and out-of-process. In-process servers are DLL library files that end up getting loaded into the same memory space as zMUD when they are running as plugins. These plugins run the fastest. Out-of-process automation servers are EXE application programs that run alongside zMUD in a separate memory space. Because data must be transferred back and forth between the applications, these types of plugins run a bit slower than the in-process DLL servers. However, since they are self-contained application programs, you can do a bit more with them. For example, consider the zChat plugin for zMUD. In addition to being a plugin, it is also a standalone chat program. This is a good example of a zMUD plugin that should probably be an out-of-process EXE plugin.
In the following detailed example, I'm going to create an in-process DLL plugin. From the File menu, select New...and in the ActiveX tab page, select the "Active X Library" to create a new library project. Delphi will create a small DLL Library for you. Next, from the File menu, select New...and in the ActiveX tag page, select the "Automation Object".
Delphi will prompt you to name your OLE Automation Server object. Enter a shortname for your object, without any spaces, in the CoClass Name field. Leave the other fields at their default values, and click OK. In this example, we will enter a name of "TestPlug"
Because you have created an Automation Object, Delphi will automatically create a Type Library for you and load the Type Library Editor. In order to implement the IzMUDPlugin interface, we need to edit this type library a bit before it's ready.
By default, you'll see this in your Type Library editor:
Project1 is your DLL Library, ITestPlug is the default Interface for your OLE Automation Object, and TestPlug is the object that implements the interface. Now we just have to tell Delphi that we want to implement the IzMUDPlugin interface in our object.
First, we need to load the zMUD Type Library into our project so that Delphi can find the IzMUDPlugin interface. From the Project Menu, select Import Type Library. Scroll to the bottom of the list, and if you have run zMUD v6.05 on your system at least once, you'll see the zMUD Library entry. Select it, then click the Create Unit button at the bottom of the window. Accept the defaults and click OK. The unit ZMUD_TLB will be added to your project. When you are bored someday, you might look through this file. It contains all of the interface information for all of the OLE Automation objects used by zMUD.
Now we've added the zMUD Type Library to our project, but we still need to add it to the Type Library editor. Select the "Project1" line in the Type Library editor. Then, on the right-side of the screen, click the Uses tab. You'll notice that two type libraries are already being used, but nothing else is shown. The trick here is to right-click on the list of libraries, and select "Show All Type Libraries". Now everything will be listed. Scroll down to the bottom, and click on the checkbox next to the zMUD Library entry. What can I say...the user interface of the Delphi Type Library is horrible (ok, I want to use even stronger language than that, but I won't). Right-click on the list of libraries and select "Show Selected" again. Now, your type library list should look like this (your paths and folders will be different):
Good. Now that the Type Library Editor knows about zMUD, it's time to fix the TestPlug object so that it implements the correct interface. Click on the TestPlug object in the list on the left. Go to the Implements tab on the right. Once again, right-click on the interface list (aarggg) and select "Insert Interface" from the popup menu. Select the IzMUDPlugin interface from the list.
Now, a very important step: click the "Refresh Implementation" button in the toolbar of the Type Library editor. It's the icon with the two green arrows on it. Clicking this button just added a bunch of code to your object that will save you lots of typing. It added all of the methods and properties for the IzMUDPlugin class, and builds sample code for each one!! This is a huge time saver.
The final step involves a small bit of editing. Delphi never added the ZMUD_TLB unit to the Uses clause of our object unit, so add this yourself. The top of your code page should now read something like this:
unit Unit1; interface uses ComObj, ActiveX, Project1_TLB, StdVcl, ZMUD_TLB; type TTestPlug = class(TAutoObject, ITestPlug, IzMUDPlugin) protected function Get_CanCapture: WordBool; safecall; . . .
Now, SAVE YOUR WORK!!! Select "Save Project As" from the File menu. You will first be prompted for the name of your Automation Object Unit (the Unit1 in the above diagram). Give your plugin file a good name. I have chosen "TestCom.Pas" (ok, so it's a horrible name). Next, you will be prompted for the name of your Project. This is very important: The name of your project will also be the name of your plugin DLL file. So, choose a really good name here. I have chosen "TestPlugin" for my project name.
That's it...you have now created an IzMUDPlugin OLE Automation object in Delphi. Now, you just need to add the detailed implementation of your Plugin.
When you created your OLE Automation object, Delphi filled in a lot of skeleton code for you. This included empty implementations of each method and property used by the IzMUDPlugin interface. Most of these routines are optional. Also, you will notice a striking similarity between the names of the plugin methods and the names of the API routines from the zMUD Developer's Kit. This makes converting an old API plugin into a new COM plugin very easy.
The most important required method that you must implement is the Register method. The skeleton code for this method looks like this:
procedure TTestPlug.Register(var Name, Company, Regcode, Description, Version, HelpFile: WideString; var LoadDef: WordBool); begin end;
Just like the RegisterPlugin routine in the zMUD Developer's Kit documentation, the Register method requires that you fill in the Name of the plugin, the Company (author) creating the plugin, your Developer's Kit registration code, a short description of your plugin, the version string, the name of the helpfile, and whether the plugin should be loaded by default.
Note that in v6.05 and later, plugins do not have to be registered anymore. The license agreement for the zMUD Developer's Kit is being rewritten. Plugins can now be created for personal use for free. It's only when you want to distribute your plugin to other zMUD users that you must pay the Developer's Kit registration fee. In either case, your plugin will load into zMUD. If the plugin is not registered, the user will get a message in the zMUD Plugin Manager stating that the plugin is not registered.
So, an example implementation might look like this:
procedure TTestPlug.Register(var Name, Company, Regcode, Description, Version, HelpFile: WideString; var LoadDef: WordBool); begin Name := 'Test Plugin'; Company := 'Zugg Software'; Regcode := '123456789012'; Description := 'This is a test plugin that does not do anything'; HelpFile := ''; LoadDef := true; end;
Compile your plugin using the "Compile" command in the Project menu. You shouldn't get any errors if you have followed this tutorial exactly.
Now you must tell zMUD about your new plugin. There are two ways to do this. A program called "zRegPlugin.exe" is included in the zMUD Developer's Kit. Simply run this program and give your plugin filename as the parameter. Your plugin will then be installed for zMUD. This method is intended for professional plugin developers who can include a call to zRegPlugin in their installer script.
For regular users, it is easier to install the plugin from within zMUD itself. Run zMUD. At the character selection screen, press <ESC> to get a blank, default MUD window. From the Plugins menu, select the Install command.
A list of currently installed plugins will be displayed:
When you select a plugin in the top list, information about the plugin (obtained from the plugin's Register routine) is shown in the area below. To install a new plugin, click the Install button. Locate your plugin file (Testplugin.dll in the example case) and click Open. The new plugin will be loaded into zMUD:
Notice that all the information that we entered into the Register method of our OLE Automation object is shown here. To uninstall a plugin, click the Remove button. The checkbox next to the plugin name indicated whether the plugin is active and loaded. zMUD only calls the methods for active plugins. You can Load and Unload plugins using the buttons on this form, or just click the checkbox next to the plugin. Loading a plugin causes the Init method of the plugin to be called. Unloading a plugin causes the Close method of the plugin to be called. For example, if we check the box next to the zChat plugin, zChat will be loaded into memory and will appear as a blue-Z in our system tray. If we then uncheck the box, zChat will be unloaded from memory. So, for some plugins, loading and unloading can take a few seconds.
This Plugin Manager can handle both the new COM-based plugins, as well as the
older API-based plugins (such as the Prompt and zChat plugins shown here).
In case you need to mess with the low level details, information about what
plugins are installed is stored in the System Registry under the key:
Well, I promised a detailed look at all the methods and properties of the IzMUDPlugin interface. But I'll save this information for the next public version of the zMUD Developer's Kit. For now, go to the zMUD Developer's Kit page (click on it from the main www.zuggsoft.com page) and read about the API routines. The COM-based routines are very similar to the API routines. This, plus the skeleton code that Delphi generates should be enough to get you going for now.
Thanks to Zor for this section!
Creating a COM and OLE Automation object in Visual Basic is pretty easy since Visual Basic does most of the work for you. Visual Basic can create both in-process (DLL) and out-of-process (EXE) servers.
When you start Visual Basic you should be presented with the New Project dialog shown below. If you have shut this option off you can select New Project from the File menu.
If you want to create an in-process server you should select the "ActiveX DLL" option - select the "ActiveX EXE" option if you want to create an out-of-process server. In this example we will create an in-process server using the ActiveX DLL project. It doesn't matter which kind of server you want to write - Visual Basic handles them in almost exactly the same way.
When you click on the Open button Visual Basic will create a blank project with a single empty class - the project will be called Project1 and the class will be called Class1. Since the program ID of your project in based on the name of the project and the object you references is the name of the class you will want to give them both descriptive names.
Click on Project1 and go to the properties window (press F4 as a handy shortcut) and change the name to whatever you want - in this case I will call the project TestPlugin.
Now do the same thing with Class1 - click on it in the Project window and then change the name in the Properties window - in this example we will call the class TestObject.
Now that we have the project created we need to hook it up to the zMUD interface by adding a reference to the zMUD type library which is contained in the zMUD executable. Select "References..." from the Project menu and scroll the list all the way to the bottom. As long as zMUD is installed on your computer you will see "Zmud Library" near the bottom. Click the checkbox next to the library to select it and click on the OK button.
At this point Visual Basic knows about the zMUD type library and the interfaces that it contains and we can start to hook up the code - double click on TestObject in the Project window and the code window for that class will open. At this point the class should be empty (except for a possible "Option Explicit" line depending on how you have the Visual Basic options set) and you can start to add code.
First we need to tell Visual Basic that this class is going to implement the IzMUDPlugin interface by adding the following line:
At this point the Object list at the top of the TestObject code window should have IzMUDPlugin in the list and the Procedure list should have all of the procedures in that interface. This would be a good point to save your work before we start to add code to the project so that it will compile and run. Click on the Save button on the toolbar and fill in the names of the files as Visual Basic asks for them - I called my class and project TestObject.cls and TestPlugin.vbp respectively.
Note that if you try to compile at this point Visual Basic will complain - this is because you've told it that the TestObject implements an Interface but you have not written any code for that Interface - we'll do that in the next section.
Now that we have the project setup it is easy to add code into it. The first thing you need to do is provide a declaration of each method in the IzMUDPlugin interface - these declarations do not need to do anything, but Visual Basic will not compile the project until you have done this.
Select IzMUDPlugin from the Object list in the TestObject code window and select each method from the Procedure list - Visual Basic will create empty procedures each time you select a procedure that does not have a definition. The most important definition that we need to write code for is the Register method so that zMUD has basic information about your plugin. The skeleton code for this procedure looks like:
Private Sub IzMUDPlugin_Register(Name As String, Company As String, Regcode As String, Description As String, Version As String, HelpFile As String, LoadDef As Boolean) End Sub
A sample implementation might look like:
Private Sub IzMUDPlugin_Register(Name As String, Company As String, Regcode As String, Description As String, Version As String, HelpFile As String, LoadDef As Boolean) Name = "Test Plugin" Company = "Zor Software" RegCode = "123456789012" Description = "This is a test plugin that does not do anything" HelpFile = "" LoadDef = True End Function
Compile your plugin using the "Make TestPlugin.dll" command in the File menu. If you followed this tutorial correctly the code should compile with no errors. You can now follow the instructions in the "Installing Your Plugin" section of the Zuggsoft document to load your plugin into zMUD.
That's it! Now you know how to create a zMUD Plugin using Visual Basic.
Right now, COM-based plugins have a similar interface to the older API-based plugins. So, why should you bother with the new COM stuff? Well, the answer is simple: the API is frozen...the COM interface is under active development. Eventually, you'll be able to do a lot more to customize zMUD using a COM-based plugin. In addition, as mentioned before, you can create a COM-based plugin in both a DLL file, and a normal EXE file, which allows more flexibility. Also, more development languages (such as Visual Basic) allow you to create OLE Automation servers in an EXE file, while they do not create DLL files.
The COM-based plugin framework for zMUD is still under active development. Ideas and suggestions are always welcome. Please post your ideas to the Developer's Forum on www.zuggsoft.com and the plugin framework will get better and better due to your feedback.