C# - 清除 System.Windows.Forms.Application.ThreadException 的所有事件处理程序
C# - Clearing All Event Handlers for System.Windows.Forms.Application.ThreadException
我看到 ThreadException 是 System.Windows.Forms.Application class 上的 public 静态事件。
通常情况下,如果我使用反射从某个对象中删除事件处理程序(例如清除匿名处理程序),我会这样做(稍微精简一点):
EventInfo ei = obj.GetType().GetEvent("FooEvent");
FieldInfo fi = obj.GetType().GetField("FooEvent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
Delegate del = (Delegate)fi.GetValue(obj)
foreach(Delegate d in del.GetInvocationList())
{
ei.RemoveEventHandler(obj, d);
}
但这种方法似乎不适用于 System.Windows.Forms.Application.ThreadException public 静态事件。
在 Application 类型上有一个名为 "eventHandlers" 的字段,但当我从该 FieldInfo 对象调用 GetValue(null) 时它似乎没有任何值,而且我确定有一个处理程序附上,因为我在调用测试之前立即添加了一个:
System.Windows.Forms.Application.ThreadException += ...my test handler...;
Type t = typeof(System.Windows.Forms.Application);
EventInfo ei = t.GetEvent("ThreadException");
FieldInfo fi = t.GetField("eventHandlers", BindingFlags.NonPublic | BindingFlags.Static);
object test = fi.GetValue(null); // Results in null
...所以我认为 "eventHandlers" 只是使用错误的字段,但我没有看到任何其他看起来不错的候选字段。关于我如何着手做这件事有什么想法吗?
如何完成您的要求
事件旨在包装委托并向调用者隐藏调用列表。所以这很难是有原因的。
此代码是一种解决方法。我不推荐它——它很容易与更新版本的 CLR 中断。但它现在似乎有效,如果这对你很重要。
首先,看一下 source code for Application. You'll find that the thread exceptions event has a custom event accessor,它将事件处理程序存储在另一个嵌套的私有 class 中,称为 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;
}
现在,如果您再次查看代码,您可能会注意到处理程序实际上是 分配的 ,而不是添加的。换句话说:只能有一个handler。没错!!!即使您使用 +=
设置它,它也会使用 =
存储在 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) => {};
然而,这将抑制默认处理程序行为,即显示对话框。
我看到 ThreadException 是 System.Windows.Forms.Application class 上的 public 静态事件。
通常情况下,如果我使用反射从某个对象中删除事件处理程序(例如清除匿名处理程序),我会这样做(稍微精简一点):
EventInfo ei = obj.GetType().GetEvent("FooEvent");
FieldInfo fi = obj.GetType().GetField("FooEvent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
Delegate del = (Delegate)fi.GetValue(obj)
foreach(Delegate d in del.GetInvocationList())
{
ei.RemoveEventHandler(obj, d);
}
但这种方法似乎不适用于 System.Windows.Forms.Application.ThreadException public 静态事件。
在 Application 类型上有一个名为 "eventHandlers" 的字段,但当我从该 FieldInfo 对象调用 GetValue(null) 时它似乎没有任何值,而且我确定有一个处理程序附上,因为我在调用测试之前立即添加了一个:
System.Windows.Forms.Application.ThreadException += ...my test handler...;
Type t = typeof(System.Windows.Forms.Application);
EventInfo ei = t.GetEvent("ThreadException");
FieldInfo fi = t.GetField("eventHandlers", BindingFlags.NonPublic | BindingFlags.Static);
object test = fi.GetValue(null); // Results in null
...所以我认为 "eventHandlers" 只是使用错误的字段,但我没有看到任何其他看起来不错的候选字段。关于我如何着手做这件事有什么想法吗?
如何完成您的要求
事件旨在包装委托并向调用者隐藏调用列表。所以这很难是有原因的。
此代码是一种解决方法。我不推荐它——它很容易与更新版本的 CLR 中断。但它现在似乎有效,如果这对你很重要。
首先,看一下 source code for Application. You'll find that the thread exceptions event has a custom event accessor,它将事件处理程序存储在另一个嵌套的私有 class 中,称为 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;
}
现在,如果您再次查看代码,您可能会注意到处理程序实际上是 分配的 ,而不是添加的。换句话说:只能有一个handler。没错!!!即使您使用 +=
设置它,它也会使用 =
存储在 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) => {};
然而,这将抑制默认处理程序行为,即显示对话框。