5

I am using a delegate which calls an unmanaged function pointer. This causes the Garbage Collector to collect it before it is used, as described in the CallbackOnCollectedDelegate MDA page on MSDN: MSDN page for CallbackOnCollectedDelegate MDA.

The resolution states that I have to marshal the appropriate delegate as an unmanaged function pointer. My initial reflex was to use:

[MarshalAs(UnmanagedType.FunctionPtr)]
public delegate void EntityCallback([MarshalAs(UnmanagedType.SysInt)] IntPtr entity);

However, the C# compiler won't let me marshal a delegate, even if this is the suggested resolution by MSDN. Moreover, the MSDN page only shows an example of the problem being thrown, but not one of the resolution.

How could I marshal my delegate as an unmanaged function pointer or keep it from being GCed?

EDIT: As suggested, I created a reference of the callback. Therefore, my code changed from/to:

// From:
foo.SetCallback(new EntityCallback(bar));

// To:
call = new EntityCallback(bar); // Referenced in class
foo.SetCallback(call);

Now this does work - but only in Debug mode. When I switch to Release, it crashes at the same point. Why is that?

EDIT 2: More complete code snippet:

public class Test
{
    private EntityCallback Call;

    private void Bar(System.IntPtr target)
    {
        ...
    }

    public Entity Foo { get; set; }

    public Test()
    {
        this.Foo = new Body.Sphere() { Visible = false }; // Irrelevant
        this.Foo.CollisionType = 3; // Irrelevant

        this.Call = new EntityCallback(this.Bar);

        this.Foo.SetCallback(this.Call, EntityCallbackType.Collision);
    }
}
Lazlo
  • 8,518
  • 14
  • 77
  • 116

1 Answers1

9

You didn't read it correctly. You must do this:

...change your code to keep a reference to that delegate on the managed side for the lifetime of the marshaled unmanaged function pointer.

In other words, just store a reference to the delegate instance in your class and make sure the class object survives long enough. Use a static if you really have to.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Edited, see my new question please. – Lazlo Sep 11 '10 at 18:23
  • 1
    I cannot tell from the very meager code snippet how long the object that stores "call" is going to be around. – Hans Passant Sep 11 '10 at 18:43
  • I still can't tell what is keeping the Test object alive. Add a destructor to the class and log something so you know when it gets collected. – Hans Passant Sep 11 '10 at 19:03
  • I know for sure Test isn't being disposed, as Test is responsible of a graphical aspect of the program - if it wasn't there, the graphics wouldn't update. Therefore, it's safe to assume that Test doesn't get disposed at all. – Lazlo Sep 11 '10 at 19:47
  • So you are getting the MDA warning, even though you are 100% sure that there is in fact a reference to the delegate? Whose wrong, the debugger or you? If you suspect the debugger then turn the warning off, Debug + Exceptions. – Hans Passant Sep 11 '10 at 19:53
  • I'm not getting the MDA warning anymore, as since this is in release, I just get "... has stopped working". It could be anything, to be honest. Sorry for not being clear, I am sure not questioning the debugger, only I have none in this case. – Lazlo Sep 11 '10 at 19:59
  • Sigh, you need to start a new question. This problem is completely unrelated. – Hans Passant Sep 11 '10 at 20:04