Question

I've made this to call unmanaged function from C code. pCallback is a function pointer so on the managed side is a delegate.

[DllImport("MyDLL.dll")]

public static extern Result SetCallback(
            IntPtr handle,
            Delegate pCallback,
            CallbackType Type);

Now I am setting

    public delegate void pfnCallback(uint PromptID, ttsEventType evt, IntPtr lData);

    public Form1()
    {
        pfnCallback cb = new pfnCallback(cback);
        (...)
        Wrapper.SetCallback(handle, cb, IntPtr.Zero, CallBackType.DEFAULT);
        (...)
        }

It gives my an error saying "... when passing delegates to unmanaged code, they must be kept alive ... "

Can anyone help me ?

Regards

Was it helpful?

Solution

You are confusing the delegate type declaration with the instance of the delegate. Yes, you made your delegate declaration public, that was wrong. Only the Form1 class uses it, it should be private. It is the instance of the delegate that matters here, the one you created with the new statement.

Right now, you are storing the instance in a local variable of the Form1 constructor. That keeps a reference on the instance for a few microseconds. As soon as the constructor completes, that reference is gone and the garbage collector can collect the delegate instance at any point after that. It cannot see that the unmanaged code keeps a reference to it, the collector can only discover references held by managed code.

Nothing good happens when the unmanaged code calls the callback on a collected delegate instance, you'll hear a loud kaboom. You must change your code so that there will be a managed reference to the instance. One easy way to do this is to add a private member to the Form1 class to store the instance.

Even that might not be good enough, the Form1 object will be garbage collected at some point in the future as well. Which collects the delegate object as well. You also must make sure that the unmanaged code cannot use the callback after that happens. Given the name of the class (Form1), that isn't that likely to happen in this specific case. But code defensively and make a call that resets the callback in the form's FormClosing event handler.

OTHER TIPS

As far as I understand the message, you should do it like this:

public delegate void pfnCallback(uint PromptID, ttsEventType evt, IntPtr lData);

public pfnCallback cb = new pfnCallback(cback);

public Form1()
{
    (...)
    Wrapper.SetCallback(handle, cb, IntPtr.Zero, CallBackType.DEFAULT);
    (...)
}

I believe you need to expand the scope of cb to ensure that that variable will continue to exist and refer to the callback function for as long as the un-managed code might want to call it. Unmanaged code doesn't participate in .NET reference tracking so if you don't force a reference to the callback to be retained in .NET code, it will be freed by the framework and the unmanaged code would be unable to properly call it after that.

Try the callback variable cb globally

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top