How to Add Pocket Outlook Features to Your Mobile Application

Architectural Overview

The Pocket Outlook Object Model (POOM) contains a few interfaces that allow you to perform sophisticated management of contacts, tasks, and a calendar. For developers who need to implement such customized and collaborative solutions, POOM can make it quick and easy. Because POOM is supported over a wide range of mobile devices, your applications will be able to work correctly on these PDAs without too many problems. Besides, Pocket Outlook provides a capability to add new menu commands for third-party add-ins. This article will focus mainly on POOM possibilities rather than external application commands. Add-ins will be discussed in the next article. C# developers can also use POOM, but recently it is harder than in C++. If you need POOM in managed code, you can either create and call an unmanaged DLL with all the stuff inside or wrap all required APIs by using the DllImport attribute. Another suitable way is to create an interop assembly from the POOM type library and then reference it from you C# application. Unfortunately, I've failed to find any TLB for POOM on the Web... So, I'll leave it for better times.

I will start by overviewing the available POOM objects and their hierarchy. Actually, the hierarchy tree is pretty simple:

  • Application
    • Tasks Folder
      • Task 1
      • Task 2
      • Task 3
      • Task 4
    • Contacts Folder
      • Contact 1
      • Contact 2
      • Contact 3
      • Contact 4
    • Calendar Folder
      • Appointment 1
      • Appointment 2
      • Appointment 3
      • Appointment 4
    • Infrared Folder
      • Item 1
      • Item 2
      • Item 3
      • Item 4

As you can see, an Application object is located on the top of hierarchy. This is literally the only object that should be created via a CoCreateInstance call. All others are returned through this object.

An Application provides a set of Folder objects. Each Folder contains an Item collection of appropriate individual items; for example, Tasks, Contacts, or Appointments. Individual items have their own set of properties, which you can manage as needed. Except for this, there is a possibility to send and receive POOM items via IR programmatically. A special Infrared Folder keeps items marked for beaming.

Note: All things related to City List and Time Zone has beenremoved from POOM since Pocket PC 2002. All calls to such methods or properties will return an E_NOTIMPL error.

Before you turn to code samples, here's a typical POOM session scenario:

  • Create an instance of a PocketOutlook.Application object
  • Call the Application.Logon method
  • Manipulate Folders and Items to perform all required operations
  • Call the Application.Logoff method
  • Release the Application object

Getting Started with POOM Objects

Now, you can begin your tour through POOM objects. As I said before, the very first task is to create an Application object and call its Logon method:

typedef CComQIPtr<IPOutlookApp,&__uuidof(IPOutlookApp)>
        IPOutlookAppPtr;
...
// define class member
IPOutlookAppPtr m_pPOOMApp;
...
// initialize it somewhere
HRESULT hr = 0;
CLSID clsid;
LPOLESTR pProgID = L"PocketOutlook.Application";
hr = CLSIDFromProgID(pProgID,&clsid);
hr = m_pPOOMApp.CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER);
if ( SUCCEEDED(hr) )
{
   // perform login
   if(SUCCEEDED(m_pPOOMApp->Logon(NULL)))
   {
      m_bLoggedIn = TRUE;
   }
}

The code snippet above shows how to create a POOM Application object. I prefer to deal with smart pointers here to get rid of referencing issues, but the same code can be easily rewritten in pure API. At this point, you are ready to get specific folders containing Tasks, Contacts, and Appointments:

typedef CComQIPtr<IFolder,&__uuidof(IFolder)> IFolderPtr;
typedef CComQIPtr<IPOutlookItemCollection,
                     &__uuidof(IPOutlookItemCollection)>
                     IPOutlookItemCollectionPtr;
typedef CComQIPtr<ITask,&__uuidof(ITask)> ITaskPtr;

BOOL CPOOMDlg::GetPOOMFolder(int nFolder, IFolderPtr& pFolder)
{
   if ( !m_bLoggedIn )
      return FALSE;

   if (SUCCEEDED(m_pPOOMApp->GetDefaultFolder(nFolder, &pFolder)))
   {
      return TRUE;
   }
   else
   {
      return FALSE;
   }
}
...
void CPOOMDlg::OnButtonEnumAll()
{
   IFolderPtr pFolder;
   if (GetPOOMFolder(olFolderTasks, pFolder))
   {
      IPOutlookItemCollectionPtr pItemCol;
      CComBSTR bstrText;

      if (SUCCEEDED(pFolder->get_Items(&pItemCol)))
      {
         int cItems = 0;
         hr = pItemCol->get_Count(&cItems);

         for (int i = 1; i <= cItems; i++)
         {
            ITaskPtr pTask;
            if ( SUCCEEDED(pItemCol->Item(i,(IDispatch**)&pTask)) )
            {
               hr = pTask->get_Subject(&bstrText);
               // process this task as required
               ...
            }
         }
      }
   }
   ...
}

So, all you should do is to obtain the required folder and then get the corresponding items collection. Regardless of the folder type (olFolderTasks, olFolderContacts, or olFolderCalendar), item enumeration is always started from index 1. To simplify further explanation, let me put in an excerpt from the pimstore.h header file declaring IFolder and IPOutlookItemCollection interfaces (detailed declarations of available interfaces and all other stuff you will find in the pimstore.h header):

IFolder : public IDispatch
{
public:
   HRESULT get_Items(IPOutlookItemCollection **ppolItems);
   HRESULT get_DefaultItemType(int *polItem);
   HRESULT get_Application(IPOutlookApp **polApp);
   HRESULT AddItemToInfraredFolder(int olItem, IDispatch *polItem);
   HRESULT SendToInfrared(void);
   HRESULT ReceiveFromInfrared(IPOutlookItemCollection **ppItems);
};

IPOutlookItemCollection : public IDispatch
{
public:
   HRESULT Add(IDispatch **ppolItem);
   HRESULT get_Count(int *pnCount);
   HRESULT Find(BSTR pwszRestriction, IDispatch **ppItem);
   HRESULT FindNext(IDispatch **ppItem);
   HRESULT Item(int iIndex, IDispatch **ppolItem);
   HRESULT Remove(int iIndex);
   HRESULT Restrict(BSTR pwszRestriction,
                    IPOutlookItemCollection **ppolItems);
   HRESULT Sort(BSTR pwszProperty, VARIANT_BOOL fDescending);
   HRESULT get_IncludeRecurrences(VARIANT_BOOL *pfIncludeRecurrences);
   HRESULT put_IncludeRecurrences(VARIANT_BOOL fIncludeRecurrences);
   HRESULT get__NewEnum(IUnknown **ppEnumerator);
   HRESULT get_Application(IPOutlookApp **polApp);
};

How to Add Pocket Outlook Features to Your Mobile Application

What can you gain from these definitions? Well, first of all, you can always receive a pointer to an Application object from any POOM object. The next useful features are the Sort, Find, and Restrict methods of the IPOutlookItemCollection interface. They all take either a pwszRestriction or pwszProperty parameter to define operation criteria. The value of this parameter is a boolean expression that can be evaluated as TRUE or FALSE for any item in the collection:

L"[OfficeLocation] = \"Pittsburgh, PA\""
L"[Department] = \"Sales\" AND [CompanyName] <> \"SomeCompany\""

Property names are enclosed by brackets. You can combine the comparison operators <, <=, >, >=, =, or <> with the logical AND and OR, as in the following sample:

void CTasksDlg::OnButtonFind()
{
   if (SUCCEEDED(m_pTaskFolder->get_Items(&m_pItemCol)))
   {
      ITaskPtr pTask;
      CComBSTR bstrText;
      HRESULT hr = m_pItemCol->Find(L"[StartDate] = \"7/4/05\"
              AND [DueDate] < \"7/6/05\"",(IDispatch**)&pTask);
      if ( S_OK == hr )
      {
         m_TasksList.ResetContent();
         hr = pTask->get_Subject(&bstrText);
         m_TasksList.AddString(bstrText);
      }
   }
}

void CTasksDlg::OnButtonFindNext()
{
   ITaskPtr pTask;
   HRESULT hr = m_pItemCol->FindNext((IDispatch**)&pTask);
   CComBSTR bstrText;
   if ( S_OK == hr && pTask != NULL )
   {
      m_TasksList.ResetContent();
      hr = pTask->get_Subject(&bstrText);
      m_TasksList.AddString(bstrText);
   }
   else
   {
      AfxMessageBox(L"No more items");
   }
}

This sample manipulates with Tasks, but you can apply the same rules for either Contacts or Appointments. What you have to pay attention to is that all find/restict conditions are applied only to non-empty properties; in other words, that that have some values. Empty fields are left out of scope.

Creating Pocket Outlook Items

After playing with collections and folders, you can move on and learn how to create and set up different POOM items. This is really a simple step: Just call IPOutlookApp::CreateItem with the appropriate item type. Its output parameter will keep an instance of the requested object:

hr = m_pPOOMApp->CreateItem(olAppointmentItem, (IDispatch**)&pAppt);
hr = m_pPOOMApp->CreateItem(olContactItem, (IDispatch**)&pContact);
hr = m_pPOOMApp->CreateItem(olTaskItem, (IDispatch**)&pTask);

Now, you have at least two different ways to set it up. First, your application may simply call the Display method:

void CPOOMDlg::OnButtonaddContact()
{
   IFolderPtr pFolder;
   if ( GetPOOMFolder(olFolderContacts, pFolder) )
   {
      IContactPtr pContact;
      HRESULT hr = 0;

      if ( SUCCEEDED(m_pPOOMApp->CreateItem(olContactItem,
                    (IDispatch**)&pContact)) )
      {
         hr = pContact->Display();
         if ( S_OK == hr )
            pContact->Save();
      }
   }
}

Another way to set up the newly created Task, Contact, or Appointment is to assign all desired values manually to its properties. The following sample illustrates it on Task and Appointment creation:

void CTasksDlg::OnButtonAddTask()
{
   static int nImportanceCnt = 0;
   int arrImportance[] = { olImportanceLow,
                           olImportanceNormal,
                           olImportanceHigh };

   ITaskPtr pTask;
   HRESULT hr = 0;

   if ( SUCCEEDED(m_pPOOMApp->CreateItem(olTaskItem,
                  (IDispatch**)&pTask)) )
   {
      hr = pTask->put_Subject(L"TEST TASK");
      hr = pTask->put_Importance(arrImportance[nImportanceCnt%3]);
      COleDateTime dtStart = COleDateTime::GetCurrentTime();
      hr = pTask->put_StartDate(dtStart);
      hr = pTask->put_DueDate(dtStart+COleDateTimeSpan(2,12,0,0));
      hr = pTask->put_ReminderSet(VARIANT_TRUE);
      hr = pTask->put_ReminderTime(dtStart+COleDateTimeSpan(1,6,0,0));
      hr = pTask->put_ReminderOptions(olSound);
      hr = pTask->put_Body(L"Here is a task body");
      hr = pTask->Save();
      pTask.Release();

      nImportanceCnt++;

      RefreshTasks();
   }
}
...
BOOL AddAppointment(IPOutlookAppPtr pApp,
                    CString& sSubject,
                    COleDateTime& dtStart,
                    CString& sBody)
{
   BOOL bRes = TRUE;
   IAppointmentPtr pAppointment;
   if ( FAILED(pApp->CreateItem(olAppointmentItem,
       (IDispatch**)&pAppointment)) )
      return FALSE;

   CComBSTR bstrSubject(sSubject);
   pAppointment->put_Subject(bstrSubject);
   pAppointment->put_Start(dtStart);
   pAppointment->put_End(dtStart + COleDateTimeSpan(0,2,0,0));
   pAppointment->put_ReminderSet(VARIANT_TRUE);
   long nMinutes = 60 * 24;
   pAppointment->put_ReminderMinutesBeforeStart(nMinutes);
   CComBSTR bstrBody(sBody);
   pAppointment->put_Body(bstrBody);

   pAppointment->Save();

   return bRes;
}

As you see, a programmer's life is relatively easy in this case. If you have set, for example, the Appointment parameters incorrectly, the Save method will return an error. Except for this, there is nothing to be confused about here. I'm leaving untouched all topics about adding, removing, and copying POOM items via their own methods or via collection objects due to its simplicity.

Beaming POOM Objects

Finally, I'll briefly overview sending or receiving POOM items through IR communications. With Pocket Outlook, it is quite an easy operation. Actually, you don't need to bother about any IR stuff; Pocket Outlook will do all 'black jobs' for you. Thus, you may call IPOutlookApp::ReceiveFromInfrared or IFolder::ReceiveFromInfrared to receive all or only a specific item type. IFolder::AddItemToInfraredFolder and IFolder::SendToInfrared beam marked items to the recipient:

void CTasksDlg::OnButtonBeamTask()
{
   int nSel = m_TasksList.GetCurSel();
   if ( nSel != LB_ERR )
   {
      IPOutlookItemCollectionPtr pItemCol;

      if (SUCCEEDED(m_pTaskFolder->get_Items(&pItemCol)))
      {
         ITaskPtr pTask;
         HRESULT hr = pItemCol->Item(nSel+1,(IDispatch**)&pTask);

         IFolderPtr pFolderIR;
         m_pPOOMApp->GetDefaultFolder (olFolderInfrared, &pFolderIR);
         pFolderIR->AddItemToInfraredFolder(olTaskItem,
                                            (IDispatch*)pTask);
      }
   }
}

void CTasksDlg::OnButtonBeamTasks()
{
   IPOutlookItemCollectionPtr pItemsFromIR;
   m_pTaskFolder->ReceiveFromInfrared(&pItemsFromIR);
   // do whatever you want with newly received tasks...
   ///...
   RefreshTasks();
}
...
void CPOOMDlg::OnButtonBeam()
{
   IFolderPtr pFolderIR;
   m_pPOOMApp->GetDefaultFolder (olFolderInfrared, &pFolderIR);
   pFolderIR->SendToInfrared();
}

void CPOOMDlg::OnButtonRecv()
{
   m_pPOOMApp->ReceiveFromInfrared();
}

These methods are supported only for the special olFolderInfrared folder type. Pocket Outlook uses standard Windows CE components for beaming and places all transmitted items to their default folders on its own; you don't need to worry about it at all.

Conclusion

In the current article, you have learned about the main features Pocket Outlook offers you as a developer. With the help of POOM, you now can implement sophisticated and customized mobile applications with rich functionality. The next article will talk about components that may be intergrated into the Pocket Outlook menu.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Due to internal controls and regulations, the amount of long term archival data is increasing every year. Since magnetic tape does not need to be periodically operated or connected to a power source, there will be no data loss because of performance degradation due to the drive actuator. Read this white paper to learn about a series of tests that determined magnetic tape is a reliable long-term storage solution for up to 30 years.

Most Popular Programming Stories

More for Developers

RSS Feeds