How to: Programmatically Set the Resolution Order for Address Lists

How to: Programmatically Set the Resolution Order for Address Lists

This topic contains a code sample in C++ that programmatically sets the order of address lists by which recipients in e-mail messages and attendees in meeting requests are resolved.

In MAPI, each profile can support multiple address lists and each address list resides in its own container. MAPI supports the SetSearchPath method in the IAddrBook interface that allows you to set a new search path in the profile that is used for name resolution. To use the IAddrBook::SetSearchPath method, you have to define the desired resolution order in a SRowSet array that holds the containers of the relevant address books in the desired order, and then specify the array as the lpSearchPath parameter. The first property for each entry in the SRowSet array must be the PR_ENTRYID property of the corresponding address book.

The code sample sets the resolution order in the following steps:

  1. Initializes numANR to the number of containers to match, and specifies the names and resolution order of the desired address lists in an ANROrder array.
  2. Initializes MAPI by using the MAPIInitialize function.
  3. Logs on to MAPI and allows the user to choose a profile.
  4. Gets a pointer to the address book from the current session.
  5. Opens the Address Book.
  6. Opens the container for the root Address Book.
  7. Opens the hierarchy table of the root address book container.
  8. Gets the list of address book containers in the hierarchy.
  9. Looks for the entry IDs of the desired address lists by comparing the names of the desired address lists in ANROrder to the existing names in the address book hierarchy.
  10. Sets the appropriate entry IDs to the SRowSet array, pNewRows.
  11. Calls and passes pNewRows as the lpSearchPath parameter to IAddrBook::SetSearchPath to set the search path.
  12. Cleans up internal buffers and pointers.
  13. Logs off from MAPI.
  14. Uninitalizes MAPI.

This code sample uses address lists that are available in the default installation of Microsoft Office Outlook: All Contacts, All Groups, and Contacts. You must run the sample after Outlook is started and is running on an initialized profile. The sample works well with names that are in one language (for example, all names are in English). It is not designed to work in multi-lingual deployments, for example the Contacts folder localized for a user running a non-English Outlook build.

  #include "stdafx.h"
#include <mapix.h>
#include <mapiguid.h>
#include <mapiutil.h>
#include <mapidefs.h>
#include <stdio.h>
#include <conio.h>

//Number of address lists to resolve against const int numANR = 3; WCHAR *ANROrder[numANR] = {_T("All Contacts"), _T("All Groups"), _T("Contacts")}; UINT numContainersFound [numANR] = {0};

// Temporary structure to allocate buffer in MAPI. This will be used as a parent buffer to free space later. LPVOID tempLink;

// Forward declaration of helper function to copy binary data STDMETHODIMP CopySBinary( LPSBinary psbDest, const LPSBinary psbSrc, LPVOID pParent);

void main() { // MAPI address book and session variables HRESULT hRes = S_OK; // Result code returned from MAPI calls. LPMAPISESSION lpSession = NULL; // Pointer to MAPI session. LPADRBOOK lpAddrBook = NULL; // Pointer to Address Book.

// Counters
ULONG         i = 0;                  // Index counter
ULONG         j = 0;                  // Index counter

// Used for querying hierarchy
ULONG                                   ulObjType = 0L;      // Object type returned by MAPI
LPMAPICONTAINER     pIABRoot = NULL;     // Root address book container
LPMAPITABLE              pHTable = NULL;      // Holds hierarchy table
LPSRowSet        pRows = NULL;        // Pointer to row set. Stores AB Address Lists

// Used for setting search path
LPSRowSet     pNewRows = NULL;        // Pointer to new row set
SizedSRowSet  (numANR, NewRows);      // New row set
SPropValue    sProps[numANR] = {0};   // Property tag structure

// Structures contaning MAPI Column Sets required for querying tables
enum {

static SizedSPropTagArray(abNUM_COLS,abCols) = {

// Initialize MAPI
if (FAILED ( hRes = MAPIInitialize(NULL))) goto Exit;

// Log on to MAPI and allow user to choose profile

// Note: This uses the current MAPI session if there is one
if (FAILED ( hRes = MAPILogonEx(NULL, NULL, NULL, MAPI_LOGON_UI, &amp;lpSession))) goto Exit;

// Open the Address Book
if (FAILED (hRes = lpSession-&gt;OpenAddressBook(NULL, NULL, NULL, &amp;lpAddrBook))) goto Cleanup;

// Open the Address Book Root container
if (FAILED (hRes = lpAddrBook -&gt; OpenEntry (
    (LPUNKNOWN *) &amp;pIABRoot )))
goto Cleanup;

// Intentionally allocate 0 bytes. This is used for buffer management.
MAPIAllocateBuffer(0L, &amp;tempLink); 

// Make sure there is a Container object
// Query hierarchy for containers
if ( MAPI_ABCONT == ulObjType ) {
    // - Call IMAPIContainer::GetHierarchyTable to open the Hierarchy
    //   table of the root address book container
    if ( FAILED ( hRes = pIABRoot -&gt; GetHierarchyTable ( CONVENIENT_DEPTH | MAPI_UNICODE,
        &amp;pHTable ) ) )
    goto Cleanup;

    // Get the list of address book containers first
    if ( FAILED ( hRes = HrQueryAllRows ( 
        (LPSPropTagArray) &amp;abCols,
        &amp;pRows ) ) )
    goto Cleanup;

    // Initialize the structures to set the search order
    ZeroMemory(&amp;NewRows, numANR * sizeof(SRow));
    pNewRows = (LPSRowSet)&amp;NewRows;

    // Set the number of rows you are going to set
    pNewRows-&gt;cRows = numANR;

    // Set the pointers to the structures
    for (i=0; i&lt;numANR; i++)
        pNewRows-&gt;aRow[i].lpProps = &amp;sProps[i];

    // Compare the list to the ones that of interest
    for (i=0; i&lt;pRows-&gt;cRows; i++) {
        if (pRows-&gt;aRow[i].lpProps[abPR_DISPLAY_NAME_W].ulPropTag == PR_DISPLAY_NAME_W) {
            LPWSTR containerName =  pRows-&gt;aRow[i].lpProps[abPR_DISPLAY_NAME_W].Value.lpszW;
            for (j=0; j&lt;numANR; j++)
                if (!wcscmp (containerName, ANROrder[j])) {
                    // First check if a container with this name has been found already
                    if (numContainersFound[j] == 0) {
                        pNewRows-&gt;aRow[j].cValues = 1;

                        // The property being passing is PR_ENTRY_ID
                        pNewRows-&gt;aRow[j].lpProps[0].ulPropTag = PR_ENTRYID;

                        // Copy the binary data over
                        if (FAILED (hRes = CopySBinary(
                            tempLink))) { 
                                printf ("Fatal Error:Failed to copy entry IDs\n");
                                goto Cleanup;
                     // More than 1 container matched the same name
                     else { 
                         printf ("Fatal Error: Duplicate container found, container name = %s\n", containerName);
                         goto Cleanup;

    // Only set the search path if all the rows have been found
    // Check the array for any entries that were blank
    for (i=0; i&lt; numANR; i++)
        if (numContainersFound [i] == 0) {
            printf ("Fatal Error: all containers were not found\n");
            goto Cleanup;

    // Everything is safe to set the search path now
    if (FAILED (hRes = lpAddrBook-&gt;SetSearchPath(0, pNewRows))) {                   
        printf ("Fatal Error: failed to set the search path\n");
        goto Cleanup;

    UlRelease (pIABRoot);

    // Log off from MAPI
    hRes = lpSession-&gt;Logoff(NULL, NULL, 0);

    // Uninitialize MAPI


///////////////////////////////////////////////////////////// // CopySBinary() // // Parameters // // Purpose // Allocates a new SBinary and copies psbSrc into it //

STDMETHODIMP CopySBinary( LPSBinary psbDest, const LPSBinary psbSrc, LPVOID pParent) { HRESULT hRes = S_OK; psbDest->cb = psbSrc->cb;

if (psbSrc-&gt;cb) {
    if (pParent)
        hRes = MAPIAllocateMore(
             (LPVOID*) &amp;psbDest-&gt;lpb);
        hRes = MAPIAllocateBuffer(
             (LPVOID*) &amp;psbDest-&gt;lpb);

if (!FAILED(hRes))

return hRes; }

See Also

About Setting the Search Path for Address Books in Outlook