65.9K
CodeProject 正在变化。 阅读更多。
Home

使用账户管理 API (IOlkAccountManger) 列出 Outlook 电子邮件账户

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.64/5 (8投票s)

2008年7月4日

CPOL

2分钟阅读

viewsIcon

72759

downloadIcon

1801

一篇关于如何使用 IOlkAccountManger 获取 Outlook 中配置的电子邮件账户的文章。

引言

本文展示了如何使用 账户管理 API 来获取 Outlook 中配置的电子邮件账户。

背景

撰写本文的原因是微软没有为所使用的接口提供头文件。此外,微软也没有记录账户的所有属性。

Using the Code

如果您想跳过技术细节,只想使用该功能,请按照以下步骤操作

  • 在您的项目中添加这些文件
    • AccountData.h - 包含用于保存账户数据的类
    • AccountHelper.h - 包含类 CAccountHelper 的声明
    • AccountHelper.cpp - 类 CAccountHelper 的实现
    • AcctMgmt.h - 包含 CLSID/IID 和属性标签声明
    • AcctMgmt.cpp - 包含实际提取账户数据的代码
  • 配置您的项目以链接 mapi32.lib
  • 在您的 *.h*.cpp 文件中包含文件 AccountHelper.h
  • 调用 GetAccounts ,将 Outlook 配置文件名传递给它,如下所示
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
    }
}

幕后细节

如果您想自己实现此功能,您需要执行以下操作

  1. 首先实现接口 IOlkAccountHelper。假设类名为 CAccountHelper
  2. 登录到 MAPI,将配置文件名存储在一个变量中。您需要在类 CAccountHelper 中将其传递回 MAPI
  3. 创建一个 IID_IOlkAccountManager 实例
  4. 创建 CAccountHelper 类的对象
  5. 调用 IOlkAccountManager:Init ,将您创建的 CAccountHelper 对象传递给它
  6. 调用 IOlkAccountManager::EnumerateAccounts 以获取 IOlkEnum 的接口
  7. 调用 IOlkEnum::GetCount 以获取账户数量
  8. 调用 IOlkEnum::Reset
  9. 在一个循环中,调用 IOlkEnum::GetNext 以检索所有账户

实际执行这些操作的 GetAccount 代码如下所示

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;
}

在演示应用程序中,我添加了两个按钮“显示账户对话框”和“显示添加账户向导”。

添加到接口的新方法具有此签名

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

要使用它,当点击上述按钮时,我添加了这段代码

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");
}

Exchange 账户的特殊处理

Exchange 账户没有传入或传出服务器。此外,exchange 账户的电子邮件 ID 也不符合我们看到的正常形式。要获取 exchange 账户的邮箱服务器或电子邮件 ID,您需要打开配置文件的全局配置文件部分。

邮箱服务器名称存储在属性标签中

PR_INTERNET_CONTENT_ID            PROP_TAG(PT_UNICODE, 0x662A)

电子邮件 ID 通常存储在这些属性标签中

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

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

在某些 exchange 2000 账户中,您找不到这些属性。在这种情况下,您必须获取用户的 IMailUse 接口,然后查询 PR_SMTP_ADDRESS 属性。

参考文献

欲了解更多信息,您可以参考 账户管理 API 参考

历史

  • 2008年7月4日:初始版本
  • 2008年8月27日:文章更新
© . All rights reserved.