• Sign In
 
  • MSDN Blogs
  • Microsoft Blog Images
  • More ...

  • Advanced search options...
  • About
  • Email Blog Author
  • RSS for posts
  • Atom
  • RSS for comments
    • OK
  • CDO (20)
  • Code Snippet (42)
  • Custom Providers (15)
  • Debugging (7)
  • DevMsgTeam (278)
  • Documentation (101)
  • DST (8)
  • EWS (7)
  • Exchange (100)
  • Gotchas (91)
  • Hotfix (28)
  • MAPI (220)
  • MAPI Download (48)
  • MFCMAPI (92)
  • MSDN (54)
  • Non Dev (11)
  • OOM (17)
  • Outlook (163)
  • Outlook 2007 Auxiliary Reference (45)
  • Outlook Integration API (11)
  • Protocol Docs (20)
  • PST/OST (22)
  • Referrals (8)
  • Vista (12)
  • WrapPST (15)
Links:
  • Download MFCMAPI
  • MFCMAPI on Facebook
  • Troubleshooting Outlook Crashes
  • Office Update Center
  • Developer Messaging Team Blog
This site is provided "AS IS" with no warranties, and confers no rights. Use of included code samples are subject to the terms specified in the Terms of Use.
Archives
  • May 2012 (1)
  • April 2012 (3)
  • March 2012 (3)
  • February 2012 (3)
  • January 2012 (1)
  • December 2011 (3)
  • November 2011 (1)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (4)
  • June 2011 (3)
  • May 2011 (3)
  • April 2011 (3)
  • March 2011 (5)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (1)
  • November 2010 (4)
  • October 2010 (1)
  • September 2010 (3)
  • August 2010 (5)
  • July 2010 (3)
  • June 2010 (3)
  • May 2010 (1)
  • April 2010 (3)
  • March 2010 (3)
  • February 2010 (3)
  • January 2010 (2)
  • December 2009 (3)
  • November 2009 (5)
  • October 2009 (4)
  • September 2009 (5)
  • August 2009 (5)
  • July 2009 (11)
  • June 2009 (6)
  • May 2009 (5)
  • April 2009 (3)
  • March 2009 (18)
  • February 2009 (10)
  • January 2009 (3)
  • December 2008 (2)
  • November 2008 (2)
  • October 2008 (5)
  • September 2008 (4)
  • August 2008 (10)
  • July 2008 (6)
  • June 2008 (8)
  • May 2008 (2)
  • April 2008 (4)
  • March 2008 (2)
  • February 2008 (2)
  • January 2008 (5)
  • December 2007 (3)
  • November 2007 (2)
  • October 2007 (3)
  • September 2007 (1)
  • August 2007 (4)
  • July 2007 (5)
  • June 2007 (3)
  • May 2007 (4)
  • April 2007 (1)
  • March 2007 (6)
  • February 2007 (3)
  • January 2007 (2)
  • December 2006 (4)
  • November 2006 (3)
  • October 2006 (1)
  • August 2006 (1)
  • June 2006 (5)
  • May 2006 (5)
  • December 2005 (1)
  • November 2005 (4)
  • October 2005 (2)
  • September 2005 (1)
  • April 2005 (3)
  • December 2004 (2)
  • September 2004 (2)
  • August 2004 (3)
  • July 2004 (3)
Blogs I Read
  • Exchange

  • Raymond Chen [MSFT]

  • Larry Osterman [MSFT]

  • Peter David

  • Aaron Margosis [MSFT]

  • Jason Johnston [MSFT]

  • Matt Stehle (MSFT)

  • Patrick Creehan [MSFT]

  • Ryan Gregg [MSFT]

    Outlook PM
  • WebDav 101

    Dan Bagley (MSFT)
  • Dave Vespa [MSFT]

  • Randy Topken

    Outlook EE

August, 2008

MSDN Blogs > SGriffin's MAPI Internals > August, 2008
  • Subscribe via RSS
Sort by: Most Recent | Most Views | Most Comments
Excerpt View | Full Post View
  • SGriffin's MAPI Internals

    Creating a Recurring Task With MAPI

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 1 Comments

    [This is now documented here: http://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;
    }
    
  • SGriffin's MAPI Internals

    Pre Vista Fix MAPI Download No Longer Available

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 1 Comments

    This is a follow up to Pre Vista Fix MAPI Download Still Available. As promised, we kept the old pre-Vista fix up until August 15th, then we took it down. I didn't note this earlier because:

    1. I was at the beach and beach trumps blog.
    2. There was an error in taking down the pre-Vista fix and I was waiting to get it corrected before posting.

    The error was that in addition to taking down the pre-Vista fix, we also broke the link between the original MAPI download article and the actual download. I just got word and confirmed this has been fixed. So here's what we have (current as of August 28th...):

  • The Vista fix: Has just ExchangeMapiCdo.exe, version 8039.0. This is the Vista fix which uses the stub library.
  • The Vista fix (other page): Has the same ExchangeMapiCdo.exe as the other page, and the scary note that the page will be taken down on August 15th.
  • We kept both pages active to not break any links. Both pages link to the same file. If you're want a "permanent" link, use the first one, the one without the scary note.

    BTW - for those interested, build 8039 is a set of timezone/dst fixes for CDO.dll on top of the previous 8022 build. I don't think anything in MAPI itself changed.

  • SGriffin's MAPI Internals

    Creating a Recurring Appointment With MAPI

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 6 Comments

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

    This is the first 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.

    Creating a Recurring Appointment

    MAPI can be used to create appointments items

    To create an appointment item

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

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

    The AddAppointment method is listed below. Note that the first parameter passed to the AddAppointment 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 AddAppointment 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 calendar item.

    AddAppointment 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 appointment items occupy multiple property sets, care must be taken when building parameters to pass to GetIDsFromNames.

    AddAppointment uses helper functions to build a structure for various appointment related properties. BuildTimeZoneStruct and BuildTimeZoneDefinition are used to build the time zone related properties discussed in [MS-OXOCAL].pdf, sections 2.2.1.39 through 2.2.1.43. BuildGlobalObjectID is used to build a structure for the PidLidGlobalObjectID and PidLidCleanGlobalObjectId properties discussed in [MS-OXOCAL].pdf, sections 2.2.1.27 and 2.2.1.28. The PidLidAppointmentRecur property is built using BuildWeeklyAppointmentRecurrencePattern. The structure it builds is documented in [MS-OXOCAL].pdf, section 2.2.1.44, PidLidAppointmentRecur. Note that while a large variety of appointment recurrence patterns are possible, BuildWeeklyAppointmentRecurrencePattern only builds a weekly appointment 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 appointment recurrence pattern creation function would need to accept these sorts of variables as parameters.

    Code Snippet

    HRESULT AddAppointment(LPMAPIFOLDER lpFolder,
    					   SYSTEMTIME* lpstStartDateLocal,
    					   SYSTEMTIME* lpstEndDateLocal,
    					   SYSTEMTIME* lpstStartFirstUST,
    					   SYSTEMTIME* lpstEndFirstUST,
    					   SYSTEMTIME* lpszClipStartUST,
    					   SYSTEMTIME* lpstClipEndUST,
    					   SYSTEMTIME* lpstFirstDOW,
    					   DWORD dwStartOffsetLocal,
    					   DWORD dwEndOffsetLocal,
    					   DWORD dwPeriod,
    					   DWORD dwOccurrenceCount,
    					   DWORD dwPatternTypeSpecific,
    					   ULONG ulDuration,
    					   LPWSTR szSubject,
    					   LPWSTR szLocation,
    					   LPWSTR szPattern)
    {
    	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[ulAppointmentProps];
    		LPMAPINAMEID rgpnmid[ulAppointmentProps];
    		LPSPropTagArray lpNamedPropTags = NULL;
    
    		ULONG i = 0;
    		for (i = 0 ; i < ulAppointmentProps ; i++)
    		{
    			if (i < ulFirstMeetingProp)
    				rgnmid[i].lpguid = (LPGUID)&PSETID_Appointment;
    			elseif (i < ulFirstCommonProp)
    				rgnmid[i].lpguid = (LPGUID)&PSETID_Meeting;
    			else
    				rgnmid[i].lpguid = (LPGUID)&PSETID_Common;
    			rgnmid[i].ulKind = MNID_ID;
    			rgnmid[i].Kind.lID = aulAppointmentProps[i];
    			rgpnmid[i] = &rgnmid[i];
    		}
    
    		hRes = lpFolder->GetIDsFromNames(
    			ulAppointmentProps, 
    			(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
    			// AddAppointments in Appointments.cpp
    
    			spvProps[p_PR_SUBJECT_W].ulPropTag          = PR_SUBJECT_W;
    			spvProps[p_PR_START_DATE].ulPropTag         = PR_START_DATE;
    			spvProps[p_PR_END_DATE].ulPropTag           = PR_END_DATE;
    			spvProps[p_PR_MESSAGE_CLASS_W].ulPropTag    = PR_MESSAGE_CLASS_W;
    			spvProps[p_PR_ICON_INDEX].ulPropTag         = PR_ICON_INDEX;
    			spvProps[p_PR_CONVERSATION_INDEX].ulPropTag = PR_CONVERSATION_INDEX;
    			spvProps[p_PR_MESSAGE_FLAGS].ulPropTag      = PR_MESSAGE_FLAGS;
    
    			spvProps[p_PidLidAppointmentSequence].Value.l = 0;
    			spvProps[p_PidLidBusyStatus].Value.l = olBusy;
    			spvProps[p_PidLidLocation].Value.lpszW = szLocation;
    			SystemTimeToFileTime(lpstStartFirstUST,&spvProps[p_PidLidAppointmentStartWhole].Value.ft);
    			SystemTimeToFileTime(lpstEndFirstUST,&spvProps[p_PidLidAppointmentEndWhole].Value.ft);
    			spvProps[p_PidLidAppointmentDuration].Value.l = ulDuration;
    			spvProps[p_PidLidAppointmentColor].Value.l = 0; // No color
    			spvProps[p_PidLidResponseStatus].Value.l = respNone;
    			spvProps[p_PidLidRecurring].Value.b = true;
    			SystemTimeToFileTime(lpszClipStartUST,&spvProps[p_PidLidClipStart].Value.ft);
    			SystemTimeToFileTime(lpstClipEndUST,&spvProps[p_PidLidClipEnd].Value.ft);
    
    			SYSTEMTIME stStandard = {0};
    			stStandard.wMonth = 0xB;
    			stStandard.wDay = 0x1;
    			stStandard.wHour = 0x2;
    			SYSTEMTIME stDaylight = {0};
    			stDaylight.wMonth = 0x3;
    			stDaylight.wDay = 0x2;
    			stDaylight.wHour = 0x2;
    			hRes = BuildTimeZoneStruct(
    				300,
    				0,
    				(DWORD)-60,
    				&stStandard,
    				&stDaylight,
    				&spvProps[p_PidLidTimeZoneStruct].Value.bin.cb,
    				&spvProps[p_PidLidTimeZoneStruct].Value.bin.lpb);
    			spvProps[p_PidLidTimeZoneDescription].Value.lpszW = L"(GMT-05:00) Eastern Time (US & Canada)";
    			SYSTEMTIME stRule1Standard = {0};
    			stRule1Standard.wMonth = 0xA;
    			stRule1Standard.wDay = 0x5;
    			stRule1Standard.wHour = 0x2;
    			SYSTEMTIME stRule1Daylight = {0};
    			stRule1Daylight.wMonth = 0x4;
    			stRule1Daylight.wDay = 0x1;
    			stRule1Daylight.wHour = 0x2;
    			if (SUCCEEDED(hRes)) hRes = BuildTimeZoneDefinition(
    				L"Eastern Standard Time",
    				0, // TZRule Flags
    				2006, // wYear
    				300, // lbias
    				0, // lStandardBias,
    				(DWORD)-60, // lDaylightBias,
    				&stRule1Standard, // stStandardDate
    				&stRule1Daylight, // stDaylightDate
    				TZRULE_FLAG_EFFECTIVE_TZREG, // TZRule Flags
    				2007, // wYear
    				300, // lbias
    				0, // lStandardBias,
    				(DWORD)-60, // lDaylightBias,
    				&stStandard, // stStandardDate
    				&stDaylight, // stDaylightDate
    				&spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.cb,
    				&spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.lpb);
    			spvProps[p_PidLidAppointmentTimeZoneDefinitionStartDisplay].Value.bin.cb  = 
    				spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.cb;
    			spvProps[p_PidLidAppointmentTimeZoneDefinitionStartDisplay].Value.bin.lpb = 
    				spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.lpb;
    			spvProps[p_PidLidAppointmentTimeZoneDefinitionEndDisplay].Value.bin.cb  = 
    				spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.cb;
    			spvProps[p_PidLidAppointmentTimeZoneDefinitionEndDisplay].Value.bin.lpb = 
    				spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.lpb;
    			if (SUCCEEDED(hRes)) hRes = BuildWeeklyAppointmentRecurrencePattern(
    				lpstStartDateLocal,
    				lpstEndDateLocal,
    				lpstFirstDOW,
    				dwStartOffsetLocal,
    				dwEndOffsetLocal,
    				dwPeriod,
    				dwOccurrenceCount,
    				dwPatternTypeSpecific,
    				&spvProps[p_PidLidAppointmentRecur].Value.bin.cb,
    				&spvProps[p_PidLidAppointmentRecur].Value.bin.lpb);
    			spvProps[p_PidLidRecurrenceType].Value.l = rectypeWeekly;
    			spvProps[p_PidLidRecurrencePattern].Value.lpszW = szPattern;
    			spvProps[p_PidLidIsRecurring].Value.b = true;
    			if (SUCCEEDED(hRes)) hRes = BuildGlobalObjectId(
    				&spvProps[p_PidLidGlobalObjectId].Value.bin.cb,
    				&spvProps[p_PidLidGlobalObjectId].Value.bin.lpb);
    			spvProps[p_PidLidCleanGlobalObjectId].Value.bin.cb  = 
    				spvProps[p_PidLidGlobalObjectId].Value.bin.cb;
    			spvProps[p_PidLidCleanGlobalObjectId].Value.bin.lpb = 
    				spvProps[p_PidLidGlobalObjectId].Value.bin.lpb;
    			SystemTimeToFileTime(lpstStartFirstUST,&spvProps[p_PidLidCommonStart].Value.ft);
    			SystemTimeToFileTime(lpstEndFirstUST,&spvProps[p_PidLidCommonEnd].Value.ft);
    			spvProps[p_PidLidSideEffects].Value.l = 
    				seOpenToDelete | seOpenToCopy | seOpenToMove | seCoerceToInbox | seOpenForCtxMenu;
    
    			spvProps[p_PR_SUBJECT_W].Value.lpszW = szSubject;
    			SystemTimeToFileTime(lpstStartFirstUST,&spvProps[p_PR_START_DATE].Value.ft);
    			SystemTimeToFileTime(lpstEndFirstUST,&spvProps[p_PR_END_DATE].Value.ft);
    			spvProps[p_PR_MESSAGE_CLASS_W].Value.lpszW = L"IPM.Appointment";
    			spvProps[p_PR_ICON_INDEX].Value.l = 0x00000401; // Recurring Appointment
    			if (SUCCEEDED(hRes)) hRes = BuildConversationIndex(
    				&spvProps[p_PR_CONVERSATION_INDEX].Value.bin.cb,
    				&spvProps[p_PR_CONVERSATION_INDEX].Value.bin.lpb);
    			spvProps[p_PR_MESSAGE_FLAGS].Value.l = MSGFLAG_READ;
    
    			if (SUCCEEDED(hRes)) hRes = lpMessage->SetProps(NUM_PROPS, spvProps, NULL);
    			if (SUCCEEDED(hRes))
    			{
    				hRes = lpMessage->SaveChanges(FORCE_SAVE);
    			}
    			if (spvProps[p_PidLidTimeZoneStruct].Value.bin.lpb)
    				delete[] spvProps[p_PidLidTimeZoneStruct].Value.bin.lpb;
    			if (spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.lpb)
    				delete[] spvProps[p_PidLidAppointmentTimeZoneDefinitionRecur].Value.bin.lpb;
    			// Do not delete p_PidLidAppointmentTimeZoneDefinitionStartDisplay,
    			// it was borrowed from p_PidLidAppointmentTimeZoneDefinitionStartDisplay
    			// Do not delete p_PidLidAppointmentTimeZoneDefinitionEndDisplay,
    			// it was borrowed from p_PidLidAppointmentTimeZoneDefinitionStartDisplay
    			if (spvProps[p_PidLidAppointmentRecur].Value.bin.lpb)
    				delete[] spvProps[p_PidLidAppointmentRecur].Value.bin.lpb;
    			if (spvProps[p_PidLidGlobalObjectId].Value.bin.lpb)
    				delete[] spvProps[p_PidLidGlobalObjectId].Value.bin.lpb;
    			// Do not delete p_PidLidCleanGlobalObjectId,
    			// it was borrowed from p_PidLidGlobalObjectId
    			if (spvProps[p_PR_CONVERSATION_INDEX].Value.bin.lpb) 
    				delete[] spvProps[p_PR_CONVERSATION_INDEX].Value.bin.lpb;
    		}
    		MAPIFreeBuffer(lpNamedPropTags);
    	}
    	if (lpMessage) lpMessage->Release();
    	return hRes;
    }
    
  • SGriffin's MAPI Internals

    Exchange System Manager for Vista

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 13 Comments

    We recently released a downloadable Exchange System Manager (ESM) for Exchange 2003 which can be installed on Vista machines. Part of what made this possible was the changes we did recently to allow Exchange's implementation of the MAPI client binaries to run on Vista.

    Some interesting tidbits from the Release Notes:

    • The downloadable ESM depends on the MAPI Download, which must be installed first.
    • Therefore - the ESM and Outlook cannot be installed on the same machine. Of course, this is old news - we've never supported the ESM and Outlook on the same machine.
    • This download is for Vista only. It does not support Windows Server 2008.

    Enjoy!

  • SGriffin's MAPI Internals

    Well Behaved Store Providers

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 1 Comments

    The first answer I give to customers asking for tips on making their store provider work with the latest versions of Outlook is "be well behaved". I've had a couple message store provider cases recently that illustrate this and give me an opportunity to expand on that statement and give some concrete guidance.

    • The case of the missing search folders

      The customer's message store wasn't working well in Outlook 2007. Outlook would recreate it's persisted search folders every time it booted. In fact, it would create them, then try to create them again and again, chewing up cycles. They had checked their code to ensure that the folders got created, and that every property Outlook set was available. Except - some properties weren't accessible through the hierarchy table.

      When Outlook loaded the hierarchy table for the message store, it looked for any folders with PR_EXTENDED_FOLDER_FLAGS stamped on them. Those that had this property, with the appropriate flags set in it, would be added to the list of known search folders. Later, when Outlook needed to access one of the search folder, it would consult the list it had built. Since this particular store didn't expose a number of properties through the hierarchy table, this list was empty. So Outlook concluded it needed to create the search folder.

      The first lesson we learn is this:

      Properties set on messages and folders should be accessible through contents tables and hierarchy tables

      This lesson was behind the PR_RECORD_KEY issues and it's been the source of countless other store providers issues I've debugged over the years. Outlook (and MFCMAPI and even Outlook Spy) expect most properties to come back the same regardless of whether they're requested through the table or through the object. The solution is not to special case properties you think Outlook might want, but instead to ensure the general case works. That's exactly what this customer did and his provider's working much better now.

    • The case of the tentatively accepted meeting

      Different customer this time. Their issue was a bit more complicated. A user would send a recurring meeting request to another user, both running their message store. The recipient would let Outlook create a tentative appointment on the calendar. If at this point, they open the series, the info bar would prompt "Please respond". If they then opened one instance of the meeting and accepted it, then went back and opened the series, the info bar now indicated "Tentatively accepted". When the same steps were performed with Exchange as the back end, both times the info bar would prompt "Please respond".

      Outlook's logic for determining which string to display hinges on two properties - dispidResponseStatus and dispidApptReplyTime. If the status is set to none or not responded, we display "Please respond". If it's set to tentative, we display "Tentatively accepted", but ONLY if the reply time is set. If the reply time isn't set, we display "Please respond" instead. When the recipient accepted a single instance of the meeting, Outlook changed the response status on the master to tentative, but didn't set a reply time. However, when Outlook then looked at the properties on the meeting, the reply time was set, so it displayed "Tentatively accepted".

      How did the reply time property get set? This leads to the second lesson:

      Message stores which manufacture Outlook's business logic properties do so at their own risk

      If Outlook didn't set a property, it's probably not a good idea to manufacture it. And if you do decide to go that route, you better be prepared to crack open the Exchange Protocol documentation and spend a good long time making sure you get everything right. In this case, the customer got out of the property manufacturing business and their meeting started working as expected.

    In both cases, as in most message store cases (see this and this), the advice boils down to "if your store acts weird, so will Outlook". Hopefully though, these lessons will help a few of you identify the source of weird behavior in your own providers.

  • SGriffin's MAPI Internals

    MAPI and IPv6

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 4 Comments

    Duncan Smith noted recently that MAPI appears to have a problem with IPv6. He contacted me to see if I knew anything about the issue. I didn't, but one of the Exchange PMs spotted the connection between Duncan's issue and the recently reported DSProxy issue over on the Ehlo blog. It turns out that issue affects regular MAPI connections - not just RPC/HTTP.

    The gist is - right now, DSProxy doesn't listen on IPv6. It only listens on IPv4. So if you're running a MAPI client on an IPv6 enabled box (such as, say, installing the MAPI download on an IPv6 enabled Exchange 2007 server), then profile creation is going to fail when the client tries to ask the server for a referral to a GC.

    As the Ehlo article notes, a fix is in the works. Until then, you can try one of the following:

    • Turn off IPv6 on your server - Ehlo gives instructions for that - this has been confirmed to work .
    • When creating a MAPI profile, instead of using the Exchange server name, use the name of a GC or DC. We've not had a chance to test this yet - we'd love to hear if someone tried it and it worked.
  • SGriffin's MAPI Internals

    Rebase That: Update to the Calendar Rebasing Library

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 0 Comments

    We just released an update to our calendar rebasing library, Tzmovelib.dll. This update includes a refresh to the documentation. One thing that didn't make it into this refresh was an updated sample and documentation for a new exported function, primarily because I didn't have it ready in time. So I'm documenting the new function here, and it will make it into the MSDN the next time we refresh the docs.

    We've added a new export to tzmovelib.dll in this refresh. It's name is HrCreateApptRebaserEx. It functions exactly the same as HrCreateApptRebaser unless a new flag, REBASE_FLAG_PHYSICAL_MOVE is passed. In that case, we use the new pTZFrom parameter to effect a rebase from an old time zone to a new time zone. Additionally, we renamed the pftInstallDateUTC parameter to ptfRebaseExpireUTC, and the REBASE_FLAG_ONLY_CREATED_PRE_PATCH flag to REBASE_FLAG_ONLY_REBASE_BEFORE_EXPIRE to reflect that this parameter and flag apply to physical moves as well. The behavior of this parameter and flag did not change.

    Resources

      Name Location
    DLL: tzmovelib.dll http://www.microsoft.com/downloads/details.aspx?FamilyID=ee0af8fd-bbb7-44de-be4d-f33cb1b59563&DisplayLang=en
    Header: tzmovelib.h http://stephengriffin.members.winisp.net/TzMoveLib/tzmovelib.h
    Sample: TzTest http://stephengriffin.members.winisp.net/TzMoveLib/tztest.zip

    Documentation

    HrCreateApptRebaserEx

    The HrCreateApptRebaserEx function initializes a IOlkApptRebaser object for use in rebasing appointments.

    Quick Info

    Header file: tzmovelib.h
    Implemented by: tzmovelib.dll
    Called by: MAPI client applications
    Pointer type: LPHRCREATEAPPTREBASEREX
    DLL entry point: HrCreateApptRebaserEx@48

    Syntax

    HRESULT HrCreateApptRebaserEx( 
        ULONG ulFlags,
        IMAPISession *pSession,
        IMsgStore *pCalendarMsgStore,
        IMAPIFolder *pCalendarFolder,
        LPCWSTR pwszUpdatePrefix,
        const FILETIME *pftRebaseExpireUTC,
        LONG lExpansionDepth,
        const TZDEFINITION *pTZFrom,
        const TZDEFINITION *pTZTo,
        const TZDEFINITION *pTZMissing,
        MAPIERROR **ppError,
        IOlkApptRebaser **ppApptRebase);

    Parameters
    ulFlags [in] Bitmask of flags used to control how rebasing is performed. The following flags can be set:

    REBASE_FLAG_UPDATE_ORGANIZED_MEETINGS
    Items in which the user is the meeting organizer will be rebased. Note that by default this will cause meeting updates to be sent out to all attendees of any meeting being rebased. Combine this flag with either REBASE_FLAG_FORCE_NO_EX_UPDATES or REBASE_FLAG_FORCE_NO_UPDATES to change the behavior of how meeting updates are handled.
    REBASE_FLAG_UPDATE_UNMARKED
    Update items that aren't marked with a time zone. If this flag is specified, the pTZMissing value passed to HrCreateApptRebaser will be used as created-in time zone for all items that do not have time zone data.
    REBASE_FLAG_UPDATE_ONLYRECURRING
    Update only recurring items.
    REBASE_FLAG_NO_UI
    Do not show any UI (prevents the display of any logon dialog boxes when opening the message store).
    REBASE_FLAG_UPDATE_MINIMIZEAPPTS
    Do not rebase items that will only have changes which occur in the past.
    REBASE_FLAG_FORCE_REBASE
    Do not check the organizer for rebasing decisions (enables items in which the user is the attendee to be rebased).
    REBASE_FLAG_FORCE_NO_EX_UPDATES
    Only send updates if the user is the organizer and recipient is non-EX.
    REBASE_FLAG_FORCE_NO_UPDATES
    Never send updates.
    REBASE_FLAG_ONLY_REBASE_BEFORE_EXPIRE
    Only rebase single instance items created before a specified date, which is either a patch install date or a physical move date.
    REBASE_FLAG_REPORTING_MODE
    Do not actually rebase, just report items that would be rebased.
    REBASE_FLAG_SEND_RESOURCE_UPDATES
    Send meeting updates to resources (attendees explicitly specified on the "Resource" line of a meeting).
    REBASE_FLAG_PHYSICAL_MOVE
    Don't skip rebasing of unaffected future recurring appointments for physical move. Only valid for HrCreateApptRebaseEx. If this flag is set, pTZFrom must be a valid time zone definition representing the "from" time zone and pTZTo then represents the "to" time zone. If this new flag is not set, then pTZFrom must be NULL, and HrCreateApptRebaseEx functions exactly the same as HrCreateApptRebase. REBASE_FLAG_PHYSICAL_MOVE is not valid with REBASE_FLAG_UPDATE_ONLYRECURRING. REBASE_FLAG_PHYSICAL_MOVE implies REBASE_FLAG_UPDATE_UNMARKED and REBASE_FLAG_UPDATE_MINIMIZEAPPTS.

    pSession [in] Required. Pointer to a MAPI session interface.

    pCalendarMsgStore [in] Required. Pointer to a message store containing items to be rebased.

    pCalendarFolder [in] Require. Pointer to a Calendar folder containing items to be rebased.

    pwszUpdatePrefix [in] Optional. Pointer to a string containing the prefix to be prepended on meeting requests. May be NULL.

    pftRebaseExpireUTC [in] Optional. Only rebase items before this date. Only used if the REBASE_FLAG_ONLY_REBASE_BEFORE_EXPIRE flag is set.

    lExpansionDepth [in] Expansion depth when expanding distribution lists to exclude EX recipients. Only used if the REBASE_FLAG_FORCE_NO_EX_UPDATES flag is set.

    pTZFrom [in] Required. Pointer to a TZDEFINITION structure describing the time zone to be rebased from.

    pTZTo [in] Required. Pointer to a TZDEFINITION structure describing the time zone to be rebased to.

    pTZMissing [in] Required. Pointer to a TZDEFINITION structure describing the time zone to be assumed if time zone information is not stamped on an item. Must not be NULL, but only used if the REBASE_FLAG_UPDATE_UNMARKED flag is set.

    ppError [out] Optional. Pointer to a pointer to a MAPIERROR structure containing version, component, and context information for the error. Can be NULL if no extended error information is desired. Free with MAPIFreeBuffer.

    ppApptRebase [out] Pointer to a pointer to the returned IOlkApptRebaser interface.

    Remarks

    Obtain this function from tzmovelib.dll by calling GetProcAddress with the function name HrCreateApptRebaserEx@48. When REBASE_FLAG_PHYSICAL_MOVE and pTZFrom are not passed, this function is identical in behavior to HrCreateApptRebaser.

    Not all of the flags are valid in combination with each other. View the "Glossary of command-line options for the Outlook Time Zone Data Update tool" section of http://support.microsoft.com/kb/931667 for more information on the various options.

  • SGriffin's MAPI Internals

    August 2008 Release of MFCMAPI

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 2 Comments

    The August 2008 Release (build 6.0.0.1008) is live: http://www.codeplex.com/MFCMAPI

    I'm continuing to dig through the protocol docs (now on version 1.01). Last release was about getting the plumbing in place for Smart Views. This release is all about parsing structures, most especially, parsing for the recurrence blob on Calendar and Task items. Here's the change list - see the Issue Tracker on Codeplex for more details, or look at the code:

    • Smart View: Added the following structures: Appointment Recurrence Pattern, Recurrence Pattern, Report Tag, Conversation Index, Task Assigners, Global Object Id, One Off Entry Id. Properties which use these structures will automatically parse. For any other properties, such as to view PR_ENTRYID as a One Off Entry ID, use the "Parse Property As Structure..." option.
    • Export: Export to XML was working, but produced invalid XML that PowerShell couldn't parse. It should be better now.
    • Properties: Property names have been updated to match the 1.01 version of the protocol docs.

    Enjoy.

  • SGriffin's MAPI Internals

    Understanding How You Use This Blog

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 0 Comments

    Ed has asked if a few of us could help out with his survey by posting a link to it here. I'm happy to oblige:

    Greetings Blog Readers,

    My name is Ed Jolly, and I am a director in the Commercial Technical Support (CTS) organization at Microsoft. I am here to request a few minutes of your time.

    We would like to learn more about blog readership through a brief survey. This is an opportunity for us to better understand what is valuable to you and what you would like to see in the future.

    Below is a link that will take you to another website to complete the survey. Based on what we learn, we may request more feedback in future surveys like this.  When you open the survey, you will see a list of blogs that CTS engineers contribute to across many different products. We have not posted a listing of these blogs in the past, and I hope it helps you find other blogs that are helpful to you.

    The blog survey is completely anonymous.

    · Location: http://www.tsisurveys.com/mssurveys/blog/index.asp

    · Availability:  Until August 22. You may receive a request to complete this survey through multiple RSS feeds. You need only to complete it one time.

    · Length: The survey can be a maximum of 11 questions.

    · Time: Less than 5 minutes (but providing more information in the open text fields may take a minute or two extra, improving our ability to understand your needs in these blogs).

    Thank you in advance for your time, participation and assistance.

    Ed Jolly (edjolly@microsoft.com)

  • SGriffin's MAPI Internals

    Forcing Plain Text With MAPI

    Posted over 4 years ago
    by Stephen Griffin - MSFT
    • 7 Comments

    We had a customer recently who was sending mails with Outlook's MAPI. They wanted to know how to force the message to be plain text, like it is in Outlook when you select Send Plain Text only in the properties for the recipient:

    PlainText

    They were using CreateOneOff to create their recipient, and they were passing MAPI_SEND_NO_RICH_INFO. Yet, if the message they were sending contained an attachment, such as a Word document, the content type of the attachment on the received mail was always application/ms-tnef instead of application/msword. In other words, they were still getting a winmail.dat attachment. If they addressed a mail in Outlook with the Send Plain Text only option, sending the same attachment, it came across without the TNEF.

    One thing to note right off the bat is that sending rich text (RTF) data and sending TNEF (winmail.dat) are actually two different concepts. RTF data describes the formatting of the text of the message, much in the same way HTML can also describe the formatting of text. TNEF is a mechanism for encapsulating MAPI properties that Exchange and Outlook do not convert natively into MIME format. One such MAPI property commonly encapsulated in TNEF is PR_RTF_COMPRESSED. But TNEF can encapsulate many more properties than just PR_RTF_COMPRESSED, such as properties dealing with certain attachments. So the presence of winmail.dat doesn't necessarily mean rich text is being transmitted. That's what's happening in this case.

    If you're trying to avoid TNEF completely, MAPI_SEND_NO_RICH_INFO is a step in the right direction, but doesn't actually tell the content converter not to use TNEF - it just tells it not to use RTF. To avoid TNEF altogether, we need to set some more flags. But which flags? We can look at what CreateOneOff generates to find out. Fortunately, we don't need to reverse engineer the structure - it was recently documented in section 2.2.5.1 of the following Exchange Protocol Documentation: [MS-OXCDATA].pdf

    Now - the documentation's in "bytes on the wire - what's a constant?" format, but with a little elbow grease, we can produce the following structure:

    { 
     DWORD dwFlags;
     BYTE ProviderUID[16]; 
     DWORD dwBitmask;
    } ONEOFFEIDHEADER, FAR *LPONEOFFEIDHEADER;
    

    which we can map our entry ID to so we can check which flags are set. The following flags are valid (determined by matching the docs up to MAPI's headers):

    #define MAPI_SEND_NO_RICH_INFO          ((ULONG) 0x00010000)
    #define ENCODING_PREFERENCE             ((ULONG) 0x00020000)
    #define ENCODING_MIME                   ((ULONG) 0x00040000)
    #define BODY_ENCODING_HTML              ((ULONG) 0x00080000)
    #define BODY_ENCODING_TEXT_AND_HTML     ((ULONG) 0x00100000)
    #define MAC_ATTACH_ENCODING_UUENCODE    ((ULONG) 0x00200000)
    #define MAC_ATTACH_ENCODING_APPLESINGLE ((ULONG) 0x00400000)
    #define MAC_ATTACH_ENCODING_APPLEDOUBLE ((ULONG) 0x00600000)
    #define OOP_DONT_LOOKUP                 ((ULONG) 0x10000000)

    With this, we can see that setting the Plain Text Only option in Outlook translates to setting the ENCODING_PREFERENCE and ENCODING_MIME flags. Unfortunately, the CreateOneOff function doesn't let you pass those flags. If you try, you'll get MAPI_E_UNKNOWN_FLAGS. We won't let that stop us though - we can use our structure to set the flags as well as read them!

    Here's how we can put this all together and get a one-off entry ID which will send without TNEF:

    { 
        DWORD dwFlags; 
        BYTE ProviderUID[16]; 
        DWORD dwBitmask; 
    } ONEOFFEIDHEADER, FAR *LPONEOFFEIDHEADER; 
    
    hr = lpAddrBook->CreateOneOff( 
        lpszName, 
        lpszAdrType, 
        lpszAddress, 
        MAPI_SEND_NO_RICH_INFO, 
        &cbEntryID, 
        &lpEntryID); 
    if (HR_SUCCEEDED(hr)) 
    { 
        LPONEOFFEIDHEADER lp1Off = (LPONEOFFEIDHEADER) (ENTRYID*) lpEntryID; 
        if ((lp1Off->dwBitmask & MAPI_SEND_NO_RICH_INFO)) 
        { 
            lp1Off->dwBitmask |= (ENCODING_PREFERENCE | ENCODING_MIME); 
        } 
    }

    [Update: 8/4/2008 5:45 - rewrote the RTF/TNEF paragraph to make it clearer]
    [Update: 8/4/2008 9:36 - realized I hadn't proofread the code - fixed it]

  • Page 1 of 1 (10 items)
    • © 2012 Microsoft Corporation.
    • Terms of Use
    • Trademarks
    • Privacy Statement
    • Report Abuse
    • 5.6.402.223