Link to home
Start Free TrialLog in
Avatar of spanout
spanout

asked on

Problem with VSTO event handler going out of scope in Outlook AddIn

I have the enclosed code and am trying to hook up the attachment events within the Inspector. These delegate handlers (eg gp2_fnAttachAdd) do not fire when I try to attach a file to an item:

            ((Outlook.ItemEvents_10_Event)Inspector).AttachmentAdd += new Outlook.ItemEvents_10_AttachmentAddEventHandler(gp2_fnAttachAdd);
            ((Outlook.ItemEvents_10_Event)Inspector).AttachmentRemove += new Outlook.ItemEvents_10_AttachmentRemoveEventHandler(gp2_fnAttachDel);

I am assuming that this is because the handlers go out of scope, although other handlers that are in the same block seem to remain in scope. I am quite new to C# and am struggling with how the garbage collection functions for this area. I enclose the AddIn c# file and the wrapper from where the delegate handlers are declared.

Any assistance on this would be very greatly appreciated, please treat me as a newbie :)


 ThisAddIn.cs InspectorWrapper.cs
Avatar of Miguel Oz
Miguel Oz
Flag of Australia image

Create a variable to hold your event instance for the lifetime of the add-in
public partial class ThisAddIn
{
   Outlook.ApplicationEvents_11_ItemSendEventHandler myHandler;
   //Your existing code...
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            myHandler = new Outlook.ApplicationEvents_11_ItemSendEventHandler(gp2_fnItemSend);
            this.Application.ItemSend += myHandler;
           //Your existing code...

         }
   //Your existing code...

}

In interop scenarios, you need a field variable to avoid the garbage collector to free up your handler.
Avatar of spanout
spanout

ASKER

Hi mas oz2003.

I have tried you solution and it will not handle the envent. This is what I did, please forgive me if it is wrong:

I added the following public statements in thisaddin:

 public partial class ThisAddIn
    {
        public Outlook.ItemEvents_10_AttachmentAddEventHandler myAttachHandler;
        public Outlook.ItemEvents_10_AttachmentRemoveEventHandler myDetachHandler;

I added my handlers in the same class

  void gp2_fnAttachFile(Outlook.Attachment myAttach)
        {
            attachcount++;
        }

        void gp2_fnDetachFile(Outlook.Attachment myAttach)
        {
            attachcount--;
        }
In my inspector wrapper, I added these:
            ((Outlook.ItemEvents_10_Event)Inspector).AttachmentAdd += Globals.ThisAddIn.myAttachHandler;
            ((Outlook.ItemEvents_10_Event)Inspector).AttachmentRemove += Globals.ThisAddIn.myDetachHandler;


When I run the addin, I can see that it is connecting the handlers, but when I attach a file, it does not fire my custom handler. Can you see what I have done wrong?

Thanks for your help


My posted code solved your issue in the ThisAddIn class only.

Your custom handler instance has to be created in the same class that are used. You are trying to call global handlers; if needed you need to pass them as parameters.

In you case at ConnectEvents method you must have all handlers as class instance and assign the instance to your handlers. Also the wrapper class must exist for the lifetime of the Add-in.
Avatar of spanout

ASKER

Thanks for your suggestions. I have moved everything into the ThisAddIn class so there is no longer a reliance on the wrapper class. The Itemsend event works perfectly, but the handlers for Attachments still seem to be going through GC:
I created the variables for the 2 handlers, atached them to the Inspectors as they are created. I run this in debug and when I open a mail item, this code runs but it never runs the gp2_fnAttachFile function when I add an attachment. Surely having everything in the ThisAddIn class keeps it alive while the mailitem is open. This certainly is the case with the send handler which works perfectly. Again, any help is appreciated, I feel stupid not being able to grasp this :(




public partial class ThisAddIn
    {
        Outlook.Inspectors _Inspectors;
        Outlook.Explorers _Explorers;

        Outlook.MailItem mailItem;
        Outlook.ItemEvents_10_AttachmentAddEventHandler myAttachHandler;
        Outlook.ItemEvents_10_AttachmentRemoveEventHandler myDetachHandler;

        Dictionary<Guid, WrapperClass> _WrappedObjects;
        public bool IsLicensed = true;
        public string Licensor = "";
        public string ExpireDate = "";
        public int attachcount = 0;
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {




            myAttachHandler = new Outlook.ItemEvents_10_AttachmentAddEventHandler(gp2_fnAttachFile);
            myDetachHandler = new Outlook.ItemEvents_10_AttachmentRemoveEventHandler(gp2_fnDetachFile);
         






            #region license check

            if (Properties.Settings.Default.Licence != "")
            {
                olicense myLicence = new olicense();
                oOutlookUtilities mysender = new oOutlookUtilities();
                
                myLicence.AccountEMail = Convert.ToString(mysender.getSenderAddress()[0]);
                myLicence.key = Properties.Settings.Default.Licence;
                myLicence.decodeLicense();
                if (myLicence.Error != "")
                {
                    MessageBox.Show("There was an error starting OutlookGuard: " + myLicence.Error);
                    IsLicensed = false;
                }
                else
                {
                    Licensor = myLicence.UserName;
                    if (myLicence.warningdays > 370) { ExpireDate = "Perpetual"; }
                    if (myLicence.warningdays < 370) { ExpireDate = Convert.ToString(myLicence.ExpiresDate).Split(' ')[0]; }

                }

                myLicence = null;
                mysender = null;
            }
            else
            {
                MessageBox.Show("This product is unregistered. Please go to the OutlookGuard website to obtain a license key, and enter the key in the settings area.");

            }

            #endregion
            
            

            this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(gp2_fnItemSend);
           
            
            _WrappedObjects = new Dictionary<Guid, WrapperClass>();


            _Inspectors = this.Application.Inspectors;
            _Inspectors.NewInspector += new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(_Inspectors_NewInspector);
            
            // Are there any open Inspector after Startup ?        
            for (int i = _Inspectors.Count; i >= 1; i--)
            {
                // Wrap the Inspector and do some usefull with it            
                WrapInspector(_Inspectors[i]);
                if (_Inspectors[i] is Outlook.MailItem)
                {
                    
                }

            }
            
            // Explorer stuff        
            _Explorers = this.Application.Explorers;
            // Are there any open Explorers after Startup ?        
            for (int i = _Explorers.Count; i >= 1; i--)
            {
                // Wrap the Explorer and do some usefull with it            
                WrapExplorer(_Explorers[i]);
            }
            
            _Explorers.NewExplorer += new Microsoft.Office.Interop.Outlook.ExplorersEvents_NewExplorerEventHandler(_Explorers_NewExplorer);
        }


        #region event handlers


        void gp2_fnAttachFile(Outlook.Attachment myAttach)
        {
            attachcount++;
        }

        void gp2_fnDetachFile(Outlook.Attachment myAttach)
        {
            attachcount--;
        }

        void gp2_fnItemSend(object item, ref bool cancel)
        {

            if (IsLicensed == true) // do not function if not licensed
            {
                oOutlookGuardActions myActions = new oOutlookGuardActions();
                if (item is Outlook.MailItem)
                {
                    myActions.RecipientList = (item as Outlook.MailItem).Recipients;
                    myActions.sSubject = (item as Outlook.MailItem).Subject;
                    myActions.sBody = (item as Outlook.MailItem).Body;
                    myActions.AttachList = (item as Outlook.MailItem).Attachments;


                    cancel = myActions.performcheck();
                    (item as Outlook.MailItem).DeferredDeliveryTime = DateTime.Now.AddSeconds(myActions.delaysend);
                    (item as Outlook.MailItem).Subject = myActions.sSubject;


                    myActions = null;

                }
            }
        }


        #endregion








        #region wrapper classes

        void _Explorers_NewExplorer(Microsoft.Office.Interop.Outlook.Explorer Explorer)
        {
            WrapExplorer(Explorer);
        }
        
        void WrapExplorer(Microsoft.Office.Interop.Outlook.Explorer Explorer)
        {
            ExplorerWrapper wrappedExplorer = new ExplorerWrapper(Explorer);
            wrappedExplorer.Closed += new WrapperClosedDelegate(wrappedObject_Closed);
            _WrappedObjects[wrappedExplorer.Id] = wrappedExplorer;
        }

        
        void _Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
        {
            //InspectorWrapper wrappedInspector = new InspectorWrapper(Inspector);
            //wrappedInspector.Closed += new WrapperClosedDelegate(wrappedObject_Closed);
            //WrappedObjects[wrappedInspector.Id] = wrappedInspector;

            (Inspector as Outlook.MailItem).AttachmentAdd += myAttachHandler;
            (Inspector as Outlook.MailItem).AttachmentRemove += myDetachHandler;
            
            
            //

        }

Open in new window

Avatar of spanout

ASKER

I have updated code below which is cleaner to look at
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Windows.Forms;
using System.Collections;

namespace OutlookAddIn1
{

    
    public partial class ThisAddIn
    {
        Outlook.Inspectors _Inspectors;
        Outlook.Explorers _Explorers;
        Outlook.MailItem mailItem;

        Outlook.ItemEvents_10_AttachmentAddEventHandler myAttachHandler;
        Outlook.ItemEvents_10_AttachmentRemoveEventHandler myDetachHandler;

   
        public bool IsLicensed = true;
        public string Licensor = "";
        public string ExpireDate = "";
        public int attachcount = 0;
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {



            
            
            

            this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(gp2_fnItemSend);
            _Inspectors = this.Application.Inspectors;
            _Inspectors.NewInspector += new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(_Inspectors_NewInspector);
            
           
        }


        #region event handlers


        void gp2_fnAttachFile(Outlook.Attachment myAttach)
        {
            attachcount++;
        }

        void gp2_fnDetachFile(Outlook.Attachment myAttach)
        {
            attachcount--;
        }

        void gp2_fnItemSend(object item, ref bool cancel)
        {

            //send code
        }


      
        void _Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
        {
            
            myAttachHandler = new Outlook.ItemEvents_10_AttachmentAddEventHandler(gp2_fnAttachFile);
            myDetachHandler = new Outlook.ItemEvents_10_AttachmentRemoveEventHandler(gp2_fnDetachFile);
         
            (Inspector as Outlook.MailItem).AttachmentAdd += myAttachHandler;
            (Inspector as Outlook.MailItem).AttachmentRemove += myDetachHandler;



        }

        #endregion

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
            
            _Inspectors = null;
            _Explorers = null;
        }

        #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(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion

    }
}

Open in new window

You are missing two field instances  and change  ThisAddIn_Startup method as follows:
Outlook.ApplicationEvents_11_ItemSendEventHandler mytemSendEventHandler;
Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler myNewInspectorEventHandler;

private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
             mytemSendEventHandler= new Outlook.ApplicationEvents_11_ItemSendEventHandler(gp2_fnItemSend);

            this.Application.ItemSend += mytemSendEventHandler;
            _Inspectors = this.Application.Inspectors;
            myNewInspectorEventHandler = new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(_Inspectors_NewInspector);
            _Inspectors.NewInspector += myNewInspectorEventHandler;
           
           
        }

Thus, any event handler must be created as a field instance before being assigned to any Outlook interop object. (My previous post shows an example fro one handler you have to do this construct for all event handlers that are used on your Add-in project)
Avatar of spanout

ASKER

sorry, I seem to be missing the point... I have the two fields set up in my code

Outlook.ItemEvents_10_AttachmentAddEventHandler myAttachHandler;
Outlook.ItemEvents_10_AttachmentRemoveEventHandler myDetachHandler;


The send function works fine, it's the attachment function that doesn't
You need 4 fields:
Outlook.ApplicationEvents_11_ItemSendEventHandler mytemSendEventHandler;
Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler myNewInspectorEventHandler;
Outlook.ItemEvents_10_AttachmentAddEventHandler myAttachHandler;
Outlook.ItemEvents_10_AttachmentRemoveEventHandler myDetachHandler;

Again any event handler must be an instance.
Note: Are you creating the same handler for all inspectors (myAttachHandler) if that is the case then you need to create the instance at ThisAddIn_Startup, otherwise you are losing the reference to them every time you assign them at _Inspectors_NewInspector. Thus all event instances must be created only once at ThisAddIn_Startup
Avatar of spanout

ASKER

Hi mas_oz2003

I'm pretty sure that I have now understood what you have been trying to tell me (sorry for being slow :) )

Please take a look and see if it looks correct to you because the events are still not firing for gp2_fnAttachFile and gp2_fnDetachFile. As before the gp2_fnItemSend fires each time. Is it because the _Inspectors_NewInspector is creating the new handlers which gets GC?

Thanks again for your help.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Windows.Forms;
using System.Collections;

namespace OutlookAddIn1
{

    
    public partial class ThisAddIn
    {




        Outlook.Inspectors _Inspectors;
        Outlook.Explorers _Explorers;
        Outlook.MailItem mailItem;

        Outlook.ItemEvents_10_AttachmentAddEventHandler myAttachHandler;
        Outlook.ItemEvents_10_AttachmentRemoveEventHandler myDetachHandler;
        Outlook.ApplicationEvents_11_ItemSendEventHandler myHandler;
        Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler myNewInspectorEventHandler;
    



   
        public bool IsLicensed = true;
        public string Licensor = "";
        public string ExpireDate = "";
        public int attachcount = 0;
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {



           


            myHandler = new Outlook.ApplicationEvents_11_ItemSendEventHandler(gp2_fnItemSend);
            this.Application.ItemSend += myHandler;


            _Inspectors = this.Application.Inspectors;

            myNewInspectorEventHandler = new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(_Inspectors_NewInspector);

            _Inspectors.NewInspector += myNewInspectorEventHandler;



        }


        #region event handlers


        void gp2_fnAttachFile(Outlook.Attachment myAttach)
        {
            attachcount++;
        }

        void gp2_fnDetachFile(Outlook.Attachment myAttach)
        {
            attachcount--;
        }

        void gp2_fnItemSend(object item, ref bool cancel)
        {

            // do something
        }


      
        void _Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
        {
            
            
            myAttachHandler = new Outlook.ItemEvents_10_AttachmentAddEventHandler(gp2_fnAttachFile);
            (Inspector as Outlook.MailItem).AttachmentAdd += myAttachHandler;

            myDetachHandler = new Outlook.ItemEvents_10_AttachmentRemoveEventHandler(gp2_fnDetachFile);
            (Inspector as Outlook.MailItem).AttachmentRemove += myDetachHandler;




        }

        #endregion

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
            
            _Inspectors = null;
            _Explorers = null;
        }

        #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(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion

    }
}

Open in new window

Almost there, you only missed initializing all handlers in the ThisAddIn_Startup method.
All handlers are refering to unmanaged code, thus the need to keep them alive. YOu are not slow just  do not have the experience in this area, M$ uses a different code model for Office interaction.
public partial class ThisAddIn
    {
        Outlook.Inspectors _Inspectors;
        Outlook.Explorers _Explorers;
        Outlook.MailItem mailItem;

        Outlook.ItemEvents_10_AttachmentAddEventHandler myAttachHandler;
        Outlook.ItemEvents_10_AttachmentRemoveEventHandler myDetachHandler;
        Outlook.ApplicationEvents_11_ItemSendEventHandler myHandler;
        Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler myNewInspectorEventHandler;

        public bool IsLicensed = true;
        public string Licensor = "";
        public string ExpireDate = "";
        public int attachcount = 0;
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            myHandler = new Outlook.ApplicationEvents_11_ItemSendEventHandler(gp2_fnItemSend);
            myAttachHandler = new Outlook.ItemEvents_10_AttachmentAddEventHandler(gp2_fnAttachFile);
            myDetachHandler = new Outlook.ItemEvents_10_AttachmentRemoveEventHandler(gp2_fnDetachFile);

            this.Application.ItemSend += myHandler;


            _Inspectors = this.Application.Inspectors;

            myNewInspectorEventHandler = new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(_Inspectors_NewInspector);

            _Inspectors.NewInspector += myNewInspectorEventHandler;
        }


        #region event handlers


        void gp2_fnAttachFile(Outlook.Attachment myAttach)
        {
            attachcount++;
        }

        void gp2_fnDetachFile(Outlook.Attachment myAttach)
        {
            attachcount--;
        }

        void gp2_fnItemSend(object item, ref bool cancel)
        {

            // do something
        }



        void _Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
        {
            (Inspector as Outlook.MailItem).AttachmentAdd += myAttachHandler;
            (Inspector as Outlook.MailItem).AttachmentRemove += myDetachHandler;
        }

        #endregion

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

            _Inspectors = null;
            _Explorers = null;
        }

        #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(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion

    }

Open in new window

Avatar of spanout

ASKER

Hi mas_oz2003

I moved the initialization into ThisAddIn_Startup method as per your code, but the events still are not firing. Should the AttachmentAdd event fire when I select an attachment to add to the email?
When you say it is not firing, does it mean you put a breakpoint inside this method (lines 38 and 43)?
you are right, we need to check that _Inspectors_NewInspector is firing, but we need an extra field and some code modifications as shown below:
       Outlook.MailItem myInspector;
        void _Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
        {
           Debug.WriteLine("_Inspectors_NewInspector started"); //this will show in your output screen in VS
           if (myInspector != null) return;
           if (!(Inspector is Outlook.MailItem)) return;
           Debug.WriteLine("_Inspectors_NewInspector attaching the mail item");          
           myInspector = Inspector;
           myInspector.AttachmentAdd += myAttachHandler;
           myInspector.AttachmentRemove += myDetachHandler;
        }

Note: Notice that code above works for a single inspector (Key idea : You need to keep a reference to your new inspector as well). If you need more that one inspector open at the time replace:
 Outlook.MailItem myInspector;
 with a list  or dictionary:
List<Outlook.MailItem> myInspectors;
and modify the method accordingly. (e.g. myInspectors.Add(myInspector))

Avatar of spanout

ASKER

Hi

I added the debug code and the only output that came out when opening a new mail item was:
_Inspectors_NewInspector started

It did not show the text _Inspectors_NewInspector attaching the mail item. It is returning on the test: if (!(Inspector is Outlook.MailItem))..

This is really strange ?

Thanks

Strange indeed. The idea is that we only use inspector that is a Mail Item, we need to determine Inspector type.
Pleae put a break point  in that line and post what you see in Quick Watch when evaluating:
Inspector
Inspector as Outlook.MailItem
Avatar of spanout

ASKER

Hi

Inspector as Outlook.MailItem comes back as null. I put a watch on Inspector and show some of the data in the attached. User generated image
Ok, keep in mind that Inspector will be a Mail item only when you open mail from places like your inbox. Your original code states that only Mial items are supposed to have the new events.
In quick watch please type:
Inspector.GetType()
Check: http://www.dotnetperls.com/gettype
and post result.
Avatar of spanout

ASKER

GetType() returns -            
Inspector.GetType()      
{Name = "InspectorClass" FullName = "Microsoft.Office.Interop.Outlook.InspectorClass"}

This was when opening an email from inbox
ASKER CERTIFIED SOLUTION
Avatar of Miguel Oz
Miguel Oz
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of spanout

ASKER

mas_oz2003

Thanks for your advice. I implemented the wrapper in the link that you provided and added my own handlers in the MailItemWrapper class:

Item.AttachmentAdd += new Outlook.ItemEvents_10_AttachmentAddEventHandler(Item_Attach);
            Item.AttachmentRemove += new Outlook.ItemEvents_10_AttachmentRemoveEventHandler(Item_Detach);
            //Item.Send += new Outlook.ItemEvents_10_AttachmentRemoveEventHandler(Item_Detach);
            Item.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(gp2_fnItemSend);

I then added a dictionary item in ThisAddIn class to strore the attachment count for each open mailitem inspector. This works well.
        public Dictionary<Guid, int> AttachmentCount;
Thanks for your perseverance.