0

I have a ribbon button that appears on tabNewMailMessage in Outlook's compose email form, this button toggles the visibility of a CustomTaskPane stuck to the side of the form.

In normal practice, everything works perfectly. But when the Compose Email form is invoked via 'Attach to email' or 'Save & Send' from other applications like MS Word or Adobe Reader, the button appears but no longer does anything.

I learned from MSDN that the NewInspector event apparently does not fire in the case of external invocation.

I have not been able to find any workarounds for this case, does anybody here know? :(

EDIT : Additionally, I have a Global class (not the hidden GlobalS class that Visual Studio creates) that contains some variables I use across the entire program. The Addin will not load anything contained in there at all either. It's hard to tell what actually does get loaded, if anyone has more information, holler back please!

EDIT Again : Tested putting string in ThisAddIn and printing it through a messageBox in toggleButton, didn't work. If anyone is confused, the ribbon button will not load if the click-event is impossible to execute, so it seems that externally invoked Compose forms skip all code in ThisAddIn and any class that isn't the ribbon itself.

I really need help figuring this out! :(

EDIT Yet Again : Here is what I've garnered so far, ThisAddIn startup events will not fire, no properties in external classes may be read from, but external methods like say ThisAddIn.SayHelloWorld() do work.

EDIT again! :

private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {

        //MessageBox.Show(,"TEST");
        try
        {               
            inspectors = Globals.ThisAddIn.Application.Inspectors;
            inspectors.NewInspector += new InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);

            foreach (Inspector insp in inspectors)
            {
                //insp.
                Inspectors_NewInspector(insp);
            }
        }
        catch (System.Exception ex)
        {
            List<string> lalala = new List<string>();
            lalala.Add(ex.GetType().ToString());
            lalala.Add(ex.Message);
            lalala.Add(ex.StackTrace);
            File.WriteAllLines(@"C:\outdebug",lalala.ToArray());
        }
    }

Again! :

void Inspectors_NewInspector(Inspector Inspect)
    {
        try
        {
            if (Inspect.CurrentItem is MailItem)
            {
                Global.mail = Inspect.CurrentItem;
                Global.inspectorWrappersValue.Add(Inspect, new InspectorWrapper(Inspect, Global.mail));
                //inspectorw
            }
        }
        catch (System.Exception ex)
        {
            List<string> lalala = new List<string>();
            lalala.Add(ex.GetType().ToString());
            lalala.Add(ex.Message);
            lalala.Add(ex.StackTrace);
            lalala.Add(Global.SiteConnectionManager.ToString());
            File.WriteAllText(@"C:\Users\cat\Desktop\outdebug.txt", string.Join("\n", lalala), Encoding.UTF8);
        }
    }
Cat
  • 127
  • 2
  • 9

1 Answers1

1

It is a good idea to use try/catch handlers around all your code in ThisAddIn.Startup, because Outlook is aggressive about eating all exceptions, so if there is a problem you will never know it.

However, I think what is really causing you a problem is an Outlook issue discussed at http://social.msdn.microsoft.com/Forums/en/vsto/thread/60c75274-e15a-4696-afa6-79de8fbd707d. The solution is to create a timer and check if there are existing inspectors when it fires. What I have below should help out a lot, but it's not a complete solution because it doesn't handle the case where NewInspector fires and the timer also indicates an inspector. So you will have to add some logic to make sure you don't add 2 task panes for an inspector.

public partial class ThisAddIn
{
    private DispatchTimer _newInspectorStartupTimer;

    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        try
        {
            // check for existing explorers and inspectors and
            // set up event handlers for new ones

            // here is how you set up the inspector event handler:
            ThisAddIn.Application.Inspectors.NewInspector += new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);

            // create a timer.  When the timer fires, check if any
            // Inspector windows currently exist, and add task panes
            // for them if needed.
            _newInspectorStartupTimer = new DispatcherTimer();
            _newInspectorStartupTimer.Interval = TimeSpan.FromSeconds(2.0);
            _newInspectorStartupTimer.Tick += new EventHandler(NewInspectorStartupTimer_Tick);
            _newInspectorStartupTimer.Start();
        }
        catch (System.Exception ex)
        {
            // log the exception type, message, and stack trace here
        }
    }

    private void NewInspectorStartupTimer_Tick(object sender, EventArgs e)
    {
        int inspectorCount = _inspectors.Count;
        if (inspectorCount > 0)
        {
            for (int i = 1; i <= _inspectors.Count; ++i)
            {
                Inspector inspector = _inspectors[i];
                Inspectors_NewInspector(inspector);
            }
        }
    }

    // Inspectors_NewInspector also has a try/catch.  Note that
    // Inspectors_NewInspector will be called multiple times
    // for each inspector, due to the timer.
    private void Inspectors_NewInspector(Inspector inspector)
    {
        try
        {
            // you need to check whether you have already created a
            // task pane for this inspector.  If not, create your
            // task pane here.
        }
        catch (System.Exception ex)
        {
            // log the exception type, message, and stack trace here
        }
    }
Jean Libera
  • 549
  • 4
  • 8
  • Jean, would you happen to know the order of how things happen with an external invocation? Specifically I would like to know if a class otehr than ribbon and thisaddin can be loaded – Cat Feb 28 '11 at 00:47
  • First, I just checked in my addin. I had Outlook closed, and opened up Word and chose "Send To". According to my log file, ThisAddIn.Startup did not find any existing inspectors. I don't fully understand your question. Our AddIn has a custom task pane and a ribbon for each inspector. However, it was tricky getting it to work from an external app. We had to add logging to figure out where things were going wrong. – Jean Libera Feb 28 '11 at 00:51
  • I just did a test with ThisAddIn and external invocation : i put MessageBox.Show("TEST"); into the first line of thisaddin, does what you expect on a normal startup, displays message box. With external invocation, the messagebox will not show until after you have closed the Compose Email window. This is probably why things are going wrong for me :( – Cat Feb 28 '11 at 00:53
  • That might be due to a parenting issue for your MessageBox. When you are in ThisAddIn, you aren't specifying which window is the parent. – Jean Libera Feb 28 '11 at 01:04
  • Theres a way for me to make ThisAddIn_Startup happen before the external compose window launches? If you could share such a method, that would be great :) – Cat Feb 28 '11 at 01:05
  • All I am saying is, at startup, we check ThisAddIn.Application.Inspectors and This.Application.Explorers. Most of the time, I think that the count on these are both 0 so we rely on the NewExplorer and NewInspector events, but if there are existing inspectors or explorers, we create custom task panes for each one. You can check these to see if that is what is happening to you, but I am doubtful. – Jean Libera Feb 28 '11 at 01:14
  • I edited my answer to show how you should have try/catch clauses in both ThisAddIn_Startup and in your NewInspector handler. – Jean Libera Feb 28 '11 at 01:24
  • i'm kind of new to try-catch debug stuff, can you help me see why my debug file isn't writing? :( added some code to question post – Cat Feb 28 '11 at 01:38
  • 1. I would try intentionally throwing an exception in ThisAddIn_Startup to test whether your logging code will work. So add "throw new OutOfMemoryException()" as the first line in ThisAddIn_Startup, set a breakpoint and start outlook normally just to check it. After testing, remove the intentional exception. (to be continued) – Jean Libera Feb 28 '11 at 01:47
  • 2. After getting logging working completely, add the same try/catch to Inspectors_NewInspector(). I am a little nervious about File.WriteAllLines because according to the doc it creates a new file. I guess it is fine as long as there is only 1 exception at a time :) – Jean Libera Feb 28 '11 at 01:51
  • I got exceptions working! Added code to question again to demonstrate changes. (more to come also) – Cat Feb 28 '11 at 02:18
  • I noticed again on launching from MS Word, the outdebug.txt is not created until the new mail compose window is closed, again, the exception is at the first line in ThisAddIn. the exception i mean is the fake out of memory exception, neither ThisAddIn_Startup nor Inspectors_NewInspector() throw exceptions if launched externally. – Cat Feb 28 '11 at 02:19
  • I think you should switch to using a StreamWriter so that you can add tracing code to figure out how far you are getting. The code is like this: StreamWriter streamWriter = new StreamWriter(filename); streamWriter.WriteLine(msg); streamWriter.Flush(); Make streamWriter a member variable somewhere so you can keep using it, and Flush each time you do a WriteLine. Add messages from Startup and NewInspector methods so you can figure out where it stops working. Or you can use something else besides StreamWriter which allows appending. – Jean Libera Feb 28 '11 at 02:43
  • StreamWriter streamwrite = new StreamWriter(@"C:\Users\cat\Desktop\Outdebug.txt"); streamwrite.Write(lalala.ToArray()); streamwrite.Flush(); does this look right to you? My outdebug is now filling up with stuff like System.String[] :( – Cat Feb 28 '11 at 03:20
  • That is because String[] does not have a ToString() method. You can use streamwrite.WriteLine(ex.GetType().ToString()); streamwrite.WriteLine(ex.Message); streamwrite.WriteLine(ex.StackTrace); streamwrite.Flush(); – Jean Libera Feb 28 '11 at 03:27
  • I have all the error logging stuff in place and working now, but externally invoking isn't throwing any errors, or at least no Outdebug.txt is being created. – Cat Feb 28 '11 at 03:32
  • Log some text immediately after the try in Inspectors_NewInspector. Also log some text at the end of the try in Inspectors_NewInspector. That is to make sure that you are getting all the way through the method. Since there are no exceptions you probably are. (to be continued) – Jean Libera Feb 28 '11 at 03:45
  • Inside your Inspectors_NewInspector, are you calling any code which would not be caught by the try/catch? For example, if your CustomTaskPane contains a WinForms UserControl, and you have code in the OnLoad event, then add try/catch code and logging to the OnLoad event. – Jean Libera Feb 28 '11 at 03:48
  • sorry, took me a while to figure out that multiple throw exceptions would only give you one error because the rest wouldn't happen. The program goes all the way through NewInspector on a normal startup, and no special uncatchable code that I could recognize, unless you mean the InspectorWrapper class I have in Inspector_NewInspector. Also I tried running the addin in debug, and while outlook(debug) was running, did an external invocation. I'm not sure if this would/should trip Visual studio breakpoints, but it definitely didn't invoke NewInspector this time. – Cat Feb 28 '11 at 04:29
  • When you call into Outlook from an external invocation, you won't trip any breakpoints. However, your logging code will still work. When I do this in my AddIn, I see the logging statement which proves that NewInspector was called. I have a simple addin that I use for testing. Tomorrow I will check out how it works in the external caller scenario, and then I can post some of the exact code in it. – Jean Libera Feb 28 '11 at 04:52
  • OK, last comment until tomorrow... I think there is an Outlook issue. It is related to this [forum post link](http://social.msdn.microsoft.com/Forums/en/vsto/thread/60c75274-e15a-4696-afa6-79de8fbd707d). In Startup, we create a 2-second Dispatch timer. When it fires, we check whether Inspectors > 0 and if so we do NewInspector processing for each inspector. That seems like a hack but it works... I can post some of the code tomorrow. – Jean Libera Feb 28 '11 at 05:23
  • I made a lot of changes to the answer. I think it is really going to work for you now. – Jean Libera Feb 28 '11 at 14:24
  • sorry, I was sick the day before. Jean, I'm still running into this odd problem where no code in ThisAddIn or other classes can run until the Modal Email.Compose window (and the 'ghost' it leaves behind) are closed. None of those timers exist yet until the externally-called Compose form is closed. Could it be the specific way in which Outlook 2010 or VSTO/.NET 4.0 assemblies work? – Cat Mar 01 '11 at 21:55
  • Are you saying that you still aren't able to add a custom task pane within the Email.Compose window, if you are opening the Email.Compose window from a different app? There are 2 different cases here. (1) Outlook is already open, and a user opens Email.Compose from Word or Windows Explorer or another app. (2) Outlook is not open, and a user opens Email.Compose from another app. In all cases, we are able to get a custom sidebar up, so the timers are working for us. – Jean Libera Mar 05 '11 at 18:05