AIM Custom Client Tutorial


This tutorial walks you through each step necessary to build a sample AIM Custom Client application in C++.

  1. The first step is to #include the necessary AIM Developer SDK header files. For Windows, we can also insert #pragmas to bring in the necessary library files for the linker. We also reference AccSupport.h since we will be using the helper classes in that header file.

    #include <stdio.h>      // printf

    #include "AccCore.h"    // AIMCC main header

    #include "AccSupport.h" // AIMCC C++ helper classes

    #ifdef _MSC_VER

    #pragma comment(lib, "acccore.lib") // aimcc main lib

    #pragma comment(lib, "accuuidlib.lib") // aimcc uuid lib

    #endif

     

  2. Next, we create a class for our application. We do this because we need a class to implement the callback interface in order to receive events. Since we are using AccSupport.h, we derive our class from CAccEventSink. We also define Init, Run, and Term methods in our class, and call them in from our main() function. Note that this is only an implementation technique and is not required.
     

    class CSampleApp : public CAccEventSink

    {

    public:

        HRESULT Init(const char* userName, const char* password)

        {

            return E_NOTIMPL;

        }

        HRESULT Run()

        {

            return E_NOTIMPL;

        }

        void Term()

        {

        }

    };

     

    int main(int argc, char* argv[])

    {

        // check args

        if (argc != 3)

        {

            printf("usage: accsample username password\n");

            return -1;

        } 

     

        // create the app object and sign on

        CAccPtr<CSampleApp> sp(new CSampleApp);

        HRESULT hr = (sp) ? sp->Init(argv[1], argv[2]) : E_OUTOFMEMORY;

        if (FAILED(hr))

        {

            printf("initialization error, hr=%08X\n", hr);

            return (int)hr;

        }

               

        // run the message loop

        hr = sp->Run();

        sp->Term();

        return (int)hr;

    }

     

  3. Now, we add a call to the AIM global API function AccCreateSession to create an IAccSession interface, the primary interface in the AIM Developer SDK. We store the result of this operation in a CAccPtr smart pointer. This pointer will automatically Release the IAccSession interface when the smart pointer is destroyed. We also add calls to Advise and Unadvise to connect and disconnect our callback interface with the IAccSession interface, as well as an AccMessageLoop call to spin our event loop while our client runs.
     

        HRESULT Init(const char* userName, const char* password)

        {       

            HRESULT hr;

            if (SUCCEEDED(hr = AccCreateSession(IID_IAccSession, (void**)&m_sp)) &&

                SUCCEEDED(hr = Advise(m_sp)))

            {

            } 

            return hr;

        }  

        HRESULT Run()

        {

            return AccMessageLoop();

        }

        void Term()

        {

            Unadvise(m_sp);

            m_sp = NULL;

        }

    private:

        CAccPtr<IAccSession> m_sp;        

     

  4. The next step is to initialize the IAccSession interface and invoke the SignOn method so that our client connects to the AIM service. We must do three things here: identify our client to the AIM network by filling in the AccClientInfoProp_Description property on the IAccClientInfo interface, specify the screenname we want to use by setting the Identity property on the IAccSession interface, and finally call IAccSession::SignOn with the password. Note that we specify our AIM Custom Client key in our description string.
     

        HRESULT Init(const char* userName, const char* password)

        {       

            // 1. create aimcc main object, hook up for events

            // 2. set information to identify this client

            // 3. specify username and password, and sign on

            HRESULT hr;

            if (SUCCEEDED(hr = AccCreateSession(IID_IAccSession, (void**)&m_sp)) &&

                SUCCEEDED(hr = Advise(m_sp)))

            {

                CAccPtr<IAccClientInfo> spClientInfo;

                hr = m_sp->get_ClientInfo(&spClientInfo);

                if (SUCCEEDED(hr))

                {

                    CAccVariant desc(L"accsample (key=ju13LC0KMdgmkiO0)");

                    spClientInfo->put_Property(AccClientInfoProp_Description, desc);

                    if (SUCCEEDED(hr = m_sp->put_Identity(CAccBstr(userName))))

                        hr = m_sp->SignOn(CAccBstr(password));

                }

            }

     

            return hr;

        }

     

  5. Now our client can sign on to the AIM network. However, we don't get much feedback about how things are going. So we will add an override for the OnStateChange callback, which is called as our SignOn attempt proceeds and ultimately succeeds or fails. We will print out the current session state when each callback is received, and if we go offline, we will post a quit message to stop the message pump (which will cause AccMessageLoop to return and the app to exit).
     

        void OnStateChange(

            IAccSession* piSession, AccSessionState state, AccResult hr)

        {

            // quit when we go offline

            printf("state change: state=%d, hr=%08X\n", state, hr);

            if (state == AccSessionState_Offline)

                AccPostQuit(hr);       

        }  

     

  6. Things are starting to get interesting. The next step is to process incoming IM sessions so that we can interact with our application from another AIM client. We will tell our client to automatically accept incoming IM sessions, and if we receive an IM that consists of the message "quit", we will sign out of the AIM service. We do this by calling IAccSecondarySession::Accept when we are prompted to decide on the disposition of incoming IM sessions, by calling IAccIm::GetConvertedText on the IM when it arrives to get it in plaintext format, and finally IAccSession::SignOff when we decide to sign out. Note that we use the CAccBstr helper class to automatically dispose of the returned string pointer.
     

        void OnSecondarySessionStateChange(

            IAccSession* piSession, IAccSecondarySession* piSecSession,

            AccSecondarySessionState state, AccResult hr)

        {

            // always accept incoming IM sessions               

            if (state == AccSecondarySessionState_ReceivedProposal)

            {

                AccSecondarySessionServiceId id;

                piSecSession->get_ServiceId(&id);

                if (id == AccSecondarySessionServiceId_Im)

                    piSecSession->Accept();           

            }                   

        }   

        void OnImReceived(

            IAccSession* piSession, IAccImSession* piImSession,

            IAccParticipant* piSender, IAccIm* piIm)

        {

            // signoff when we get an IM that says "quit"

            CAccBstr text;

            piIm->GetConvertedText(OLESTR("text/plain"), &text);       

            if (text == OLESTR("quit"))

                m_sp->SignOff();       

        }   

     

Now we have an (albeit minimal) AIM Custom Client that can sign into the AIM network, receive IMs, and sign out. The full code is shown below. This code is also included in the SDK under samples/clients/accsample. For a more full-featured (but more complex) client, see the "amfcbuddy" sample under samples/clients/amfcbuddy,

///----------------------------------------------------------------------------

///

/// File Name: accsample.cpp

/// Copyright (c) 2005-2006 America Online, Inc.  All rights reserved.

///

///----------------------------------------------------------------------------

 

#include <stdio.h>      // printf

#include "AccCore.h"    // AIMCC main header

#include "AccSupport.h" // AIMCC C++ helper classes

#ifdef _MSC_VER

#pragma comment(lib, "acccore.lib") // aimcc main lib

#pragma comment(lib, "accuuidlib.lib") // aimcc uuid lib

#endif

 

class CSampleApp : public CAccEventSink

{

public:

    HRESULT Init(const char* userName, const char* password)

    {       

        // 1. create aimcc main object, hook up for events

        // 2. set information to identify this client

        // 3. specify username and password, and sign on

        HRESULT hr;

        if (SUCCEEDED(hr = AccCreateSession(IID_IAccSession, (void**)&m_sp)) &&

            SUCCEEDED(hr = Advise(m_sp)))

        {

            CAccPtr<IAccClientInfo> spClientInfo;

            hr = m_sp->get_ClientInfo(&spClientInfo);

            if (SUCCEEDED(hr))

            {

                CAccVariant desc(L"aatlbuddy (key=ju13LC0KMdgmkiO0)");

                spClientInfo->put_Property(AccClientInfoProp_Description, desc);

                if (SUCCEEDED(hr = m_sp->put_Identity(CAccBstr(userName))))

                    hr = m_sp->SignOn(CAccBstr(password));

            }

        }

 

        return hr;

    }

    HRESULT Run()

    {

        // run a message loop until AccPostQuit is called

        return AccMessageLoop();

    }

    void Term()

    {

        // clean up events and aimcc object

        Unadvise(m_sp);

        m_sp = NULL;

    }

 

    void OnStateChange(

        IAccSession* piSession, AccSessionState state, AccResult hr)

    {

        // quit when we go offline

        printf("state change: state=%d, hr=%08X\n", state, hr);

        if (state == AccSessionState_Offline)

            AccPostQuit(hr);       

    }  

    void OnSecondarySessionStateChange(

        IAccSession* piSession, IAccSecondarySession* piSecSession,

        AccSecondarySessionState state, AccResult hr)

    {

        // always accept incoming IM sessions               

        if (state == AccSecondarySessionState_ReceivedProposal)

        {

            AccSecondarySessionServiceId id;

            piSecSession->get_ServiceId(&id);

            if (id == AccSecondarySessionServiceId_Im)

                piSecSession->Accept();           

        }                   

    }   

    void OnImReceived(

        IAccSession* piSession, IAccImSession* piImSession,

        IAccParticipant* piSender, IAccIm* piIm)

    {

        // signoff when we get an IM that says "quit"

        CAccBstr text;

        piIm->GetConvertedText(OLESTR("text/plain"), &text);       

        if (text == OLESTR("quit"))

            m_sp->SignOff();       

    }   

 

private:

    CAccPtr<IAccSession> m_sp;        

};

 

int main(int argc, char* argv[])

{

    // check args

    if (argc != 3)

    {

        printf("usage: accsample username password\n");

        return -1;

    } 

 

    // create the app object and sign on

    CAccPtr<CSampleApp> sp(new CSampleApp);

    HRESULT hr = (sp) ? sp->Init(argv[1], argv[2]) : E_OUTOFMEMORY;

    if (FAILED(hr))

    {

        printf("initialization error, hr=%08X\n", hr);

        return (int)hr;

    }

           

    // run the message loop

    hr = sp->Run();

    sp->Term();

    return (int)hr;

}

 


Questions? Visit http://developer.aim.com/
Last updated: 03/03/2006