MailItem.GetConversation() on shared mailbox - outlook

I am using following code to display properties from original email in conversation.
void DemoConversation()
{
object selectedItem =
Application.ActiveExplorer().Selection[1];
// For this example, you will work only with
//MailItem. Other item types such as
//MeetingItem and PostItem can participate
//in Conversation.
if (selectedItem is Outlook.MailItem)
{
// Cast selectedItem to MailItem.
Outlook.MailItem mailItem =
selectedItem as Outlook.MailItem; ;
// Determine store of mailItem.
Outlook.Folder folder = mailItem.Parent
as Outlook.Folder;
Outlook.Store store = folder.Store;
if (store.IsConversationEnabled == true)
{
// Obtain a Conversation object.
Outlook.Conversation conv =
mailItem.GetConversation();
// Check for null Conversation.
if (conv != null)
{
// Obtain Table that contains rows
// for each item in Conversation.
Outlook.Table table = conv.GetTable();
Debug.WriteLine("Conversation Items Count: " +
table.GetRowCount().ToString());
Debug.WriteLine("Conversation Items from Table:");
while (!table.EndOfTable)
{
Outlook.Row nextRow = table.GetNextRow();
Debug.WriteLine(nextRow["Subject"]
+ " Modified: "
+ nextRow["LastModificationTime"]);
}
Debug.WriteLine("Conversation Items from Root:");
// Obtain root items and enumerate Conversation.
Outlook.SimpleItems simpleItems
= conv.GetRootItems();
foreach (object item in simpleItems)
{
// In this example, enumerate only MailItem type.
// Other types such as PostItem or MeetingItem
// can appear in Conversation.
if (item is Outlook.MailItem)
{
Outlook.MailItem mail = item
as Outlook.MailItem;
Outlook.Folder inFolder =
mail.Parent as Outlook.Folder;
string msg = mail.Subject
+ " in folder " + inFolder.Name;
Debug.WriteLine(msg);
}
// Call EnumerateConversation
// to access child nodes of root items.
EnumerateConversation(item, conv);
}
}
}
}
}
void EnumerateConversation(object item,
Outlook.Conversation conversation)
{
Outlook.SimpleItems items =
conversation.GetChildren(item);
if (items.Count > 0)
{
foreach (object myItem in items)
{
// In this example, enumerate only MailItem type.
// Other types such as PostItem or MeetingItem
// can appear in Conversation.
if (myItem is Outlook.MailItem)
{
Outlook.MailItem mailItem =
myItem as Outlook.MailItem;
Outlook.Folder inFolder =
mailItem.Parent as Outlook.Folder;
string msg = mailItem.Subject
+ " in folder " + inFolder.Name;
Debug.WriteLine(msg);
}
// Continue recursion.
EnumerateConversation(myItem, conversation);
}
}
}
It works fine on my personal inbox and shared mailbox added as additional inbox.
But all other shared mailboxes which I have full access but have auto-mapped in my Outlook client doesn't work.
Does anyone know if mailItem.GetConversation() supposed to work with shared mailboxes which are not added as additional account but are auto-mapped?
Because on these shared mailboxes, I get Conversation Items Count: 0even if there are other emails in the same conversation.
Thank you.

It works fine on my personal inbox and shared mailbox added as additional inbox.
It seems you just added a second account to the profile, not a shared mailbox.
GetConversation returns Null (Nothing in Visual Basic) if no conversation exists for the item. No conversation exists for an item if the store does not support Conversation view (for example, Outlook is running in classic online mode against a version of Microsoft Exchange earlier than Microsoft Exchange Server 2010).
Use the IsConversationEnabled property of the Store object to determine whether the store supports Conversation view and GetConversation is supposed to work.

Indeed, GetRootItems() return 0 for shared mailbox which is frustrating because Outlooks shows the mails as Conversations. What I have noticed is, that each email in Inbox of that shared mailbox has ConversationID assigned correctly, so solution is to iterated the folder and build own conversation collection based on ConversationID value. It contains not only incoming messages but also sent items, so really all elements of conversations. It works as charm at least for my purpose which was to calculate response time for that shared mailbox and average length of conversations.

Related

Outlook error in inbox folder / MAPIFolder C# (Microsoft.Office.Interop)

i always have the following errors in my inbox:
Native View To inspect the native object, enable native code debugging.
I have the following code:
public void CheckForEmails()
{
Outlook._Application _app = new Outlook.Application();
Outlook._NameSpace _ns = _app.GetNamespace("MAPI");
Outlook.MAPIFolder inbox = _ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
_ns.SendAndReceive(true);
List<Outlook.MailItem> ReceivedEmail = new List<Outlook.MailItem>();
Outlook.Items items = inbox.Items;
foreach (Outlook.MailItem mail in inbox.Items)
ReceivedEmail.Add(mail);
}
I have two mail addresses set up in Outlook.
IMAP and Exchange.
Perhaps relevant to mention.
First of all, don't use the foreach loop with COM objects. You will not be able to release COM objects in a timely manner and can reach the limit of COM references hold with the Exchange server.
The Items class from the Outlook object model is a collection of items that can represent your managed list of emails.

Office.Interop how to find email using PR_SEARCH_KEY in sent Folder

I am using Outlook Object model (Interop) for my softawre.
Before I send email, I get and keep the PR_SEARCH_KEY of the email that we created.
If I want to find the email in sent folder using PR_SEARCH_KEY, how can I do that in c# using Office.Interop (not EWS or not redemption)?
I tried to find it from SentFolder.Items.Find(filter). But it does not work as the PR_SEARCH_KEY is binary.
Thanks !
public Outlook.MailItem FindEmailFromSentFolder(string emailId)
{
try
{
if (_sentFolderItems == null)
return null;
// find the sent mail from sent folder based on PR_Serach_Key
var filter = string.Format("#SQL=\"http://schemas.microsoft.com/mapi/proptag/0x300B0102\" = '{0}'",
emailId);
var item = _sentFolderItems.Find(filter);
if (item != null && item is Outlook.MailItem)
return item as Outlook.MailItem;
}
catch (Exception ex)
{
return null;
}
return null;
}
As you already noticed, OOM won't let you search for any binary property - you will need Extended MAPI (C++ or Delphi) or Redemption (any language).
Your best bet is to set some string property on the outgoing message and then look for it in the Sent Items folder.

Uniquely identify Mailitem

I need to store a model for every used MailItem. For this I've written following Method
private readonly static Dictionary<string, PermitCustomPaneViewmodel> ViewmodelLookup = new Dictionary<string, PermitCustomPaneViewmodel>();
public static PermitCustomPaneViewmodel CreateOrGet(MailItem c)
{
if (c.EntryID == null)
c.Save();
if (!ViewmodelLookup.ContainsKey(c.EntryID))
{
var vm = new PermitCustomPaneViewmodel(c);
c.Unload += () => ViewmodelLookup.Remove(c.EntryID);
ViewmodelLookup.Add(c.EntryID, vm);
}
return ViewmodelLookup[c.EntryID];
}
When the Model already exists, I look it up and return it. If it was not created, I create it and remove the entry after the MailItem will be unloaded.
However I have observed that the MailItem object will not be vailid all the time untill unload is called. In order to reliable identify the MailItem I used the EntryID. The problem now is this only works if the Item is saved.
So currently I save the Item if no EntryID was found. But this automaticly saves the item under draft.
Is there a way to distingush MailItem's that is not saved in a way so it can be used in a Dictionary<,>.
New created items don't have the EntryID property set. Get the ID assigned by the store provider you must save it. If you need to identify a new MailItem object you may consider adding a user property to the item by using the UserProperties.Add method which reates a new user property in the UserProperties collection. For example:
Sub AddUserProperty()
Dim myItem As Outlook.ContactItem
Dim myUserProperty As Outlook.UserProperty
Set myItem = Application.CreateItem(olContactItem)
Set myUserProperty = myItem.UserProperties _
.Add("LastDateSpokenWith", olDateTime)
myItem.Display
End Sub
Be aware, 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. Basically it works fine as long as the message is staying in its parent folder or it may be changed if the Outlook item is moved to a different folder (depends on the store provider).
You may also consider using the message id from the message MIME header (PR_INTERNET_MESSAGE_ID and PR_TRANSPORT_MESSAGE_HEADERS). But they are not set on newly created items. These properties are available on the message received from an SMTP server or through the SMTP connector.

EWS - Access Shared Calendars

I would like to list the permission from every exchange shared calendar.
I have two exchange online account :
vikramsingh#gmail.com
vikramsinghmrt#outlook.com
Now, i have shared a calendar to vikramsinghmrt#outlook.com from vikramsingh#gmail.com.
Now when i fetch the calendar list using Exchange EWS API, i got all calendar (include shared calendar), But there is no way to know that which one shared calendar.
I have use below code :
PropertySet propSet = new PropertySet(BasePropertySet.FirstClassProperties, FolderSchema.Permissions);
Folder folder = Folder.Bind(service, folderid, propSet);
if (folder.Permissions.Count != 0)
{
for (int t = 0; t < folder.Permissions.Count; t++)
{
if (folder.Permissions[t].UserId.DisplayName != null || folder.Permissions[t].UserId.PrimarySmtpAddress != null)
{
}
}
}
But this return userId.DisplayName and UserId.PrimarySmtpAddress to null every time.
How could i know that which calendar is SHARED ?
You can use the properties RequiredAttendees and OptionalAttendees and check for the E-Mail address of the shared calendar. If one of the properties containts the mail address of the shared calendar, it is from the shared calendar.
If you check the Folder Associated Items collection of each folder using an Associated Traversal for Shared folders there should be an Item of Type IPM.Sharing.SyncState within that collection.
ItemView iv = new ItemView(1000);
iv.Traversal = ItemTraversal.Associated;
SearchFilter sf = new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "IPM.Sharing.SyncState");
FindItemsResults<Item> fiItems = Folder.FindItems(sf, iv);
I got the answer. We have calendar call as below. service.FindFolders(new FolderId(WellKnownFolderName.Root, "email")
In this call we pass my email id. (My credential email).
If i need to access other user calendar(Shared calendar). I need to pass other user email address here. like service.FindFolders(new FolderId(WellKnownFolderName.Root, "Other user email").
Service object is created by my credential and just pass email of other user. Now you can get all calendar.

Outlook custom plugin code not working as expected (Downlaod Email attachment)

I am tring to save attachemnt from a incoming mail in outllok. This is my code. My issue is it downloads correctly when email arrives(when my laptop is open), but this mail is scheduled everyday at 6:00 AM, I open my laptop at 9 and mail is already present and it is not downloading as expected?Do i need to do anything with code?
const string destinationDirectory = #"\\prod_data\Service Now\";
Outlook.MAPIFolder inBox = this.Application.ActiveExplorer()
.Session.GetDefaultFolder(Outlook
.OlDefaultFolders.olFolderInbox);
Outlook.Items inBoxItems = inBox.Items;
Outlook.MailItem newEmail = null;
inBoxItems = inBoxItems.Restrict("[Unread] = true");
try
{
foreach (object collectionItem in inBoxItems)
{
newEmail = collectionItem as Outlook.MailItem;
if (newEmail != null)
{
if (newEmail.Attachments.Count > 0)
{
for (int i = 1; i <= newEmail
.Attachments.Count; i++)
{
if (newEmail.Attachments[i].FileName.Contains( "LogicView Issue Report"))
{
newEmail.Attachments[i].SaveAsFile(destinationDirectory +
newEmail.Attachments[i].FileName);
}
}
}
}
}
}
I'd suggest checking out the MarkForDownload property value. It returns an OlRemoteStatus constant that determines the status of an item once it is received by a remote user. For example:
Sub DownloadItems()
Dim mpfInbox As Outlook.Folder
Dim obj As Object
Dim i As Integer
Set mpfInbox = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
'Loop all items in the Inbox folder
For i = 1 To mpfInbox.Items.Count
Set obj = mpfInbox.Items.Item(i)
'Verify if the state of the item is olHeaderOnly
If obj.DownloadState = olHeaderOnly Then
MsgBox ("This item has not been fully downloaded.")
'Mark the item to be downloaded.
obj.MarkForDownload = olMarkedForDownload
End If
Next
End Sub
You can use the Start method of the SyncObject class to begin synchronizing a user's folders using the specified Send\Receive group.
Do not use Application.NewMail event - it fires only when Outlook is running at the time the email is received by the Exchange server.
Use Items.ItemAdd event on the Inbox folder (Namespace.GetDefaultFolder(olFolderInbox)) - it will fire when the email downloaded to the cached store when Outlook synchronizes the Inbox folder (assuming you are using cached mode). If Outlook is online, no events will fire. In that case, you can use Items.Find/FindNext to process all unread messages ([Unread] = 'true').
//declare on the global/class level to make sure
//it does not get garbage collected
private Outlook.Items inBoxItems;
...
//in your addin startup code
inBoxItems = inBox.Items
inBoxItems.ItemAdd += inboxItemsItemAdd;
...
private void inboxItemsItemAdd(object item)
{
...
}

Resources