2

我正在使用一个NativeWindow对象对非托管窗口的消息泵进行子类化,目的是拦截其消息。

代码结构看起来像这样(它的伪 C#,请原谅小的语法问题):

class AppSubclass : Control {

    class SpecialAppWndProc : NativeWindow {

        protected override void WndProc(ref Message m) {

            switch (m.msg) {

                // do stuff
                if (SpecialEvent != null) SpecialEvent(x);
            }

            base.WndProc(ref m);

        }

        public delegate void SpecialEventHandler(int Xstart);
        public event SpecialEventHandler SpecialEvent;

        ~SpecialAppWndProc() {

            DebugTrace("Help! Save me!");

        }

    }

    private SpecialAppWndProc specialAppWndProc = new SpecialAppWndProc();

    private void StartMonitoring() {

        // do stuff


        specialAppWndProc.AssignHandle(hWndUnmanagedWindow);
        specialAppWndProc.SpecialEvent += new SpecialAppWndProc.SpecialEventHandler(specialAppWndProc_SpecialEvent);

    }

    /* ... event handler ... */

    public AppSubClass() {

        StartMonitoring();

    }

}

现在,我认为如果我的对象因 GC 而死,设置一个事件侦听器就足以让垃圾收集器处于停顿状态。如果不是,是否有可能追踪如何以及为什么?我从来不知道.Net会因为代码错误而杀死对象(例外和偶尔的静默失败似乎是事情的一般要点),我不知道主机应用程序如何或为什么(我的应用程序是一个 COM 服务器非托管代码)也有足够的知识来杀死我的对象。

鉴于该对象看似随机死亡(我无法确定一组特定事件,只是它在调用 StartMonitoring() 后不到一秒到几分钟内死亡。

看起来这HandleRef可能会解决我的问题,但是我不清楚如何在这种情况下使用它,而且我想不出如何将它放入我的代码中(除了可能在 AppSubclass 级别声明一个然后分配它SpecialAppWndProc 对象。

那么,在我准备好让我的对象死亡之前,如何防止它死亡呢?

4

1 回答 1

2

您需要存储对对象的引用。

事件向另一个方向工作,使事件将触发的对象保持活动状态,而不是事件源。

如果您添加一些对 GC.Collect 和 GC.WaitForPendingFinalizers 的调用,我很确定您可以很快引发问题。

让我进一步充实我的答案。

事件基本上只是变相的代表。伪装只是删除了与委托相关的一些功能,因此外部代码无法对底层委托做任何想做的事情,但从本质上讲,它是一个普通的委托。

那么什么是委托呢?引用单个方法的委托由两部分组成:

  1. 方法参考
  2. 对象引用(目标)

当定义它的对象调用该事件时,例如触发“Button.Click”事件,会在特定对象(例如 Form1 的实例)上触发特定方法(例如 bt_Click)。

因此,该事件包含一个向外的引用,指向定义该方法的对象。该事件不会对该其他对象执行任何操作,因此其他对象(如我上面示例中的 Form1)不会以任何方式与该事件相关,包含对该对象的引用。

因此,在您的情况下,假设您有以下代码:

AppSubclass app = new AppSubclass(); // this starts monitoring

如果您现在让该变量超出范围,则该对象有资格被收集,因为没有任何东西持有对它的引用。AppSubclass 和 SpecialAppWndProc 之间存在内部引用无关紧要,两种方式都可能存在引用,但如果没有外部代码持有对它的引用,则这些对象有资格收集。

因此,您需要在某处存储对您的对象的引用,以避免被收集。

要回答您最初的问题,即“C#:什么正在破坏我的 NativeWindow 对象以及为什么?”,答案是垃圾收集器破坏了您的 NativeWindow 对象,原因是没有对它的根引用(通过根引用,我的意思是存储在静态变量中的引用,其他根引用的成员变量,或作为活动方法中的局部变量。)

于 2009-12-22T23:38:08.010 回答