Outlook Add-In : Get Contact from Mail - outlook

I am doing a bit add-in development for outlook 2010 and I'm trying to get the ContactItems associated to an email (MailItem). I think the MailItem.Links collection should return what I want, but it's empty. Maybe I'm on the wrong path, but I'm out of ideas at the moment.
I have an Outlook.MailItem and I like to get the associated Outlook.ContactItem. When you open a mail with outlook and hover over the mail-adresses a contact-popup appears, so the link must be somewhere in the MailItem, but I don't know where.
For example, I tried using the MailItem.Links collection which says in tooltip that it represents the contacts to which the item is linked.
Explorer explorer = application.ActiveWindow() as Explorer;
MailItem mail = explorer.Selection as MailItem;
foreach (Link l in mail.Links)
{
System.Diagnostics.Debug.WriteLine("Link: " + l.Name);
}
The MailItem so far is correct, I can do whatever I want with it but
the MailItem.Links collection is empty.

You should try using the MailItem.Recipients collection which contains the addresses where the message will or has been be sent to (i.e. To, CC, BCC). You will have to check the Recipient.AddressEntry to see if the address exists in the Contacts Address Book (CAB) via GetContact, otherwise you will have to resolve it using ExchangeUser via GetExchangeUser.
The AddressEntry.AddressEntryUserType will tell you what type of recipient is included in the message - Exchange User or List, CAB, or basic SMTP address.

Related

Outlook flickering on adding too much recipients to "To" field when composing a new e-mail

I have an VSTO Outlook Add-in. In compose windows, every time a recipient is added to the "To" field, I iterate over all the recipients to search for some recipients not satisfying a particular condition. I add the ones that do not comply some rules to a List of strings. This list contains the displays names or e-mail addresses of the recipients that didn't comply the rules (I am not creating a list of Outlook COM objects).
I have noticed that the more recipients I add to the "To" field, the more slow Outlook it is by processing all them. I don't know what is Outlook internally doing for each recipient but I can see each of them are underlined maybe because they are resolved fine for Outlook, I don't know. During this process I can see that the "To" field is flickering too much.
Why is it happening? The culprit is my VSTO Add-in or the Outlook itself that it is not able to process well too much recipients and it has difficulties.
I simply do this:
List<string> recipientsNotSatisfyingCondition = new List<string>();
Outlook.Recipients recipients = this.MyMailItem?.Recipients;
try
{
for (int i = 1; i <= recipients.Count; i++)
{
Outlook.Recipient recipient = recipients[i];
if (recipient_does_not_complains_some_rules)
{
recipientsNotSatisfyingCondition.Add(recipient.Name ?? recipient.Address);
}
if (recipient != null)
{
Marshall.ReleaseComObject(recipient);
recipient = null;
}
}
}
catch (Exception ex)
{
// Log error
}
finally
{
if (recipients != null)
{
Marshall.ReleaseComObject(recipients);
recipients = null;
}
}
The code looks good. But I'd suggest calling the Recipients.ResolveAll method which attempts to resolve all the Recipient objects in the Recipients collection against the Address Book before iterating over all recipients in the collection and getting the Address property.
On the Extended MAPI level, there is no "recipient" object - recipients are stored in an IMAPITable object (IMessage::GetRecipientTable) that stores all recipients, and the whole table can be retrieved in a single call using IMAPITable::QueryRows or HrQueryAllRows. That would require Extended MAPI (C++ or Delphi), but the bigger problem is that you are working with a new message that might not be saved yet and the changes made on the OOM or Outlook UI level are not yet committed to the underlying MAPI object (IMessage). You can call MailItem.Save before retrieving IMessage object from MailItem.MAPIOBJECT and recipients using IMessage::GetRecipientTable / HrQueryAllRows, but saving the message might be undesirable.
If using Redemption (any language, I am its author) is an option, you can bypass OOM (and thus eliminate the flickering) using SafeMailItem object. It also exposes Commit method that forces Outlook to commit the data to the IMessage object without saving it in the Drafts folder and preserves the item's dirty state.
As a quick test, does the following script cause flickering? You can run it in OutlookSpy (I am also its author) - open a draft message with a large number of recipients, click Script button on the OutlookSpy ribbon, paste the script, click Run:
set sItem = CreateObject("Redemption.SafeMailItem")
sItem.Item = Application.ActiveInspector.CurrentItem
sItem.Commit 'commit changes to MAPI without saving the message
for each recip in sItem.Recipients
Debug.Print recip.Address
next

Unable to fetch received-by of a message via Microsoft Graph

Based on my previous question, I'm trying to fetch messages from a particular folder like Deleted Items.
I am following this document to achieve the above scenario: https://learn.microsoft.com/en-us/graph/api/user-list-messages
GET https://graph.microsoft.com/v1.0/me/mailFolders/deleteditems/messages
Using the above query, I'm getting all deleted messages with a lot of information(html code) that I don't want.
I want to customize the response by retrieving only particular attributes like subject, importance, sender, sentDateTime, receiver, receivedDateTime.
I tried to query something like below using $select:
GET https://graph.microsoft.com/v1.0/me/mailFolders/deleteditems/messages?$select= subject, importance, sender, sentDateTime, receiver, receivedDateTime.
But I'm getting 400:BadRequest error like below:
{
"error": {
"code": "RequestBroker--ParseUri",
"message": "Could not find a property named 'receiver' on type 'Microsoft.OutlookServices.Message'.",
"request_id": "54f9adf-7435-5r8c-a3g6-48gx6343ac",
"date": "2022-05-24T07:35:06"
}
}
How to include receiver details along with sender details???
I tried to reproduce the same in my environment and got the same error.
As I already mentioned in the comment, there is no such attribute named 'receiver'. To resolve the error, try removing that receiver in the query and check the response.
If you want to include receiver details along with sender details, you can try including toRecipients that gives info about receiver like below as an alternative:
GET https://graph.microsoft.com/v1.0/me/mailFolders/deleteditems/messages?$select=subject,importance,sender,sentDateTime,receivedDateTime,toRecipients
Response:
UPDATE:
As #Dmitry Streblechenko mentioned, this only works when you are the only receiver of those messages. If there are multiple recipients, take a while to know MAPI properties and OutlookSpy as he suggested.
Firstly, there is no receiver property. Since the message comes from a mailbox that you explicitly connect to, wouldn't receiver be the mailbox owner? Unless the message was dragged from another mailbox in Outlook.
Note that you can always request any MAPI property in your Graph query. In your particular case, you probably want PR_RECEIVED_BY_NAME / PR_RECEIVED_BY_EMAIL_ADDRESS / PidTagReceivedRepresentingSmtpAddress MAPI properties. To retrieve PidTagReceivedRepresentingSmtpAddress property, use
?$expand=singleValueExtendedProperties($filter=id eq 'String 0x5D08')
You can see available MAPI properties as well as construct a Graph query that requests them in OutlookSpy (I am its author) - click IMessage button to see all available MAPI propetrties of a selected message or click Message (Graph) | Query Parameters | Expand.

Office interop is unable to get the updated TaskItem from outlook when task is updated from Outlook Web/Office Online

I am developing an outlook add in that retrieves a TaskItem from outlook desktop and opens it. Here is my code.
private static Microsoft.Office.Interop.Outlook.TaskItem GetLatestTask(string entryId)
{
Microsoft.Office.Interop.Outlook.MAPIFolder taskFolder = Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderTasks);
List<Microsoft.Office.Interop.Outlook.TaskItem> liTask = new List<Microsoft.Office.Interop.Outlook.TaskItem>();
foreach (Microsoft.Office.Interop.Outlook.TaskItem taskItem in taskFolder.Items)
{
if (taskItem.EntryID == entryId)
return taskItem;
}
}
This works fine usually. However if i update the task from outlook web/Office online, then try to get the task using the code, the task i get is not updated and still contains the old values.
So for example i have a task with a subject named "Test" then i update this in outlook web to "Test Updated", I would still get a task with a subject named "Test".
If I check the task list in outlook desktop, I can see that the task's subject has already been updated in the Task list. However opening it would still display the old item.
Once I restart Outlook, The add in gets the updated item.
Can anyone point me in the right direction? Thank you.
First of all, iterating over all items in the folder is not really a good idea:
foreach (Microsoft.Office.Interop.Outlook.TaskItem taskItem in taskFolder.Items)
{
if (taskItem.EntryID == entryId)
return taskItem;
}
Instead, you may consider using the Find/FindNext or Restrict methods of the Items class. Read more about them in the following articles with code sample:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Also relying on the EntryID property for identifying items in Outlook is not the best way. The Entry ID changes when an item is moved into another store, for example, from your Inbox to a Microsoft Exchange Server public folder, or from one Personal Folders (.pst) file to another .pst file. Solutions should not depend on the EntryID property to be unique unless items will not be moved. Instead, you may introduce your own custom property for identifying items.
If you use cached data most probably you need to sync your data with the server side. SyncObjects is a set of SyncObject objects representing the Send/Receive groups for a user. Use the SyncObjects property to return the SyncObjects object from a NameSpace object.
Set mySyncObjects = Application.GetNameSpace("MAPI").SyncObjects
The SyncObject.Start method begins synchronizing a user's folders using the specified Send\Receive group.
Public Sub Sync()
Dim nsp As Outlook.NameSpace
Dim sycs As Outlook.SyncObjects
Dim syc As Outlook.SyncObject
Dim i As Integer
Dim strPrompt As Integer
Set nsp = Application.GetNamespace("MAPI")
Set sycs = nsp.SyncObjects
For i = 1 To sycs.Count
Set syc = sycs.Item(i)
strPrompt = MsgBox( _
"Do you wish to synchronize " & syc.Name &"?", vbYesNo)
If strPrompt = vbYes Then
syc.Start
End If
Next
End Sub
Hopefully, after that, your items will be updated with a new data.

How to get smtp address of current Outlook store

We have user with 3-4 shared email address in Outlook.
I am developing add-in where it will extract the email address of selected store and will get it's contact folder from People.
My problem is I don't know how to get email address of SelectedStore.
Here is my code.
string recipientName = SelectedStore.EmailAddress; // This is what I want to make it work
Outlook.Recipient recip = ns.CreateRecipient(recipientName);
recip.Resolve();
if (recip.Resolved)
{
Outlook.MAPIFolder folderContacts = ns.GetSharedDefaultFolder(recip, Outlook.OlDefaultFolders.olFolderContacts);
}
Any help will be appreciated.
Thank you.
For the mailbox owner, you can either try to read the MAPIFolder.Store property to get to the parent store, then read the PR_MAILBOX_OWNER_ENTRYID property (DASL name "http://schemas.microsoft.com/mapi/proptag/0x661B0102") using Store.PropertyAccessor.GetProperty. You can then use the store owner entry id to call Namespace.GetAddressEntryFromID. Once you have the AddressEntry object, you can use AddressEntry.GetExchangeUser.PrimarySmtpAddress.
Note that PR_MAILBOX_OWNER_ENTRYID property is only available in the online stores. You might want to use Redemption (I am its author) and its RDOExchangeMailboxStore.Owner.SmtpAddress property. RDOExchangeMailboxStore can be retrieved using RDOSession.GetRDOObjectfromOutlookObject(Store) or using RDOSession.GetStoreFromID.
You can also try to retrieve the store entry id and parse it - its format is documented and you can extract the EX type address of the owner. You can then construct the GAL entry id to open the AddressEntry object. From there, you can retrieve the SMTP address.
Just to let you know, I found the solution.
Outlook.MAPIFolder folderContacts = store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
should do the trick.

Access MailItem properties of Outlook msg attachment

I'm trying to use vbscript to extract mail item details from hundreds of old Exchange Journal Outlook PST files. All the items in the PST archives are made up of an "envelope" mail item with an .msg attachment that's the actual e-mail whose properties I want to capture.
I can access the attachment object, but cannot extract the MailItem properties and collections listed below, presumably because the attachment isn't opening as a Mail Item. I've tried to use the PropertyAccessor object, but I can't get any results from it.
The only workaround I've come up with is to use the SaveAsFile method to save each attachment to file, then CreateItemFromTemplate to open the saved file, which then provides full access to the Mail Item properties of the object. However, the delay caused by the disk operations is excessive. My basic "workaround" code for returning a "objMsg" MailItem object is:
Set objOL=WScript.CreateObject("Outlook.Application")
set objNS=objOL.GetNameSpace("MAPI")
Set objPST = objNS.Folders(strPSTname).Folders("Inbox").Items
Set objItem = objPST.Items(1)
objItem.Attachments(1).SaveAsFile(conTempPath)
Set objMsg = objOL.CreateItemFromTemplate(conTempPath)
Is anyone aware of a better method for accessing the Mail Item properties of a .msg attachment?
objItem.Class
objItem.ReceivedTime
objItem.SenderEmailAddress
objItem.SenderName
objItem.SentOnBehalfOfName
objItem.Subject
objItem.Attachments
objItem.Recipients
objItem.To
objItem.Cc
objItem.Bcc
This is the best you can do in the Outlook Object Model. Your options are:
Extended MAPI (usable from either C++ or Delphi) - call IAttach::OpenProperty(PR_ATTACH_DATA_OBJ, IID_IMessage, ...)
Redemption (I am its author), which is usable from any language - its RDOAttachment object exposes the EmbeddedMsg property.

Resources