我需要创建一个RichTextBox子类,除了没有订阅UserPreferenceChanged之外,它在所有方面都是相同的。此事件导致我的应用挂起。我必须使用RichTextBox,并且无法将其交换为具有MultiLine = True的TextBox或其他类似的东西。
这是System.Windows.Forms.RichTextBox订阅的地方;
protected override void OnHandleCreated(EventArgs e)
{
...
SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.UserPreferenceChangedHandler);
}
这是处理程序的签名;
private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e)
处理程序不是虚拟的,所以我无法覆盖它。处理程序是私有的,所以我不能做一个简单的 - = 来取消订阅。我已经研究过使用反射去除处理程序,但是我无法让它工作 - 这是我到目前为止所拥有的;
public partial class MyRichTextBox : RichTextBox
{
...
private void UnsubscribeUserPreferenceChanged()
{
FieldInfo fieldInfo = typeof(SystemEvents).GetField("OnUserPreferenceChangedEvent", BindingFlags.NonPublic | BindingFlags.Static);
// fieldInfo.ToString() = "System.Object.OnUserPreferenceChangedEvent"
object eventObj = fieldInfo.GetValue(this);
// eventInfo.ToString() = "System.Object"
PropertyInfo propInfo = typeof(RichTextBox).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
// propInfo.ToString() = "System.ComponentModel.EventHandlerList Events"
EventHandlerList list = (EventHandlerList)propInfo.GetValue(this, null);
// list.ToString() = "System.ComponentModel.EventHandlerList"
...
现在我可以打电话了;
list.RemoveHandler(eventObj, list[eventObj]);
并且没有例外,但我相信它会默默地失败,因为如果我试图以这样的方式访问该委托;
list[eventObj].ToString()
我得到一个NullReferenceException,因为EventHandlerList中没有这样的对象键。我在MyTextBox变得可见之后调用UnsubscribeUserPreferenceChanged(),因此处理程序应该在列表中,因为它是在OnHandleCreated中为RichTextBox添加的。
任何人都有关于如何取消订阅连接到超类中的私有事件处理程序的SystemEvent的任何指示?
答案 0 :(得分:3)
一旦你知道方法名称,由于以下方便的CreateDelegate
重载,这很容易:
==7042== Invalid read of size 1
==7042== at 0x4045F71: ??? (in /tmp/.glamhkos (deleted))
==7042== by 0xCFFFFFF: ???
==7042== by 0xD68A04F: ???
==7042== by 0xFFEFFFE3F: ???
==7042== Address 0xd000002 is 0 bytes after a block of size 2 alloc'd
==7042== at 0x4C2B1B6: memalign (vg_replace_malloc.c:760)
...
==7042== by 0x4016E1: main (in /.../)
==7042== Invalid read of size 1
==7042== at 0x4045F71: ??? (in /tmp/.glamhkos (deleted))
==7042== by 0xD0017FF: ???
==7042== by 0xD68C04F: ???
==7042== by 0xFFEFFFE3F: ???
==7042== by 0x1: ???
==7042== by 0x7B23325: ??? (in /usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.352.79)
==7042== by 0x7B2D9A6: ??? (in /usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.352.79)
==7042== by 0x7C7DD40: ??? (in /usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.352.79)
==7042== by 0x7C51383: ??? (in /usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.352.79)
==7042== by 0x7C52A16: ??? (in /usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.352.79)
==7042== by 0x7C6F553: ??? (in /usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.352.79)
...
==7042== Address 0xd001802 is 6,146 bytes inside a block of size 6,852,752 in arena "client"
所以有问题的代码可能是这样的:
public static Delegate CreateDelegate(
Type type,
object target,
string method
)
答案 1 :(得分:1)
您应该使用Type.GetEvent
代替Type.GetField
。
只有这样你才能在运行时删除它:
private void UnsubscribeuserPreferenceChanged()
{
MethodInfo handler = typeof(RichTextBox).GetMethod("UserPreferenceChangedHandler", BindingFlags.Instance | BindingFlags.NonPublic);
EventInfo evt = typeof(SystemEvents).GetEvent("UserPreferenceChanged", BindingFlags.Static | BindingFlags.Public);
MethodInfo remove = evt.GetRemoveMethod(true);
remove.Invoke(null, new object[]
{
Delegate.CreateDelegate(evt.EventHandlerType, null, handler)
});
}
答案 2 :(得分:-1)
我找到了一种方法来解决此问题,方法是遍历所有订阅系统事件的对象,然后在执行期间取消订阅。这是上述方法和我发现的另一个代码的结合,该代码捕获了我应用程序中所有对象注册的所有系统事件
public static void UnsubscribeSystemEvents()
{
try
{
var handlers = typeof(SystemEvents).GetField("_handlers", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
var handlersValues = handlers.GetType().GetProperty("Values").GetValue(handlers);
foreach (var invokeInfos in (handlersValues as IEnumerable).OfType<object>().ToArray())
foreach (var invokeInfo in (invokeInfos as IEnumerable).OfType<object>().ToArray())
{
var syncContext = invokeInfo.GetType().GetField("_syncContext", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo);
if (syncContext == null)
throw new Exception("syncContext missing");
if (!(syncContext is WindowsFormsSynchronizationContext))
continue;
var threadRef = (WeakReference)syncContext.GetType().GetField("destinationThreadRef", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(syncContext);
if (!threadRef.IsAlive)
continue;
var thread = (System.Threading.Thread)threadRef.Target;
if (thread.ManagedThreadId == 1)
continue; // Change here if you have more valid UI threads to ignore
var dlg = (Delegate)invokeInfo.GetType().GetField("_delegate", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(invokeInfo);
var handler = (UserPreferenceChangedEventHandler)Delegate.CreateDelegate(typeof(UserPreferenceChangedEventHandler), dlg.Target, dlg.Method.Name);
SystemEvents.UserPreferenceChanged -= handler;
}
}
catch ()
{
//trace here your errors
}
}