Подписка на события DTE не работает - события не вызываются
Я сделал расширение внутри пакета, и я вызываю следующий код (происходит, когда пользователь нажимает кнопку на панели инструментов):
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)
{
}
Первая и главная проблема заключается в том, что подписка на событие не работает. Я пробовал:
- Открытие новых документов
- Отключение от отладки (таким образом, предположительно запуск OnEnterDesignMode
- Сохранение документа
Ни один из них не имеет никакого эффекта, и функции обратного вызова никогда не вызывались.
Вторая проблема заключается в том, что подписка на строку событий работает УСПОЛЬЗУЕТСЯ (сама подписка, обратный вызов не работает, как описано выше), но через некоторое время работает строка подписки, например:
_dte.Events.DebuggerEvents.OnEnterBreakMode -= DebuggerEvents_OnEnterBreakMode;
Вызывает исключение:
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)
Любые идеи будут приветствоваться
Спасибо!
Виталий
Ответы
Ответ 1
Проводя ответ, который я получил от форумов MSDN, Райан Молден, если он кому-то поможет:
Я считаю, что проблема здесь в том, как CLR обрабатывает конечные точки COM (событие раковины). Если я правильно помню, когда вы попадаете в _applicationObject.Events.DebuggerEvents часть вашей "цепи", CLR будет создать новый объект DebuggerEvents для доступ к ресурсу и НЕ Кэш он, поэтому он возвращается к вам, вы зарегистрируйте обработчик события (который создает сильный реф между ТЕМПЕРАТНЫЙ объект и ваш объект делегату, но НЕ от вашего объект к временному объекту, который будет препятствовать GC). Тогда вы не Храните этот объект в любом месте, чтобы он немедленно GC имеет право и будет в конечном итоге будет GC'ed.
Я изменил код для хранения DebuggerEvents в качестве поля, и все это начало работать нормально.
Ответ 2
Вот что @VitalyB означает использование кода:
// 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...
}