How To Manually Resolve an Address Book Entry by Using MAPI (266351)



The information in this article applies to:

  • Microsoft Extended Messaging Application Programming Interface (MAPI)

This article was previously published under Q266351

SUMMARY

When you try to resolve the display name of an address book entry by calling the IAddrBook::ResolveName method without the MAPI_DIALOG flag, the call may return MAPI_E_AMBIGUOUS_RECIP if more than one address book entry contains the string that is specified in the PR_DISPLAY_NAME property. This can be a problem, especially if one entry's name is a substring or duplicate of another entry's name.

This article shows how to programmatically traverse the address books and uniquely resolve a name based on some other criteria.

MORE INFORMATION

The following function searches for an address book entry that is an exact match for the desired string property. It then builds the ADRLIST structure required for the ModifyRecipients method and adds the recipient to the message.

IMPORTANT: Display names are not necessarily unique. Therefore, it may be desirable to use something other than PR_DISPLAY_NAME as your criteria for resolving an entry.

The prototype of the function would resemble the following:
   hRes = ManualResolve(
                  lpSession,
                  lpMessage,
                  "MyName",
                  PR_DISPLAY_NAME); // PR_DISPLAY_NAME is used in this
                                    // sample, but any string property
                                    // may be used.
				
//Forward definition of helper function.
HRESULT CopySBinary(LPSBinary psbDest,
   const LPSBinary psbSrc,
   LPVOID pParent);

HRESULT ManualResolve(
   LPMAPISESSION lpMAPISession,
   LPMESSAGE lpMessage, 
   char *szName, 
   ULONG PropTagToCompare)
{
   HRESULT         hRes = S_OK;
   ULONG           ulObjType = 0;
   LPSPropTagArray pPropTag = NULL;
   
   LPMAPICONTAINER pAddrRoot = NULL;
   LPMAPITABLE     pBooks = NULL;
   ULONG           ulCount = 0;
   LPSRowSet       pABCRows = NULL;
   LPSRowSet       pRows = NULL;
   LPADRLIST       pAdrList = NULL;
   LPABCONT        pABC = NULL;
   LPADRBOOK       pAddrBook = NULL;
   LPMAPITABLE     pTable = NULL;
   
   enum {abcPR_ENTRYID,
      abcPR_DISPLAY_NAME, 
      abcNUM_COLS};
   
   static SizedSPropTagArray(abcNUM_COLS,abcCols) = {abcNUM_COLS,
      PR_ENTRYID,
      PR_DISPLAY_NAME, 
   };   
   
   enum {abPR_ENTRYID,
      abPR_DISPLAY_NAME,
      abPR_RECIPIENT_TYPE, 
      abPR_ADDRTYPE, 
      abPR_DISPLAY_TYPE,
      abPropTagToCompare, 
      abNUM_COLS};
   
   static SizedSPropTagArray(abNUM_COLS,abCols) = {abNUM_COLS,
      PR_ENTRYID,
      PR_DISPLAY_NAME,
      PR_RECIPIENT_TYPE, 
      PR_ADDRTYPE, 
      PR_DISPLAY_TYPE,
      PropTagToCompare
   };   
   
   hRes = lpMAPISession->OpenAddressBook(
      NULL,
      NULL,
      NULL,
      &pAddrBook);
   if (FAILED(hRes)) goto Cleanup;
   
   // Open root address book (container).
   hRes = pAddrBook->OpenEntry(
      0L,
      NULL,
      NULL,
      0L,
      &ulObjType,
      (LPUNKNOWN*)&pAddrRoot
      );
   if (FAILED(hRes)) goto Cleanup;
   
   // Get a table of all of the Address Books.
   hRes = pAddrRoot->GetHierarchyTable(0, &pBooks);
   if (FAILED(hRes)) goto Cleanup;
   
   // Restrict the table to the properties that we are interested in.
   hRes = pBooks->SetColumns((LPSPropTagArray)&abcCols, 0);
   if (FAILED(hRes)) goto Cleanup;
   
   // Get the total number of rows returned. Typically, this will be 1.
   hRes = pBooks->GetRowCount(0, &ulCount);
   if (FAILED(hRes)) goto Cleanup;
   
   for (;;)
   {
      hRes = pBooks->QueryRows(
         1,
         NULL,
         &pABCRows);
      if (FAILED(hRes)) goto Cleanup;
      
      if (!pABCRows->cRows)
      {
         break;
      }
      
      if (PR_ENTRYID == pABCRows->aRow->lpProps[abcPR_ENTRYID].ulPropTag)
      {
         hRes = pAddrRoot->OpenEntry(
            pABCRows->aRow->lpProps[abcPR_ENTRYID].Value.bin.cb,
            (ENTRYID*)pABCRows->aRow->
              lpProps[abcPR_ENTRYID].Value.bin.lpb,
            NULL,
            0L,
            &ulObjType,
            (LPUNKNOWN*)&pABC);
         if (FAILED(hRes)) goto Cleanup;
         
         if (ulObjType == MAPI_ABCONT)
         {
            
            hRes = pABC->GetContentsTable(0, &pTable);
            if (FAILED(hRes)) goto Cleanup;
            
            hRes = pTable->SetColumns((LPSPropTagArray)&abCols, 0);
            if (FAILED(hRes)) goto Cleanup;

            hRes = pTable->SeekRow(
               BOOKMARK_BEGINNING,
               0,
               NULL);
            if (FAILED(hRes)) goto Cleanup;
            
            //Set a restriction so that we only find close matches.
            SRestriction    sres;
            SPropValue      spvResType;
            
            spvResType.ulPropTag = PR_ANR;
            spvResType.Value.lpszA = szName;
            
            sres.rt = RES_PROPERTY;
            sres.res.resProperty.relop = RELOP_EQ;
            sres.res.resProperty.ulPropTag = PR_ANR;
            sres.res.resProperty.lpProp = &spvResType;
            
            hRes = pTable->Restrict(
               &sres,
               NULL
               );
            if (FAILED(hRes)) break;
            
            //End FindRow code.
            for (;;)
            {
               hRes = pTable->QueryRows(
                  1,
                  NULL,
                  &pRows);
               if (FAILED(hRes)) goto Cleanup;
               if (!pRows->cRows)
               {
                  break;
               }
               
               if (PropTagToCompare == pRows->aRow->
                     lpProps[abPropTagToCompare].ulPropTag)
               {
                  //TODO: Add any additional testing here.
                  if (strcmp(szName, pRows->aRow->
                        lpProps[abPropTagToCompare].Value.lpszA) == 0)
                  { 
// Allocate memory for new Address List structure.
                     hRes = MAPIAllocateBuffer(
                              CbNewADRLIST(1), 
                              (LPVOID*)&pAdrList);
                     if (FAILED(hRes)) goto Cleanup;
                     
                     ZeroMemory(pAdrList, CbNewADRLIST(1));
                     pAdrList->cEntries = 1;

// Allocate memory for SPropValue structure that indicates what
// recipient properties will be set. To resolve a name that
// already exists in the Address book, this will always be 1.
                     hRes = MAPIAllocateBuffer(
                        abNUM_COLS * sizeof(SPropValue),
                        (LPVOID*)&pAdrList->aEntries->rgPropVals);
                     if (FAILED(hRes)) goto Cleanup;
                     
//TODO: We are setting 5 properties below. 
//If this changes, modify these two lines.
                     ZeroMemory(pAdrList->aEntries->rgPropVals, 
                       5 * sizeof(SPropValue));
                     pAdrList->aEntries->cValues = 5;
                     
// Fill out addresslist with required property values.
                     LPSPropValue pProps = pAdrList->aEntries->rgPropVals;
                     LPSPropValue pProp;
                     
                     pProp = &pProps[abPR_ENTRYID];
                     pProp->ulPropTag = PR_ENTRYID;
                     CopySBinary(&pProp->Value.bin,
                        &pRows->aRow->lpProps[abPR_ENTRYID].Value.bin,
                        pAdrList);
       
                     pProp = &pProps[abPR_RECIPIENT_TYPE];
                     pProp->ulPropTag = PR_RECIPIENT_TYPE;
                     pProp->Value.l = MAPI_TO;
                     
                     pProp = &pProps[abPR_DISPLAY_NAME];
                     pProp->ulPropTag = PR_DISPLAY_NAME;
                     hRes = MAPIAllocateMore(
                        1+strlen(pRows->aRow->
                          lpProps[abPR_DISPLAY_NAME].Value.lpszA),
                        pAdrList,
                        (LPVOID*)&pProp->Value.lpszA);
                     if (FAILED(hRes)) goto Cleanup;

                     strcpy(pProp->Value.lpszA,
                       pRows->aRow->
                         lpProps[abPR_DISPLAY_NAME].Value.lpszA);
                     
                     pProp = &pProps[abPR_ADDRTYPE];
                     pProp->ulPropTag = PR_ADDRTYPE;
                     hRes = MAPIAllocateMore(
                              1+strlen(pRows->aRow->
                                lpProps[abPR_ADDRTYPE].Value.lpszA),
                              pAdrList,
                              (LPVOID*)&pProp->Value.lpszA);
                     if (FAILED(hRes)) goto Cleanup;

                     strcpy(pProp->Value.lpszA,
                       pRows->aRow->
                         lpProps[abPR_ADDRTYPE].Value.lpszA);
                     
                     pProp = &pProps[abPR_DISPLAY_TYPE];
                     pProp->ulPropTag = PR_DISPLAY_TYPE;
                     pProp->Value.l = pRows->aRow->
                       lpProps[abPR_DISPLAY_TYPE].Value.l;
                     
                     hRes = lpMessage->ModifyRecipients(
                        MODRECIP_ADD,
                        pAdrList);
                     if (FAILED(hRes)) goto Cleanup;
                     
                     if (pAdrList) FreePadrlist(pAdrList);
                     pAdrList = NULL;
                     
                     hRes = lpMessage->SaveChanges(KEEP_OPEN_READWRITE);
                     if (FAILED(hRes)) goto Cleanup;
                     
//Because we are done with our work, we will exit. We have all
//of the cleanup duplicated below so that we don't leak memory.
                     goto Cleanup;
                  }
               }
               if (pRows) FreeProws(pRows);
               pRows = NULL;
            }
            if (pTable) pTable->Release();
            pTable = NULL;
         }
      }
      if (pABCRows) FreeProws(pABCRows);
      pABCRows = NULL;
   }
   
Cleanup:
   if (pAdrList) FreePadrlist(pAdrList);
   UlRelease(pTable);
   if (pRows) FreeProws(pRows);
   if (pABCRows) FreeProws(pABCRows);
   UlRelease(pABC);
   UlRelease(pBooks);
   UlRelease(pAddrRoot);
   UlRelease(pAddrBook);
   MAPIFreeBuffer(pPropTag);
   return hRes;
}//ManualResolve.

HRESULT CopySBinary(LPSBinary psbDest,
   const LPSBinary psbSrc,
   LPVOID pParent)
{
   HRESULT     hRes = S_OK;
   psbDest -> cb = psbSrc -> cb;
   if (psbSrc -> cb)
   {
      if (pParent)
         hRes = MAPIAllocateMore(psbSrc -> cb, 
                  pParent, 
                  (LPVOID *) & psbDest->lpb);
      else
         hRes = MAPIAllocateBuffer(psbSrc -> cb, 
                  (LPVOID *) & psbDest->lpb);
      if (!FAILED(hRes))
         CopyMemory(psbDest->lpb,psbSrc->lpb,psbSrc -> cb);
   }	
   return hRes;
}
				

REFERENCES

The CopySBinary helper function is based on code from "Inside MAPI", by Irving De la Cruz and Les Thaler.

Modification Type:MinorLast Reviewed:8/29/2005
Keywords:kbhowto kbMsg KB266351