取消 Hook 后 C# 仍然 Hook 到事件

标签 c# events memory-leaks hook

我目前正在调试一个包含内存泄漏的大(非常大!)C# 应用程序。它主要使用 Winforms 作为 GUI,尽管一些控件是在 WPF 中制作的,并由 ElementHost 托管。直到现在,我发现许多内存泄漏是由于事件没有被解除 Hook (通过调用 -=)引起的,我已经能够解决这个问题。

但是,我刚刚遇到了类似的问题。有一个名为 WorkItem(短期)的类,它在构造函数中注册到另一个名为 ClientEntityCache(长期)的类的事件。这些事件永远不会解除 Hook ,我可以在 .NET 探查器中看到 WorkItem 的实例在它们不应该因为这些事件而保持事件状态时保持事件状态。因此,我决定让 WorkItem 实现 IDisposable,并在 Dispose() 函数中以这种方式解除事件:

public void Dispose()
{
  ClientEntityCache.EntityCacheCleared -= ClientEntityCache_CacheCleared;
  // Same thing for 10 other events
}

编辑

这是我用于订阅的代码:

public WorkItem()
{
  ClientEntityCache.EntityCacheCleared += ClientEntityCache_CacheCleared;
  // Same thing for 10 other events
}

我还更改了注销代码以不调用新的 EntityCacheClearedEventHandler。

编辑结束

我在使用 WorkItem 的代码中的适当位置调用了 Dispose,当我调试时,我可以看到该函数确实被调用,并且我为每个事件执行 -=。但我仍然遇到内存泄漏,我的 WorkItems 在被处置后仍然保持事件状态,在 .NET 探查器中我可以看到实例保持事件状态,因为事件处理程序(如 EntityCacheClearedEventHandler)仍然将它们放在它们的调用列表中。我试图不止一次(多次-=)解开它们,只是为了确保它们没有被钩住不止一次,但这无济于事。

任何人都知道为什么会发生这种情况,或者我可以做些什么来解决这个问题? 我想我可以更改事件处理程序以使用弱委托(delegate),但这需要处理大量遗留代码。

谢谢!

编辑:

如果这有帮助,这里是 .NET 探查器描述的根路径: 很多东西指向 ClientEntityCache,它指向 EntityCacheClearedEventHandler,它指向 Object[],它指向 EntityCacheClearedEventHandler 的另一个实例(我不明白为什么),它指向 WorkItem。

最佳答案

可能是多个不同的委托(delegate)函数连接到事件。希望下面的小例子能让我更清楚地理解我的意思。

// Simple class to host the Event
class Test
{
  public event EventHandler MyEvent;
}

// Two different methods which will be wired to the Event
static void MyEventHandler1(object sender, EventArgs e)
{
  throw new NotImplementedException();
}

static void MyEventHandler2(object sender, EventArgs e)
{
  throw new NotImplementedException();
}


[STAThread]
static void Main(string[] args)
{
  Test t = new Test();
  t.MyEvent += new EventHandler(MyEventHandler1);
  t.MyEvent += new EventHandler(MyEventHandler2); 

  // Break here before removing the event handler and inspect t.MyEvent

  t.MyEvent -= new EventHandler(MyEventHandler1);      
  t.MyEvent -= new EventHandler(MyEventHandler1);  // Note this is again MyEventHandler1    
}

如果您在删除事件处理程序之前中断,您可以在调试器中查看调用列表。见下文,有 2 个处理程序,一个用于 MyEventHandler1,另一个用于方法 MyEventHandler2。

enter image description here

现在两次删除 MyEventHandler1 后,MyEventHandler2 仍然被注册,因为只剩下一个委托(delegate)看起来有点不同,它不再显示在列表中,但在删除 MyEventHandler2 的委托(delegate)之前它仍然会被引用由事件。

enter image description here

关于取消 Hook 后 C# 仍然 Hook 到事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6141058/

相关文章:

javascript - 这段nodejs代码是循环引用吗?

c# - 为什么 CPU 使用率不增加?

c# - GeckoFX - 实现搜索功能 - 如何使用 nsIWebBrowserFind 接口(interface)

c# - 在 Web 应用程序中使用条码渲染框架生成条码

c# - WPF DataGridRow ContextMenu MenuItem 单击事件未触发

C# - 事件设计注意事项

java 内存泄漏,visualvm 显示错误数据

c# - 将 SortedList 或 Dictionary<int, string> 添加到 ResourceDictionary

java - 绑定(bind)数据触发更改后 Swing/JTable 不更新

iphone - 使用 NSOperationQueue 并尝试更改 slider /选择器等时,iPhone 设备上遇到大量泄漏