首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我可以使用反射取消订阅SystemEvent吗?

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

Stack Overflow用户
提问于 2016-10-11 14:58:31
回答 3查看 721关注 0票数 2

我需要创建一个RichTextBox子类,它在所有方面都是一样的,除了它不订阅UserPreferenceChanged。这个事件导致我的应用程序挂起。我必须使用RichTextBox,不能将它与MultiLine=True或其他类似的东西交换为TextBox。

这是System.Windows.Forms.RichTextBox订阅的地方;

代码语言:javascript
复制
protected override void OnHandleCreated(EventArgs e)
{
    ...
    SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.UserPreferenceChangedHandler);
}

这是处理程序的签名;

代码语言:javascript
复制
private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e)

处理程序不是虚拟的,所以我不能重写它。处理程序是私有的,所以我不能做一个简单的-=来取消订阅。我已经研究过使用反射来移除处理程序,但是我无法让它工作--这就是我到目前为止所拥有的;

代码语言:javascript
复制
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"
    ...

现在我可以打电话给你了;

代码语言:javascript
复制
list.RemoveHandler(eventObj, list[eventObj]);

当然也没有例外,但我认为这是在默默地失败,因为如果我试图以同样的方式访问该代表;

代码语言:javascript
复制
list[eventObj].ToString()

我得到一个NullReferenceException,因为EventHandlerList中没有这样的对象键。在UnsubscribeUserPreferenceChanged()变得可见之后,我调用了MyTextBox (),因此处理程序应该在列表中,因为它是在OnHandleCreated中为RichTextBox添加的。

有人知道如何取消订阅链接到超类中的私有事件处理程序的SystemEvent吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-10-11 15:53:18

一旦您知道了方法名,由于以下方便的CreateDelegate重载,这是相当容易的:

代码语言:javascript
复制
public static Delegate CreateDelegate(
    Type type,
    object target,
    string method
)

因此,所讨论的代码可能如下:

代码语言:javascript
复制
private void UnsubscribeUserPreferenceChanged()
{
    var handler = (UserPreferenceChangedEventHandler)Delegate.CreateDelegate(
        typeof(UserPreferenceChangedEventHandler), this, "UserPreferenceChangedHandler");
    SystemEvents.UserPreferenceChanged -= handler;
}
票数 4
EN

Stack Overflow用户

发布于 2016-10-11 15:15:31

您应该使用Type.GetEvent而不是Type.GetField

只有这样,您才能在运行时删除它:

代码语言:javascript
复制
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)
    });
}
票数 0
EN

Stack Overflow用户

发布于 2020-07-29 18:42:13

我找到了一种解决这个问题的方法,方法是迭代所有订阅系统事件的对象,然后在执行期间取消订阅。这是上述接近程序和我发现的另一段代码的组合,它捕获了应用程序中所有对象注册的所有系统事件。

代码语言:javascript
复制
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
        }
    }
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39980299

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档