views:

67

answers:

4

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

+3  A: 

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);
    (...)
}
Bobby
+3  A: 

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.

BlueMonkMN
it's already visibly and globally declared
jose
@jose - not in your original snippet, "cb" is a local variable. The instance matters, not the type declaration.
Hans Passant
A: 

Try the callback variable cb globally

Adeel
it's already global and public :(
jose
You sample code is showing that it is not global.
BlueMonkMN
pfnCallback is not the variable name. cb is.
BlueMonkMN
+1  A: 

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.

Hans Passant
Thanks for your response. I've understand and tried what you said. But now I'm getting an unhandled Win32 exception, when running the callback associated function.
jose
Maybe you ought to start a new question about that. Be sure to document it well, "I'm getting an exception" isn't going to help anybody help you.
Hans Passant