Creating a Recurring Task With MAPI

[This is now documented here: https://msdn.microsoft.com/en-us/library/cc963264.aspx ]

This is the second part of a three part series documenting the MFCMAPI sample add-in CreateOutlookItemsAddin. We're in the process of updating the MAPI documentation and these articles are a preview of some of the new content. This article assumes you have downloaded the add-in from the above link and are running it inside the current version of MFCMAPI. Feedback on the article and on the code is especially welcome.

Part 1: Creating a Recurring Appointment With MAPI

Creating a Recurring Task

MAPI can be used to create task items

To create a task item

  1. Open a message store. See Opening a Message Store.
  2. Open a Task folder in the message store. See PR_IPM_TASK_ENTRYID.
  3. Call the Task folder's IMAPIFolder::CreateMessage method to create the new message.
  4. Follow the guidance in [MS-OXOTASK].pdf to set the appropriate task related properties.
  5. Save the message.

MFCMAPI demonstrates these steps with the AddTask function. AddTask takes numerous parameters from the Add Task dialog box that is displayed when the user selects Add Task on the Addins menu in MFCMAPI. The DisplayAddTaskDialog method in Task.cpp displays the dialog box and passes values from the dialog box to the AddTask method. The DisplayAddTaskDialog method does not relate directly to creating a task item using MAPI, so it is not listed here. Note that MFCMAPI does not ensure a task folder has been selected. Creating task items in non task folders may lead to undefined behavior.

The AddTask method is listed below. Note that the first parameter passed to the AddTask method is a pointer to an IMAPIFolder interface. Given lpFolder that represents an IMAPIFolder interface, the code calls IMAPIFolder::CreateMessage. The CreateMessage method returns a success code and a pointer to a pointer to an IMessage interface. Most of the AddTask function code handles the work of property setting in preparation for IMAPIProp::SetProps. If the SetProps call succeeds, IMAPIProp::SaveChanges commits the changes to the store and creates a new task item.

AddTask sets a number of named properties. See Using MAPI to Create Outlook 2007 Items for a discussion of named properties and how they are created. Since the named properties used for task items occupy multiple property sets, care must be taken when building parameters to pass to GetIDsFromNames.

AddTask uses a helper function to build a structure representing a task recurrence for PidLidTaskRecurrence. This function is named BuildWeeklyTaskRecurrencePattern. The structure it builds is documented in [MS-OXOTASK].pdf, section 2.2.2.2.15, PidListTaskRecurrence, and [MS-OXOCAL].pdf, section 2.2.1.44.1, RecurrencePattern Structure. Note that while a large variety of recurrence patterns are possible, BuildWeeklyTaskRecurrencePattern only builds a weekly recurrence pattern. It also makes a number of assumptions, such as the calendar type (Gregorian), the first day of the week (Sunday), and number of modified or deleted instances (none). A more general purpose recurrence pattern creation function would need to accept these sorts of variables as parameters.

Code Snippet

 HRESULT AddTask(LPMAPIFOLDER lpFolder,
                                   SYSTEMTIME* lpstStart,
                                   SYSTEMTIME* lpstEnd,
                                   SYSTEMTIME* lpstFirstDOW,
                                   DWORD dwPeriod,
                                   DWORD dwOccurrenceCount,
                                   DWORD dwPatternTypeSpecific,
                                   LPWSTR szSubject,
                                   LPWSTR szBody)
{
    if (!lpFolder) return MAPI_E_INVALID_PARAMETER;
HRESULT hRes = S_OK;
LPMESSAGE lpMessage = 0;
// create a message and set its properties
hRes = lpFolder->CreateMessage(0,
    0,
    &lpMessage);
if (SUCCEEDED(hRes))
{
    MAPINAMEID  rgnmid[ulTaskProps];
    LPMAPINAMEID rgpnmid[ulTaskProps];
    LPSPropTagArray lpNamedPropTags = NULL;

    ULONG i = 0;
    for (i = 0 ; i < ulTaskProps ; i++)
    {
        if (i < ulFirstTaskProp)
            rgnmid[i].lpguid = (LPGUID)&PSETID_Common;
        else
            rgnmid[i].lpguid = (LPGUID)&PSETID_Task;
        rgnmid[i].ulKind = MNID_ID;
        rgnmid[i].Kind.lID = aulTaskProps[i];
        rgpnmid[i] = &rgnmid[i];
    }

    hRes = lpFolder->GetIDsFromNames(
        ulTaskProps, 
        (LPMAPINAMEID*) &rgpnmid, 
        NULL, 
        &lpNamedPropTags);
    if (SUCCEEDED(hRes) && lpNamedPropTags)
    {
        // Since we know in advance which props
        // we'll be setting, we can statically
        // declare most of the structures involved
        // and save expensive MAPIAllocateBuffer calls
        // For brevity, code to set most spvProps
        // has been removed. For the complete listing, see
        // AddMail in Mails.cpp
        
        spvProps[p_PR_MESSAGE_CLASS_W].ulPropTag  = PR_MESSAGE_CLASS_W;
        spvProps[p_PR_ICON_INDEX].ulPropTag       = PR_ICON_INDEX;
        spvProps[p_PR_SUBJECT_W].ulPropTag        = PR_SUBJECT_W;
        spvProps[p_PR_MESSAGE_FLAGS].ulPropTag    = PR_MESSAGE_FLAGS;
        spvProps[p_PR_BODY_W].ulPropTag           = PR_BODY_W;

        spvProps[p_PidLidTaskMode].Value.l = tdmtNothing;
        SYSTEMTIME stStartUTC = {0};
        TzSpecificLocalTimeToSystemTime(NULL,lpstStart,&stStartUTC);
        SystemTimeToFileTime(&stStartUTC,&spvProps[p_PidLidCommonEnd].Value.ft);

        spvProps[p_PidLidTaskStatus].Value.l = tsvNotStarted;
        spvProps[p_PidLidPercentComplete].Value.dbl = 0.0;
        spvProps[p_PidLidTaskState].Value.l = tdsOWNNEW;
        spvProps[p_PidLidTaskDeadOccurrence].Value.b = false;
        spvProps[p_PidLidTaskOwner].Value.lpszW = L"Unknown";
        spvProps[p_PidLidTaskFRecurring].Value.b = true;
        spvProps[p_PidLidTaskOwnership].Value.l = tovNew;
        spvProps[p_PidLidTaskAcceptanceState].Value.l = tdvNone;
        spvProps[p_PidLidTaskFFixOffline].Value.b = true;
        SystemTimeToFileTime(lpstStart,&spvProps[p_PidLidTaskDueDate].Value.ft);
        spvProps[p_PidLidTaskComplete].Value.b = false;

        spvProps[p_PR_MESSAGE_CLASS_W].Value.lpszW = L"IPM.Task";
        spvProps[p_PR_ICON_INDEX].Value.l = 0x501; // Unassigned Recurring Task
        spvProps[p_PR_SUBJECT_W].Value.lpszW = szSubject;
        spvProps[p_PR_MESSAGE_FLAGS].Value.l = MSGFLAG_READ;
        spvProps[p_PR_BODY_W].Value.lpszW = szBody;

        hRes = BuildWeeklyTaskRecurrencePattern(
            lpstStart,
            lpstEnd,
            lpstFirstDOW,
            dwPeriod,
            dwOccurrenceCount,
            dwPatternTypeSpecific,
            &spvProps[p_PidLidTaskRecurrence].Value.bin.cb,
            &spvProps[p_PidLidTaskRecurrence].Value.bin.lpb);
        if (SUCCEEDED(hRes))
        {
            hRes = lpMessage->SetProps(NUM_PROPS, spvProps, NULL);
            if (SUCCEEDED(hRes))
            {
                hRes = lpMessage->SaveChanges(FORCE_SAVE);
            }
        }
        if (spvProps[p_PidLidTaskRecurrence].Value.bin.lpb)
            delete[] spvProps[p_PidLidTaskRecurrence].Value.bin.lpb;
    }
    MAPIFreeBuffer(lpNamedPropTags);
}
if (lpMessage) lpMessage->Release();
return hRes;

}