我看到ThreadException是System.Windows.Forms.Application类上的一个公共静态事件。
通常,如果我使用反射从某个对象中删除事件处理程序(例如,清除匿名处理程序),我将执行以下操作(精简一点):
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公共静态事件。
“应用程序”类型上有一个名为“ 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”只是使用错误的字段,但是我看不到其他任何看起来像是好的候选对象。关于如何执行此操作的任何想法?
答案 0 :(得分:1)
事件旨在包装委托并在调用者中隐藏调用列表。因此有一个困难的原因。
此代码是一种解决方法。我不建议这样做,因为它可能很容易被更新版本的CLR破坏。但是,如果这对您很重要,它现在似乎可以正常工作。
首先,看一下source code for Application。您会发现线程异常事件有一个custom event accessor,该事件将事件处理程序存储在另一个名为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) => {};
但是,这将抑制默认的处理程序行为,即显示一个对话框。