none
VS2010: Catching BuildEvents using an Add-In

    Question

  • Hi!

    Currently, I'm building an Add-In to hook into VS2010, catch build events and do something with them. Currently, I have all the boiler plate code regarding hooking the events working, and I have the add-in set to load when the IDE starts up. My problem is this: hooking into the build events doesn't work when the IDE starts up initially, but if I unload and reload the add-in it starts working. Does anyone have any ideas why this might be the case? Here's my add-in code:

     

    public class Connect : IDTExtensibility2
    {
      private DTE2 _applicationObject;
      private AddIn _addInInstance;
      private _dispBuildEvents_OnBuildBeginEventHandler onBuildBeginHandler = null;
      private _dispBuildEvents_OnBuildProjConfigBeginEventHandler onBuildProjConfigBeginHandler = null;
    
      public Connect()
      {
        this.onBuildBeginHandler = new _dispBuildEvents_OnBuildBeginEventHandler(this.OnBuildBegin);
        this.onBuildProjConfigBeginHandler = new _dispBuildEvents_OnBuildProjConfigBeginEventHandler(this.OnBuildProjConfigBegin);
      }
    
      public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
      {
    	_applicationObject = (DTE2)application;
    	_addInInstance = (AddIn)addInInst;
    
        _applicationObject.Events.BuildEvents.OnBuildBegin += onBuildBeginHandler;
        _applicationObject.Events.BuildEvents.OnBuildProjConfigBegin += onBuildProjConfigBeginHandler;
      }
    
      void OnBuildProjConfigBegin(string Project, string ProjectConfig, string Platform, string SolutionConfig)
      {   System.Windows.Forms.MessageBox.Show("OnBuildProjConfigBegin");
      }
    
      void OnBuildBegin(vsBuildScope Scope, vsBuildAction Action)
      {
       System.Windows.Forms.MessageBox.Show("OnBuildBegin");
      }
    
      public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
      {
        // cleanup
        try
        {
         _applicationObject.Events.BuildEvents.OnBuildBegin -= onBuildBeginHandler;
         _applicationObject.Events.BuildEvents.OnBuildProjConfigBegin -= onBuildProjConfigBeginHandler;
       }
       catch (SystemException)
       {
        // eat it
       }
      }
    }

     

    Wednesday, September 15, 2010 10:04 PM

Answers

  • I believe the problem here is how the CLR handles COM endpoints (event sinks).  If I recall correctly when you hit the _applicationObject.Events.BuildEvents part of your 'chain' the CLR will create a NEW BuildEvents object for the property access and WON'T cache it, therefor it comes back to you, you sign up an event handler to it (which creates a strong ref between the TEMPORARY object and your object due to the delegate, but NOT from your object to the temporary object, which would prevent the GC).  Then you don't store that object anywhere so it is immediately GC eligible and will eventually be GC'ed.

     

    Ryan

    • Marked as answer by Victor_Chen Monday, October 11, 2010 1:28 AM
    Friday, October 08, 2010 2:41 PM
  • Only suggestion I have is to scope the BuildEvents object to your class. Make a data member of type BuildEvents in your OnConnect, and store it there, so the GC doesn't eat your event handler. Just a wild guess, as I would have expected your OnBuildEvent handler to eventually get GC's even in your working scenario. So I'm not entirely sure this would be the isssue. But it's worth a try.

    Sincerely,


    Ed Dore
    • Marked as answer by Victor_Chen Friday, September 24, 2010 5:12 AM
    Friday, September 17, 2010 2:19 AM

All replies

  • Only suggestion I have is to scope the BuildEvents object to your class. Make a data member of type BuildEvents in your OnConnect, and store it there, so the GC doesn't eat your event handler. Just a wild guess, as I would have expected your OnBuildEvent handler to eventually get GC's even in your working scenario. So I'm not entirely sure this would be the isssue. But it's worth a try.

    Sincerely,


    Ed Dore
    • Marked as answer by Victor_Chen Friday, September 24, 2010 5:12 AM
    Friday, September 17, 2010 2:19 AM
  • Hi Jduv,

     

    Thanks for your post.

    Just as Ed said, I suggest to add a member for BuildEvents, and the code below works fine on my side.

    private BuildEvents buildEvents;

     

    public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)

    {

    _applicationObject = (DTE2)application;

    _addInInstance = (AddIn)addInInst;

    buildEvents = _applicationObject.Events.BuildEvents;

    buildEvents.OnBuildBegin += new _dispBuildEvents_OnBuildBeginEventHandler(BuildEvents_OnBuildBegin);

    }

     

    private void OnBuildBegin(vsBuildScope Scope, vsBuildAction Action)

    {

    System.Windows.Forms.MessageBox.Show("OnBuildBegin");

    }

    Hope this can help.

     

    Best Regards,

    Ziwei Chen

    Monday, September 20, 2010 8:48 AM
  • Thanks guys.  Can either of you help me understand why I'm being garbage collected? I'm just curious. One would think that during environment startup the add-in would be loaded and the buildevents object properly scoped via the DTE reference. Unless of course I'm thinking about this wrong.

    Thanks for the help, you guys are brilliant.

    -jduv

    Tuesday, September 21, 2010 1:18 AM
  • Hi Jduv,

     

    Thanks for your post.

    I suppose it's because that if you set the event handler directly to applicationObject, the handler will just be maintained in that method scope.

    It's just my personal idea and I'm sorry I'm not familiar with the GC part.

    If you don't mind, I will close this thread, and if you have further questions, welcome to post a new thread at any time.

    Good luck.

     

    Best Regards,

    Ziwei Chen

     

    Friday, September 24, 2010 5:12 AM
  • I believe the problem here is how the CLR handles COM endpoints (event sinks).  If I recall correctly when you hit the _applicationObject.Events.BuildEvents part of your 'chain' the CLR will create a NEW BuildEvents object for the property access and WON'T cache it, therefor it comes back to you, you sign up an event handler to it (which creates a strong ref between the TEMPORARY object and your object due to the delegate, but NOT from your object to the temporary object, which would prevent the GC).  Then you don't store that object anywhere so it is immediately GC eligible and will eventually be GC'ed.

     

    Ryan

    • Marked as answer by Victor_Chen Monday, October 11, 2010 1:28 AM
    Friday, October 08, 2010 2:41 PM