最近,使用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
lpszAddressOptional 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;
}