Archive for the ‘Open AIM’ Category

Welcoming Gmadkat to the Blogging World

Sunday, February 22nd, 2009

Gmadkat's Open AIM BlogOpen AIM developers can now enjoy a new resource and blogger covering the Open AIM SDK. Gowri IMed me over the weekend to let me know that she started her own blog, called Gmadkat’s Open AIM Blog. You can follow her posts at the www.gmadkat.com site.

I have known and worked with Gowri for a number of years now. Unfortunately, I just realized that I have never asked her where “madkat” came from. Be sure to check out her blog and let us know what ideas and issues you want answers on about Open AIM. Gowri is actually on the Open AIM team, so she has first-hand knowledge of all the current issues and questions that developers are stuck on. I look forward to her many great posts to come.

Lets all give Gowri a nice warm welcome to the blogging world.

P.S. Thanks for the kudos on my CoreWitness plugin — I enjoy developing it just as much as I enjoy using it. ;)

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.

Tip on Finding Preferences in Open AIM

Tuesday, January 13th, 2009

Every now and then I have to look up a preference specifier in AIM in order to use it in a plugin.

For example, the other day I wanted to determine if the user has enabled the pushing of AIM Plugins to their buddy feed. I usually check in technote 10 on preferences in the AIM SDK technotes. However, you can also do this empirically in AIM with my CoreWitness plugin. All ‘aimcc.*’ preference changes will cause the OnPreferenceChanged event to fire on all listeners so you can simply make a change in the client to see what preference specifier is changing.

To make it easier, the latest version of my CoreWitness plugin lets you filter the messages being logged by pressing the * button in the title bar. To filter the log to just the OnPreferenceChanged event, open the filter dialog and proceed as follows. Click on the ‘clear all’ button and then start typing the word “preference” in the filter edit box until you see “OnPreferenceChange” appear in the list. Select this event and click OK. Here is a screen shot of this step:

DAccEvents Monitor Filter

After doing the above, I changed the ‘I have installed a new AIM Plugin’ check box in the privacy tab of AIM 6.9’s Settings and clicked apply. Here is a screen shot of my results:

As you can see the preference is called ‘aimcc.privacy.disclosePluginsToBuddyFeed’. Finding the preference specifier is only the beginning. I will cover how to read it in a future post.

NOTE: This will only work for aimcc.* preferences. So it is possible that you can make a change in the settings that does not get notified via OnPreferenceChange because it is internal to the client. Internal preferences are not available to pluigns at this time.

Easy tool for generating AIM plugin fingerprints

Sunday, December 7th, 2008

Ever wanted a command line version of acchash.exe that simply displays the fingerprint and copies it to the clipboard without any mouse clicks?

Here you go:

use Digest::SHA;
use Win32::Clipboard;
 
$file = shift;
 
if ( -e $file && -f $file )
{
   $hash = uc( Digest::SHA->new(256)->addfile($file, 'b')->hexdigest );
   print "Plugin Fingerprint for '$file' (copied to clipboard): $hash\n";
   Win32::Clipboard()->Set( $hash );
}
else
{
   print "Usage: gushash [plugin_file_to_hash]\n";
}

Save this as gushash.pl in a location where your command shell can find it.

You will need Perl and the Digest-SHA package to use this script. The latter can be obtained through the ppm (Perl Package Manager) tool that is installed with Perl.

I’m always making little tools like this in Perl and Python to help me automate stuff.

This is here in case someone finds it useful…

Open AIM: Modeless Window Support for Plugins

Sunday, December 30th, 2007

Have you ever wondered why tab and other navigation keys don’t work in some of the modeless dialog windows created by plugins? The reason for this is because Windows requires applications to call IsDialogMessage() for the currently active modeless window in order to translate the keyboard messages into dialog navigation commands.

This is relatively easy to implement in a standalone application since the code is all compiled and built together. However, in a plugin world you have to create an API to support this since it requires the cooperation between the hosting application, that owns the message pump, and the plugins, that know which modeless window is currently active.

The Open AIM SDK added support for registering the active dialog back in version 1.3 via a property called IAccPluginInfoProp_Windows. This property accepts an array of UINTs that are the window references. In the Windows environment you only have to provide a one-element array with the window handle of the currently active modeless dialog window or an empty array.

Open AIM clients need to listen for changes to IAccPluginInfoProp_Windows and keep track of the currently active dialog window in any plugin. On every window message, Open AIM clients should call IsDialogMessage() if it has at least one active modeless dialog window registered. If the message is handled, then the client is done otherwise the client can continue on with its own processing of the message. If all plugins are working correctly, there should only be one active modeless dialog window at a time even if there is more than one open.

I created a handy little class that you can use to make this easy to manage in your plugins. Here is a part of the header (Download the full source here):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class COpenAIMPluginWindowReg
{
public:
    // Call this once from the first instance of your plugin and
    // a second time, with a null pointer, when the last instance is shutdown.
    static void SetPluginInfoPtr(IAccPluginInfo *pPluginInfo);
 
    // Call this from WM_ACTIVATE
    static void OnActivate(HWND hWnd, WPARAM wParam);
 
private:
    static CComPtr<IAccPluginInfo>  s_spPluginInfo;
 
};

As you can see, this class has just two static methods and thus very simply to use. You call COpenAIMPluginWindowReg::SetPluginInfoPtr(pPluginInfo); from the first instance of your plugin. I typically call this from the IAccPlugin::Init() method and keep track if the number of instances of my plugin that are running. You must also call COpenAIMPluginWindowReg::SetPluginInfoPtr(NULL); when the last instance of your plugin is shutdown.

Then, in every modeless dialog window you create, add a handler for the WM_ACTIVATE message and call:

    COpenAIMPluginWindowReg::OnActivate(hWnd, wParam);

The OnActivate static method takes care of maintaining the IAccPluginInfoProp_Windows property for your plugin as the window is activated and deactivated.

I have released this source under an MIT style license. See the source files for more information. Again you can download the full source here.

Questions? Comments?

Open AIM: How to register a plugin command

Monday, December 17th, 2007

Hello again. I am back. Today I am starting a new series of posts on Open AIM. For this first post, I will start by introducing a handy little function similar to the one I wrote for my plug-ins that makes it easy to register commands that your plug-in will handle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// The following code is public domain. Written by Gus Verdun
static void RegisterPluginCommand(IAccPluginInfo * pluginInfo, 
                                               int id, UINT flags, 
                                               LPCWSTR pCommand)
{
    CComPtr <IAccCommand> spCommand;
    pluginInfo->AddCommand(id, &spCommand);
    if (spCommand)
    {
        CComBSTR str(pCommand);
        put_Text(spCommand, str);
        put_Flags(spCommand, (AccCommandFlags)flags);
    }
}

I normally call this function from the IAccPlugin::Init method, but you can call it at any time you want to add a command. The first parameter is the IAccPluginInfo interface pointer that is passed to you on the Init() call. The second parameter is the command identifier that is assigned by you and handled in your plugin’s IAccCommandTarget methods. The third parameter, flags, are the AccCommandFlags that let you indicate where you want this command to appear in the UI. Finally, the fourth parameter is the actual text that will be displayed in the client’s UI.

Here is a simple example to define a command that will show only in the plug-in settings page.

RegisterPluginCommand(pPluginInfo, -1, 
     AccCommandFlags_PreferencesUi, 
     OLESTR("Settings...") );

For localization purposes, you can easily change this function to load the command string from a resource. I will leave that as an exercise for you to try.

Hope this helps. Comments? Questions?

Easy Way to Tell if Your Plug-in is Running in AIMcc 1.3 or Later

Monday, August 27th, 2007

The other day I was working on the CoreWitness plug-in in order to prepare it for use with the latest AIM SDK, version 1.3.0. I quickly ran into the issue of what to do when it’s loaded in a client running a previous version of AIMcc.

The first clue came when I ran the client to debug some of the new CoreWitness code. The release notes in AIMcc indicated that a new property, called AccSessionProp_Pause, was added to IAccSession. I started my test run the way I always do by running AIM 6.1, but that client is using the older AIMcc 1.2.4.

Here was what I saw when looking at that property from the CoreWitness trace:

{Unknown} = {Empty}
hr=E_INVALIDARG 0x80070057 (2147942487U) (-2147024809L)

“{Unknown}” is shown because “AccSessionProp_Pause” is not in the typelib of the older AIMcc version that loaded the plug-in. The encouraging news is you get E_INVALIDARG.

Reading this property in AIMcc 1.3.0 shows:

AccSessionProp_Pause = {Empty}
hr=E_INVALIDARG 0x80070057 (2147942487U) (-2147024809L)

Wait, they both return E_INVALIDARG? This must be a write only property. OK, so this won’t work for this property. I could try to set the AccSessionProp_Pause to false, but that might have some unwanted effects. Another thing I could do is inspect the typelib to see if it knows about AccSessionProp_Pause, but I will save that as a last resort. Are there any other new readable properties?

The release notes mentioned another new property; this one is in the IAccPluginInfo interface called AccPluginInfoProp_Windows. Lets, see what it looks like from CoreWitness in AIMcc 1.2.4 and then in 1.3.0:

{Unknown} = {Empty}
hr=E_INVALIDARG 0x80070057 (2147942487U) (-2147024809L)

and

AccPluginInfoProp_Windows = {Empty}

BINGO, that’s exactly what what we need. We can simply adjust our logic based on the support of the AccPluginInfoProp_Windows property. In CoreWitness, I use this method to filter out what data I show and add more command actions when running under AIMcc 1.3.0 since this version lets me specify where these menu items go in the UI.