Subscription to DTE events doesn't seem to wor

2019-03-25 07:56发布

问题:

I've made an extension inside a package and I am calling the following code (occurs when a user presses a button in the toolbar):

DocumentEvents documentEvents = (DTE2)GetService(typeof(DTE));
_dte.Events.DebuggerEvents.OnEnterBreakMode += DebuggerEvents_OnEnterBreakMode;
_dte.Events.DebuggerEvents.OnEnterDesignMode += DebuggerEvents_OnEnterDesignMode;
_dte.Events.DebuggerEvents.OnContextChanged += DebuggerEvents_OnContextChanged;
_dte.Events.DocumentEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(DocumentEvents_DocumentSaved);
_dte.Events.DocumentEvents.DocumentOpened += new _dispDocumentEvents_DocumentOpenedEventHandler(DocumentEvents_DocumentOpened);
void DocumentEvents_DocumentOpened(Document Document)
{
}

void DocumentEvents_DocumentSaved(Document Document)
{
}

void DebuggerEvents_OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
}

void DebuggerEvents_OnContextChanged(Process NewProcess, Program NewProgram, Thread NewThread, StackFrame NewStackFrame)
{
}

private void DebuggerEvents_OnEnterDesignMode(dbgEventReason reason)
{
}

The first and the major problem is that the subscription to the event doesn't work. I've tried:

  • Opening new documents
  • Detaching from debug (thus supposedly triggering OnEnterDesignMode
  • Saving a document

None of these seem to have any effect and the callback functions were never called.

The second issue is that the subscription to the event line works USUALLY (the subscription itself, the callback doesn't work as described above) but after a while running the subscription line, e.g:

_dte.Events.DebuggerEvents.OnEnterBreakMode -= DebuggerEvents_OnEnterBreakMode;

Causes an exception:

Exception occured!
System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
   at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis, IntPtr pThread)
   at System.Runtime.InteropServices.UCOMIConnectionPoint.Unadvise(Int32 dwCookie)
   at EnvDTE._dispDebuggerEvents_EventProvider.remove_OnEnterDesignMode(_dispDebuggerEvents_OnEnterDesignModeEventHandler A_1)

Any ideas will be welcome

Thanks! Vitaly

回答1:

Posting an answer that I got from MSDN forums, by Ryan Molden, in case it helps anyone:

I believe the problem here is how the CLR handles COM endpoints (event sinks). If I recall correctly when you hit the _applicationObject.Events.DebuggerEvents part of your 'chain' the CLR will create a NEW DebuggerEvents 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.

I changed the code to store DebuggerEvents as a field and it all started to work fine.



回答2:

Here is what @VitalyB means using code:

// list where we will place events.
// make sure that this variable is on global scope so that GC does not delete the evvents
List<object> events = new List<object>();

public void AddEvents(EnvDTE dte)
{
    // create an event when a document is open
    var docEvent = dte.Events.DocumentEvents;

    // add event to list so that GC does not remove it
    events.Add(docEvent );

    docEvent.DocumentOpened += (document)=>{

        Console.Write("document was opened!");
    };

    // you may add more events:
    var commandEvent = dte.Events.CommandEvents;
    events.Add(commandEvent );

    commandEvent.AfterExecute+=  etc...

}