Now that you have looked at all of the basic interfaces for working with the various MAPI objects, let's take a more detailed look at how you can use them to perform specific tasks with e-mail.
Initialize and log into a MAPI session.
Get the pointer to the table of message stores from the MAPI session object.
Search the table based on the display name matching the store you want to open.
Open the message store.0
The following sample shows how to open a message store:
HRESULT hr = S_OK; /////////////////////////////////////////// // Step 1 - Initialize COM/MAPI, and log on IMAPISession *pIMapi = NULL; if(FAILED(CoInitializeEx(NULL, 0))) return FALSE; if(MAPIInitialize(NULL) != S_OK) return FALSE; if(MAPILogonEx(0, NULL, NULL, 0, &pIMapi) != S_OK) { MAPIUninitialize(); return FALSE; } ////////////////////////////////////////////////////// // Step 2 - Get the pointer to the Message Store Table IMAPITable *pIMapiStoresTable = NULL; hr = pIMapi->GetMsgStoresTable(0, &pIMapiStoresTable); if(FAILED(hr)) { pIMapi->Logoff(0, 0, 0); pIMapi->Release(); pIMapi = NULL; MAPIUninitialize(); return FALSE; } //////////////////////////////////////////////////////////// // Step 3 - Query the table for the entry that matches the // name of the store we are interested in. IMsgStore *pPop3Store = NULL; while(1) { SRowSet *pRowSet = NULL; SizedSPropTagArray(2, tblColumns) = {2,{PR_DISPLAY_NAME, PR_ENTRYID}}; pIMapiStoresTable->SetColumns((LPSPropTagArray) &tblColumns, 0); hr = pIMapiStoresTable->QueryRows(1, 0, &pRowSet); if(FAILED(hr)) break; if(pRowSet->cRows != 1) break; //////////////////////////////////////////////////////////// // Step 4 - Compare the name with "POP3". If it's a // match, open it if(_tcscmp(TEXT("POP3"), pRowSet->aRow[0].lpProps[0]. Value.lpszW) == 0) {ENTRYID *pEntry = (ENTRYID *) pRowSet->aRow[0].lpProps[1].Value.bin.lpb; ULONG ulStoreBytes = pRowSet->aRow[0].lpProps[1].Value.bin.cb; pIMapi->OpenMsgStore(NULL, ulStoreBytes, pEntry, NULL, NULL, &pPop3Store); FreeProws(pRowSet); break; } // Free buffers allocated by MAPI FreeProws(pRowSet); }; // Clean up the store table pIMapiStoresTable->Release(); // Make sure we opened the store if(!pPop3Store) { pIMapi->Logoff(0, 0, 0); pIMapi->Release(); pIMapi = NULL; MAPIUninitialize(); return FALSE; } // Do something now..
Open a MAPI message store that will be used to contain the new message. See "Opening a Message Store" for details.
Open the Drafts folder.
Create a new message using the IMAPIFolder::CreateMessage() function.
Add the subject and the message flags to the message using the IMessage::SetProps() function.
Add the message body by using the IMessage::OpenProperty() function to get the IStream interface that you can write the message to.
Create a recipient list.
Call IMessage::SubmitMessage() to move the message from the Drafts folder to the Outbox. The next time the message store is synchronized, the message will be sent.
The following sample shows how to create a new message:
////////////////////////////////////////// // Step 2 - Open up the Pop3 Drafts folder LPSPropValue rgprops = NULL; LPSPropValue lppPropArray = NULL; ULONG cValues = 0; IMAPIFolder *pPOPDraftsFolder = NULL; SizedSPropTagArray(2, rgTags) = {2,{PR_CE_IPM_DRAFTS_ENTRYID,PR_OBJECT_TYPE}}; // Now get the Drafts folder hr = pPop3Store->GetProps((LPSPropTagArray)&rgTags, MAPI_UNICODE, &cValues, &rgprops); if(FAILED(hr)) return FALSE; hr = pPop3Store->OpenEntry(rgprops[0].Value.bin.cb, (LPENTRYID)rgprops[0].Value.bin.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&pPOPDraftsFolder); if(FAILED(hr)) return FALSE; ////////////////////////////////////////// // Step 3 - Create a new message object IMessage *pNewMessage = NULL; hr = pPOPDraftsFolder->CreateMessage(NULL, 0, &pNewMessage); if(FAILED(hr)) return FALSE; ////////////////////////////////////////// // Step 4 - Add a subject and message flags SPropValue sMsgProps[2]; TCHAR tchSubject[128] = TEXT("\0"); memset(&sMsgProps, 0, sizeof(sMsgProps)); wsprintf(tchSubject, TEXT("Test Message Subject")); sMsgProps[0].ulPropTag = PR_MESSAGE_FLAGS; sMsgProps[0].Value.ul = MSGFLAG_UNSENT; sMsgProps[1].ulPropTag = PR_SUBJECT; sMsgProps[1].Value.lpszW = tchSubject; hr = pNewMessage->SetProps(2, sMsgProps, NULL); if(FAILED(hr)) { pNewMessage->Release(); return FALSE; } ////////////////////////////////////////// // Step 5 - Stream the message body IStream *pStream = NULL; TCHAR tchBody[255] = TEXT("\0"); wsprintf(tchBody, TEXT("This is a test of the message body")); hr = pNewMessage->OpenProperty(PR_BODY, NULL, 0, MAPI_CREATE|MAPI_MODIFY, (IUnknown **)&pStream); if(FAILED(hr)) { pNewMessage->Release(); return FALSE; } // Copy the body into the stream DWORD dwLength = (lstrlen(tchBody)+1)*sizeof(TCHAR); pStream->Write(tchBody, dwLength, NULL); pStream->Release(); ////////////////////////////////////////// // Step 6 - Create a recipient list SizedADRLIST(1, msgAdrList); // Allocate a buffer for the entry SPropValue rgMsgProps[3]; memset(&rgMsgProps, 0, sizeof(rgMsgProps)); rgMsgProps[0].ulPropTag = PR_ADDRTYPE; rgMsgProps[0].Value.lpszW = TEXT("SMTP"); rgMsgProps[1].ulPropTag = PR_EMAIL_ADDRESS; rgMsgProps[1].Value.lpszW = TEXT("emailname@emailaddress.com"); rgMsgProps[2].ulPropTag = PR_RECIPIENT_TYPE; rgMsgProps[2].Value.ul = MAPI_TO; msgAdrList.cEntries = 1; msgAdrList.aEntries->cValues = 3; msgAdrList.aEntries->rgPropVals = rgMsgProps; // Add the list to the message hr = pNewMessage->ModifyRecipients(MODRECIP_ADD, (LPADRLIST)&msgAdrList); // Free the buffer MAPIFreeBuffer((LPADRLIST)&msgAdrList); ////////////////////////////////////////// // Step 7 - Submit the message hr = pNewMessage->SubmitMessage(0); if(FAILED(hr)) return FALSE; // Clean up pNewMessage->Release(); pPOPDraftsFolder->Release(); pPop3Store->Release();
Open a MAPI message store that contains the message you want to read. See "Opening a Message Store" for details.
Open the Inbox folder.
Get the table of messages in the Inbox folder.
Open the first message by using the IMsgStore::OpenEntry() function with the ENTRYID of the message you want to open.
Open the message and retrieve its properties for display.
To read the contents of a message, you would do the following:
////////////////////////////////////////// // Step 2 - Open up the Pop3 Inbox folder LPSPropValue rgprops = NULL; ULONG cValues = 0; IMAPIFolder *pPOPInboxFolder = NULL; SizedSPropTagArray(2, rgTags) = {2,{PR_CE_IPM_INBOX_ENTRYID,PR_OBJECT_TYPE}}; // Now get the Inbox folder hr = pPop3Store->GetProps((LPSPropTagArray)&rgTags, MAPI_UNICODE, &cValues, &rgprops); if(FAILED(hr)) return FALSE; hr = pPop3Store->OpenEntry(rgprops[0].Value.bin.cb, (LPENTRYID)rgprops[0].Value.bin.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&pPOPInboxFolder); if(FAILED(hr)) return FALSE; //////////////////////////////////////////////////////////// // Step 3 - Get the list of messages in the folder and their // ENTRY IDs IMAPITable *pIInboxTable = NULL; IMessage *pMsg = NULL; hr = pPOPInboxFolder->GetContentsTable(0, &pIInboxTable); if(FAILED(hr)) { pPOPInboxFolder->Release(); return FALSE; } while(1) { SRowSet *pRowSet = NULL; // Get the From, Subject and ID fields SizedSPropTagArray(3, tblMessages) = {3,{PR_SENDER_NAME,PR_SUBJECT,PR_ENTRYID}}; pIInboxTable->SetColumns((LPSPropTagArray)&tblMessages, 0); hr = pIInboxTable->QueryRows(1, 0, &pRowSet); if(pRowSet->cRows != 1) break; for(int nVal = 0; nVal < pRowSet->aRow[0].cValues; nVal++) { TCHAR tchProperties[10] = TEXT("\0"); DWORD dwType = 0, dwID = 0; dwType = PROP_TYPE(pRowSet->aRow[0].lpProps[nVal].ulPropTag); dwID = PROP_ID(pRowSet->aRow[0].lpProps[nVal].ulPropTag); wsprintf(tchProperties, TEXT("0x%08x Type:%04x ID:%04x\r\n"), pRowSet->aRow[0].lpProps[nVal].ulPropTag, dwType, dwID); // Do something, such as show the messages in a list? OutputDebugString(tchProperties); } ///////////////////////////////////////////////////////// // Step 4 - Open the message. For the sake of this example, // let's open the first message and break out hr = pPop3Store->OpenEntry (pRowSet->aRow[0].lpProps[2].Value.bin.cb, (LPENTRYID) pRowSet->aRow[0].lpProps[2].Value.bin.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&pMsg); FreeProws(pRowSet); break; } //////////////////////////////////////////////////////////// // Step 5 - Do something w/ the message if(pMsg) { // Get the from and subject LPSPropValue rgMsgprops = NULL; ULONG cMsgValues = 0; SizedSPropTagArray(2, rgMsgTags) = {2,{PR_SENDER_NAME, PR_SUBJECT}}; hr = pMsg->GetProps((LPSPropTagArray)&rgMsgTags, MAPI_UNICODE, &cMsgValues, &rgMsgprops); // Get the body IStream *pStream = NULL; TCHAR tchBody[255] = TEXT("\0"); hr = pMsg->OpenProperty(PR_BODY, NULL, 0, 0, (IUnknown **)&pStream); if(FAILED(hr)) { pMsg->Release(); pIInboxTable->Release(); pPOPInboxFolder->Release(); return FALSE; } // Read from the stream into the tchBody hr = pStream->Read(tchBody, 254, NULL); pStream->Release(); // Do something with it: TCHAR tchMsg[1024] = TEXT("\0"); wsprintf(tchMsg, TEXT("From: %s\r\nSubject: %s\r\nMsg: %s"), rgMsgprops[0].Value.lpszW,rgMsgprops[1]. Value.lpszW, tchBody); MessageBox(NULL, tchMsg, TEXT("New Message!"), MB_OK|MB_ICONINFORMATION); // Msg cleanup MAPIFreeBuffer(rgMsgprops); pMsg->Release(); } // Cleanup pIInboxTable->Release(); pPOPInboxFolder->Release();
Message attachments are used to send one or more additional "blobs" of data, such as a picture or sound file, along with an e-mail message. Each individual attachment to a message is supported by the IAttach interface.
Although the IAttach interface has no unique methods, it is derived from the IMAPIProp interface. Table 11.22 describes the properties that are used to configure an attachment.
Property Tag | Property Type | Description |
---|---|---|
PR_ATTACH_DATA_BIN | PT_BINARY | IStream interface that can be used to access the attachment |
PR_ATTACH_FILENAME | PT_TSTRING | Display name for the attachment |
PR_ATTACH_METHOD | PT_LONG | Must be set to PR_ATTACH_BY_VALUE |
PR_ATTACH_NUM | PT_LONG | Number that uniquely identifies the attachment within the message |
PR_ATTACH_SIZE | PT_LONG | The size, in bytes, of the attachment and all of the attachment properties |
PR_ENTRYID | PT_BINARY | The object's entry identifier |
PR_LAST_MODIFICATION_TIME | PT_SYSTIME | The last date and time the object was modified |
PR_NULL | PT_NULL | A NULL value |
PR_OBJECT_TYPE | PT_LONG | The type of object |
PR_PARENT_ENTRYID | PT_BINARY | The parent object's entry identifier |
Create a new message that will have an attachment. See the section "Creating a New Message" for details.
After you have completed setting up the message subject, body, and recipient list, use the IMessage::CreateAttach() method to get the interface pointer for a new IAttach object.
Set up the properties for the attachment. You need to configure the PR_ATTACH_METHOD, PR_ATTACH_DATA_BIN, and PR_ATTACH_FILENAME property tags.
Stream in the binary data for the file. This is accomplished by using the IAttach::OpenProperty() method to get the IStream interface for the data property. You can then use the IStream::Write() method to stream the data to the attachment.
Submit the message to the Outbox using IMessage::SubmitMessage(). This will set up the message to be sent the next time the store is synchronized.
The following example creates a message with an attachment:
// This is at the point where our message has been created, // and we are about to send it. Before we do, let's attach // a sound file to the message. //////////////////////////////////////// // Step 2 - Attach a file to the message IAttach *pAttach = NULL; ULONG ulAttachNo = 0; hr = pNewMessage->CreateAttach(NULL, 0, &ulAttachNo, &pAttach); if(FAILED(hr)) return FALSE; /////////////////////////////////////////////////// // Step 3 - Set up the properties for the attachment SPropValue rgAttachProps[3]; TCHAR tchFileName[MAX_PATH] = TEXT("\0"); TCHAR tchFilePath[MAX_PATH] = TEXT("\0"); wsprintf(tchFileName, TEXT("Alarm1.wav")); wsprintf(tchFilePath, TEXT("\\Windows\\Alarm1.wav")); memset(&rgAttachProps, 0, sizeof(rgAttachProps)); rgAttachProps[0].ulPropTag = PR_ATTACH_METHOD; rgAttachProps[0].Value.ul = ATTACH_BY_VALUE; // Specify that we are attaching binary data. We'll stream // in the data using IStream below. rgAttachProps[1].ulPropTag = PR_ATTACH_DATA_BIN; rgAttachProps[2].ulPropTag = PR_ATTACH_FILENAME; rgAttachProps[2].Value.lpszW = tchFileName; hr = pAttach->SetProps(3, rgAttachProps, NULL); //////////////////////////////////////////////////////////// // Step 4 - Using IStream, let's stream in the attachment data // from the file HANDLE hAttachFile = NULL; IStream *pAttachStream = NULL; // Get the pointer to the property stream hr = pAttach->OpenProperty(PR_ATTACH_DATA_BIN, NULL, 0, MAPI_CREATE|MAPI_MODIFY, (IUnknown **)&pAttachStream); // Open up the file hAttachFile = CreateFile(tchFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ NORMAL, 0); if(hAttachFile == INVALID_HANDLE_VALUE) { pAttach->Release(); pNewMessage->Release(); pPOPDraftsFolder->Release(); pPop3Store->Release(); return FALSE; } // Read from the file, and write it to the stream DWORD dwRead = 0, dwWritten = 0; LPVOID lpBuffer = NULL; lpBuffer = (LPVOID)LocalAlloc(LPTR, 4096); do { ReadFile(hAttachFile, lpBuffer, 4096, &dwRead, NULL); if(dwRead > 0) { hr = pAttachStream->Write(lpBuffer, dwRead, &dwWritten); if(FAILED(hr)) break; } }while(dwRead > 0); CloseHandle(hAttachFile); LocalFree(lpBuffer); pAttachStream->Commit(STGC_DEFAULT); pAttachStream->Release(); ////////////////////////////////////////// // Step 5 - Submit the message hr = pNewMessage->SubmitMessage(0); if(FAILED(hr)) return FALSE; // Clean up pAttach->Release(); pNewMessage->Release(); pPOPDraftsFolder->Release(); pPop3Store->Release();
Open an IMessage interface pointer for the message that contains attachments. See "Reading a Message" for details.
Get the attachment table for the message using IMessage::GetAttachmentTable().
Query the table for the PR_ATTACH_NUM for the attachment you are interested in opening.
Get the properties from the table entry that you will use to open the attachment stream.
Using the IMessage::OpenAttach() method with the attachment number you found in the query, you can get the IAttach interface pointer.
Get the IStream pointer for the PR_ATTACH_DATA_BIN property. In addition, create a new file handle for the output of the data stream.
Read from the stream and output to the file.
The following example shows how to open a message attachment:
//////////////////////////////////////////////////////////// // Step 2 - Already have a message that is opened // so, let's go ahead and get the message's attachment by // first getting the attachment table for the message if(!pMsg) return FALSE; IMAPITable *pAttachTable = NULL; hr = pMsg->GetAttachmentTable(0, &pAttachTable); //////////////////////////////////////////////////////////// // Step 3 - Get the properties from the table for the // attachments for the first attachment. We'll only extract // the first one in this example. SRowSet *pAttachRowSet = NULL; SizedSPropTagArray(3, tblAttachColumns) = {3,{PR_ATTACH_NUM,PR_ATTACH_SIZE,PR_ATTACH_FILENAME}}; pAttachTable->SetColumns((LPSPropTagArray)&tblAttachColumns, 0); hr = pAttachTable->QueryRows(1, 0, &pAttachRowSet); if(pAttachRowSet->cRows != 1) return FALSE; //////////////////////////////////////////////////////////// // Step 4 - Grab the properties that we'll use long lAttachNum = 0; DWORD dwAttachSize = 0; TCHAR tchAttachName[MAX_PATH] = TEXT("\0"); lAttachNum = pAttachRowSet->aRow[0].lpProps[0].Value.l; dwAttachSize = pAttachRowSet->aRow[0].lpProps[1].Value.ul; _tcscpy(tchAttachName, pAttachRowSet->aRow[0].lpProps[2]. Value.lpszW); FreeProws(pAttachRowSet); //////////////////////////////////////////////////////////// // Step 5 - Get the IAttach interface for the attachment IAttach *pAttach = NULL; hr = pMsg->OpenAttach(lAttachNum, 0, 0, &pAttach); if(FAILED(hr)) return FALSE; //////////////////////////////////////////////////////////// // Step 6 - Get the IStream interface to stream the PR_ATTACH_ // DATA_BIN property out. Also, set up a file to write to. IStream *pAttachStream = NULL; HANDLE hAttachFile = NULL; TCHAR tchFilePath[MAX_PATH] = TEXT("\0"); wsprintf(tchFilePath, TEXT("\\%s"), tchAttachName); hAttachFile = CreateFile(tchFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); hr = pAttach->OpenProperty(PR_ATTACH_DATA_BIN, NULL, 0, 0, (IUnknown **)&pAttachStream); if(hAttachFile == INVALID_HANDLE_VALUE || FAILED(hr)) { pAttachStream->Release(); pAttach->Release(); pAttachTable->Release(); pMsg->Release(); pIInboxTable->Release(); pPOPInboxFolder->Release(); return FALSE; } //////////////////////////////////////////////////////////// // Step 7 - Read from the stream, write to the file. LPVOID lpBuffer = NULL; DWORD dwRead = 0, dwWritten = 0; lpBuffer = (LPVOID)LocalAlloc(LPTR, 4096); do { memset(lpBuffer, 0, 4096); pAttachStream->Read(lpBuffer, 4096, &dwRead); if(dwRead > 0) WriteFile(hAttachFile, lpBuffer, dwRead, &dwWritten, NULL); } while(dwRead > 0); CloseHandle(hAttachFile); LocalFree(lpBuffer); // Done. Clean up pAttachStream->Release(); pAttach->Release(); pAttachTable->Release(); pMsg->Release(); pIInboxTable->Release(); pPOPInboxFolder->Release();