C# Outlook Plugin- Event Not Firing - c#

I am working on a C# plugin for Outlook that will act when an item is added to the Deleted Items folder. Roughly speaking, the code looks like this. I see the log vent when the method is added, but when I delete an item, the other log event doesn't get fired. What am I missing?
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
try
{
Outlook._Application olApp = new Outlook.ApplicationClass();
Outlook._NameSpace olNS = olApp.GetNamespace("MAPI");
Outlook.MAPIFolder deletedFolder = olNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);
deletedFolder.Items.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(DeletedItems_ItemAdd);
log("addDeletedItemEventHandler method added", LogLevel.Debug);
}
catch (Exception e)
{
log("Exception in addDeletedItemEventHandler: " + e.Message, LogLevel.Error);
}
}
private void DeletedItems_ItemAdd(object Item)
{
log("DeletedItems_ItemAdd - Fired", LogLevel.Debug);
}
Dmitry's solution was correct. For anyone else who's looking to solve this problem:
I defined the following at the class level:
Outlook.Items oiDeletedFolderItems;
And in the OnConnection method, I added/ changed:
oiDeletedFolderItems = deletedFolder.Items;
oiDeletedFolderItems.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(DeletedItems_ItemAdd);

The COM object that fires the events (Items) must be alive to fire events. In your case, the compiler creates a temporary variable to hold the result of the call to deletedFolder.Items; as soon as that variable is garbage collected, the events stop firing.
Store Items object in a class variable and hook events on that object.

Related

VSTO add-in to delete Outlook email after being read

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.'
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.
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.

Outlook: How to safely loop through all stores and and set ItemAdd event to all default folders for sent mail?

For the case you have more accounts/stores in your Outlook and want that ItemAdd event fires e.g. for all sent items folder.
This is what I have so far but the event is not firing for all sent items folder:
foreach (Outlook.Store store in _outlookNameSpace.Stores)
{
// _SentItems = null;
// _items = null;
try
{
_SentItems = store.GetDefaultFolder(OlDefaultFolders.olFolderSentMail);
_items = _SentItems.Items;
_items.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(items_ItemAdd); // BUGBUG: The problem is probably here, as the object needs to be alive which is firing the event?
}
catch
{
AppUtils.DoLog("Skipping this store.");
}
}
These guys are defined as global class variables:
Outlook.NameSpace _outlookNameSpace;
Outlook.MAPIFolder _SentItems;
Outlook.Items _items;
Create a wrapper class that takes Items object as a parameter in its constructor, saves it in a field, and sets up an ItemAdd event handler. You can then initialize a wrapper for each store and store then in a list to ensure the wrappers (and their Items objects) stay alive and can raise events.

Suscribing a method to "item-add" event from "Items" Collection With C#

I'm currently migrating a VSTO add in written in VB to C# for outlook.
The general idea, is to log every single email information into a database of my own.
I've searched thoroughly and it seems that NewMail / NewMailEX events(from the application object) are the best options to handle it.
However, both events won't trigger for emails received when outlook client is down.
I'm having problems while trying to process all mails that are downloaded on startup from the exchange server, so i thought that "Item-add" event from items collection, might address this issue.
I know for a fact that this can be addressed within "item-add" event because we are actually handling this issue in the VB code.
However, when a try suscribing to the "item-add" event for each Inbox folder in Outlook, nothing happens!
There is no error nor exception thrown whatsoever.
in our VB code,
we could managed to suscribe to the mentioned event with this code:
outlookNameSpace = Me.Application.GetNamespace("MAPI")
inbox = outlookNameSpace.Stores(account).GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)
Mailitem = inbox.Items
Private Sub Items_ItemAdd(ByVal item As Object) Handles Mailitem.ItemAdd
Here is my failing C# code:
//looping to fetch all my inboxes
public static void InitialOutlookConfiguration(Outlook.Application myOutlookInstance)
{
Outlook.Accounts myAccounts = myOutlookInstance.GetNamespace("MAPI").Accounts;
foreach(Outlook.Account myAccount in myAccounts)
{
Outlook.MAPIFolder inbox = myAccount.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
string storeID = myAccount.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).StoreID;
myInboxes.Add(inbox, storeID);
foreach(Outlook.MAPIFolder inbox in myInboxes.Keys)
{
Outlook.Items myInboxItems = inbox.Items;
myInboxItems.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(OnNewItem);
}
}
}
The object raising the events (myInboxItems) must be alive - otherwise it gets released by the Garbage Collector and no events are raised.
The usual pattern is to introduce your own wrapper class that takes the COM object in question (Items) as a constructor parameter, stores it in a class member and sets up an event handler. You can then then create that wrapper class for each Inbox folder and store each wrapper in a list. That list must be declared on the class level to ensure it (and its items) stay alive when InitialOutlookConfiguration() completes.
public List<Outlook.Items> myInboxMailItems = new List<Outlook.Items>();
public Items InboxMails;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
//watch.Start();
//chequear el orden en que solicitamos la ejecucion de la configuracion
//this.Application.Startup += new Outlook.ApplicationEvents_11_StartupEventHandler(OnOutlookOutlookStartup);
Outlook.Accounts myAccounts = this.Application.GetNamespace("MAPI").Accounts;
foreach (Outlook.Account myAccount in myAccounts)
{
Outlook.MAPIFolder inbox = myAccount.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
myInboxMailItems.Add(inbox.Items);
foreach (Outlook.Items i in myInboxMailItems)
{
i.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(test);
}
}
}
public void test(object i)
{
System.Windows.Forms.MessageBox.Show("Eureka!");
}
Just in case anybody sticks with the same issue, thanks Dimitry for your garbage collector insight!

Is it possible to catch "Send" event on Outlook 2010 add-in in a button's onAction function?

Because I'm in the ribbon's class, there is no pointer to the Outlook.Application object. So, I cannot use
Application.ItemSend += new ApplicationEvents_11_ItemSendEventHandler(MyItemSendEventHandler)
this event handler.
How can I reach Outlook.Application object in ribbon class or are there another way to catch send event?
public void SendEnMail(Office.IRibbonControl control) //OnAction Function
{
Outlook.Application oApp = new Outlook.Application();
Outlook._MailItem myMail = (Outlook._MailItem)oApp.CreateItem(Outlook.OlItemType.olMailItem);
myMail.Display(true);
Outlook.Application application = Globals.ThisAddIn.Application;
application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
void Application_ItemSend(object Item, ref bool Cancel)
{
string a = ((Microsoft.Office.Interop.Outlook.MailItem)Item).Body;
System.Windows.Forms.MessageBox.Show(a);
Cancel = true;
}
I cannot catch the ItemSend event like that. but if I write the eventhandler to ThisAddIn class
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
void Application_ItemSend(object Item, ref bool Cancel)
{
string a = ((Microsoft.Office.Interop.Outlook.MailItem)Item).Body;
System.Windows.Forms.MessageBox.Show(a);
Cancel = true;
}
like this, it is working.
I usually do this:
Outlook.Application application = Outlook.Application.GetActiveInstance();
But this should work the same:
Outlook.Application application = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
Or if youre making a COMAddIn you can catch the application object in the OnConnection event.
Hope it helps
Edit:
It makes sence that binding to the event is done when the addin starts. As when you bind to the event on the onAction you will (from that point on) keep catching the ItemSend for every Item the amount of times you clicked the button (as it adds a new handler). You could also try to catch the mail.SendEvent
private Outlook.MailItem myMail;
public void SendEnMail(Office.IRibbonControl control) //OnAction Function
{
myMail = (Outlook.MailItem)oApp.CreateItem(Outlook.OlItemType.olMailItem);
myMail.Send += mail_Send;
}
void mail_Send(ref bool Cancel)
{
string a = myMail.Body;
System.Windows.Forms.MessageBox.Show(a);
Cancel = true;
}
Edit2:
Excuse me as i'm using a different library.
According to MSDN it should have been the Send event instead of SendEvent event. I've changed the example accordingly.
Hope it works this time.
I think i see the issue is youre using the _MailItem, which is an interface of the MailItem interface. use the MailItem interface instead

Outlook 2010 Com addin - NewExplorer never fires

For some reason in my app my FolderSwitch works on the main Explorer that opens with the application but the NewExplorer event never fires, so obviously the FolderSwitch event won't fire on a new Explorer.
I can't work out why the event doesn't fire.
private List<_Outlook.Explorer> ListOfExplorerWindows = new List<_Outlook.Explorer> { };
private _Outlook.Application Application;
public void OnConnection(object Application, Extensibility.ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
{
this.Application = (_Outlook.Application)Application;
}
public void OnStartupComplete(ref Array custom)
{
_Outlook.Explorer Explorer = this.Application.ActiveExplorer();
Explorer.FolderSwitch += new _Outlook.ExplorerEvents_10_FolderSwitchEventHandler(Explorer_FolderSwitch);
ListOfExplorerWindows.Add(Explorer);
this.Application.Explorers.NewExplorer += new _Outlook.ExplorersEvents_NewExplorerEventHandler(Explorers_NewExplorer);
}
private void Explorers_NewExplorer(_Outlook.Explorer Explorer)
{
Explorer.FolderSwitch += new _Outlook.ExplorerEvents_10_FolderSwitchEventHandler(Explorer_FolderSwitch);
ListOfExplorerWindows.Add(Explorer);
}
For any events you want to keep around when using VSTO, you are required to keep around a class-level member (Explorer, Application, Inspector, CommandBar, etc.) to keep the GC Thread from removing them. This is a resource optimization, but can also be a painful lesson to learn.
See related MSDN Forum post regarding event lifetime or similar SO post.

Categories

Resources