views:

116

answers:

1

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);
    }
}
+5  A: 

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
Edited, see my new question please.
Lazlo
I cannot tell from the very meager code snippet how long the object that stores "call" is going to be around.
Hans Passant
Added a more complete code snippet.
Lazlo
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
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
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
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
Sigh, you need to start a new question. This problem is completely unrelated.
Hans Passant