Outlook Addin shows same dialog in other opened Outlook - c#

I have a Outlook AddIn that shows a dialog every time a user sends a message.
I am opening the dialog and getting the message information on onItemSend event. This is working as expected: Once the message is sent, it opens a dialog, with some information such as recipients, subject, etc.
The problem is when the user keeps Outlook opened and send a message from another source, such as phone or tablet. After the user sends the message, the opened Outlook will display the dialog, even if it was not sent from that instance.
For instance, there was a user that has the addin installed at his home and office. He kept his home Outlook opened and after a busy day at work, sending several messages and displaying the dialogs, he checks his outlook at home. It will be several opened dialogs, regarding the messages he sent during the day.
Is there a way to restrict the dialogs? Kind of open the dialog where the user is sending the message from? This way the user will not have any dialog in any other opened outlook...
Thanks
EDIT
Just checked my code and there was a misunderstand (of my part off course). I removed some code from the methods, just to be easier to read...
There are two event handlers in the code:
Outlook.Folder sentbox;
Outlook.Items myOlItems = null;
this.itemsendhndler = newicrosoft.Office.Interop.Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
this.Application.ItemSend += itemsendhndler;
this.hndler = new Outlook.ItemsEvents_ItemAddEventHandler(Application_ItemAdd);
sentbox = this.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail) as Outlook.Folder;
myOlItems = sentbox.Items;
myOlItems.ItemAdd += this.hndler;
Then, in Application_ItemAdd there is the onItemSend method, that I thought it was the Application_ItemSend...
void Application_ItemAdd(object Item)
{
this.onItemSend(Item);
}
private void Application_ItemSend(object item, ref bool Cancel)
{
Outlook.MailItem mail = item as MailItem;
if (mail == null)
return;
//Make sure this is a E-mail message
if (string.Compare(OutlookItemHelper.GetMessageClass(mail), "IPM.Note") != 0)
return;
mail.DeleteAfterSubmit = false;
Outlook.Folder sentFolder = this.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail) as Outlook.Folder;
if (sentFolder != null)
mail.SaveSentMessageFolder = sentFolder; // override the default sent items location
Cancel = false;
}
private void onItemSend(object Item)
{
Outlook.MailItem itm = null;
itm = (Outlook.MailItem)Item;
using (ExampleDialog dlg = new ExampleDialog())
{
//code after actions...
}
}
Anyway, the dialog is not being opened in Application_ItemSend event. My bad guys...I learnt something new today!
Thanks guys and I apologize for the newbie question...

Are you sure? Application.ItemSend will only fire on the instance of Outlook that actually sends the message. Items.ItemAdd event on the Sent Items folder on the other hand will fire in each instance of Outlook. Is that what your code is doing?

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: detect when "Send/Receive" has finished after startup

I am developing an outlook plugin in C#.
I want to be able to detect when outlook has finished the "send/receive" option after startup, so that I can then run operations on the received mail items.
What I have tried so far:
Manually calling Application.Session.SendAndReceive() on startup.
This runs fine, but the method returns before the send/receive is complete, which is the opposite of what I want
Overriding Application.NewMail and Application.NewMailEx - neither of these trigger as one might hope at startup (NewMailEx doesn't fire at all, NewMail is unreliable)
Calling NameSpace.SyncObjects.AppFolders.Start(); and registering the SyncObjects.AppFolders.SyncEnd event - this event fires before outlook has finished downloading mail
Iterating through NameSpace.SyncObjects, calling Start(), and registering SyncEnd - this method doesn't fire at all.
What is a solution here which will work depenably?
It seems that there is a hack to detect when syncing has finished; namely to override Application.Reminders.BeforeReminderShow as DWE suggests in this SO answer here
This event (in my testing) always fires after outlook syncing has finished.
Then, in order to make sure the reminder window fires, you add a new reminder at startup, and then hide the reminder again within Reminders_BeforeReminderShow
The code then being something like:
public partial class ThisAddIn
{
private ReminderCollectionEvents_Event reminders; //don't delete, or else the GC will break things
AppointmentItem hiddenReminder;
private void ThisAddIn_Startup(object sender, EventArgs e)
{
//other stuff
hiddenReminder = (AppointmentItem)Application.CreateItem(OlItemType.olAppointmentItem); //add a new silent reminder to trigger Reminders_BeforeReminderShow.This method will be triggered after send/receive is finished
hiddenReminder.Start = DateTime.Now;
hiddenReminder.Subject = "Autogenerated Outlook Plugin Reminder";
hiddenReminder.Save();
reminders = Application.Reminders;
reminders.BeforeReminderShow += Reminders_BeforeReminderShow;
}
private void Reminders_BeforeReminderShow(ref bool Cancel)
{
if (hiddenReminder == null) return;
bool anyVisibleReminders = false;
for (int i = Application.Reminders.Count; i >= 1; i--)
{
if (Application.Reminders[i].Caption == "Autogenerated Outlook Plugin Reminder") //|| Application.Reminders[i].Item == privateReminder
{
Application.Reminders[i].Dismiss();
}
else
{
if (Application.Reminders[i].IsVisible)
{
anyVisibleReminders = true;
}
}
}
Cancel = !anyVisibleReminders;
hiddenReminder?.Delete();
hiddenReminder = null;
//your custom code here
}
}
Yup, this is very kludgy, but that's the nature of working with outlook, and I haven't seen any free alternative which can actually claim to work reliably, whereas this works in all of the use cases I've tried it in. So that's a hit I'm willing to take to get a working solution.

Display custom task pane when appointment item is opened (meeting occurrence & individual meeting)

I have some text embedded in the meeting invites sent by me. I want to launch a custom task pane when user opens the appointment from the calendar that contains text specified by me.
I am working with the InspectorsEvents_NewInspectorEventHandler to get the open event and check if the appointment item is opened. In case of appointment i call the code to display custom task pane.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors = this.Application.Inspectors;
inspectors.NewInspector += new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
Outlook.AppointmentItem appointmentItem = Inspector.CurrentItem as Outlook.AppointmentItem;
if (appointmentItem != null)
{
(appointmentItem as Microsoft.Office.Interop.Outlook.ItemEvents_10_Event).Open += _appointment_Open;
(appointmentItem as Microsoft.Office.Interop.Outlook.ItemEvents_10_Event).Close += ThisAddIn_Close;
}
}
private void _appointment_Open(ref bool Cancel)
{
if ((Globals.ThisAddIn.ribbonObj as Ribbon) != null && (Globals.ThisAddIn.ribbonObj as Ribbon).IsLoggedOn)
{
Object selObject = this.Application.ActiveExplorer().Selection[1];
if (selObject is Outlook.AppointmentItem)
{
Outlook.AppointmentItem apptItem = (selObject as Outlook.AppointmentItem);
//Without display() the taskpane is displayed on the calendar screen
apptItem.Display();
//Dispose already open task panes
(Globals.ThisAddIn.ribbonObj as Ribbon).DisposeCustomTaskPanes();
if (FindCustomId(apptItem.Body))
{
(Globals.ThisAddIn.ribbonObj as Ribbon).edit_Cick(null);
}
}
Marshal.ReleaseComObject(selObject);
}
}
edit_click()
{
CustomTaskPane myCustomTaskPane =
Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl, "edit pane");
}
By using apptItem.Display();the appointment is opened and then the task pane is displayed with opened item only. If display() is not used then taskpane is opened in the calendar view of outlook not on the opened item.
The problem with this approach occurs when i am opening and recurrence item. If i open 'just this one' item then the scenario works fine. But if i open 'open entire series' then the open() event is fired twice and two windows are opened, one with meeting occurrence and other with meeting series. 'open series' will open only one window if i remove the display() method call.
My goal is to avoid opening the custom task pane when user has opened a meeting series. The task pane will be displayed only if user opens a meeting occurrence or individual meeting.
Also, is there a way to distinguish when appointment is opened as a meeting occurrence or meeting series. In open_event i get the Appointment.RecurrenceState as olApptOccurrence for both cases.
Do not use the AppointmentItem.Open event to show the task pane - use NewInspector event.
in new inspector i am distinguishing between master occurrence and occurrence item
Outlook.OlRecurrenceState rState = appointmentItem.RecurrenceState;
if (rState == Outlook.OlRecurrenceState.olApptMaster)
return;
appointmentItem.Open += AppointmentItem_Open;
(appointmentItem as Microsoft.Office.Interop.Outlook.ItemEvents_10_Event).Close += ThisAddIn_Close;
Then in the open event i am opening the custom task pane. With this i am able to achieve the desired result. Will update as answer once all test cases are passed.

Save Email Upon Send in Outlook Add-In

I'm making an Outlook Add-in (Visual Studio 2010, .NET 4.0, C#), and I would like to automatically archive a user's email after they send it. What I have so far is the following:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
//Create an event handler for when items are sent
Application.ItemSend += new ApplicationEvents_11_ItemSendEventHandler(saveEmail);
}
private void saveEmail(object Item, ref bool Cancel)
{
}
What I've found through debugging is that my saveEmail method fires off right before the email actually sends. This is OK, ideally I would like it to be fired off immediately after the email is sent successfully, so if there's a way to do that I'd appreciate some pointers.
In any case, I can get inside that method and what I'd like to do is access that email as an Outlook.MailItem object and use the .SaveAs method with whatever parameters I choose. How would I go about grabbing the currently-opened-and-about-to-be-sent-email as a MailItem object?
you can try with this code
private void saveEmail(object Item, ref bool Cancel)
{
var msg = Item as Outlook.MailItem;
msg.SaveAs(yourPath, Outlook.OlSaveAsType.olMSG);
}

How to capture a Outlook Appointment delete event on custom folder?

I am developing an Outlook AddIn. One part of it that I organize appointments in a specific folder. I want to capture if an element gets deleted (in this case moving out of "my" folder counts as deleted).
I found the article https://stackoverflow.com/questions/10579240/how-to-capture-a-c-sharp-outlook-addin-appointment-delete-event and his/her solution helped a lot, but I have a huge problem: the event only fires in that "session", where my folder was created, not when I get the folder object from outlook.
My code looks like this:
private Outlook.MAPIFolder _CalendarMAPIFolder = null;
private Outlook.MAPIFolderEvents_12_Event _CalendarFolder = null;
private Outlook.Items _CalendarItems = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Outlook.MAPIFolder calendarFolder =
this.Application.GetNamespace("mapi").GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
// get my-Folder (if not found, create it)
try
{
_CalendarMAPIFolder = calendarFolder.Folders["my-Folder"];
}
catch
{
_CalendarMAPIFolder = calendarFolder.Folders.Add("my-Folder");
}
_CalendarItems = _CalendarMAPIFolder.Items;
_CalendarFolder = _CalendarMAPIFolder as Outlook.MAPIFolderEvents_12_Event;
if (_CalendarFolder == null)
{
MessageBox.Show("can not cast MAPIFolder to Folder");
}
_CalendarFolder.BeforeItemMove += new Outlook.MAPIFolderEvents_12_BeforeItemMoveEventHandler(Folder_BeforeItemMove);
Debug.Print("events registered");
}
public void Folder_BeforeItemMove(
Object Item,
Outlook.MAPIFolder MoveTo,
ref bool Cancel)
{
Outlook.AppointmentItem aitem = Item as Outlook.AppointmentItem;
string s = "";
if (aitem != null) s = aitem.Subject;
//Cancel = false;
MessageBox.Show("Test! " + s);
}
Does anyone have a solution?
Thank you ;)
Edit: I still have no clue :(((((((
By definition, BeforeItemMove only fires when the user (Outlook client) initiates an Item be moved. It will not fire for synchronization events (i.e. Exchange sync).
If you are connecting Outlook to an Exchange Server, you should look at EWS (Exchange Web Services) if you wish to be notified of folder change events outside the client application (i.e. session). EWS offers push, pull, or streaming notification options. You would attach a notification to "Item deletion" operation.
It was all Microsofts fault! It was a Bug, I just needed to Update Outlook!

Categories

Resources