I have a simple add-in that inserts a CommandBarButton in the context menu of the MailItem list. The code inserts the button fine, but when I start to use outlook a little my code eventually stops running and doesn't add the button to the context menu any longer.
I've narrowed down one reproducible scenario:
Start outlook in debug
Right click first MailItem to confirm the CommandBarButton is there (it is)
Right click second mail item to confirm the CommandBarButton is there (it is)
Choose "Open" from the context menu and then close the email after it opens.
Right click first MailItem and the CommandBarButton is gone.
It would seem that when Outlook loses focus the add-in fails. I can also cause the add-in to fail by changing to a different mail folder and right clicking an item.
I'm not getting any exceptions, it just dies. I've started my project over twice now, carefully adding each piece of code and trying to follow the various VSTO sample I have. In the end I always seem to end up with a crashing add-in.
Here is the code, it's a bit big:
{
public partial class ThisApplication
{
private Outlook.Explorer _activeExplorer = null;
private IncomingMailTracker _incomingMailTracker = null;
private Office.CommandBar _contextMenu = null;
private bool _someFlag = false;
private Office.CommandBarButton _contextButton = null;
private void ThisApplication_Startup(object sender, System.EventArgs e)
{
try
{
_activeExplorer = this.ActiveExplorer();
_activeExplorer.SelectionChange += new Microsoft.Office.Interop.Outlook.ExplorerEvents_10_SelectionChangeEventHandler(ThisApplication_SelectionChange);
_activeExplorer.CommandBars.OnUpdate += new Microsoft.Office.Core._CommandBarsEvents_OnUpdateEventHandler(CommandBars_OnUpdate);
Trace.Listeners.Add(new ConsoleTraceListener());
}
catch (COMException comException)
{
Trace.TraceError(comException.ToString());
MessageBox.Show(comException.Message);
}
catch(Exception exception)
{
Trace.TraceError(exception.ToString());
MessageBox.Show(exception.Message);
}
}
void ThisApplication_SelectionChange()
{
//MessageBox.Show("Selection Changed");
}
private void ThisApplication_Shutdown(object sender, System.EventArgs e)
{
}
void CommandBars_OnUpdate()
{
if(_someFlag)
{
return;
}
try
{
// Try to get the context menu
try
{
_contextMenu = _activeExplorer.CommandBars["Context Menu"];
}
catch(System.Reflection.TargetInvocationException itemNotFoundException){}
if (_contextMenu != null)
{
AddContextMenuButton();
}
}
catch (COMException e)
{
Trace.TraceError(e.Message);
MessageBox.Show(e.Message);
}
catch(Exception exception)
{
Trace.TraceError(exception.Message);
MessageBox.Show(exception.Message);
}
}
private void AddContextMenuButton()
{
const string buttonTagAndCaption = "Send Something to NetSuite";
Office.CommandBarButton button;
try
{
button = (Office.CommandBarButton)_contextMenu.FindControl(
Office.MsoControlType.msoControlButton,
Type.Missing,
buttonTagAndCaption,
Type.Missing,
Type.Missing);
if(button == null)
{
if (_contextButton != null)
{
// remove the event handler
_contextButton.Click -= button_Click;
}
ChangingBar(_contextMenu, false);
_contextButton = (Office.CommandBarButton)_contextMenu.Controls.Add(Office.MsoControlType.msoControlButton,
Type.Missing,
string.Empty,
1,
true);
_contextButton.Tag = buttonTagAndCaption;
_contextButton.Caption = buttonTagAndCaption;
_contextButton.Priority = 1;
_contextButton.Visible = true;
_contextButton.Enabled = true;
_contextButton.Click += new Microsoft.Office.Core._CommandBarButtonEvents_ClickEventHandler(button_Click);
_contextButton.Style = Microsoft.Office.Core.MsoButtonStyle.msoButtonIconAndCaption;
ChangingBar(_contextMenu, true);
Trace.WriteLine("Context Menu Button Created", "Information");
}
}
catch (COMException e)
{
MessageBox.Show(e.Message);
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
}
void button_Click(Microsoft.Office.Core.CommandBarButton Ctrl, ref bool CancelDefault)
{
try
{
MessageBox.Show("Context menu option clicked");
}
catch (Exception e)
{
Trace.TraceError(e.Message);
MessageBox.Show(e.Message);
}
}
// Called once to prepare for changes to the command bar, then again with
// Restore = true once changes are complete.
private void ChangingBar(Office.CommandBar bar, bool restore)
{
bool oldProtectFromCustomize = false;
bool oldIgnore = false;
if(restore)
{
// Restore the Ignore Changes flag
_someFlag = oldIgnore;
// Restore the protect-against-customization bit
if(oldProtectFromCustomize)
{
bar.Protection = bar.Protection & Office.MsoBarProtection.msoBarNoCustomize;
}
}
else
{
// Store the old Ignore Changes flag
oldIgnore = _someFlag;
_someFlag = true;
// Store old protect-against-customization bit setting then clear
// CAUTION: Be careful not to alter the property if there is no need,
// as changing the Protection will cause any visible CommandBarPopup
// to disappear unless it is the popup we are altering.
oldProtectFromCustomize = (bar.Protection & Office.MsoBarProtection.msoBarNoCustomize) == Office.MsoBarProtection.msoBarNoCustomize;
if(oldProtectFromCustomize)
{
bar.Protection = bar.Protection &~ Office.MsoBarProtection.msoBarNoCustomize;
}
}
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisApplication_Startup);
this.Shutdown += new System.EventHandler(ThisApplication_Shutdown);
}
#endregion
}
}
If the problem isn't obvious, I would like to move to plan B: If anyone has a working and reliable example of adding a button to a context menu using C# and Outlook 2003 I will pay you for it. I'm desperate at this point and just need to get caught up on this project. Of course I would rather learn what I'm doing wrong, but without any errors I'm pretty stuck.
Thanks for reading and for any help you might be able to offer.
-Steve