I'm trying to register a handler for the WindowSelectionChanged event. It works, but when quitting Outlook, it will discard any changes to NormalEmail.dotm file.
For example, any changes to the Quick Part Gallery (Insert->Text->QuickParts) will only be visible during the current session; closing Outlook and starting it again will show the previous list of elements, even if they were deleted, or new ones were added).
Deleting the file :
C:\Users[USER]\AppData\Roaming\Microsoft\Templates\ NormalEmail.dotm
should prompt Outlook to create a new one, but this won't happen when the Addin is enabled.
This can be reproduced by registering even an empty method to the WindowSelectionChange event:
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.Inspectors.NewInspector += new Outlook.InspectorsEvents_NewInspectorEventHandler(RegisterChangeHandler);
}
void RegisterChangeHandler(Outlook.Inspector inspector)
{
Microsoft.Office.Interop.Word.Application app = Application.ActiveExplorer().ActiveInlineResponseWordEditor?.Application;
--> app.WindowSelectionChange += WinSelectionChange; <-- this line causes the bug
}
private void WinSelectionChange(Selection Sel)
{
/* nothing */
}
...
}
This causes no exceptions; the result is the same when putting try/catch blocks in place, or when the callback is unregistered in the Application.Quit event.
Question: How can Outlook be made to commit the NormalEmail.dotm file, while also being able to register a callback for WindowSelectionChange event ?
(the project type is "2013 and 2016 VSTO Addin")
Related
in my Addin I attached to the ItemChange event like this;
public void attachEvents()
{
_CalendarItems.ItemChange += Item_Change;
and I wrote a function like this:
public void Item_Add(Object item)
{
Outlook.AppointmentItem myAppointment = item as Outlook.AppointmentItem;
if (myAppointment != null)
{
[...]
My problem is that this method does not get called just once when I call myAppointment.save() but 3-4 times. Why is that?
And there are only get() calls in the eventhandler so there is NOTHING changed in the appointmentItem (which would probably trigger further events of course).
Best regards
Hannes
That is expected. Especially with Exchange profiles configured in Outlook. You may find similar forum threads like the following one - Outlook 2016 produces an extra ItemChange event after an appointment is created.
I want to handled the BeforeAttachmentAdd event from an Outlook mail item. But my code works in Visual Studio environment but not out. Have you an idea?
This is my code:
namespace MyOutlookProject
{
using Microsoft.Office.Interop.Outlook;
using OutlookApplication = Microsoft.Office.Interop.Outlook.Application;
using OutlookAttachment = Microsoft.Office.Interop.Outlook.Attachment;
using OutlookInspector = Microsoft.Office.Interop.Outlook.Inspector;
using OutlookMail = Microsoft.Office.Interop.Outlook.MailItem;
class MailManager
{
public void StartUp(OutlookApplication application)
{
_inspectors = application.Inspectors;
_inspectors.NewInspector += Inspectors_NewInspector;
}
private void Inspectors_NewInspector(OutlookInspector Inspector)
{
if (Inspector.CurrentItem is OutlookMail)
{
OutlookMail mail = (Inspector.CurrentItem as OutlookMail);
mail.BeforeAttachmentAdd += Mail_BeforeAttachmentAdd;
}
}
private void Mail_BeforeAttachmentAdd(OutlookAttachment Attachment, ref bool Cancel)
{
/*Never called without Visual Studio*/
}
}
}
Thanks for your help.
The object firing the events (mail variable in your code) must be on the global/class level to prevent it from being garbage collected. The variable is local in you case.
On a general note, you can have multiple inspectors open, so it might make sense to have a wrapper object that holds references to the inspector and its mail item, and have a list of such wrappers in your addin.
From what I see you may be hitting a My button stopped working issue
From the book Of E. Carter and E. Lippert VSTO 2007
It states
One issue commonly encountered when beginning to program agains Office events in .NET is known as the "my button stopped working" issue. A developer will write some code to handle a Click event raised by a CommandBarButton in the Office toolbar object model. This code will sometimes work temporarily but then stop. The user will click the button, but the Click event appears to have stopped working. The cause of this issue is connecting an event handler to an object whose lifetime does not match the desired lifetime of the event. This tipcally occurs when the object to which you are connecting an event handler goes out of scope or gets sets to null so that it gets garbage collected.
I think in your case that the .NET RCW object of type OulookMail manipulated with variable mail that is the culprit. Its lifetime is not properly handled. The fact that this does not happen in Visual Studio is that you are probably in Debug mode that changes a little the Garbage Collection so your object is not destroyed yet when you do the testing.
I'am very new to threading and quite unclear as to why this is happening in my code, when I click on a button that verifies hyperlinks in my document, I start a new thread that does the verification once it starts I want to disable the ribbon button and enable it again after thread finished but this is not happening and I dont know what is the mistake .Here is what I have tried so far:
public class Alpha :Ribbon1
{
// This method that will be called when the thread is started
public void Beta()
{
foreach() { //do something } after this loop ,enable the button again
button.enable=true //not applying
} }
private void button_Click(object sender, RibbonControlEventArgs e)
{
Alpha oAlpha = new Alpha();
// Create the thread object, passing in the Alpha.Beta method
Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));
// MessageBox.Show("Please wait till the document is checked for invalid links");
// Start the thread
oThread.Start();
button7.Label = "Pls wait";
button7.Enabled = false;
}
Ribbon needs to be rendered again after enable/disable for change to take effect, you can do this by calling IRibbonUI.Invalidate()
Parts of our UI uses IObservableElementEnumerable.EnumerableChanged in order to update if the user e.g. deletes a domain object from a folder.
When the UI is disposed, we unsubscribe from the event... or so we thought. It turns out that the unsubscribe doesn't have any effect, and our event handler is still called. This caused a number of odd bugs, but also leads to memory leaks.
The only time unsubscription works, is if we store the IObservableElementEnumerable reference instead of calling IObservableElementEnumerableFactory.GetEnumerable(obj) again. But this, in turn, is likely to keep a live reference to the folder object, which will break if the folder itself is deleted by the user.
This is particularly puzzling as the GetEnumerable() documentation clearly states: "It is expected that subsequent calls with the same domain object will yield the same instance of IObservableElementEnumerable." Is this not to be interpreted as a guarantee?
Should there be any reason for unsubscription not working?
The following code replicates the issue on Petrel 2011 (add to a simple plugin with a menu extension, or get the full solution here (DropBox)):
using System;
using System.Linq;
using System.Windows.Forms;
using Slb.Ocean.Core;
using Slb.Ocean.Petrel;
using Slb.Ocean.Petrel.Basics;
using Slb.Ocean.Petrel.UI;
namespace ObservableElementEnumerable
{
public class OEEForm : Form
{
private Droid _droid;
private bool _disposed;
public OEEForm()
{
IInput input = PetrelProject.Inputs;
IIdentifiable selected = input.GetSelected<object>().FirstOrDefault() as IIdentifiable;
if (selected == null)
{
PetrelLogger.InfoOutputWindow("Select a folder first");
return;
}
_droid = selected.Droid;
GetEnumerable().EnumerableChanged += enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable subscribed");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && !_disposed)
{
GetEnumerable().EnumerableChanged -= enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable unsubscribed (?)");
_droid = null;
_disposed = true;
}
}
IObservableElementEnumerable GetEnumerable()
{
if (_disposed)
throw new ObjectDisposedException("OEEForm");
object obj = DataManager.Resolve(_droid);
IObservableElementEnumerableFactory factory = CoreSystem.GetService<IObservableElementEnumerableFactory>(obj);
IObservableElementEnumerable enumerable = factory.GetEnumerable(obj);
return enumerable;
}
void enumerable_EnumerableChanged(object sender, ElementEnumerableChangeEventArgs e)
{
PetrelLogger.InfoOutputWindow("Enumerable changed");
if (_disposed)
PetrelLogger.InfoOutputWindow("... but I am disposed and unsubscribed!");
}
}
public static class Menu1
{
public static void OEEBegin1_ToolClick(object sender, System.EventArgs e)
{
OEEForm f = new OEEForm();
f.Show();
}
}
}
To replicate:
Run Petrel with the plugin
Load a project with a folder with objects
Select the folder
Activate the plugin menu item
With the popup open, delete an object in the folder
Close the Form popping up
Delete an object in the folder
The message log should clearly show that the event handler is still called after the form is disposed.
You already keep a reference to the underlying enumerable by connecting the event. Events are references as well. Just keep a reference to the enumerable and unsubscribe from the same instance as the one you subscribe to.
To deal with the issue of objects that are deleted by the user you need to listen to the delete event.
I've written this little MS Outlook 2003 VSTO Add-In using C# and Visual Studio 2008. It is meant to check each Mail Item being sent for the word "attach" in the body and if found, then check the number of attachments. If that number is zero, then ask the user if they really mean to send the message. It's supposed to work like the Gmail labs feature which does the same thing.
The odd thing is that it works, but the first time I run it, I get a pause, like the mail item window is hung for about 45 seconds. Once it gets past that, it runs very fast for the rest of the time I have Outlook open. If I close Outlook though, then the next time I re-open it and send a message, I will have this wait again.
Any ideas, peoples?
Here's the code for my Add-In:
namespace OutlookAttacher
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
void Application_ItemSend(object Item, ref bool Cancel)
{
if (Item is Microsoft.Office.Interop.Outlook.MailItem)
{
Microsoft.Office.Interop.Outlook.MailItem currentItem = Item as Microsoft.Office.Interop.Outlook.MailItem;
Cancel = true;
if (currentItem.Body.Contains("attach"))
{
if (currentItem.Attachments.Count > 0)
{
Cancel = false;
//MessageBox.Show("This message will be sent now.");
currentItem.Send();
}
else
{
DialogResult ans = MessageBox.Show("This message has no attachments. Are you sure you want to send it?", "OutlookAttacher", MessageBoxButtons.YesNo);
if (ans.Equals(DialogResult.Yes))
{
Cancel = false;
//MessageBox.Show("This message will be sent now.");
currentItem.Send();
}
}
}
else
{
Cancel = false;
//MessageBox.Show("This message will be sent now.");
currentItem.Send();
}
}
}
}
}
Any suggestions for improving the code are welcome as well, since this is my first stab at an Outlook Add-In.
Update: I am running this on a 5-year Dell laptop, 2 GB of Ram and I-don't-know-which Intel CPU. I like the idea of adding a trace / debugging it. I will have to go figure out how to step through the code so I can see where it might be taking the longest time. Thanks y'all!
OK, I'm really embarrassed. The delay I was seeing was just Outlook synching with my Exchange server. I was at home when I was testing and Outlook was still connecting via HTTP. I am seeing it work fast today, in the office, so no HTTP. Thanks for the replies anyway.
:-)