I'm trying to write an Outlook Add-in that deletes an email after the email has been read completely. The problem is that the read-flag turns true the second we click on it and it doesn't give us much time to read the email.
I tried to delete the email after closing it:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend = new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
foreach (Outlook.MailItem mail in inbox.Items)
{
((Outlook.ItemEvents_10_Event)mail).Close = new Outlook.ItemEvents_10_CloseEventHandler(MailItem_Close);
}
}
void MailItem_Close(ref bool Cancel)
{
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Outlook.Items inboxReadItems = inbox.Items.Restrict("[Unread]=false");
foreach (Outlook.MailItem mail in inboxReadItems)
{
mail.Delete();
}
}
This was the idea i came up with, sometimes it works but most of the times it ends with an error in the mail.Delete(): The Error: System.Runtime.InteropServices.COMException: 'The item’s properties and methods cannot be used inside this event procedure.'
CodePudding user response:
This is as inefficient as it gets - you set up event handlers on (potentially) thousands of emails in the Inbox folder. Do not do that.
Track the Explorer.SelectionChange
event and set up event handlers only on those emails that are selected (and remove event handlers from the previous selections). If the item is unread, trap its read state change, and put its entry id in a list. When selection changes, delete the previously unread email(s) from that list.
CodePudding user response:
First of all, your event handler may not be fired because the source object is declared in the methods and the scope of that object is limited by the method/function. After execution of the method/function ends, the garbage collector may swipe the heap (unpredictable) with your source object, so you may never get the event fired.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend = new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
foreach (Outlook.MailItem mail in inbox.Items)
{
((Outlook.ItemEvents_10_Event)mail).Close = new Outlook.ItemEvents_10_CloseEventHandler(MailItem_Close);
}
}
Instead, you need to declare the sourceobject at the class level if you want to keep getting events fired, in your case maintain a list of objects (which is also inefficient when dealing with COM objects):
List<Outlook.MailItem> items = new List<Outlook.MailItem>();
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend = new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
Outlook.MAPIFolder inbox = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
foreach (Outlook.MailItem mail in inbox.Items)
{
((Outlook.ItemEvents_10_Event)mail).Close = new Outlook.ItemEvents_10_CloseEventHandler(MailItem_Close);
list.Add(mail);
}
}
But in that case you may reach the limit of currently held and opened objects in Outlook with Exchange. So, the better solution is to handle the SelectionChange
event of the Explorer
class and deal with the currently selected items only. Every time the selection is changed, your source objects are renowned (old are released and new ones are subscribed).
But a better yet solution is to implement an item wrapper for Outlook items, so you could subscribe to the Close
event or any other events in a convenient fashion. See Implement a wrapper for inspectors and track item-level events in each inspector for more information.