Sending a Message

4/8/2010

New messages are created and sent from a message store's Drafts folder. After you create a message, you set its property values (subject, body, list of recipients, and so on) and then post the message.

To send a message

  1. Initialize the MAPI subsystem, and log onto a MAPI session. For more information, see Beginning a MAPI Session.

  2. Establish a connection to a message store. For more information, see Connecting to a Message Store.

  3. Create a message object. For more information, see Creating a Message.

  4. Prepare and set the list of recipients for the message. Typically, there are three properties to set for each recipient: PR_RECIPIENT_TYPE, PR_ADDRTYPE, and PR_EMAIL_ADDRESS.

    1. Allocate memory for the list of recipients by calling MAPIAllocateBuffer with the total size of the recipient list.

      ULONG          cRecipients = 1;                     // Send a message to one recipient.
      LPWSTR               pszTo = L"you@mycompany.com";  // The e-mail address of the recipient.
      ULONG cRecipientProperties = 3;                     // Set three properties for the recipient.
      LPADRLIST   pRecipientList = NULL;
      ULONG      cbRecipientList = 0;
      
      cbRecipientList = sizeof(ADRLIST) + 
                              cRecipients * (sizeof(ADRENTRY) + 
                                              cRecipientProperties * (sizeof(SPropValue) + 
                                                              (wcslen(pszTo)+3) * sizeof(WCHAR)));
      
      hr = MAPIAllocateBuffer(cbRecipientList, (LPVOID FAR *)&pRecipientList);
      
    2. Initialize the recipient list by using memset to set the entire buffer to 0, and then begin filling-in values for the recipient list.

      memset(pRecipientList, 0, cbRecipientList);
      
      pRecipientList->cEntries = cRecipients;
      pRecipientList->aEntries[0].cValues = cRecipientProperties;
      
    3. Initialize the array of recipient properties.

      int cbRecipientProperties = 0;
      cbRecipientProperties = cRecipientProperties * sizeof(SPropValue);
      
      hr = MAPIAllocateBuffer(cbRecipientProperties, (LPVOID FAR *)&pRecipientList->aEntries[0].rgPropVals);
      memset(pRecipientList->aEntries[0].rgPropVals, 0, sizeof(SPropValue) * 3);
      
    4. Set the recipient type property value to indicate whether the recipient is listed on the To:, Cc:, or Bcc: fields by using the PR_RECIPIENT_TYPE property. This is the first of three properties that are being set in the recipient list.

      pRecipientList->aEntries[0].rgPropVals[0].ulPropTag = PR_RECIPIENT_TYPE;
      pRecipientList->aEntries[0].rgPropVals[0].Value.ul  = MAPI_TO;
      
    5. Set the address type property value to SMTP by using the PR_ADDRTYPE property. This is the second of three properties that are being set in the recipient list.

      pRecipientList->aEntries[0].rgPropVals[1].ulPropTag   = PR_ADDRTYPE;
      pRecipientList->aEntries[0].rgPropVals[1].Value.lpszW = L"SMTP";
      
    6. Set the e-mail address for each message recipient by using the PR_EMAIL_ADDRESS property. This is the third and final property that is being set in the recipient list.

      pRecipientList->aEntries[0].rgPropVals[2].ulPropTag   = PR_EMAIL_ADDRESS;
      pRecipientList->aEntries[0].rgPropVals[2].Value.lpszW = pszTo;
      
    7. Add the list of recipients to your message by calling the IMessage::ModifyRecipients method with the MODRECIP_ADD flag.

      hr = pMsg->ModifyRecipients(MODRECIP_ADD, pRecipientList);
      
    8. Free the memory resources taken by the Drafts Folder by calling MAPI IUnknown::Release, and by the list of recipients by calling MAPIFreeBuffer.

      hr = pFldrDrafts->Release();
      pFldrDrafts = NULL;
      
      hr = MAPIFreeBuffer(pRecipientList->aEntries[0].rgPropVals);
      hr = MAPIFreeBuffer(pRecipientList);
      
  5. Prepare and set the following properties of the message: PR_MSG_STATUS, PR_MESSAGE_FLAGS, and PR_SUBJECT.

    1. Allocate memory for the message property array by calling MAPIAllocateBuffer with the total size of the message properties. To find out how many bytes of memory the properties will require, add the size of the SPropTagArray data type, to the size of four SPropValue structure (one for each property), and then add the number of characters in the subject line, multiplied by the number of bytes taken be each of its UNICODE characters.

      LPSPropValue rgpMsgProperties = NULL;   // A pointer to the property value structure.
      int          cbMsgProperties  = 0;      // The size, in bytes, of the properties.
      int          cMsgProperties   = 4;      // The number of properties.
      LPWSTR       pszSubject       = L"This is only a MAPI Text Message";
      
      cbMsgProperties = sizeof(SPropTagArray) + 
                                              cMsgProperties * (sizeof(SPropValue) + 
                                                                        (wcslen(pszSubject) + 3) * sizeof(WCHAR));
      
      hr = MAPIAllocateBuffer(cbMsgProperties, (LPVOID FAR *)&rgpMsgProperties);
      
    2. Initialize the message property array by using memset to set the entire buffer to 0.

      memset(rgpMsgProperties, 0, cbMsgProperties);
      
    3. Set the message's subject by setting the PR_SUBJECT property to a value of pszSubject.

      rgpMsgProperties[0].ulPropTag   = PR_SUBJECT;
      rgpMsgProperties[0].Value.lpszW = pszSubject;
      
    4. Set a couple flags on the message by setting the PR_MESSAGE_FLAGS property to the bitmask value MSGFLAG_UNSENT and MSGFLAG_FROMME.

      rgpMsgProperties[1].ulPropTag = PR_MESSAGE_FLAGS;
      rgpMsgProperties[1].Value.ul  = MSGFLAG_FROMME | MSGFLAG_UNSENT;   // Notice how to combine multiple flags.
      
    5. Tag the message to identify that it is intended to be received as an SMTP message (as opposed to SMS) by setting the PR_MSG_STATUS property to a value of MSGSTATUS_RECTYPE_SMTP.

      rgpMsgProperties[2].ulPropTag = PR_MSG_STATUS;
      rgpMsgProperties[2].Value.ul  = MSGSTATUS_RECTYPE_SMTP;
      
    6. Set the message's importance level to High by setting the PR_IMPORTANCE property to a value of IMPORTANCE_HIGH.

      rgpMsgProperties[3].ulPropTag = PR_IMPORTANCE;
      rgpMsgProperties[3].Value.ul  = IMPORTANCE_HIGH;
      
    7. Add the array of properties to the message by calling the IMAPIProp::SetProps method on the IMessage object.

      hr = pMsg->SetProps(cMsgProperties, rgpMsgProperties, NULL);
      
    8. Free the memory buffer for the message property array by calling MAPIFreeBuffer.

      hr = MAPIFreeBuffer(rgpMsgProperties);
      
  6. Prepare and write the text body of the message.

    1. Declare a NULL IStream interface object, and then call the IMAPIProp::OpenProperty method of the IMessage object to get a reference to the message body text stream.

      LPSTREAM pStream = NULL;
      hr = pMsg->OpenProperty(PR_BODY, NULL, 0, MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN *)&pStream);
      
    2. Write text to the body of the message by calling the IStream::Write method of the IStream object.

      LPWSTR  pszBody = L"This text belongs to the body of the message.";
      ULONG    cbBody = 0;
      ULONG cbWritten = 0;
      
      cbBody = (wcslen(pszBody) + 1) * sizeof(WCHAR);
      
      hr = pStream->Write(pszBody, cbBody, &cbWritten);
      
    3. If it is no longer needed, release the IStream object by calling MAPI IUnknown::Release on it.

      pStream->Release();
      pStream = NULL;
      
  7. Submit the message by calling the IMessage::SubmitMessage method of the IMessage object.

    hr = pMsg->SubmitMessage(0);
    
  8. If it is no longer needed, release the IMessage object by calling MAPI IUnknown::Release on it.

    pMsg->Release();
    pMsg = NULL;
    

Example

The following code example is a function that encapsulates the entire set of programmatic steps necessary to send an e-mail message from a Windows Mobile device.

Note

To make the following code example easier to read, security checking and error handling are not included. This code example should not be used in a release configuration unless it has been modified to include them.

void MessagingApp(void)
{
    ICEMAPISession  * pSession    = NULL;
    IMsgStore       * pMsgStrore  = NULL;
    IMessage        * pMsg        = NULL;
    IMAPITable      * pTable      = NULL;
    SRowSet         * pSRowSet    = NULL;
    IMAPIFolder     * pFldrDrafts = NULL;

    ULONG                rgTags[] = { 1, {PR_CE_IPM_DRAFTS_ENTRYID} };
    LPSPropValue          rgProps = NULL;
    ULONG                 cValues = 0;

    hr = MAPIInitialize(NULL);                                       // Initialize the MAPI COM server.
    hr = MAPILogonEx(0, NULL, NULL, 0, (LPMAPISESSION *)&pSession);  // Logon and get a Session pointer.

    hr = pSession->GetMsgStoresTable(0, &pTable);                    // Get a pointer to Message Store table (Accounts collection).

    hr = pTable->QueryRows(1, 0, &pSRowSet);                         // Get a pointer to the first row, which contains all the
                                                                     // properties of the first Message Store.

    // Get the message store's Entry ID.
    hr = pSession->OpenMsgStore(0,
                                pSRowSet->aRow[0].lpProps[0].Value.bin.cb, 
                                (ENTRYID *)pSRowSet->aRow[0].lpProps[0].Value.bin.lpb, 
                                NULL, 0, &pMsgStrore);

    // Get the Drafts Folder's Entry ID.
    hr = pMsgStrore->GetProps((LPSPropTagArray)rgTags, MAPI_UNICODE, &cValues, &rgProps);

    // Get a handle to the Drafts Folder.
    hr = pMsgStrore->OpenEntry(rgProps[0].Value.bin.cb,
                               (LPENTRYID)rgProps[0].Value.bin.lpb, 
                                NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&pFldrDrafts);

    // Create a new Message object in the Drafts folder.
    hr = pFldrDrafts->CreateMessage(NULL, 0, &pMsg);

    // Setup the Recipient List.
    int cRecipients          = 1;               // Count of the number of Recipients. Sending to only one person.
    int cRecipientProperties = 3;               // Count of properties for each Recipient.
    int nRecipientIndex      = 0;

    WCHAR * pszTo = L"someone@microsoft.com";   // Address of Recipient.

    ADRLIST * pRecipientList = NULL;            // List of Recipients, stored in an Address List.
                                                // This can be a semi-colon separated list.

    int cbRecipientList = 0;                    // The size of the Recipient List, in bytes.
    cbRecipientList = sizeof(ADRLIST) + 
                        cRecipients * (sizeof(ADRENTRY) + 
                                        cRecipientProperties * (sizeof(SPropValue) + 
                                                                    (wcslen(pszTo) + 3) * sizeof(WCHAR)));

    // Allocate memory for the Address List.
    hr = MAPIAllocateBuffer(cbRecipientList, (LPVOID FAR *)&pRecipientList);

    memset(pRecipientList, 0, cbRecipientList);

    pRecipientList->cEntries            = cRecipients;
    pRecipientList->aEntries[0].cValues = cRecipientProperties;

    // The size of the array of Recipient properties, in bytes.
    int cbRecipientProperties = 0;
    cbRecipientProperties = cRecipientProperties * sizeof(SPropValue);

    hr = MAPIAllocateBuffer(cbRecipientProperties, (LPVOID FAR *)&pRecipientList->aEntries[nRecipientIndex].rgPropVals);
    memset(pRecipientList->aEntries[nRecipientIndex].rgPropVals, 0, sizeof(SPropValue) * 3);
    
    pRecipientList->aEntries[nRecipientIndex].cValues = cRecipientProperties;
     
    pRecipientList->aEntries[nRecipientIndex].rgPropVals[0].ulPropTag  = PR_RECIPIENT_TYPE;
    pRecipientList->aEntries[nRecipientIndex].rgPropVals[0].Value.ul   = MAPI_TO;

    pRecipientList->aEntries[nRecipientIndex].rgPropVals[1].ulPropTag  = PR_ADDRTYPE;
    pRecipientList->aEntries[nRecipientIndex].rgPropVals[1].Value.LPSZ = TEXT("SMTP");

    pRecipientList->aEntries[nRecipientIndex].rgPropVals[2].ulPropTag  = PR_EMAIL_ADDRESS;
    pRecipientList->aEntries[nRecipientIndex].rgPropVals[2].Value.LPSZ = pszTo;

    hr = pMsg->ModifyRecipients(MODRECIP_ADD, pRecipientList);         // Add the Recipients List to the message.

    hr = pFldrDrafts->Release();

    pFldrDrafts = NULL;

    hr = MAPIFreeBuffer(pRecipientList->aEntries[nRecipientIndex].rgPropVals);
    hr = MAPIFreeBuffer(pRecipientList);

    // Setup the message's properties.
    LPSPropValue rgpMsgProperties = NULL;   // An array of properties.
    int           cbMsgProperties = 0;      // The size of the array of properties.
    int            cMsgProperties = 4;      // The number of properties for the message.

    LPWSTR             pszSubject = L"This is only a MAPI Text Message";    // The message Subject line.

    cbMsgProperties = sizeof(SPropTagArray) + 
                                    cMsgProperties * (sizeof(SPropValue) + 
                                                    (wcslen(pszSubject) + 3) * sizeof(WCHAR));

    hr = MAPIAllocateBuffer(cbMsgProperties, (LPVOID FAR *)&rgpMsgProperties);
    memset(rgpMsgProperties, 0, cbMsgProperties);

    rgpMsgProperties[0].ulPropTag   = PR_SUBJECT;
    rgpMsgProperties[0].Value.lpszW = pszSubject;

    rgpMsgProperties[1].ulPropTag   = PR_MESSAGE_FLAGS;
    rgpMsgProperties[1].Value.ul    = MSGFLAG_FROMME | MSGFLAG_UNSENT;   // A combination of flags.

    rgpMsgProperties[2].ulPropTag   = PR_MSG_STATUS;
    rgpMsgProperties[2].Value.ul    = MSGSTATUS_RECTYPE_SMTP;

    rgpMsgProperties[3].ulPropTag   = PR_IMPORTANCE;
    rgpMsgProperties[3].Value.ul    = IMPORTANCE_HIGH;

    hr = pMsg->SetProps(cMsgProperties, rgpMsgProperties, NULL);
    hr = MAPIFreeBuffer((void *)rgpMsgProperties);

    LPSTREAM pStream = NULL;
    hr = pMsg->OpenProperty(PR_BODY, NULL, 0, MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN *)&pStream);

    LPWSTR pszBody  = L"Text in Body of Message.";
    ULONG cbBody    = 0;
    ULONG cbWritten = 0;

    cbBody = (wcslen(pszBody) + 1) * sizeof(WCHAR);

    hr = pStream->Write(pszBody, cbBody, &cbWritten);

    pStream->Release();
    pStream = NULL;

    hr = pMsg->SubmitMessage(0);

    pMsg->Release();
    pMsg = NULL;
}

Both the Windows Mobile Professional SDK and the Windows Mobile Standard SDK ship with a code sample called Sending E-mail, which you can build and run to gain a better understanding of the fundamental messaging concepts. The default location for the sample:

C:\Program Files\Windows Mobile 6.5.3 DTK\Samples\Common\CPP\Win32\SendMail\SendMail.sln

Compiling the Code

To compile the code sample, you must add references to the CE MAPI type library and the CE MAPI header files, to your project.

  1. Add preprocessor directives that include the header files in with the rest of your project files when you build your project. You do this by typing the following statements at the top of either your main C++ source file or your header file.

    #include <cemapi.h>
    #include <mapidefs.h>
    #include <mapiutil.h>
    #include <mapix.h>
    
  2. Specify a linker dependency to the CE MAPI type library as a project property.

    1. Press Alt+F7, and the Project Property Pages dialog box appears.
    2. In the dialog box, navigate to the Input property page for the Linker (navigate to Configuration Properties > Linker > Input).
    3. In Additional Dependencies, type cemapi.lib, and then click OK.
  3. Rebuild the Solution (press Ctrl+Alt+F7).

Robust Programming

A robust program is one that continues normal execution after surviving circumstances that cause lesser programs to crash. Robust programs are designed from the ground-up with error handling in mind. To facilitate this, Win32 COM functions and methods always return success/failure information in the form of a 32-bit HRESULT, which you can use to "trap" conditions that you anticipate will cause problems for your program.

The header file winerror.h contains the error code definitions along with a host of generic error handling macros that you can use in your code. For example, you can use the following two macros to test for success and failure of any COM procedure call.

#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)   // Note that non-negative numbers indicate success.
#define FAILED(hr)    ((HRESULT)(hr) < 0)

The following code example is a typical method that demonstrates robust programming.

BOOL MAessaging::LogOntoMAPI()
{
    HRESULT hr;
    hr = MAPILogonEx(0, NULL, NULL, 0, (LPMAPISESSION *)&pSession);

    if(FAILED(hr))
    {
        MessageBox(NULL, _T("Failed to log onto the MAPI Session."), _T("Warning"), MB_OK);
        return FALSE;
    }

    return TRUE;
}

See Also

Tasks

Beginning a MAPI Session
Connecting to a Message Store
Creating a Message
Ending a MAPI Session

Concepts

Messaging Samples

Other Resources

Messaging Common Tasks
Messaging Application Development
Messaging