C #: Что разрушает мой объект NativeWindow и почему? - PullRequest
2 голосов
/ 23 декабря 2009

Я использую объект 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.

Итак, как мне предотвратить смерть моего объекта до того, как я буду готов к его смерти?

1 Ответ

2 голосов
/ 23 декабря 2009

Вам необходимо сохранить ссылку на ваш объект.

Событие работает в другом направлении, сохраняя объект, к которому событие будет запущено, а не источник события.

Если вы добавите несколько вызовов в GC.Collect и GC.WaitForPendingFinalizers, я почти уверен, что вы можете спровоцировать проблему довольно быстро.

Позвольте мне конкретизировать мой ответ.

Событие - это просто замаскированный делегат. Маскировка просто удаляет некоторые возможности, связанные с делегатами, так что внешний код не может делать с базовым делегатом все, что ему нужно, но в глубине души это обычный делегат.

Так что же такое делегат? Делегат, который ссылается на один метод, состоит из двух вещей:

  1. Справочник методов
  2. Ссылка на объект (цель)

Когда событие вызывается объектом, который его определяет, например, вызывается событие «Button.Click», определенный метод (например, bt_Click) запускается для конкретного объекта (например, экземпляра Form1).

Таким образом, событие содержит ссылку наружу в направлении объекта, для которого определен метод. Событие ничего не делает с этим другим объектом, поэтому другой объект, такой как Form1 в моем примере выше, никоим образом не связан с этим событием и не содержит ссылку на этот объект.

Итак, в вашем случае, скажем, у вас есть этот код:

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

Если вы теперь позволите этой переменной выпасть из области видимости, этот объект будет пригоден для сбора, поскольку ничто не содержит ссылку на него. То, что существуют внутренние ссылки между AppSubclass и SpecialAppWndProc, не имеет значения, ссылки могут быть в обоих направлениях, но если никакой внешний код не содержит ссылку на него, эти объекты могут быть собраны.

Так что вам нужно где-то хранить ссылку на ваш объект, чтобы избежать его сбора.

Чтобы ответить на ваш первоначальный вопрос, который был «C #: что уничтожает мой объект NativeWindow и почему?», Ответ состоит в том, что сборщик мусора уничтожает ваш объект NativeWindow, и причина в том, что здесь нет ссылка на него (под корневой ссылкой я имею в виду ссылку, хранящуюся в статической переменной, переменной-члене других корневых ссылок или в качестве локальной переменной в активном методе.)

...