Archive for the ‘Software Development’ Category

CoreWitness 1.3.0

Monday, March 9th, 2009

CoreWitness Plug-inI just released an update to my CoreWitness plugin. This minor release adds the showing of the current preference value in the OnPreferenceChanged and OnPreferenceInvalid events. Here is a sample trace I get when I toggle all the check boxes in the privacy tab off, click apply, and then check them again and click save:

01:58.754 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.Privacy.DiscloseTypingStatus'
   new value = No
01:58.755 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.privacy.discloseBuddyFeed'
   new value = No
01:58.764 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.Privacy.DiscloseIdleTime'
   new value = No
01:58.777 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.privacy.disclosePluginsToBuddyFeed'
   new value = No
01:58.777 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.Privacy.DiscloseWireless'
   new value = No
02:03.859 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.Privacy.DiscloseTypingStatus'
   new value = Yes
02:03.860 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.privacy.discloseBuddyFeed'
   new value = Yes
02:03.868 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.Privacy.DiscloseIdleTime'
   new value = Yes
02:03.869 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.Privacy.DiscloseWireless'
   new value = Yes
02:03.869 [gusblog  ] DAccEvents::OnPreferenceChange - 'aimcc.privacy.disclosePluginsToBuddyFeed'
   new value = Yes

You can get the latest version here.

Please note that activation of the CoreWitness plugin is now automatic. No registration is needed.

This is a free plugin for Open AIM developers.

Checking AIM Capabilities Via JavaScript

Thursday, February 12th, 2009

Open AIMWith so many different AIM clients out there, how can we tell if a client supports a particular feature? The answer is capabilities. Each screen name has a list of capabilities (GUIDs) that are asserted by the client and plugins.

How can I read these capabilities in the AIM SDK, you might ask? You can see what a user supports via the AccUserProp_Capabilities property and what the client asserts via the AccSessionProp_Capabilities property.

Here is a dump of the capabilities property for one of my screen names as seen by my CoreWitness plugin:

  AccUserProp_Capabilities = Array { ...GUIDs removed from trace here... } 
      {09461345-4C7F-11D1-8222-444553540000} = Direct IM
      {748F2420-6287-11D1-8222-444553540000} = Chat
      {09461343-4C7F-11D1-8222-444553540000} = File Transfer
      {67753137-6A38-6C77-4672-762D49755071} = UUID of a plugin: 'Gus Verdun's Core Test Plug-in (Dev)'
      {09461346-4C7F-11D1-8222-444553540000} = Buddy Icon
      {9FD3969B-84CB-45C1-B5DF-72DF5FAAB98E} = RX-Plugin

The AIM SDK returns an array of GUIDs in string form for those two properties. The CoreWitness plugin will automatically map the GUIDs to friendly names. When it encounters a GUID it does not recognize, it scans to see if the GUID matches that of any plugin you have installed. Yes, you can assert your plugin’s key as a capability if you want too. However, this is not required even thought your plugin’s key is reserved for you. I chose long ago to assert a different GUID as a capability for my RX-Plugin as you can see above. The capability is used to change the behavior of the IMpulse feature when both users have the RX-Plugin installed.

The AIM SDK always returns VB arrays in JavaScript so you must convert them to a JS array by using the toArray() method. Here is one way you can test to see if a buddy supports a particular capability:

// Test to see if buddy (IAccUser) has the RX-Plugin capability asserted
function HasRXPlugin(buddy)
{
    if ( SafeHasCapability(buddy, '{9FD3969B-84CB-45C1-B5DF-72DF5FAAB98E}') )
        alert('yes');
    else
        alert('no');
}
 
// cap must be in upper case.
function SafeHasCapability(user, cap)
{
    try {
        return  user.capabilities.toArray().join('').indexOf(cap) >= 0;
    } catch ( e ) {
        return false;
    }
}

The first function calls the more general function to test if the buddy has the capability asserted by my RX-Plugin.

You can use capabilities in your plugins for anything. They can be used to indicate that something is “on”, “selected”, etc. You can even have more than one—within reason. The AIM Vote ‘08 plugin uses capabilities to indicate your vote for McCain or Obama and then tallies up the number of buddies with either of those capabilities.

How to Debug a C++ AIM Plugin

Friday, January 30th, 2009

Here are some tips on how you can debug your own C++ plugins for AIM.

First, I suggest you get about three or four screen names. Developing C++ plugins requires a lot of client re-starts which can get you log-in rate-limited very fast. Also don’t forget to take advantage of the edit and continue feature in Visual Studio while debugging since this will save you a login cycle.

Debugging a plugin is a little bit different than an entire application. I typically compile my plugin in Visual Studio. Then launch AIM using my IM control panel program. Incidentally, this app also lets me kill AIM completely which I need to do when I crash or want to force a restart. When I see the login window, I attach the debugger via the attach to process dialog (CTRL+ALT+P). Look for the aim6.exe process and attach to it. I then set my breakpoints and log in.

NB: There are two things you should note with the debugger. First is to make sure you are debugging native code and not script; very easy to miss. Second, is that your breakpoints will appear as inactive at this time since the plugin is not loaded yet. As long as you make sure you are debugging native code you should not have to worry too much about the breakpoints being disabled. They should light up once you are online.

AIM has its own crash handler that collects stack trace and other information to send along. Since I am developing my own plugin, it makes no sense to report these crashes to AOL. You can temporarily disable it by renaming the C:\Program Files\Common Files\AOL\AOLDiag\tbdiag.dll file to something else. Without it, all crashes get handled by Windows which lets you attach the debugger at the crash point.

With just these few tips you should be able to get started on debugging your own plugins easily.

Pushing Buddy Updates via JavaScript

Wednesday, January 28th, 2009

Here is the JavaScript edition of my previous post:

function SafePushTestFeed(session)
{
    try {
        manager = session.buddyFeedManager;
        myitem = manager.createBuddyFeed();
        myitem.feedTitle = 'Feed Title';
        myitem.feedLink = 'Feed Link';
        myitem.feedDescription = 'Feed Description'; 
        myitem.feedPublisher = 'Feed Publisher'; 
        myitem.itemTitle = 'Item Title';
        myitem.itemLink = 'Item Link';
        myitem.itemDescription = 'Item Description'; 
        myitem.itemGuid = 'Item Guid';
        myitem.itemCategory = 'Item Category';
        manager.push(myitem);
    }
    catch ( e ) 
    {
        // You can look this number up in the 
        // CoreWitness Symbol Lookup Dialog
        trace("Exception HRESULT=" + e.number);
    }
}

The buddyFeedManager is a property of the IAccSession interface. In C++ you access properties via the get_Property(prop, value) or get_XXX methods. However, from JavaScript you can access any property including the AccXXXProp_ ones by just using the property’s trailing name (everything after the AccXXXProp_ or get_). Property and method names are not case sensitive in JavaScript.

You can read more about the these interfaces by searching for the interface reference pages on IAccBuddyFeed and IAccBuddyFeedManager.

Closing a Docked AIM Module Window

Thursday, January 22nd, 2009

It has come to my attention that you can’t programmatically close an AIM Module window while it is docked. The window.external.window.close() method does not appear to work in this situation.

For now, the only workaround is to un-dock the window before closing it:

// global aliases as described in a prior post
wnd = window.external.window;
client = window.external.client;
prefs = window.external.prefs;
 
function CloseDockedWindowWorkAround()
{
    // un-dock the window briefly.
    if (client.apiVersion == 1)
       client.docked = false;
    wnd.close();
}

This workaround will be needed in AIM 6.8 through AIM 6.9 Beta 2 (they all return an API version of 1).

I will get this issue fixed in the next AIM 6.9 beta since the plugin window is briefly shown in its undocked state before disappearing.

NOTE: This only applies to the dockable plugin window. All extra windows opened by the plugin can be closed without this workaround since those windows can never be docked.

Tips on Developing AMO Plugins

Monday, January 19th, 2009

AIM 6.8 and the AIM 6.9 betas all support AIM Modules. These are HTML and JavaScript based plugins that have full access to the Open AIM API plus client level services like HTTP requests and window management.

One of the nicest aspects of AMO plugins is that you can install and uninstall them without having to sign off. This is fantastic from both a development and user perspective.

I recommend that you get an extra plugin key for developing plugins. Once you publish version 1 and set a finger print (optional) you will not be able to develop your plugin further with that key since only plugins with the registered fingerprint will be allowed to load. The solution to this is to get an extra key that you use for development purposes. Don’t forget to change to the deploy key before publishing.

Here are five tips to help get you started on developing your first plugin:

1 You can install a plugin by running the .amo file. I typically do “start plugin.amo” from the command line or make file. AIM registers itself as a handler for this file extension. You can install a plugin even while offline.

1You should enable the debugging setting in IE for other apps. This helps you attach the JavaScript debugger during development and warns you about unhandled exceptions. With debugging enabled (i.e. not disabled), you can insert “debugger;” statements in your code to trigger the debugger. Go to the Internet Settings control panel and uncheck the highlighted option is to do this.

Enable IE Debugging for AMO plugins

1Create aliases for the window.external.* objects. This makes it easier to use these by assigning them to a variable. I put these at the top of my JavaScript file:

debug=1;
prefs = window.external.prefs;
client= window.external.client;
wnd = window.external.window;
plugin= client.pluginInfo;

1Set the AMO_TRACE_ENABLE environment variable to “true” in order to use the window.external.client.trace(msg) function that sends output to the debugger. I do it via the Environment Variables dialog in the System Properties control panel (Windows+Break key, click on Advanced tab, then “Environment Variables” button.)

Set AMO_TRACE_ENABLE environment variable

I also create a trace function that helps me leave trace message that are suppressed when the plugin is deployed. This is a good practice. Note that this code uses the global variables ‘debug’ and ‘client’ that I set up the previous tip. Remember to set debug=0 when you deploy your plugin.

function trace(m)
{
    if (debug)
        client.trace(m);
}

1If your plugin fails to install for some reason, make sure your plugin.xml file is well-formed and that the minimum required attributes are there. Here are the minimum. The UUID must be your key with {}’s:

<?xml version="1.0" encoding="utf-8"?>
<plugin 
    schema     = "1"
    type       = "html"
    uuid       = "{00000000-0000-0000-0000-000000000000}"
    name      = "any name you want"
    .
    .
    .
</plugin>

Bonus Tip: See my posts tagged with “tips” for more.

Reading an AIM SDK Preference via JavaScript

Thursday, January 15th, 2009

Last year AOL released AIM 6.8 which is the first version of AIM to support plugins written in HTML and JavaScript. These plugins are called AIM Modules. They are a zip file that contains the plugin’s manifest, content, and code with a .amo extension. NOTE: a.m.o (addons.mozilla.org) and AMO plugins are not related.

A complete introduction to AMOs is beyond the scope of this post. You can read more about the AIM Module Plugin API on the AIM Developer web site. I have written a few of these plugins under the AMO Factory brand.

In my previous post, I demonstrated how to read a preference via C++. Here is a sample that accomplishes the same in JavaScript.

// Make a shorter name for this. See the AMO API for docs on this class
client = window.external.client;
// Get the primary IAccSession object. 
// (primary is the first account that signs on.)
session = client.primarySession;
// Get the IAccPreferences object for this session
prefs = session.prefs;
 
// See the IAccPreferences API
function safeGetPref(spec, default)
{
    try {
        // method names of AIMcc objects are not case sensitive.
        return prefs.GetValue(spec);
    } catch (e) {}
    return default;
}
 
function canDisclosePluginsToBuddyFeed()
{
    return safeGetPref("aimcc.privacy.disclosePluginsToBuddyFeed", 
        false);
}
 
if (canDisclosePluginsToBuddyFeed()) 
{
    alert('preference is true');
}

I recommend that you use try/catch blocks around AIMcc method calls since they can return COM error results that throw exceptions in JavaScript. The safeGetPref() function takes care of this and returns the default value if AIMcc returns any error.

TIP: The e.number property in the catch block has the HRESULT as a negative number. You can find the symbolic name for this error via the Symbol Lookup dialog in my CoreWitness plugin. Just enter the number in the find box to do the reverse lookup.

Reading an AIM SDK Preference via C++

Wednesday, January 14th, 2009

The AIM SDK (AIMcc) has an interface called IAccPreferences that is used for reading and writing preferences. Preferences are associated with a session so they can only be accessed once you have an IAccSession object.

It is up to clients to implement the actual data store behind these preferences by registering an object that implements either IAccPreferencesHook or IAccPreferencesHook2. You can find more information about these interfaces in technote 3 of the AIM SDK.

In this two-part series I will provide some sample code for reading preferences. This first post provides an ATL/C++ example and my next post will provide a JavaScript example.

In the process of making easy to use sample code, I will need to create some helper classes. These classes will be part of an evolving distribution called AccEx which I am hereby placing in the public domain. I will start by distributing these as source files only until I have more classes. AccEx distributions will be available here.

For this post, I created a helper class in AccEx called CAccExPreferences in order to make it easy to read and write preferences in C++. This class has overloaded methods to help you read a preference in various native formats like bool, int, float, etc. and one method to write a preference from a CComVariant.

So let’s use this thing. Like I said above, the IAccPreferences interface is implemented by AIMcc and associated with the IAccSession since they are (mostly) per-user preferences. Therefore, the only thing you need to initialize a CAccExPreferences object is an IAccSession pointer. The class will take care of acquiring the IAccPreferences interface for you. Here is a simple use-case that reads a real AIMcc preference.

// This sample source is public domain
#include <stdafx.h>
#include "AccExPreferences.h"
 
static WCHAR kDisclosePluginsToBuddyFeed[] = 
    OLESTR("aimcc.privacy.disclosePluginsToBuddyFeed");
 
HRESULT CanDisclosePluginsToBuddyFeed(IAccSession *pIAccSession)
{
    bool enabled;
    AccEx::CAccExPreferences prefs(pIAccSession);
 
    if ( SUCCEEDED( 
            prefs.GetPref(kDisclosePluginsToBuddyFeed, enabled) 
         ) && enabled )
    {
        // preference is enabled.
        return S_OK;
    }
    return S_FALSE;
}

Stay tuned for my next post where I will do the same using JavaScript.

AIM Blast Power User Tips

Sunday, January 11th, 2009

By now the web is buzzing with comments on AIM Blast. Don’t know what it is? AIM Blast is like an email list for IMs. You can create a permanent “blast group”, invite your friends, and then send/receive messages to/from all who are online at the time. It’s that simple. See the AIM Blast page for more details.

Blast groups appear as an automatically managed group in your buddy list. Once you have a blast group, just send an IM to the []-bracketed blast group buddy name.

You might have noticed that you need to reply with //1 or //2 when you receive an IM with the invitation to a blast group in order to accept or decline the invitation. It turns out there are many more commands that you can send to the blast group once you have joined.

Here is a list of additional commands you can send to blast group (NOTE: Most of these commands are handled by the host w/o being relayed to the blast group members):


//off
Stop receiving IMs. Use this when you are not interested in the current conversation. Don’t forget to send //on since this is in effect until then.

//on
Start receiving IMs again.

//invite name
Invite name to AIM Blast. An easy way to do this without having to go through the AIM Blast web site.

//history
Display last 10 IMs. This is an excellent way to check if you missed any recent blast messages.

//stats
Display stats

//soundon
Turn buddy sound on for the group

//soundoff
Turn buddy sound off for the group

//help
Get the list of // commands.

I found one more command that is not listed in the //help command and that is the //roll command to roll 2 six-sided dice; great for playing games and making random decisions via AIM Blast.

One last tip is that my Link Buddy plugin (also available from the AIM Gallery) has a link that gets you authenticated access to the AIM Blast web site so you don’t have to enter your password once you are signed-in to AIM.

Link Buddy Plugin

Open AIM 2.0

Thursday, March 6th, 2008

Open AIMWith AOL’s recent announcement on Open AIM 2.0 now live and getting lots of great attention, I thought I should write a quick post on it. This is truly exciting news. We are dramatically expanding our community when we open things up like this. So with that, I want to say “Welcome” to all the great developers out there. Here is this incredible AIM network that you can now use and build on.

I have actually been a part of the AIM core team for a number of months now, so I am available to help answer questions on Open AIM. I can now claim to have written code in imapp.dll, acccore.dll, coolcoreXX.dll, and xprtX.dll. Lastly, this guy sits next to me.

There are more great things to come.