使用MAPI的MAPISendMail发送邮件碰到的巨大的坑

  最近,使用MAPI32.DLL提供的MAPISendMail发送邮件,在Foxmail可以成功,在Outlook始终不成功,怎么看、反复尝试都没试出代码问题。这时一个细节吸引了我的注意,把程序填充的地址删掉,手动填上去尽然可以发送成功,这样我就怀疑我给MapiRecipDesc填充收件人地址是否有什么问题,检查了代码,很正常呀,XXX@xxx.com,这怎么不行呢?再去MSDN查了一下,https://msdn.microsoft.com/en-us/library/windows/desktop/dd296720(v=vs.85).aspx。原来需要加上SMTP:,比如刚才的正确地址是SMTP:XXX@xxx.com。MSDN原文解释如下:

MapiRecipDesc structure

lpszAddress

Optional pointer to the recipient or sender's address; this address is provider-specific message delivery data. Generally, the messaging system provides such addresses for inbound messages. For outbound messages, the lpszAddress member can point to an address entered by the user for a recipient not in an address book (that is, a custom recipient).

The format of the address is address type:email address. Examples of valid addresses are FAX:206-555-1212 and SMTP:M@X.COM.


fuck!太坑了。

附MAPI发送邮件代码:

#include <MAPI.h>
#include <atlstr.h>
#include <shellapi.h>

#define UTILITY_DLL_DECLARRE DECLARE_EXPORT
#define OPERATE_OK                          0
#define OPERATE_FAIL                        -1
#define OPERATE_INPUT_PARAM_ERROR           -2

/************************************
* @method:    startProcess
* @brief: 启动进程
* @param: const chConstStringA& strAppPath 可执行程序全路径
* @param: const chConstStringA& strParams 参数
* @param: DWORD& exitCode 应用返回码
* @param: DWORD dwCreationFlag
* @param: bool bWaitProcessFinished 是否等待进程结束
* @return:   extern int
* @retval: 没有找到则返回UC_ERROR_ID
*/
UTILITY_DLL_DECLARRE bool startProcess(const chConstStringA& strAppPath, const chConstStringA& strParams, DWORD& exitCode, DWORD dwCreationFlag = 0, bool bWaitProcessFinished = false);

#define SEND_MAIL_LOAD_LIB_FAIL             -3
#define SEND_MAIL_MEMORY_FAIL               -4
#define SEND_MAIL_LOAD_FUNC_FAIL            -5
#define SEND_MAIL_LOGON_FAIL                -6

//邮箱附件
struct MailAttachment
{
    chStringA sFileName;                            ///< 文件名
    chStringA sFileFullPath;                        ///< 文件全路径
};

//邮件接收者类型
enum MailReciptType
{
    MAIL_TO = 1,                                    ///< 收件人
    MAIL_CC                                         ///< 抄送者 (copy recipt)
};

//邮件接收者信息
struct MailRecipInfo
{
    chStringA sName;                                ///< 接收者名字    
    chStringA sAddress;                             ///< 接收者邮箱地址
    MailReciptType eType;                           ///< 接收者类型
};


//邮件信息
struct MailInfo
{
    chStringA sSubject;                             ///< 主题
    chStringA sNoteText;                            ///< 正文
    chStringA sSenderAddr;                          ///< 发送者邮箱地址 (lpOriginator)

    chObjList<MailRecipInfo> lstRecips;             ///< 接收者(含收件人和抄送者)
    chObjList<MailAttachment> lstAttachs;           ///< 附件
};

/************************************
* @method:   sendMail
* @brief: 发送邮件
* @param: const MailInfo& mailInfo  邮件信息
* @return:   int
* @retval: 操作结果 0--成功;其余值失败
*/
UTILITY_DLL_DECLARRE int sendMail(const MailInfo& mailInfo);



bool startProcess(const chConstStringA& strAppPath, const chConstStringA& strParams, DWORD& exitCode, DWORD dwCreationFlag /* = 0*/, bool bWaitProcessFinished /*= false*/)
{
    STARTUPINFOW si;
    PROCESS_INFORMATION pi;
    bool bRet = false;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    chStringA strCmd;
    strCmd.Format("\"%s\" %s", strAppPath.c_str(), strParams.c_str());
    chStringW strCmdW = chUTF82W(strCmd);
    if (CreateProcessW(NULL, strCmdW.data(), NULL, NULL, FALSE, dwCreationFlag, NULL, NULL, &si, &pi) == TRUE)
    {
        bRet = true;
        if (bWaitProcessFinished)
        {
            HANDLE hProcess;
            CloseHandle(pi.hThread);
            hProcess = pi.hProcess;
            //等待进程返回;
            if (hProcess != 0)
            {
                exitCode = 0;
                WaitForSingleObject(hProcess, INFINITE);
                if (!GetExitCodeProcess(hProcess, &exitCode))
                {
                    exitCode = 1;
                }
                CloseHandle(hProcess);
            }
        }
    }
    else
    {
        chWARNINGx(false, "Fail create process [%s]", strAppPath.c_str());
    }
    return bRet;
}

int sendMail(const MailInfo& mailInfo)
{
    int iRet = OPERATE_OK;
    if (!mailInfo.lstRecips.size())
    {
        return OPERATE_INPUT_PARAM_ERROR;
    }

    //装入MAPI32.DLL动态库
    HMODULE hMod = LoadLibrary("MAPI32.DLL");
    if (hMod == NULL)
    {
        chWARNINGx(false, "LoadLibrary MAPI32.DLL fail");
        return SEND_MAIL_LOAD_LIB_FAIL;
    }

    //获取邮件相关的函数地址
    LPMAPILOGON lpMapiLogon = (LPMAPILOGON)::GetProcAddress(hMod, "MAPILogon");
    LPMAPISENDMAIL lpMapiSendMail = (LPMAPISENDMAIL)::GetProcAddress(hMod, "MAPISendMail");
    LPMAPILOGOFF lpMapiLogoff = (LPMAPILOGOFF)::GetProcAddress(hMod, "MAPILogoff");
    if (NULL == lpMapiLogon || NULL == lpMapiSendMail || NULL == lpMapiLogoff)
    {
        chWARNINGx(false, "Fail get mail proc addr ");
        FreeLibrary(hMod);
        return SEND_MAIL_LOAD_FUNC_FAIL;
    }

    LHANDLE hMapiSession = 0;
    ULONG status = 0; 
    status = lpMapiLogon(NULL, NULL, NULL, MAPI_LOGON_UI | MAPI_PASSWORD_UI, NULL, &hMapiSession);
    if (status != SUCCESS_SUCCESS)
    {
        chWARNINGx(false, "Fail MAPILogon code:%X", status);
        FreeLibrary(hMod);
        return SEND_MAIL_LOGON_FAIL;
    }

    //分配内存保存附件信息
    MapiFileDesc*        pAttachments = NULL;
    int nAttachments = mailInfo.lstAttachs.size();
    if (nAttachments > 0)
    {
        pAttachments = new MapiFileDesc[nAttachments];
        if (!pAttachments)
        {
            chWARNINGx(false, "Error allocating memory for pAttachments");
            FreeLibrary(hMod);
            return SEND_MAIL_MEMORY_FAIL;
        }
    }

    //添加附件
    int nIndex = 0;
    int nValidAttachments = 0;
    for (const chObjList<MailAttachment>::iterator it = mailInfo.lstAttachs.begin();
        it != mailInfo.lstAttachs.end() && it.hasData(); ++it, nIndex++)
    {
        const MailAttachment& attach = *it;
        if (attach.sFileFullPath.empty())
        {
            chWARNINGx(false, "Empty attachment full path,skip");
            continue;
        }
        pAttachments[nIndex].ulReserved = 0;
        pAttachments[nIndex].flFlags = 0;
        pAttachments[nIndex].nPosition = 0xFFFFFFFF;
        pAttachments[nIndex].lpszPathName = (LPSTR)attach.sFileFullPath.c_ptr();
        pAttachments[nIndex].lpszFileName = (LPSTR)(attach.sFileName.empty() ? attach.sFileFullPath.c_ptr() : attach.sFileName.c_ptr());
        pAttachments[nIndex].lpFileType = NULL;
        nValidAttachments++;
    }

    //邮件发送者
    MapiRecipDesc*       pSender = NULL;
    if (!mailInfo.sSenderAddr.empty())
    {
        pSender = new MapiRecipDesc;
        if (!pSender)
        {
            chWARNINGx(false, "Error allocating memory for pSender");
            FreeLibrary(hMod);
            if (pAttachments)
            {
                delete[] pAttachments;
                pAttachments = NULL;
            }
            return SEND_MAIL_MEMORY_FAIL;
        }
        pSender->ulReserved = 0;
        pSender->ulRecipClass = MAPI_ORIG;
        pSender->lpszAddress = (LPSTR)mailInfo.sSenderAddr.c_ptr();
        pSender->lpszName = (LPSTR)mailInfo.sSenderAddr.c_ptr();
        pSender->ulEIDSize = 0;
        pSender->lpEntryID = NULL;
    }

    //邮件接收者
    int nRecips = mailInfo.lstRecips.size();
    MapiRecipDesc*       pRecipients = new MapiRecipDesc[nRecips];
    if (!pRecipients)
    {
        chWARNINGx(false, "Error allocating memory for pRecipients");
        FreeLibrary(hMod);
        if (NULL == pAttachments)
        {
            delete[] pAttachments;
            pAttachments = NULL;
        }
        if (NULL == pSender)
        {
            delete pSender;
            pSender = NULL;
        }
        return SEND_MAIL_MEMORY_FAIL;
    }

    // 设置收件人
    int nValidRecips = 0;
    size_t i = 0;
    for (const chObjList<MailRecipInfo>::iterator recipIt = mailInfo.lstRecips.begin(); recipIt != mailInfo.lstRecips.end() && recipIt.hasData(); ++recipIt, i++)
    {
        const MailRecipInfo& recipInfo = *recipIt;
        if (recipInfo.sAddress.empty())
        {
            chWARNINGx(false, "Empty recip address,skip");
            continue;
        }
        pRecipients[i].ulReserved = 0;
        pRecipients[i].ulRecipClass = (recipInfo.eType == MAIL_TO) ? MAPI_TO : MAPI_CC;
        pRecipients[i].lpszAddress = (LPSTR)recipInfo.sAddress.c_str();
        pRecipients[i].lpszName = (LPSTR)(recipInfo.sName.empty() ? recipInfo.sAddress.c_str() : recipInfo.sName.c_str());
        pRecipients[i].ulEIDSize = 0;
        pRecipients[i].lpEntryID = NULL;
        nValidRecips++;
    }

    //邮件结构信息
    MapiMessage message;
    memset(&message, 0, sizeof(message));
    message.lpszSubject = (LPSTR)mailInfo.sSubject.c_ptr();           //主题
    message.lpszNoteText = (LPSTR)mailInfo.sNoteText.c_ptr();         //正文内容
    if (NULL != pSender)
    {
        message.lpOriginator = pSender;                               //发送者
    }

    message.nFileCount = nValidAttachments;                           //附件个数
    message.lpFiles = nValidAttachments ? pAttachments : NULL;        //附件信息
    message.nRecipCount = nValidRecips;                               //接收者个数
    message.lpRecips = nValidRecips ? pRecipients : NULL;             //接收者信息

    //发送邮件
    int nError = lpMapiSendMail(hMapiSession, 0, &message, MAPI_DIALOG, 0);
    if (nError != SUCCESS_SUCCESS)
    {
        //错误提示信息
        WARNING_TRACE("SendMail failed:%d", nError);
        iRet = OPERATE_FAIL;
    }

    lpMapiLogoff(hMapiSession, NULL, 0, 0);

    //释放分配的内存
    if (pAttachments)
    {
        delete[] pAttachments;
        pAttachments = NULL;
    }
    if (pSender)
    {
        delete pSender;
        pSender = NULL;
    }
    if (pRecipients)
    {
        delete[] pRecipients;
        pRecipients = NULL;
    }
    FreeLibrary(hMod);
    return iRet;
}



  • 0
    点赞
  • 2
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值