Creating an AIM Plugin in C# Using the .NET Framework

Enable the Subscriptions block here!
AIM Plugin in C#

by Doug Schwartz
March 2, 2007

Introduction

The latest major release of AOL instant messaging, AIM 6, includes support for plugins, which are pieces of code that add features and functionality to the AIM client. This article discusses how you can create a simple plugin in C# using the Microsoft .NET framework. This plugin lets the user translate their message using a scheme where letters are shifted 13 places--"a" becomes "n" and so on. What this plugin really demonstrates is how you, the developer, can intercept the instant message (IM) before it is sent by the user, and do whatever you want with the text.

What Do I Need to Develop a Plugin?

  1. The first step is to get an account on the AIM developer website.
  2. Next, get a developer key for your plugin. There are different keys for different stages of development. For now, you need only a developer key. Later on, if you decide to make your plugin available to others, you will need a deployment key. To get a key, go to the AIM Plugin Registration web page and follow the instructions. You will need the key value to run your plugin.
  3. Once you have registered, you can download the SDK. This article leads you through creating a C# plugin, Rotate. For the complete source for this article, see Source Code.

Creating the Plugin

I used Visual Studio 2003 to create the source code for this plugin. If you are using a different version, such as Visual Studio 2005, there may be some slight variations in the location of some settings. At worst case, you can grab the source, open it in Visual Studio 2005, and it will convert the project to the newer version.

We are going to create the plugin in the following order:

  • Create the C# project.
  • Configure the project to use COM.
  • Create a reference to the SDK library.
  • Add self-registering.
  • Implement the interfaces.
  • Create an action and a preferences form.
  • Creating the C# Project

    Perform the following steps to create the Rotate project:

    1. Open Visual Studio
    2. Click File -> New -> Project.
    3. In the Project Types section, select Visual C# Projects.
    4. In the Templates section, select Class Library.
    5. In the Name section, enter Rotate.
    6. Enter "C:\Plugins" in the Location section.
    7. Click OK

    Your project window should look like Figure 1.

    The new project settings
    Figure 1. The new project settings

    Configure the Project to Use COM

    The library that comes with the SDK is written to use the Microsoft Common Object Model (COM). To use this type of library with the .NET framework, we must add a project setting.

    1. Select Project -> Rotate Properties.
    2. In the Configuration section, select All Configurations.
    3. In the left window, select Configurations, and then Build.
    4. In the right window, set the Register for COM Interop setting to true.
    5. Click OK.

    Your property window should look like Figure 2.

    The rotate properties
    Figure 2. The Rotate project settings

    Creating a Reference to the SDK Library

    To create a reference to the SDK library, AccCoreLib.dll, perform the following steps:

    1. Select Project -> Add Reference.
    2. Click the COM tab.
    3. Click Browse and navigate to the folder where you installed the SDK, and then dist\release and select acccore.dll.
    4. Make sure the component name appears in the bottom window, and click OK.

    Your reference window should look like Figure 3.

    The COM reference
    Figure 3. The COM reference

    You also must create references to two .NET libraries, System.Drawing.dll and System.Windows.Forms.dll. Add these references by selecting Project -> Add Reference, clicking the .NET tab, selecting the System.Drawing and System.Windows.Forms components, and clicking OK.

    Your reference window should look like Figure 4.

    The Windows references
    Figure 4. The Windows references

    Adding Self-Registering

    Self-registering allows your application to add information to the registry so that your plugin is known to AIM. Add the following code to your class:

    #region Plugin Registration
    [ComRegisterFunctionAttribute]
    public static void RegisterFunction(Type t)
    {
       RegistryKey key = Registry.LocalMachine.CreateSubKey(PluginKeyName(t));
       key.SetValue("Name", t.Name); 
    } 
    [ComUnregisterFunctionAttribute]
    public static void UnregisterFunction(Type t)
    {
       Registry.LocalMachine.DeleteSubKey(PluginKeyName(t));
    } 
    private static string PluginKeyName(Type t)
    {
       return "Software\\America Online\\AIM\\Plugins\\" + 
          '{' + t.GUID.ToString() + '}';
    } 
    #endregion

    Implementing the Interfaces

    Now that we have a reference to the COM library, let's use some of that code. We are going to implement the IAccPlugin and IAccCommandTarget interfaces.

    But first, to keep from getting errors as we implement these interfaces, add the following declarations to the top of your Class1.cs source file:

    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Forms;
    
    using Microsoft.Win32;
    
    using AccCoreLib;

    Now we are going to change our class declaration to include a reference to our developer key (replace DEVELOPER_KEY in the following code with the key value) and the two interfaces we use from the SDK. Change your class declaration from:

    public class Class1

    to:

    [GuidAttribute("DEVELOPER_KEY")]
    public class Class1 : IAccPlugin, IAccCommandTarget

    NOTE: The source to this article does not have a GUID. You must get a key to run the plugin.

    As you enter the interface names, Visual Studio will prompt you to press the Tab key to create empty methods to implement the interfaces. You should then have two new regions, IAccPlugin Members and IAccCommandTarget Members. The next step is to add code to the empty methods in these regions.

    Before we implement these methods, we need to create some variables; otherwise, we will get errors as we implement the methods. At the top of the class, just above the empty constructor, add the following declarations:

    const int kCommandId = 0;
    private AccSession m_session;
    private bool xlate = false;

    The first declaration creates a constant value that represents a command. The second is a session object we use to keep track of user preferences. The last is a Boolean that tells our plugin whether to translate the text message.

    Let's implement some methods. Find the Init method in the IAccPlugin region and add the following code:

    this.m_session = session;
    
    IAccCommand command = pluginInfo.AddCommand(kCommandId);
    command.set_Property(AccCommandProp.AccCommandProp_Text, "Toggle Translation");
    
    this.m_session.BeforeImSend +=new DAccEvents_BeforeImSendEventHandler(m_session_BeforeImSend);

    As soon as you type "+=" after this.m_session.BeforeImSend, Visual Studio prompts you to press the Tab key to add the rest of the statement, and then prompts you to create the empty m_session_BeforeImSend method to handle the BeforeImSend event. We will add code to this event handler in a moment.

    Add the following code to the Shutdown method:

    this.m_session = null;

    This releases the memory we use to keep the preference information while our plugin is loaded.

    Add the following code to the m_session_BeforeImSend method:

    if(this.xlate)
    {
       im.Text = this.transmogrify(im.Text);
    }

    Let's implement the transmogrify method. I am not going to show all 52 case statements, but you get the idea.

    private string transmogrify(string s)
    {
       StringBuilder output = new StringBuilder();
    
       char[] chs = s.ToCharArray();
    
       for (int i = 0; i < s.Length; i++)
       {
          switch(chs[i])
          {
             case 'a':
                output.Append("n");
                break;
    
             // ...
    
             default:
                output.Append(chs[i].ToString());
                break;
          }
       }
    
       return output.ToString();
    }

    Figure 5 shows an example of the rotated text.

    Rotated Hello world text
    Figure 5. A rotated "Hello world"

    Creating an Action and a Preferences Form

    Actions are commands that the user invokes at the bottom of the IM page. The preferences form is displayed whenever the user wants to set the preferences for the plugin. We are going to create a simple action that toggles between sending translated text and plain text. We are going to create a form with two buttons so the user can select whether to send translated text or plain text.

    The Exec method is called when the action associated with the plugin is selected; the QueryStatus method is called when the user selects the plugin preferences. By default, QueryStatus returns false, but since we are going to let the user set a preference, we want to return true when the command is to set the preferences.

    Replace the existing code in the QueryStatus method in the IAccCommandTarget region with the following code:

    if (command == (int)AccCommandId.AccCommandId_Preferences || command == kCommandId)
    {
       return true;
    }
    else
    {
       return false;
    }

    Add the following to the Exec method in the IAccCommandTarget region:

    if (command == kCommandId)
    {
       if(!xlate)
       {
          MessageBox.Show("Translation is enabled");
    
          this.xlate = true;
       }
       else
       {
          MessageBox.Show("Translation is disabled");
    
          this.xlate = false;
       }
    }
    else if (command == (int)AccCommandId.AccCommandId_Preferences)
    {
       Form MyForm = new Form();
       MyForm.AutoScaleBaseSize =
          new System.Drawing.Size(5, 13);
       MyForm.ClientSize =
          new System.Drawing.Size(292, 266);
       MyForm.Text = "Plugin Settings";
    
       Label label1 = new Label();
       label1.Font =
          new System.Drawing.Font(
             "Microsoft Sans Serif",
             8.25F, 
             System.Drawing.FontStyle.Bold,
             System.Drawing.GraphicsUnit.Point,
             ((System.Byte)(0)));
       label1.Location =
           new System.Drawing.Point(32, 24);
       label1.Name = "label1";
       label1.Size =
          new System.Drawing.Size(144, 23);
       label1.TabIndex = 0;
       label1.Text = "Translate the IM text?";
    
       MyForm.Controls.Add(label1);
    
       Button button1 = new Button();
       button1.Location =
          new System.Drawing.Point(40, 208);
       button1.Name = "button1";
       button1.TabIndex = 1;
       button1.Text = "Yes";
       button1.Click +=
          new System.EventHandler(this.button1_Click);
    
       MyForm.Controls.Add(button1);
    
       Button button2 = new Button();
       button2.Location =
          new System.Drawing.Point(144, 208);
       button2.Name = "button2";
       button2.TabIndex = 2;
       button2.Text = "No";
       button2.Click +=
          new System.EventHandler(this.button2_Click);
       
       MyForm.Controls.Add(button2);
    
       MyForm.Show();
    }

    Once again, as you type the "+=," Visual Studio creates the empty event handler. You need to change these two methods as follows:

    private void button1_Click(object sender, System.EventArgs e)
    {
       this.xlate = true;
    }
    
    private void button2_Click(object sender, System.EventArgs e)
    {
       this.xlate = false;
    }

    Figure 6 shows what this form looks like when the user selects Edit -> Settings -> Plugins -> Rotate -> Settings.

    Plugin Preferences Form
    Figure 6. The preferences form in action

    Figure 7 shows what the user sees in the Actions menu:

    Toggle Translation action
    Figure 7. The "Toggle Translation" action

    Figure 8 shows what the user sees the first time they select this action:

    Selecting the Toggle Translation action the first time
    Figure 8. Selecting the "Toggle Translation" action the first time

    Conclusion

    Now you understand how to create a simple plugin. If you are feeling brave, take a look at the SDK and try to figure out how you would create a plugin for the recipient of the translated text. Which event would you trap? (Hint: the answer is below, in white text on this white background. Just highlight the bracketed word on the following line and you can see the answer.)
    [BeforeImReceived]

    Source Code

    The source code discussed in this article is available in the following .zip files.

    • rotate.zip contains the complete source code for this article
    • unrotate.zip is a simple Windows form utility that you can use to unrotate text. Just copy the rotated text from the IM window into the text field and click the Unrotate button. I've included the source in case you are interested.

    References

    Here are a number of web pages where you can get further information about plugins.