C#: What is destroying my NativeWindow object and why?

932 views Asked by At

I am using a NativeWindow object to subclass an unmanaged window's message pump, with the purpose of intercepting its messages.

Code structure looks something like this (its psuedo C#, please excuse minor syntax problems):

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();

    }

}

Now, I thought that setting an event listener would be sufficient to keep the Garbage Collector at bay, if my object is dieing because of the GC. If it isn't, is it possible to trace how-and-why? I have never known .Net to just kill objects due to code bugs (exceptions and the occasional silent-failure seem to be the general gist of things) and I have no idea how or why the host app (my app is a COM server for unmanaged code) would have enough knowledge to kill my objects either.

Given that the object dies seemingly randomly (I haven't been able to pinpoint a certain set of events, only that it dies anywhere from less than a second to a few minutes after StartMonitoring() is called.

It would appear that HandleRef might solve my woes, however I am unclear on how to use that in this context and I can't think of how to fit it in my code (other than maybe declaring one at the AppSubclass level and then assigning it the SpecialAppWndProc object.

So, how do I prevent my object from dieing before I am ready for it to die?

1

There are 1 answers

3
Lasse V. Karlsen On BEST ANSWER

You need to store a reference to your object.

The event works the other direction, keeping the object the event will fire towards alive, not the event source.

If you add a few calls to GC.Collect and GC.WaitForPendingFinalizers I'm pretty sure you can provoke the problem pretty quick.

Let me flesh out my answer a bit more.

An event is basically just a delegate in disguise. The disguise just removes some of the capabilities associated with delegates, so that outside code cannot do whatever it wants with the underlying delegate, but at heart, it is a normal delegate.

So what is a delegate? A delegate that refers to a single method consists of two things:

  1. A method reference
  2. An object reference (the target)

When the event is invoked by the object that defines it, like a "Button.Click" event being fired, a specific method (for instance, bt_Click) is fired on a specific object (like an instance of Form1).

The event thus contains a reference outwards, towards the object on which the method is defined. The event does not do anything to that other object, so that other object, like Form1 in my example above, does not in any way related to this event contain a reference back to the object.

So in your case, let's say you have this code:

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

If you now let that variable fall out of scope, that object is eligible for collection since nothing holds a reference to it. That there are references internally between AppSubclass and SpecialAppWndProc does not matter, there could be references both ways, but if no outside code holds a reference to it, those objects are eligible for collection.

So you need to store a reference to your object, somewhere, for it to avoid being collected.

To answer your original question, which was "C#: What is destroying my NativeWindow object and why?", the answer is that it's the Garbage Collector that destroys your NativeWindow object, and the reason is that there isn't a rooted reference to it (by rooted reference, I mean a reference stored in a static variable, a member variable of other rooted references, or as a local variable in an active method.)