winforms 我可以使用反射取消订阅SystemEvent吗?

ktecyv1j  于 2023-03-09  发布在  其他
关注(0)|答案(4)|浏览(127)

我需要创建一个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(),因此处理程序应该在列表中,因为它是为RichTextBox添加到OnHandleCreated中的。
有人知道如何取消订阅挂接到超类中的私有事件处理程序的SystemEvent吗?

lb3vh1jj

lb3vh1jj1#

一旦知道了方法名,就非常容易了,因为有下面的CreateDelegate重载:

public static Delegate CreateDelegate(
    Type type,
    object target,
    string method
)

所以这个代码可能是这样的:

private void UnsubscribeUserPreferenceChanged()
{
    var handler = (UserPreferenceChangedEventHandler)Delegate.CreateDelegate(
        typeof(UserPreferenceChangedEventHandler), this, "UserPreferenceChangedHandler");
    SystemEvents.UserPreferenceChanged -= handler;
}
af7jpaap

af7jpaap2#

您应该使用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)
    });
}
db2dz4w8

db2dz4w83#

对于常规控件,而不是RichtTextBox,我也遇到了同样的问题。Ivan Stoev的代码变成了这样:

private void UnsubscribeUserPreferenceChanged(System.Windows.Forms.Control control) 
{
  var handler = (UserPreferenceChangedEventHandler)Delegate.CreateDelegate(
            typeof(UserPreferenceChangedEventHandler), control, "UserPreferenceChanged");
  SystemEvents.UserPreferenceChanged -= handler;
}

它从SystemEvents取消订阅Control.UserPreferenceChanged(私有)方法。

g6baxovj

g6baxovj4#

我找到了一种修复方法,通过迭代订阅systemevents的所有对象,然后在执行时取消订阅。它是上述方法和另一个代码的组合,我发现该代码捕获应用中所有对象注册的所有系统事件

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
        }
    }

相关问题