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: | Minor | Last Reviewed: | 8/29/2005 |
---|
Keywords: | kbhowto kbMsg KB266351 |
---|
|