2
votes

I am working on a Simple MAPI interface with Visual Studio 2008 to send email and attachments from an application. Works great with Thunderbird and Outlook 6 but Outlook 2013 is giving me all sorts of grief.

There are two key issues:

1) The email goes to Outlook's outbox but when it sends it bounces back (or appears to as I think this is internal) with the message "None of your e-mail accounts could send to this recipient."

  • If I compose a new message in Outlook with that exact email address it works fine. I've tried two different outbound SMTP accounts that I know both work, and work manually, but with IMAP they are stuck. The IMAP code I used didn't have originator data and I've hacked that in to try to make it work (the code I'm posting is a bit raw because I'm still trying to figure this out)

2) Outlook will display "A program is trying to send an e-mail message on your behalf".

  • I've set the the Outlook Trust Centre access to "Never warn about activity" as described in loads of online help but the problem persists. I can't help but think these might be related?

I'm wondering if Outlook 2013 is needing more data than I'm providing or if I'm making some cockeyed assumption.

Note: these test are on three different machines - Thunderbird on my main Win 10 development machine, Outlook 6 on a XP virtual box, and Outlook 2013 is on another Windows 10 machine.

Note on the code: I'm using CPtrArrays to store the data passed by the calling function. You'll see the GetAt() in setting up the recipients.

Thanks!

MapiRecipDesc sender[1];
MapiRecipDesc recipient[50];
MapiFileDesc fileDesc[20];

sender[0].ulRecipClass = MAPI_ORIG;
sender[0].lpszAddress = "[email protected]";
sender[0].lpszName = "Me";
sender[0].lpEntryID = 0;
sender[0].ulEIDSize = 0;
sender[0].ulReserved = 0;

iToCount = 0;
iIndex = 0;
while (iIndex < m_paTo.GetCount()) {
    recipient[iToCount].ulRecipClass = MAPI_TO;
    recipient[iToCount].lpszAddress = (char *) m_paTo.GetAt(iToCount);
    recipient[iToCount].lpszName = (char *) m_paTo.GetAt(iToCount);
    recipient[iToCount].lpEntryID = 0;
    recipient[iToCount].ulEIDSize = 0;
    recipient[iToCount].ulReserved = 0;
    iIndex++;
    iToCount++;
}

iIndex = 0;
while (iIndex < m_paCC.GetCount()) {
    recipient[iToCount].ulRecipClass = MAPI_CC;
    recipient[iToCount].lpszAddress = (char *) m_paCC.GetAt(iIndex);
    recipient[iToCount].lpszName = (char *) m_paCC.GetAt(iIndex);
    recipient[iToCount].lpEntryID = 0;
    recipient[iToCount].ulEIDSize = 0;
    recipient[iToCount].ulReserved = 0;
    iIndex++;
    iToCount++;
}

iIndex = 0;
while (iIndex < m_paBCC.GetCount()) {
    recipient[iToCount].ulRecipClass = MAPI_BCC;
    recipient[iToCount].lpszAddress = (char *) m_paBCC.GetAt(iIndex);
    recipient[iToCount].lpszName = (char *) m_paBCC.GetAt(iIndex);
    recipient[iToCount].lpEntryID = 0;
    recipient[iToCount].ulEIDSize = 0;
    recipient[iToCount].ulReserved = 0;
    iIndex++;
    iToCount++;
}

iFileCount = 0;
iIndex = 0;
while (iIndex < m_paAttachments.GetCount()) {
    fileDesc[iFileCount].flFlags = 0;
    fileDesc[iFileCount].lpFileType = 0;
    fileDesc[iFileCount].lpszFileName = (char *) m_paAttachments.GetAt(iIndex);
    fileDesc[iFileCount].lpszPathName = (char *) m_paAttachments.GetAt(iIndex);
    fileDesc[iFileCount].nPosition = -1;
    fileDesc[iFileCount].ulReserved = 0;
    iIndex++;
    iFileCount++;
}



TCHAR szSubject[_MAX_PATH];
TCHAR szMessage[5001];
::StrCpy(szSubject, m_sSubject);
::StrCpy(szMessage, m_sMessage);


MapiMessage message;
::ZeroMemory(&message, sizeof(message));
message.lpszSubject = szSubject;
message.nRecipCount = iToCount;
message.lpRecips = recipient;
message.nFileCount = iFileCount;
message.lpFiles = fileDesc;
message.lpszNoteText = szMessage;
message.flFlags = MAPI_SENT | MAPI_UNREAD;
message.lpszConversationID = "123";
message.lpOriginator = sender;



//int nError = SendMail(0, (ULONG_PTR)hWndParent, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
int nError = SendMail(0, (ULONG_PTR)hWndParent, &message, MAPI_LOGON_UI, 0);

if (nError != SUCCESS_SUCCESS && 
    nError != MAPI_USER_ABORT && 
    nError != MAPI_E_LOGIN_FAILURE) {
    CString sMessage;
    CString sTest = recipient[0].lpszAddress;
    sMessage.Format("MapiMail:: SendMail Error code %d Recip count %d: first Recip: %s", nError, message.nRecipCount, sTest);
    AfxMessageBox(sMessage);
    lLog.WriteString(sMessage);
    return false;
}
1
I found that if I put SMTP: before the email address being sent to Outlook it solves problem 1. I haven't seen that documented anywhere but ran across someone with the same problem. I tested with Thunderbird and it seems happy with it too. I'm wondering if my problem #2 is more of an O/S / Software issue than programming? - KokoCa
Further to this: when sending using SMTP: I was getting a winmail.dat attachment on the emails and any attachment I tried to add was not sent, although it was in the Outlook outbox. Turns out that if you remove the address entirely and put <email@server> in the NAME field in the recipient structure, it removes the winmail.dat and sends attachments correctly. Thunderbird is okay with it too. Note: I'm documenting here now, hoping this may help others as I've had to dig all over for this. - KokoCa
I've also found that Outlook doesn't pay attention to the trust centre settings as described in dozens of articles. You have to add a DWORD PromptSimpleMAPISend with a value of 2 in: Computer\HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\x.0\Outlook\Security - KokoCa
You can post an answer to your own question. It's hard to read the comments. - Barmak Shemirani

1 Answers

1
votes

With Barmak Shemirani's advice I'm posting my own answer. If anyone else is looking for this info, maybe I can save them some time by posting it here in one place.

  1. The "None of your e-mail accounts could send to this recipient." problem can be solved by putting the email address of the recipient in pointy brackets in the name field in the recipient structure. Leave the address field blank. So, for example

    ::StrCpy(szTo, "<[email protected]>");
    recipient[iToCount].ulRecipClass = MAPI_TO;
    recipient[iToCount].lpszAddress = 0;
    recipient[iToCount].lpszName = szTo;
    recipient[iToCount].lpEntryID = 0;
    recipient[iToCount].ulEIDSize = 0;
    recipient[iToCount].ulReserved = 0;
    

    I've tested this with Outlook 2013 and 2016 and with Outlook 6, Thunderbird, and EM Client and they're all happy with it. Apparently you can put the name as well PersonName <[email protected]>, but I haven't tested that.

  2. The issue of Outlook displaying:

    A program is trying to send an e-mail message on your behalf

    is a software configuration issue. Most websites advise using the Trust Centre to set the Programmatic Access to allow other applications access via MAPI. You have to set this running the program as administrator but you also have to run Outlook as administrator for it to work

    The workaround is to edit the registry and add a DWORD PromptSimpleMAPISend with a value of 2 in:

    Computer\HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\x.0\Outlook\Security
    
  3. Bonus answer. If anyone is wondering about HTML in MAPI, it isn't supported. MAPI apparently pre-dates the common usage of HTML email.

    There is a workaround. You can leave your message body blank and instead put your message in html in a file and put that file as the first attachment in the MAPI message. I named my file with an .html extension.

    It is a bit of fluke - the email clients pick it up and display the HTML in the body of the email. Your html file will still be an attachment followed by any other attachments.

I've tested with Thunderbird, Outlook, and EM Client. I took a quick look at one web email reader and it did not display the html text (although the attachment was available to read).