Using MAPI to Create Outlook 2007 Items
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
Summary: Learn how to use the Microsoft Exchange Server Protocols documentation to create a contact item in Microsoft Office Outlook 2007 using the Messaging Application Programming Interface (MAPI). (16 printed pages)
Randy Byrne, Senior Program Manager, Outlook Product Team, Microsoft Corporation
Stephen Griffin, Escalation Engineer, Developer Support Messaging, Microsoft Corporation
June 2008
Applies to: Microsoft Office 2007 Outlook
Contents
Overview of Outlook Extensibility
MAPI and Outlook Items
Exchange Server Protocols Documentation Used to Create an Outlook Contact
Installation Instructions
Running MFCMAPI
Mapping Property Names and Types
Using MAPI to Create a Contact
Conclusion
Additional Resources
Download MFCMAPI.
Download the CreateOutlookItemsAddin project.
Overview of Outlook Extensibility
Until the publication of the Microsoft Exchange Server Protocols, the properties required to create a Microsoft Office Outlook contact item had not been documented. This article shows you how to use the Exchange Server Protocols documentation to create a contact item using the Messaging Application Programming Interface (MAPI).
Microsoft Office Outlook 2007 provides a Component Object Model (COM) type library to enable developers to customize Outlook. The common name for this type library is the Outlook object model. Most Outlook customizations are built by using either native or managed add-ins that use the Outlook object model. An Outlook add-in must implement the IDTExtensibility2 interface. This interface provides events that enable your DLL or assembly to load in the Outlook process. After the add-in is loaded, native add-ins communicate with the Outlook object model directly through COM, while managed add-ins use COM Interop and communicate with the object model through the primary interop assembly (PIA) wrapper.
Both the Outlook object model and the add-in architecture are strategic for Outlook extensibility. However, creating an Outlook add-in is not appropriate for all scenarios. The Messaging Application Programming Interface provides an alternative extensibility model for scenarios that require store, address book, or transport providers. To implement a provider, you use MAPI and a native development tool such as Microsoft C++. Other solutions require low-level data access through MAPI to properties and structures that are not exposed in the Outlook object model. For example, the following scenarios lend themselves to using MAPI rather than an add-in that is built on the Outlook object model:
Creating a synchronization application that syncs contact items between a phone device and the default Contacts folder in multiple versions of Outlook. Using the Outlook object model and an add-in is problematic because all calls to the object model run on the main foreground thread in Outlook. For synchronization scenarios that must not block the Outlook user interface (UI), reading and writing items via MAPI is recommended. When you use MAPI instead of the Outlook object model, you should consider doing synchronization work on a background thread.
Creating a Windows service application that writes appointment data to the default Calendar folder for a specific Outlook profile. Use of the Outlook object model is not supported in a Windows service application. The object model can display UI, which is one of the reasons that use of the object model is not supported in a service application.
Creating a native component that exposes functionality that is not available in the Outlook object model. Although the Outlook object model has been improved significantly in Outlook 2007, the object model does not accommodate some advanced scenarios. Also, the use of MAPI is not supported in managed code. There are circumstances when a developer wraps MAPI code in a native component that can be remotely invoked from managed or native code. For more information about supported APIs, see the Microsoft Knowledge Base article, The support guidelines for client-side messaging development.
In most scenarios, Microsoft recommends using the Outlook object model and an add-in component for Outlook customization. The object model encapsulates the Outlook business logic and guarantees that this business logic is followed when creating or modifying items. However, there are scenarios that require the use of MAPI instead of the Outlook object model. MAPI is a low-level API used to access data in stores and address book containers. Let’s consider how you would accomplish the first scenario listed earlier, namely creating an Outlook contact using MAPI.
MAPI and Outlook Items
MAPI is essentially Outlook item agnostic. Outlook items such as e-mail messages, appointments, contacts, or task items do not represent separate types in the world of MAPI. An item is represented by the IMessage interface, which is generic to all Outlook item types. The purpose of this article is to show you how to create a first-class Outlook contact item by using MAPI instead of the Outlook object model.
When developers try to create or modify an Outlook item via MAPI, they have confronted the problem that not all Outlook item properties and data structures are documented. In the upcoming version of the Outlook 2007 MAPI Reference, both named and MAPI properties will be documented for the developer community. In the interim, this article shows you how to use the Microsoft Exchange Server Protocols Documentation to gain an understanding of the properties required to create a complete Outlook item using MAPI.
The Microsoft Exchange Server Protocols documentation provides detailed technical specifications for Microsoft proprietary protocols (including extensions to industry-standard or other published protocols) that are implemented and used in the Microsoft Exchange Server system (specifically the Exchange Server 2007 Service Pack 1 release) to interoperate or communicate with other products. There are over sixty documents in the documentation set that consist of thousands of pages of detailed technical specifications for the over-the-wire protocols that enable Exchange Server to communicate with clients such as the Microsoft Office Outlook 2007 Service Pack 1 release.
Exchange Server Protocols Documentation Used to Create an Outlook Contact
Let's focus on the specific documents that will help you accomplish the task of creating an Outlook contact item using MAPI. Because the scope of this exercise is narrow, you should familiarize yourself with the following documents.
Table 1. Exchange Server Protocols Documentation
Protocol |
Description |
---|---|
Defines the common basic structures that are used in remote operations. |
|
Specifies the properties and operations that are permissible on contacts and personal distribution lists. |
|
[MS-OXPROPS]: Office Exchange Protocols Master Property List Specification |
Contains the definition of each property that is used in the objects that are described by [MS-OXO]-prefixed documents. |
Microsoft documents the protocol requirements for a specific item type in an object protocol specification. An object protocol specification document is named [MS-OXOTTTT], where TTTT represents the type of object described in the document. Common object protocol specifications are as follows.
Table 2. Common object protocol specifications
Protocol |
Description |
---|---|
[MS-OXOCAL]: Appointment and Meeting Object Protocol Specification |
Specifies the properties and operations for appointment, meeting request, and response messages. |
Specifies the properties and operations that are permissible on contacts and personal distribution lists. |
|
Specifies the properties and operations that are permissible on e-mail messages. |
|
Specifies the properties and operations that are permissible for task, task request, and response messages. |
In each of the object protocol specification documents, consult Section 4, "Protocol Examples," for a step-by-step presentation of the properties required to create the example item in Outlook or Exchange Server. Be aware that the protocol examples assume that you are writing a protocol client to communicate with an Exchange server or a protocol server to communicate with a client such as Microsoft Office Outlook 2007. The protocol documentation does not discuss MAPI as the means to create protocol objects. In this article, you will learn how to translate between protocol documents and MAPI types and properties.
Installation Instructions
To obtain the sample code used in this article, follow these steps.
To install the sample code used in this article
Download the current version of the MFCMAPI executable to a folder on your system.
Extract all the files in Mfcmapi.exe.[version].zip to an empty folder on your hard drive.
Download the current version of the CreateOutlookItemsAddin project.
Extract all the files in CreateOutlookItemsAddin.zip to the folder where you extracted MFCMAPI in Step 2.
Copy Mfcmapi.exe from the folder used in Step 2 to the build directory for the CreateOutlookItemsAddin project (\CreateOutlookItemsAddin\Debug)
Open the CreateOutlookItemsAddin project in Visual Studio to examine the source code.
Running MFCMAPI
The following steps assume that you have downloaded the current version of MFCMAPI executable and the CreateOutlookItemsAddin project. This section will guide you to the menu item that enables you to create a contact using MFCMAPI and the CreateOutlookItemsAddin project.
Start Mfcmapi.exe in the CreateOutlookItemsAddin\Debug folder that is created when you follow the installation instructions.
Click OK to dismiss the MFCMAPI splash screen.
On the Session menu, select Logon and Display Store Table.
A profile dialog box appears. Select the correct profile, and then click OK. Otherwise MFCMAPI logs on to a MAPI session.
Double-click Mailbox - [User Name] in the store table list view.
In the folder tree view, expand the Root - Mailbox node.
In the folder tree view, expand the IPM_SUBTREE node.
In the folder tree view, double-click the Contacts folder under IPM_SUBTREE.
On the Addins menu, select Add Contact to open the Add Contact dialog box.
Click OK to create the sample contact shown in Figure 1 in your default Contacts folder.
Figure 1. Sample contact item created using MFCMAPI
Mapping Property Names and Types
The sample code that accompanies this article shows you how to create an Outlook contact item using MFCMAPI and the CreateOutlookItemsAddin project. MFCMAPI is a reference application for MAPI developers, and full source for MFCMAPI is available on CodePlex. MFCMAPI supports an add-in model so that extensions to the application can ship as a separate DLL component. In this case, the download that accompanies this article provides the CreateOutlookItemsAddin project for MFCMAPI. Discussion of the add-in architecture used by MFCMAPI is beyond the scope of this technical article.
Before diving into the sample code, it's important to understand how a MAPI developer can use the protocols documentation. MAPI properties are defined in MAPI Properties in the Outlook 2007 MAPI Reference and in the Mapitags.h file that is included in the Windows SDK. However, named properties used by Outlook are not documented in the MAPI Programmer’s Reference. A forthcoming version of the Outlook 2007 MAPI Reference will document all named properties used by Office Outlook. In the meantime, you must map canonical property names and property types defined in the protocol documentation to MAPI property sets, property identifiers, property tags, and property types. This mapping is straightforward when you understand that the protocol documentation uses its own identifiers for property name and property types.
Let's start with the types defined in the [MS-OXCDATA] document. Table 3 illustrates the mapping for commonly used property types. For a complete mapping of all property types, consult [MS-OXCDATA].
Table 3. Property type mapping to MAPI property value types
Canonical property type |
Property type value |
Property type specification |
MAPI property type |
---|---|---|---|
PtypInteger16 |
0x0002 |
2 bytes, a 16-bit integer |
PT_SHORT, PT_I2 |
PtypInteger32 |
0x0003 |
4 bytes, a 32-bit integer |
PT_LONG, PT_I4 |
PtypFloating32 |
0x0004 |
4 bytes, a 32-bit floating point number |
PT_FLOAT, PT_R4 |
PtypFloating64 |
0x0005 |
8 bytes, a 64-bit floating point number |
PT_DOUBLE, PT_R8 |
PtypCurrency |
0x0006 |
8 bytes, a 64-bit signed, scaled integer representation of a decimal currency value, with 4 places to the right of the decimal point |
PT_CURRENCY |
PtypFloatingTime |
0x0007 |
8 bytes, a 64-bit floating point number in which the whole number part represents the number of days since December 30, 1899, and the fractional part represents the fraction of a day since midnight |
PT_APPTIME |
PtypErrorCode |
0x000A |
4 bytes, a 32-bit integer encoding error information |
PT_ERROR |
PtypBoolean |
0x000B |
1 byte, restricted to 1 or 0 |
PT_BOOLEAN |
PtypInteger64 |
0x0014 |
8 bytes, a 64-bit integer |
PT_LONGLONG, PT_I8 |
PtypString |
0x001F |
Variable size, a string of Unicode characters in UTF-16LE encoding with terminating null character (2 bytes of 0) |
PT_UNICODE |
PtypString8 |
0x001E |
Variable size, a string of multi-byte characters in externally specified encoding with terminating null character (single 0 byte) |
PT_STRING8 |
PtypTime |
0x0040 |
8 bytes, a 64-bit integer representing the number of 100-nanosecond intervals since January 1, 1601 |
PT_SYSTIME |
PtypGuid |
0x0048 |
16 bytes, a GUID with Data1, Data2, and Data3 fields in little-endian format |
PT_CLSID |
PtypBinary |
0x0102 |
Variable size, a COUNT followed by that many bytes |
PT_BINARY |
PtypMultipleInteger32 |
0x1003 |
Variable size, a COUNT followed by that many PtypInteger32 values |
PT_MV_LONG, PT_MV_I4 |
PtypMultipleString |
0x101F |
Variable size, a COUNT followed by that many PtypString values |
PT_MV_UNICODE |
PtypMultipleString8 |
0x101E |
Variable size, a COUNT followed by that many PtypString8 values |
PT_MV_STRING8 |
PtypMultipleTime |
0x1040 |
Variable size, a COUNT followed by that many PtypTime values |
PT_MV_SYSTIME |
PtypMultipleGuid |
0x1048 |
Variable size, a COUNT followed by that many PtypGuid values |
PT_MV_CLSID |
PtypMultipleBinary |
0x1102 |
Variable size, a COUNT followed by that many PtypBinary values |
PT_MV_BINARY |
PtypObject or PtypEmbeddedTable |
0x000D |
The property value is a COM object |
PT_OBJECT |
Property name mapping is also clearly defined in the [MS-OXPROPS] document. The two most important canonical naming conventions in the protocols documentation are PidTag<Name> and PidLid<Name>. The value of name is usually not the same when comparing the canonical property name to its MAPI property name. For example, the canonical name for the Subject property is PidTagSubject, and the MAPI property name is PR_SUBJECT. Table 4 shows the naming conventions and how they map to MAPI property naming conventions.
Table 4. Property name mapping to MAPI names
Canonical name |
Description |
MAPI property name |
---|---|---|
PidTag<Name> |
A property defined by MAPI and specified by a property tag. All properties in MAPI have a property tag that is a 32-bit number with bits 16 through 31 defining a unique property identifier, and bits 0 through 15 defining the property type. The property identifier for properties defined by MAPI must fall in the range of 0x001 and 0x7FF. |
PR_<Name> |
PidLid<Name> |
A named property that uses a 32-bit numeric value as its name. The property is defined by this numeric name and a property set that is specified by a GUID. The property identifier for all named properties must fall in the range of 0x8000 and 0x8FF. |
A named property with ulKind = MNID_ID and Kind.lID = a valid property set GUID. |
PidName<Name> |
A named property that uses a string as its name . The property is defined by this string name and a property set GUID. |
A named property with ulKind = MNID_STRING and Kind.lID = a valid property set GUID. |
Using MAPI to Create a Contact
Given the mapping of property names and types between the Exchange Server Protocols documentation and MAPI, the next step is to determine which properties are required to create a contact item. The exact list of properties depends on the properties that you want to implement in your solution. For the example discussed in this article, the properties are defined in Contacts.cpp. Note that you must also specify a property set for the named properties associated with a specific item type. In this case, the property set is PSETID_Address, which is used for contact items.
// PSETID_Address GUID {00062004-0000-0000-c000-000000000046}
// Propset used for contact items
DEFINE_OLEGUID(PSETID_Address, MAKELONG(0x2000+(0x04),0x0006),0,0);
#define PidLidFileUnderList 0x8026
#define PidLidAutoLog 0x8025
#define PidLidAddressBookProviderEmailList 0x8028
#define PidLidAddressBookProviderArrayType 0x8029
#define PidLidFileUnder 0x8005
#define PidLidFileUnderId 0x8006
#define PidLidContactCharSet 0x8023
#define PidLidEmail1DisplayName 0x8080
#define PidLidEmail1AddressType 0x8082
#define PidLidEmail1EmailAddress 0x8083
#define PidLidEmail1OriginalDisplayName 0x8084
#define PidLidEmail1OriginalEntryID 0x8085
#define PidLidWorkAddressStreet 0x8045
#define PidLidWorkAddressCity 0x8046
#define PidLidWorkAddressState 0x8047
#define PidLidWorkAddressPostalCode 0x8048
#define PidLidWorkAddressCountry 0x8049
#define PidLidWorkAddressCountryCode 0x80DB
#define PidLidPostalAddressId 0x8022
#define PidLidAddressCountryCode 0x80DD
#define PidLidContactItemData 0x8007
#define PidLidHtml 0x802B
After you define the named properties, the next step is to construct an array that is passed to IMAPIProp::GetIDsFromNames. IMAPIProp::GetIDsFromNames performs for each named property in the array a conversion from the name (numeric name or string name) to the property tag. The aulContactProps array contains the named properties that are to be converted by the GetIDsFromNames method.
// The array is the list of named properties to be set.
ULONG aulContactProps[] = {
PidLidFileUnderList,
PidLidAutoLog,
PidLidAddressBookProviderEmailList,
PidLidAddressBookProviderArrayType,
PidLidFileUnder,
PidLidFileUnderId,
PidLidContactCharSet,
PidLidEmail1DisplayName,
PidLidEmail1AddressType,
PidLidEmail1EmailAddress,
PidLidEmail1OriginalDisplayName,
PidLidEmail1OriginalEntryID,
PidLidWorkAddressStreet,
PidLidWorkAddressCity,
PidLidWorkAddressState,
PidLidWorkAddressPostalCode,
PidLidWorkAddressCountry,
PidLidWorkAddressCountryCode,
PidLidPostalAddressId,
PidLidAddressCountryCode,
PidLidContactItemData,
PidLidHtml,
};
The GetIDsFromNames method is called in the AddContact method in Contacts.cpp. AddContact takes numerous parameters from the Add Contact dialog box that is displayed when the user selects Add Contact on the Addins menu in MFCMAPI. The DisplayAddContactDialog method in Contacts.cpp displays the dialog box and passes values from the dialog box to the AddContact method. The DisplayAddContactDialog method does not relate directly to creating a contact item using MAPI, so it is not listed here. The AddContact method is listed below. Note that the first parameter passed to the AddContact 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 AddContact 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 contact item.
HRESULT AddContact(LPMAPIFOLDER lpFolder,
LPWSTR szFullName,
LPWSTR szGivenName,
LPWSTR szMiddleName,
LPWSTR szSurName,
LPWSTR szInitials,
LPWSTR szFileUnder,
LPWSTR szEmailDisplayName,
LPWSTR szEmailAddressType,
LPWSTR szEmailAddress,
LPWSTR szEmailOriginalDisplayName,
LPWSTR szCompany,
LPWSTR szBusinessStreet,
LPWSTR szBusinessCity,
LPWSTR szBusinessState,
LPWSTR szBusinessPostalCode,
LPWSTR szBusinessCountry,
LPWSTR szBusinessCountryCode,
LPWSTR szBusinessPhone,
LPWSTR szURL,
LPWSTR szNotes
)
{
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[ulContactProps];
LPMAPINAMEID rgpnmid[ulContactProps];
LPSPropTagArray lpNamedPropTags = NULL;
ULONG i = 0;
for (i = 0 ; i < ulContactProps ; i++)
{
rgnmid[i].lpguid = (LPGUID)&PSETID_Address;
rgnmid[i].ulKind = MNID_ID;
rgnmid[i].Kind.lID = aulContactProps[i];
rgpnmid[i] = &rgnmid[i];
}
hRes = lpFolder->GetIDsFromNames(
ulContactProps,
(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
// AddContact in contacts.cpp.
spvProps[p_PR_DISPLAY_NAME_PREFIX_W].ulPropTag =
PR_DISPLAY_NAME_PREFIX_W;
spvProps[p_PR_SURNAME_W].ulPropTag =
PR_SURNAME_W;
spvProps[p_PR_MIDDLE_NAME_W].ulPropTag =
PR_MIDDLE_NAME_W;
spvProps[p_PR_GIVEN_NAME_W].ulPropTag =
PR_GIVEN_NAME_W;
spvProps[p_PR_GENERATION_W].ulPropTag =
PR_GENERATION_W;
spvProps[p_PR_INITIALS_W].ulPropTag =
PR_INITIALS_W;
spvProps[p_PR_COMPANY_NAME_W].ulPropTag =
PR_COMPANY_NAME_W;
spvProps[
p_PR_BUSINESS_HOME_PAGE_W].ulPropTag =
PR_BUSINESS_HOME_PAGE_W;
spvProps[
p_PR_BUSINESS_TELEPHONE_NUMBER_W].ulPropTag =
PR_BUSINESS_TELEPHONE_NUMBER_W;
spvProps[p_PR_POSTAL_ADDRESS_W].ulPropTag =
PR_POSTAL_ADDRESS_W;
spvProps[p_PR_STREET_ADDRESS_W].ulPropTag =
PR_STREET_ADDRESS_W;
spvProps[p_PR_LOCALITY_W].ulPropTag =
PR_LOCALITY_W;
spvProps[p_PR_STATE_OR_PROVINCE_W].ulPropTag =
PR_STATE_OR_PROVINCE_W;
spvProps[p_PR_POSTAL_CODE_W].ulPropTag =
PR_POSTAL_CODE_W;
spvProps[p_PR_COUNTRY_W].ulPropTag =
PR_COUNTRY_W;
spvProps[p_PR_DISPLAY_NAME_W].ulPropTag =
PR_DISPLAY_NAME_W;
spvProps[p_PR_MESSAGE_CLASS_W].ulPropTag =
PR_MESSAGE_CLASS_W;
spvProps[p_PR_ICON_INDEX].ulPropTag =
PR_ICON_INDEX;
spvProps[p_PR_SUBJECT_PREFIX_W].ulPropTag =
PR_SUBJECT_PREFIX_W;
spvProps[p_PR_SUBJECT_W].ulPropTag =
PR_SUBJECT_W;
spvProps[p_PR_BODY_W].ulPropTag =
PR_BODY_W;
spvProps[p_PidLidFileUnderList].
Value.MVl.cValues =
5;
LONG lFileUnder[5];
// Surname, Given Middle
lFileUnder[0] = 0x00008017;
// Given Middle Surname Generation
lFileUnder[1] = 0x00008037;
// Company Name (PR_COMPANY_NAME)
lFileUnder[2] = 0x00003a16;
// Surname, Given Middle\r\nCompany Name
lFileUnder[3] = 0x00008019;
// Company Name\r\nSurname, Given Middle
lFileUnder[4] = 0x00008018;
spvProps[p_PidLidFileUnderList].
Value.MVl.lpl =
lFileUnder;
// Do not journal.
spvProps[p_PidLidAutoLog].Value.b = false;
spvProps[p_PidLidAddressBookProviderEmailList].
Value.MVl.cValues = 1;
LONG lAddressBookProviderEmail[1];
// Email1 is defined.
lAddressBookProviderEmail[0] = 0x00000000;
spvProps[p_PidLidAddressBookProviderEmailList].
Value.MVl.lpl =
lAddressBookProviderEmail;
// Email1 is defined.
spvProps[p_PidLidAddressBookProviderArrayType].
Value.l =
0x0000001;
spvProps[p_PidLidFileUnder].Value.lpszW =
szFileUnder;
// Surname, Given Middle
spvProps[p_PidLidFileUnderId].Value.l =
0x8017;
// Generic value denoting
// "western" character set.
spvProps[p_PidLidContactCharSet].Value.l =
0x00000100;
spvProps[p_PidLidEmail1DisplayName].
Value.lpszW =
szEmailDisplayName;
spvProps[p_PidLidEmail1AddressType].
Value.lpszW =
szEmailAddressType;
spvProps[p_PidLidEmail1EmailAddress].
Value.lpszW =
szEmailAddress;
spvProps[p_PidLidEmail1OriginalDisplayName].
Value.lpszW =
szEmailOriginalDisplayName;
spvProps[p_PidLidWorkAddressStreet].
Value.lpszW =
szBusinessStreet;
spvProps[p_PidLidWorkAddressCity].
Value.lpszW =
szBusinessCity;
spvProps[p_PidLidWorkAddressState].
Value.lpszW =
szBusinessState;
spvProps[p_PidLidWorkAddressPostalCode].
Value.lpszW =
szBusinessPostalCode;
spvProps[p_PidLidWorkAddressCountry].
Value.lpszW =
szBusinessCountry;
spvProps[p_PidLidWorkAddressCountryCode].
Value.lpszW =
szBusinessCountryCode;
// Work address is mailing address.
spvProps[p_PidLidPostalAddressId].Value.l = 2;
spvProps[p_PR_STREET_ADDRESS_W].Value.lpszW =
szBusinessStreet;
spvProps[p_PR_LOCALITY_W].Value.lpszW =
szBusinessCity;
spvProps[p_PR_STATE_OR_PROVINCE_W].Value.lpszW =
szBusinessState;
spvProps[p_PR_POSTAL_CODE_W].Value.lpszW =
szBusinessPostalCode;
spvProps[p_PR_COUNTRY_W].Value.lpszW =
szBusinessCountry;
spvProps[p_PidLidAddressCountryCode].Value.lpszW =
szBusinessCountryCode;
spvProps[p_PidLidContactItemData].
Value.MVl.cValues = 6;
LONG lContactItemData[6];
// Display work address
lContactItemData[0] = 0x00000002;
// Display Email1
// (PidLidEmail1DisplayName)
lContactItemData[1] = 0x00008080;
// Display Business Telephone
// (PR_BUSINESS_TELEPHONE_NUMBER)
lContactItemData[2] = 0x3A08;
// Display Home Telephone
// (PR_HOME_TELEPHONE_NUMBER)
lContactItemData[3] = 0x3A09;
// Display Business Fax
// (PR_BUSINESS_FAX_NUMBER)
lContactItemData[4] = 0x3A24;
// Display Mobile Telephone
// (PR_MOBILE_TELEPHONE_NUMBER)
lContactItemData[5] = 0x3A1C;
spvProps[p_PidLidContactItemData].
Value.MVl.lpl =
lContactItemData;
// These two must be the same.
spvProps[p_PidLidHtml].Value.lpszW = szURL;
spvProps[p_PR_BUSINESS_HOME_PAGE_W].
Value.lpszW = szURL;
spvProps[p_PR_DISPLAY_NAME_PREFIX_W].
Value.lpszW = L"";
spvProps[p_PR_SURNAME_W].
Value.lpszW = szSurName;
spvProps[p_PR_MIDDLE_NAME_W].
Value.lpszW = szMiddleName;
spvProps[p_PR_GIVEN_NAME_W].
Value.lpszW = szGivenName;
spvProps[p_PR_GENERATION_W].
Value.lpszW = L"";
spvProps[p_PR_INITIALS_W].
Value.lpszW = szInitials;
spvProps[p_PR_COMPANY_NAME_W].
Value.lpszW = szCompany;
spvProps[p_PR_BUSINESS_TELEPHONE_NUMBER_W].
Value.lpszW =
szBusinessPhone;
spvProps[p_PR_DISPLAY_NAME_W].
Value.lpszW = szFullName;
spvProps[p_PR_MESSAGE_CLASS_W].
Value.lpszW = L"IPM.Contact";
spvProps[p_PR_ICON_INDEX].
Value.l = 512; // Contact icon
spvProps[p_PR_SUBJECT_PREFIX_W].
Value.lpszW = L"";
spvProps[p_PR_SUBJECT_W].
Value.lpszW = szFullName;
spvProps[p_PR_BODY_W].
Value.lpszW = szNotes;
// Call BuildOneOff to create
// one-off SMTP address.
hRes = BuildOneOff(
szEmailDisplayName,
szEmailAddressType,
szEmailAddress,
&spvProps[p_PidLidEmail1OriginalEntryID].
Value.bin.cb,
&spvProps[p_PidLidEmail1OriginalEntryID].
Value.bin.lpb);
if (SUCCEEDED(hRes))
{
// Call SetProps on Message.
hRes = lpMessage->
SetProps(NUM_PROPS, spvProps, NULL);
if (SUCCEEDED(hRes))
{
// If SetProps succeeds,
// call SaveChanges.
hRes = lpMessage->
SaveChanges(FORCE_SAVE);
}
}
if (spvProps[p_PidLidEmail1OriginalEntryID].
Value.bin.lpb)
delete[] spvProps[p_PidLidEmail1OriginalEntryID].
Value.bin.lpb;
}
// Release memory.
MAPIFreeBuffer(lpNamedPropTags);
}
return hRes;
}
Conclusion
Using the Exchange Server Protocols documentation and MAPI code, you can create first-class Outlook items without relying on the Outlook object model. As you can see from the previous code example, there is a great deal of property-level code that you must write to map named properties to property identifiers. Some property values must be written in a certain sequence for the item to be constructed correctly. Although certain scenarios make a compelling case for MAPI, the Outlook object model encapsulates the business logic of the Outlook items and reduces the amount of code that you have to write to do the job. To confirm this point, take a look at the following listing that uses the Outlook object and Microsoft Visual C# to create the sample contact discussed in this article.
private void AddContact()
{
try
{
Outlook.ContactItem oContact =
Application.CreateItem(
Outlook.OlItemType.olContactItem)
as Outlook.ContactItem;
oContact.FirstName = "Jacqueline";
oContact.LastName = "Haddad";
oContact.Initials = "J.H.";
oContact.CompanyName = "Microsoft";
oContact.Email1Address = "someone@example.com";
oContact.Email1AddressType = "SMTP";
oContact.Email1DisplayName =
"Jacqueline Haddad (someone@example.com)";
oContact.BusinessAddressStreet = "1 Microsoft Way";
oContact.BusinessAddressCity = "Redmond";
oContact.BusinessAddressState = "WA";
oContact.BusinessAddressPostalCode = "95802";
oContact.BusinessAddressCountry = "USA";
oContact.BusinessTelephoneNumber = "800-555-1212";
oContact.WebPage = "http://www.codeplex.com/mfcmapi";
oContact.Body = "This is a sample note.";
oContact.Save();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
The choice between using the Outlook object model or MAPI should be dictated by your specific scenario. For most cases, use of the Outlook object model is preferred. If you are writing managed code, using the object model and primary interop assemblies is mandatory. If you have a special situation in which having your code run on the Outlook UI thread would cause issues for the user, the object model's interface does not give you access to, or control of, the information you need, or you need a higher performance interface to the data and are willing to handle the added complexities of coding to the MAPI interfaces, you should consider using the Exchange Server Protocols documentation in conjunction with MAPI to address your solution requirements.
Additional Resources
For more information, see the following resources: