No PR_EMAIL_ADDRESS for recipient?

S

Slava Barouline

I noticed something strange when testing my Outlook Add-in

If I create a new email in Outlook 2003 and start to type some email
address, a blue drop-down appears and helps me to complete the address.

Sometimes it suggests only email address without display name, i.e.
(e-mail address removed) instead of say John Smith <[email protected]>

If I select the address from email, it appears that Outlook treats these
addresses differently.

If I look at recipients table using Outlook Spy, I can see recipients email
address only in PR_DISPLAY_NAME, PR_RECIPIENT_DISPLAY_NAME and strange tag
0x6001 (0x6001001E)

PR_EMAIL_ADDRESS is not found

I thought PR_EMAIL_ADDRESS is always present

Should I use PR_DISPLAY_NAME if PR_EMAIL_ADDRESS is not found?

Thanks
 
K

Ken Slovak - [MVP - Outlook]

Have you tried saving the email before looking for PR_EMAIL_ADDRESS?
 
K

Ken Slovak - [MVP - Outlook]

Well, here at least until I save the email or send it there is nothing in
the Recipients table when I open the item using the IMessage button in
OutlookSpy. Once I do that I have PR_EMAIL_ADDRESS no matter whether the
suggestion was just email address or name + email address. I don't have that
strange property you mention.

I tested this on a Exchange profile, is that similar to yours or are you
using only a PST file?

If the display name property gives you what you want then use it.
 
S

Slava Barouline

I do MailItem.Save in the code, but it dosn't seem to help.

If I press button in OutlookSpy, it works OK - I can see PR_EMAIL_ADDRESS
fine.

Maybe doing MailItem.Save is not enough?

Sometimes popup has only display name and no email address - it's worse as I
cannot use display name as email address.

I have Exchange profile
 
K

Ken Slovak - [MVP - Outlook]

Saving the item should be enough.

If you can see the property in OutlookSpy then it's there in the recipient
entry in the recipients table.

Maybe it's the code used. You haven't shown a code sample, let's see that
and see if anyone spots anything.
 
S

Slava Barouline

Just to remind the problem
If I create a new email and start typing email address in To, Outlook
suggests some emails in a popup
Most emails look like Slava Barouline < email@com > , but sme have only
address or display name
The later fill recipients table incorrectly and don't get resolved even if I
call MailItem.Save in my code
I suspect Outlook delays resolving for some reason.
If I save email in Outlook by pressing a button, emails get resolved and
look OK in recipients table

Here it is (very scary code):

//Save before processing sent email:
......

//Save before accessing PR_BODY
FMailItemToProcess.Save;
ASubject := FMailItemToProcess.Subject;
ABody := GetEmailBody;

....

//Get Recipients List
AnEmailAddresses := GetRecipientEmailAddresses;

......

//Get emails from recipients table

function TMessageProcessor.GetRecipientEmailAddresses: TStringList;
var
OneMessage: IMessage;
RecTable:IMAPITable;
begin

RecTable:=nil;
OneMessage :=IUnknown(FMailItemToProcess.MAPIOBJECT) as IMessage;
OleCheck(OneMessage.GetRecipientTable(0,RecTable));
result := GetRecipientEmailAddressesFromTable(RecTable);
RecTable := nil;
OneMessage := nil;

end;

function
TMessageProcessor.GetRecipientEmailAddressesFromTable(RecTable:IMAPITable):
TStringList;
type
LTSPropTagArray =
record
cValues : ULONG;
aulPropTag : array[0..3] of ULONG;
end;
const
FPropTagArray : LTSPropTagArray = (cValues:4;
aulPropTag:(
PR_DISPLAY_NAME,
PR_EMAIL_ADDRESS,
PR_ADDRTYPE,
PR_ENTRYID)
);
var
NRec :integer;
ListRecTable:IMAPITable;
RecCount:Ulong;
lppRows:pSRowSet;
EMailAddress, AnAddressType, ADisplayName: string;
i: Integer;
DistList: IDistList;
AddrBook: IAddrBook;
lpcbeid, ulObjectType: ULONG;
lppeid: PENTRYID;
ProcessInternalEmails, AskConfirmation: Boolean;
begin

result := TStringList.Create;
lppRows:=nil;
ProcessInternalEmails := False;
AskConfirmation := True;

OleCheck(RecTable.SetColumns(@FPropTagArray,TBL_BATCH));
OleCheck(RecTable.GetRowCount(0,RecCount));

dmMain.LogLine('Found ' + IntToStr(RecCount) + ' recipient(s)');

if RecCount>0 then
try

OleCheck(RecTable.QueryRows(RecCount,TBL_NOADVANCE,lppRows));
for Nrec:=0 to RecCount-1 do
begin

// PR_EMAIL_ADDRESS is an optional property
// therefore we must access it in a try except
try
AnAddressType :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-2].Value.lpsza;
if UpperCase(AnAddressType) = 'MAPIPDL' then
begin
dmMain.LogLine('Found private distribution list - trying to
expand');

//!!! Get DistList
AddrBook := GetAddressBook;

//The IAddrBook.OpenEntry method opens an address book entry
//and returns a pointer to an interface that can be used
//to access the entry.

lpcbeid :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-1].Value.bin.cb;
lppeid :=
LPENTRYID(lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-1].Value.bin.lpb);

DistList:=nil;
ulObjectType:=8; //MAPI_DISTLIST

OleCheck(AddrBook.OpenEntry(
lpcbeid,
lppeid,
@IID_IDistList,
MAPI_BEST_ACCESS or MAPI_DEFERRED_ERRORS,
ulObjectType,
IUnknown(DistList)));

OleCheck(DistList.GetContentsTable(0,ListRecTable));

with GetRecipientEmailAddressesFromTable(ListRecTable) do
for i:= 0 to Count - 1 do
result.AddObject(Strings, Objects);

DistList:=nil;
ListRecTable := nil;

end

//Check Exchange email address type and ProcessInternalEmails
setting
else if (UpperCase(AnAddressType) = 'EX')
and not dmMain.ProcessInternalEmails then
begin

if ProcessInternalEmails then
begin
EMailAddress :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-3].Value.lpsza;
ADisplayName :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-4].Value.lpsza;
dmMain.LogLine('Recipient #' + IntToStr(Nrec + 1) + ' - email
address: ' + EMailAddress + ' , display name: ' + ADisplayName);
result.AddObject(EMailAddress,
TStringObject.Create(ADisplayName));
end
else
begin
EMailAddress :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-3].Value.lpsza;
ADisplayName :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-4].Value.lpsza;
dmMain.LogLine('Recipient #' + IntToStr(Nrec + 1) + ' - email
address: ' + EMailAddress + ' , display name: ' + ADisplayName);
dmMain.LogLine('Email address has Exchange address type');

//Manual processing
if AskConfirmation and FIsManualProcessing then
begin
if MessageDlg(strExchangeRecipientAddressTypeConfirmation,
mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
ProcessInternalEmails := True;
result.AddObject(EMailAddress,
TStringObject.Create(ADisplayName));
end
else
begin
ProcessInternalEmails := False;
dmMain.LogLine('Email address skipped');
end;
AskConfirmation := False;
end
//Automatic processing - skip
else
dmMain.LogLine('Email address skipped');

end;

end
else
begin

//In case PR_EMAIL_ADDRESS is not found, use PR_DISPLAY_NAME
instead
ADisplayName :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-4].Value.lpsza;
try
EMailAddress :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-3].Value.lpsza;
except
dmMain.LogLine('Failed to read PR_EMAIL_ADDRESS, using
PR_DISPLAY_NAME instead');
EMailAddress := ADisplayName;
end;

dmMain.LogLine('Recipient #' + IntToStr(Nrec + 1) + ' - email
address: ' + EMailAddress + ' , display name: ' + ADisplayName);
result.AddObject(EMailAddress,
TStringObject.Create(ADisplayName));
end;

except
on e: Exception do
dmMain.LogLine('Failed to read email address from recipient table
row with error message: ' + e.Message);
end;
end;

except
on e: Exception do
dmMain.LogLine('Failed to retrieve recipients table with error
message: ' + e.Message);
end;

// Free each element in the pRows buffer
FreeProws(lppRows);

end;
 
K

Ken Slovak - [MVP - Outlook]

I don't see anything offhand but I'm no C++ programmer or even Extended MAPI
programmer.

Have you tried resolving the recipients collection (Recipients.ResolveAll)
and seeing if that helps?

One thing is if the email addresses are in the GAL and the email display
name is not unique in the GAL then resolution will fail. In that case the
alias must be fully qualified to resolve ("(e-mail address removed)") and not just
"joe".




Slava Barouline said:
Just to remind the problem
If I create a new email and start typing email address in To, Outlook
suggests some emails in a popup
Most emails look like Slava Barouline < email@com > , but sme have only
address or display name
The later fill recipients table incorrectly and don't get resolved even if
I call MailItem.Save in my code
I suspect Outlook delays resolving for some reason.
If I save email in Outlook by pressing a button, emails get resolved and
look OK in recipients table

Here it is (very scary code):

//Save before processing sent email:
.....

//Save before accessing PR_BODY
FMailItemToProcess.Save;
ASubject := FMailItemToProcess.Subject;
ABody := GetEmailBody;

...

//Get Recipients List
AnEmailAddresses := GetRecipientEmailAddresses;

.....

//Get emails from recipients table

function TMessageProcessor.GetRecipientEmailAddresses: TStringList;
var
OneMessage: IMessage;
RecTable:IMAPITable;
begin

RecTable:=nil;
OneMessage :=IUnknown(FMailItemToProcess.MAPIOBJECT) as IMessage;
OleCheck(OneMessage.GetRecipientTable(0,RecTable));
result := GetRecipientEmailAddressesFromTable(RecTable);
RecTable := nil;
OneMessage := nil;

end;

function
TMessageProcessor.GetRecipientEmailAddressesFromTable(RecTable:IMAPITable):
TStringList;
type
LTSPropTagArray =
record
cValues : ULONG;
aulPropTag : array[0..3] of ULONG;
end;
const
FPropTagArray : LTSPropTagArray = (cValues:4;
aulPropTag:(
PR_DISPLAY_NAME,
PR_EMAIL_ADDRESS,
PR_ADDRTYPE,
PR_ENTRYID)
);
var
NRec :integer;
ListRecTable:IMAPITable;
RecCount:Ulong;
lppRows:pSRowSet;
EMailAddress, AnAddressType, ADisplayName: string;
i: Integer;
DistList: IDistList;
AddrBook: IAddrBook;
lpcbeid, ulObjectType: ULONG;
lppeid: PENTRYID;
ProcessInternalEmails, AskConfirmation: Boolean;
begin

result := TStringList.Create;
lppRows:=nil;
ProcessInternalEmails := False;
AskConfirmation := True;

OleCheck(RecTable.SetColumns(@FPropTagArray,TBL_BATCH));
OleCheck(RecTable.GetRowCount(0,RecCount));

dmMain.LogLine('Found ' + IntToStr(RecCount) + ' recipient(s)');

if RecCount>0 then
try

OleCheck(RecTable.QueryRows(RecCount,TBL_NOADVANCE,lppRows));
for Nrec:=0 to RecCount-1 do
begin

// PR_EMAIL_ADDRESS is an optional property
// therefore we must access it in a try except
try
AnAddressType :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-2].Value.lpsza;
if UpperCase(AnAddressType) = 'MAPIPDL' then
begin
dmMain.LogLine('Found private distribution list - trying to
expand');

//!!! Get DistList
AddrBook := GetAddressBook;

//The IAddrBook.OpenEntry method opens an address book entry
//and returns a pointer to an interface that can be used
//to access the entry.

lpcbeid :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-1].Value.bin.cb;
lppeid :=
LPENTRYID(lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-1].Value.bin.lpb);

DistList:=nil;
ulObjectType:=8; //MAPI_DISTLIST

OleCheck(AddrBook.OpenEntry(
lpcbeid,
lppeid,
@IID_IDistList,
MAPI_BEST_ACCESS or MAPI_DEFERRED_ERRORS,
ulObjectType,
IUnknown(DistList)));

OleCheck(DistList.GetContentsTable(0,ListRecTable));

with GetRecipientEmailAddressesFromTable(ListRecTable) do
for i:= 0 to Count - 1 do
result.AddObject(Strings, Objects);

DistList:=nil;
ListRecTable := nil;

end

//Check Exchange email address type and ProcessInternalEmails
setting
else if (UpperCase(AnAddressType) = 'EX')
and not dmMain.ProcessInternalEmails then
begin

if ProcessInternalEmails then
begin
EMailAddress :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-3].Value.lpsza;
ADisplayName :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-4].Value.lpsza;
dmMain.LogLine('Recipient #' + IntToStr(Nrec + 1) + ' - email
address: ' + EMailAddress + ' , display name: ' + ADisplayName);
result.AddObject(EMailAddress,
TStringObject.Create(ADisplayName));
end
else
begin
EMailAddress :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-3].Value.lpsza;
ADisplayName :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-4].Value.lpsza;
dmMain.LogLine('Recipient #' + IntToStr(Nrec + 1) + ' - email
address: ' + EMailAddress + ' , display name: ' + ADisplayName);
dmMain.LogLine('Email address has Exchange address type');

//Manual processing
if AskConfirmation and FIsManualProcessing then
begin
if MessageDlg(strExchangeRecipientAddressTypeConfirmation,
mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
ProcessInternalEmails := True;
result.AddObject(EMailAddress,
TStringObject.Create(ADisplayName));
end
else
begin
ProcessInternalEmails := False;
dmMain.LogLine('Email address skipped');
end;
AskConfirmation := False;
end
//Automatic processing - skip
else
dmMain.LogLine('Email address skipped');

end;

end
else
begin

//In case PR_EMAIL_ADDRESS is not found, use PR_DISPLAY_NAME
instead
ADisplayName :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-4].Value.lpsza;
try
EMailAddress :=
lppRows.aRow[Nrec].lpProps[lppRows.aRow[Nrec].cValues-3].Value.lpsza;
except
dmMain.LogLine('Failed to read PR_EMAIL_ADDRESS, using
PR_DISPLAY_NAME instead');
EMailAddress := ADisplayName;
end;

dmMain.LogLine('Recipient #' + IntToStr(Nrec + 1) + ' - email
address: ' + EMailAddress + ' , display name: ' + ADisplayName);
result.AddObject(EMailAddress,
TStringObject.Create(ADisplayName));
end;

except
on e: Exception do
dmMain.LogLine('Failed to read email address from recipient table
row with error message: ' + e.Message);
end;
end;

except
on e: Exception do
dmMain.LogLine('Failed to retrieve recipients table with error
message: ' + e.Message);
end;

// Free each element in the pRows buffer
FreeProws(lppRows);

end;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top