Click here to Skip to main content
15,885,309 members
Articles / Programming Languages / C++
Article

Using Account Management API (IOlkAccountManger) to List Outlook Email Accounts

Rate me:
Please Sign up or sign in to vote.
3.64/5 (8 votes)
28 Aug 2008CPOL2 min read 70.6K   1.8K   16   16
An article on how to use the IOlkAccountManger to get email accounts configured in Outlook
Image 1

Introduction

This article shows how to use the Account Management APIs to get email accounts configured in Outlook.

Background

The reason for writing this article is that Microsoft didn't provide a header file for the interface used. Moreover, not all the properties of the account are documented by Microsoft.

Using the Code

If you want to skip the technical details and just want to use the feature, follow these steps:

  • Add these files in your project:
    • AccountData.h - Contains class to hold the account data
    • AccountHelper.h - Contains declaration of class CAccountHelper
    • AccountHelper.cpp - Implementation of class CAccountHelper
    • AcctMgmt.h - Contains the CLSID/IID and the property tags declaration
    • AcctMgmt.cpp - Contains the code which actually extracts the account data
  • Configure your project to link mapi32.lib
  • Include the file AccountHelper.h in your *.h or *.cpp file
  • Call GetAccounts passing it the Outlook profile name as shown below:
C++
TCHAR szProfile[] = _T("ProfileName");    // Add your outlook profile name here
AccountData *pAccountData = NULL;    // This will contain the email account data
DWORD cAccountData = 0;    // This will contain the count of the email account

if(FAILED(GetAccounts(szProfile, &cAccountData, &pAccountData)))
{
    AfxMessageBox(_T("Failed to get account data"));
    return;
}
else
{
    for(DWORD i = 0 ; i <cAccountData; i++)
    {
        _tprintf(_T("Is Exchange account : %d"),pAccountData[i].lIsExchange);
        _tprintf(_T("Email ID is : %s"),pAccountData[i].szEmailID);
        _tprintf(_T("Incoming Server (POP/IMAP) is : %s"),
            pAccountData[i].szIncomingServer);
        _tprintf(_T("Outgoing Server is : %s"),pAccountData[i].szOutgoingServer);
        _tprintf(_T("Account Stamp : %s"),pAccountData[i].szAccountStamp);
        //similarly all other properties
    }
}

Inside the Hood

If you want to implement this yourself, you need to do these things:

  1. First implement this interface IOlkAccountHelper. Let's assume that the class name is CAccountHelper
  2. Login to MAPI, store the profile name in a variable. You will need it to pass back to MAPI in the class CAccountHelper
  3. Create an instance of IID_IOlkAccountManager
  4. Create an object of the class CAccountHelper
  5. Call the IOlkAccountManager:Init passing the CAccountHelper object you created
  6. Call IOlkAccountManager::EnumerateAccounts to get an interface to IOlkEnum
  7. Call IOlkEnum::GetCount to get the accounts count
  8. Call IOlkEnum::Reset
  9. In a loop, call IOlkEnum::GetNext to retrieve all the accounts

The code for GetAccount which actually does these looks like this:

C++
HRESULT GetAccounts(LPWSTR lpwszProfile, DWORD* pcAccounts, AccountData** ppAccounts)
{
    HRESULT hRes = S_OK;
    LPMAPISESSION lpSession;
    LPOLKACCOUNTMANAGER lpAcctMgr = NULL;

    hRes = MAPILogonEx(0,
        (LPTSTR)lpwszProfile,
        NULL,
        fMapiUnicode | MAPI_EXTENDED | MAPI_EXPLICIT_PROFILE |
        MAPI_NEW_SESSION | MAPI_NO_MAIL | MAPI_LOGON_UI,
        &lpSession);
    if(FAILED(hRes))
    {
        AfxMessageBox(L"Failed to login to the selected profile");
    }

    hRes = CoCreateInstance(CLSID_OlkAccountManager,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IOlkAccountManager,
        (LPVOID*)&lpAcctMgr);
    if(SUCCEEDED(hRes) && lpAcctMgr)
    {
        CAccountHelper* pMyAcctHelper = new CAccountHelper(lpwszProfile, lpSession);
        if(pMyAcctHelper)
        {
            LPOLKACCOUNTHELPER lpAcctHelper = NULL;
            hRes = pMyAcctHelper->QueryInterface
                (IID_IOlkAccountHelper, (LPVOID*)&lpAcctHelper);
            if(SUCCEEDED(hRes) && lpAcctHelper)
            {
                hRes = lpAcctMgr->Init(lpAcctHelper, ACCT_INIT_NOSYNCH_MAPI_ACCTS);
                if(SUCCEEDED(hRes))
                {
                    LPOLKENUM lpAcctEnum = NULL;

                    hRes = lpAcctMgr->EnumerateAccounts(&CLSID_OlkMail,
                        NULL,
                        OLK_ACCOUNT_NO_FLAGS,
                        &lpAcctEnum);
                    if(SUCCEEDED(hRes) && lpAcctEnum)
                    {
                        DWORD cAccounts = 0;

                        hRes = lpAcctEnum->GetCount(&cAccounts);
                        if(SUCCEEDED(hRes) && cAccounts)
                        {
                            AccountData* pAccounts = new AccountData[cAccounts];

                            hRes = lpAcctEnum->Reset();
                            if(SUCCEEDED(hRes))
                            {
                                DWORD i = 0;
                                for (i = 0; i < cAccounts; i++)
                                {
                                    LPUNKNOWN lpUnk = NULL;

                                    hRes = lpAcctEnum->GetNext(&lpUnk);
                                    if(SUCCEEDED(hRes) && lpUnk)
                                    {
                                        LPOLKACCOUNT lpAccount = NULL;

                                        hRes = lpUnk->QueryInterface(IID_IOlkAccount, 
                                               (LPVOID*)&lpAccount);
                                        if(SUCCEEDED(hRes) && lpAccount)
                                        {
                                            ACCT_VARIANT pProp = {0};
                                            //suppress the use of outer hRes
                                            HRESULT hRes = S_OK; 
                                            //Account ID
                                            hRes = lpAccount->GetProp
                                                   (PROP_ACCT_ID, &pProp);
                                            if(SUCCEEDED(hRes) && pProp.Val.dw)
                                            {
                                                pAccounts[i].lAccountID = pProp.Val.dw;
                                            }
                                            //Account Name
                                            hRes = lpAccount->GetProp(PROP_ACCT_NAME, 
                                                              &pProp);
                                            if(SUCCEEDED(hRes) && pProp.Val.pwsz)
                                            {
                                                pAccounts[i].szAccountName = 
                                                                      pProp.Val.pwsz;
                                            }

                                            //Is Exchange account flag
                                            hRes = lpAccount->GetProp
                                                      (PROP_ACCT_IS_EXCH, &pProp);
                                            if(SUCCEEDED(hRes) && pProp.Val.dw)
                                            {
                                                pAccounts[i].lIsExchange = pProp.Val.dw;
                                            }
                                            else
                                            {
                                                pAccounts[i].lIsExchange = 0;
                                            }

                                            //Account Send Stamp
                                            hRes = lpAccount->GetProp
                                                     (PROP_ACCT_SEND_STAMP, &pProp);
                                            if(SUCCEEDED(hRes) && pProp.Val.pwsz)
                                            {
                                                pAccounts[i].szSendStamp = pProp.Val.pwsz;
                                                lpAccount->FreeMemory
                                                           ((LPBYTE)pProp.Val.pwsz);
                                            }
                                            //similarly retrieve all other properties.
                                            //the sample code has the complete code
                                            //it's been removed from here just for clarity
                                        }

                                        if(lpAccount)
                                            lpAccount->Release();
                                        lpAccount = NULL;
                                    }

                                    if(lpUnk)
                                        lpUnk->Release();
                                    lpUnk = NULL;
                                }

                                *pcAccounts = cAccounts;
                                *ppAccounts = pAccounts;
                            }
                        }
                    }

                    if(lpAcctEnum)
                        lpAcctEnum->Release();
                }
            }
            if(lpAcctHelper)
                lpAcctHelper->Release();
        }

        if(pMyAcctHelper)
            pMyAcctHelper->Release();
    }

    if(lpAcctMgr)
        lpAcctMgr->Release();
    lpSession->Logoff(0,0,0);
    lpSession->Release();
    return hRes;
}

In the demo application, I have added two buttons "Show Account Dialog" & "Show Add Account Wizard".

The new method added to the interface has this signature:

C++
HRESULT IOlkAccountManager::DisplayAccountList (
        HWND hwnd,
        DWORD dwFlags,
        LPCWSTR lpwszReserved,
        DWORD dwReserved,
        const CLSID * pclsidReserved1,
        const CLSID * pclsidReserved2
    );

To use this, I have added this code when the above mentioned buttons are clicked:

C++
LPOLKACCOUNTMANAGER lpAcctMgr = NULL;

hRes = CoCreateInstance(CLSID_OlkAccountManager,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_IOlkAccountManager,
    (LPVOID*)&lpAcctMgr);
if(SUCCEEDED(hRes) && lpAcctMgr)
{
    CAccountHelper* pMyAcctHelper = new CAccountHelper(szProfileName, lpSession);
    if(pMyAcctHelper)
    {
        LPOLKACCOUNTHELPER lpAcctHelper = NULL;
        hRes = pMyAcctHelper->QueryInterface(IID_IOlkAccountHelper, 
                (LPVOID*)&lpAcctHelper);
        if(SUCCEEDED(hRes) && lpAcctHelper)
        {
            hRes = lpAcctMgr->Init(lpAcctHelper, ACCT_INIT_NOSYNCH_MAPI_ACCTS);
            if(SUCCEEDED(hRes))
            {
                hRes = lpAcctMgr->DisplayAccountList(this->m_hWnd,dwFlags,0,0,0,0);
                if(hRes == MAPI_E_INVALID_PARAMETER)
                {
                    AfxMessageBox(L"dwReserved, pclsidReserved1 or 
                        pclsidReserved2 were non-NULL.");
                }
                else if(hRes == E_ACCT_UI_BUSY)
                {
                    AfxMessageBox(L"The account dialog class could not be created.");
                }
                else if( hRes == MAPI_E_USER_CANCEL)
                {
                    AfxMessageBox(L"The Account Settings dialog box returned an error.");
                }
                else if(hRes == MAPI_E_CALL_FAILED)
                {
                    AfxMessageBox(L"The Add New E-Mail property sheet 
                            returned an error.");
                }
            }
            else
            {
                AfxMessageBox(L"Failed to initialize IOlkAccountManager");
            }
            lpAcctHelper>Release();
        }
        else
        {
            AfxMessageBox(L"Failed to get Account Helper interface");
        }
        pMyAcctHelper->Release();
    }
    else
    {
        AfxMessageBox(L"Oops!!! Out of memory");
    }
    lpAcctMgr->Release();
}
else
{
    AfxMessageBox(L"Ohhhhhhh No!!! failed to get IOlkAccountManager interface");
}

Special Handling of Exchange Account

Exchange accounts don't have an incoming or outgoing server. Also the email ID for the exchange account is not in the normal form we see to like. To get mailbox server or email ID of the exchange account, you need to open the Global profile section of the profile.

The mailbox server name is stored in the property tag:

C++
PR_INTERNET_CONTENT_ID            PROP_TAG(PT_UNICODE, 0x662A)

The email ID is in general stored in these property tags:

C++
PROP_EXCHANGE_EMAILID            PROP_TAG(PT_UNICODE, 0x663D) //email ID

PROP_EXCHANGE_EMAILID2           PROP_TAG(PT_UNICODE, 0x6641) //email ID 

In some exchange 2000 accounts, you won't find these properties. In that case, you have to get the IMailUse interface of the user, then query for PR_SMTP_ADDRESS property.

References

For further reading, you may refer to Account Management API Reference.

History

  • 4th July, 2008: Initial version
  • 27th August, 2008: Article updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Cannot disclose
India India
I am a Compute Engineer, currently working for a telecom company. I have interest in C#, Visual C++, MAPI, COM/DCOM, Windows administration and Networking.
I love programming!!!

Comments and Discussions

 
Questionsir how can i configure a account in outlook using C# Pin
Mehran Shafqat9-May-16 23:08
Mehran Shafqat9-May-16 23:08 
QuestionHow did you came to know about PROP_ACCT_POP3_SERVER?? Pin
Shubham Soni19-May-15 1:30
Shubham Soni19-May-15 1:30 
AnswerRe: How did you came to know about PROP_ACCT_POP3_SERVER?? Pin
Ashutosh Bhawasinka19-May-15 14:39
Ashutosh Bhawasinka19-May-15 14:39 
GeneralWonderfull ! Pin
Wrangly9-Apr-13 21:40
Wrangly9-Apr-13 21:40 
GeneralThanks! Pin
Member 966550114-Feb-13 1:52
Member 966550114-Feb-13 1:52 
Questionhi,i read your article, i have a problem,i think you can help me . Pin
lin9866652128-Feb-12 19:26
lin9866652128-Feb-12 19:26 
Generalserver name of MAPI account Pin
Ștefan-Mihai MOGA22-Feb-11 2:08
professionalȘtefan-Mihai MOGA22-Feb-11 2:08 
RantPlease give credit where due Pin
David Barrett12-Mar-09 16:56
David Barrett12-Mar-09 16:56 
GeneralRe: Please give credit where due Pin
Ashutosh Bhawasinka12-Mar-09 18:15
Ashutosh Bhawasinka12-Mar-09 18:15 
GeneralRe: Please give credit where due Pin
David Barrett13-Mar-09 4:35
David Barrett13-Mar-09 4:35 
GeneralIs there a way to implement this over C# Pin
edsanfor2316-Jan-09 6:03
edsanfor2316-Jan-09 6:03 
GeneralRe: Is there a way to implement this over C# Pin
Ashutosh Bhawasinka16-Jan-09 7:55
Ashutosh Bhawasinka16-Jan-09 7:55 
GeneralRe: Is there a way to implement this over C# Pin
kfironit@gmail.com2-Apr-09 0:29
kfironit@gmail.com2-Apr-09 0:29 
GeneralRe: Is there a way to implement this over C# Pin
bindike12-Apr-09 1:11
bindike12-Apr-09 1:11 
QuestionReload account order Pin
Member 319984812-Jan-09 6:02
Member 319984812-Jan-09 6:02 
AnswerRe: Reload account order Pin
Ashutosh Bhawasinka12-Jan-09 6:07
Ashutosh Bhawasinka12-Jan-09 6:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.