Как сделать то, что вы спрашиваете
События предназначены для переноса делегата и сокрытия списка вызовов от вызывающей стороны.Поэтому есть причина, по которой это сложно.
Этот код является обходным путем.Я не рекомендую это - это могло легко сломаться с более новой версией CLR.Но, похоже, это работает прямо сейчас, если это важно для вас.
Сначала взгляните на исходный код для приложения.Вы обнаружите, что событие исключений потока имеет пользовательский метод доступа к событию , который хранит обработчики событий в другом, вложенном, закрытом классе, называемом ThreadContext
.Вот фрагмент:
//From .NET reference source
public static event ThreadExceptionEventHandler ThreadException
{
add
{
Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AffectThreadBehavior Demanded");
IntSecurity.AffectThreadBehavior.Demand();
ThreadContext current = ThreadContext.FromCurrent();
lock(current)
{
current.threadExceptionHandler = value; //Here is where it gets stored
}
}
Чтобы сделать то, что вы просите, нам нужно получить этот экземпляр, который мы можем сделать с помощью этого хитрого кода:
static private Type ApplicationType => typeof(Application);
static private Type ThreadContextType => ApplicationType.GetNestedType("ThreadContext", BindingFlags.NonPublic);
static private object GetCurrentThreadContext()
{
var threadContextCurrentMethod = ThreadContextType.GetMethod("FromCurrent", BindingFlags.Static | BindingFlags.NonPublic);
var threadContext = threadContextCurrentMethod.Invoke(null, new object[] { });
return threadContext;
}
Теперьесли вы посмотрите на код еще раз, вы можете заметить, что обработчик на самом деле назначен , а не добавлен.Другими словами: может быть только один обработчик. Это верно !!!Даже если вы установите его с помощью +=
, он сохраняется в ThreadContext с использованием =
..., поэтому он заменит любой предыдущий обработчик.
Итак, если вы хотите увидеть, на какой метод ссылаются, выможно извлечь его с помощью
static private ThreadExceptionEventHandler GetCurrentThreadExceptionEventHandler()
{
var threadContext = GetCurrentThreadContext();
var threadExceptionHandler = ThreadContextType.GetField("threadExceptionHandler", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(threadContext) as System.Threading.ThreadExceptionEventHandler;
return threadExceptionHandler;
}
И затем мы можем удалить его с помощью
Application.ThreadException -= GetCurrentThreadExceptionEventHandler();
Полное решение выглядит так:
static private Type ApplicationType => typeof(Application);
static private Type ThreadContextType => ApplicationType.GetNestedType("ThreadContext", BindingFlags.NonPublic);
static private object GetCurrentThreadContext()
{
var threadContextCurrentMethod = ThreadContextType.GetMethod("FromCurrent", BindingFlags.Static | BindingFlags.NonPublic);
var threadContext = threadContextCurrentMethod.Invoke(null, new object[] { });
return threadContext;
}
static private void RemoveThreadExceptionEventHandler()
{
var threadContext = GetCurrentThreadContext();
var threadExceptionHandler = ThreadContextType.GetField("threadExceptionHandler", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(threadContext) as System.Threading.ThreadExceptionEventHandler;
Application.ThreadException -= threadExceptionHandler;
}
Что-то еще, что может работатьточно так же
Если вы просто не хотите, чтобы какой-либо обработчик запускался, вы можете просто заменить его (поскольку он только один) пустым методом:
Application.ThreadException += (s,e) => {};
Однако это подавит поведение обработчика по умолчанию, то есть отображение диалога.