Sam Charchian
Microsoft Corporation
July 1999
Summary: The Microsoft Windows® CE operating system provides developers with an API to handle the reading, manipulation, and sending of e-mail. The Windows CE Mail API is not related to the Messaging API (MAPI). However, it is somewhat similar to Simple MAPI. (4 printed pages)
Click here to copy the PenPal sample files.
PenPal allows users to browse their message store and view the message contents of each message. PenPal employs MFC’s CSplitterWnd class to present the user with a dual-pane UI. A CTreeCtrl containing the folders and messages is on the left, and a CEdit that shows the message bodies is on the right. Users can create new mail complete with attachments. Users can also refresh the tree on the left at any time.
Figure 1. The PenPal sample application
The PenPal sample demonstrates the use of the following:
All of PenPal’s interaction with the Window CE Mail API is encapsulated in a single C++ class, CEMailHandler, providing the following base functionality:
Before using any other Mail API functions, we must first call MailOpen() to open the message store and obtain a handle to the mail context. When we are done with all Mail API functions, we call MailClose() to release this context. I decided to have PenPal’s SubmitMail() function manage it’s own mail context to make PenPal more easily portable.
The source code for this sample is heavily commented so that you can quickly incorporate the e-mail concepts into your Windows CE applications.
Note In an effort to reduce RAM usage, when a file is attached to a message, the Mail API deletes the original file from the file system. If you are developing an application that needs to keep the attached file, be sure to make a duplicate of the file first. Attempting to attach a file that exists in ROM will generate an error, as the file cannot be deleted!
When using common controls such as CTreeCtrl and CEdit, make sure you have called InitCommonControls() before your Create() has completed. Neglecting to do so will result in a non-functional control and no helpful error messages. For example:
BOOL CLeftView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
DWORD dwStyle, const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext)
{
// Without calling InitCommonControls(), the tree wouldn’t paint!
InitCommonControls();
// Set up the tree control’s style.
dwStyle |= TVS_HASLINES | TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP
| TVS_LINESATROOT | TVS_HASBUTTONS;
return CWnd::Create(WC_TREEVIEW, NULL, dwStyle, rect,
pParentWnd, nID);
}
PenPal uses the CTreeCtrl to display the folders and subjects of messages in the store. It uses the ItemData of each item to hold the OID (Object ID) of the object it corresponds to in the message store. The OID is a unique identifier for each object.
// Set the ItemData to the OID for later retrieval.
pDirTree->SetItemData(hTreeMailItem, (DWORD)pMsg->oid);
When the user selects an item in the tree, we retrieve the OID of the selected object and open it with the Mail API MailGet().
// Get the MsgOID from the itemdata in the ctreectrl.
dwMsgOID = GetItemData(pNMTreeView->itemNew.hItem);
.
.
// Setup the MailMsg Object for the call to MailGet()
oMsg.oid = dwMsgOID; // Set the OID to get
// Look in all folders for it.
oMsg.dwFlags = MAIL_FOLDER_ALL | MAIL_GET_BODY;
bResult = MailGet(m_hMailHandle, &oMsg);
When refreshing the tree, we need to first delete the entire tree. When deleting the tree with DeleteTree(TVI_ROOT), it’s a good idea to disable any processing that occurs in the OnSelchanged() event because deleting the tree can cause several selection changed events to occur as selected items are deleted. I accomplished this by setting a flag that tells my OnSelchanged() to do nothing. This made my refresh about five times faster.
PenPal uses the CSplitterWnd class to handle the display/resizing of our Tree and Edit controls. As usual, the CSplitterWnd is a member of my MainFrame class and is created in CMainFrame::OnCreateClient(). The only big difference I found was that the default implementation of OnClose() didn’t call my DestroyWindow(). As a result, neither of the splitter’s windows were being destroyed properly. To fix this, I overrode OnClose(), and called DestroyWindow() myself. In my DestroyWindow(), I called the DestroyWindow() of my splitter’s two windows.
BOOL CMainFrame::DestroyWindow()
{
m_fOkToSendChildMsgs = false;
// Destroy Panes (LEFT)
CLeftView* pLeft = GetLeftPane();
pLeft->DestroyWindow();
delete pLeft;
// Destroy Panes (RIGHT)
CPenPalView* pPPV = GetRightPane();
pPPV->DestroyWindow();
delete pPPV;
return CFrameWnd::DestroyWindow();
}
Even though the Pocket Outlook Object Model provides developers with an easy way to manipulate the information stored in Windows CE’s Pocket Outlook applications, a simple way to access the e-mail functionality on your device is still required. This sample illustrates the steps necessary to build robust mail capabilities into your Windows CE applications.