Discussion:
P/Invoke async callback is impossible?
(too old to reply)
sherman
2009-11-30 04:39:40 UTC
Permalink
Raw Message
hi, all

1. Native API import code as below
// This method holds a callback function pointer, this callback function will be called later.
[DllImport("MyDll")]
public static extern bool SetCallback(IntPtr connected);

// This is callback definition
public delegate void ConnectionCallback(int id);

2. C# testing code is listed below
public class MyTestClass
{
public static void Main()
{
callback = new ConnectionCallback(OnConnected);
SetCallback(Marshal.GetFunctionPointerForDelegate(callback));

Console.WriteLine("Server is running, press any key to exit...");
Console.ReadKey();
}

public static void OnConnected(int id)
{
Console.WriteLine("A client is connected!");
}

static ConnectionCallback callback;
}

Why "OnConnected" is never be called? What's wrong!
Thanks for your help!

Sherman
11/30/2009
Jeroen Mostert
2009-11-30 06:08:09 UTC
Permalink
Raw Message
Post by sherman
1. Native API import code as below
// This method holds a callback function pointer, this callback function will be called later.
[DllImport("MyDll")]
public static extern bool SetCallback(IntPtr connected);
// This is callback definition
public delegate void ConnectionCallback(int id);
2. C# testing code is listed below
public class MyTestClass
{
public static void Main()
{
callback = new ConnectionCallback(OnConnected);
SetCallback(Marshal.GetFunctionPointerForDelegate(callback));
Console.WriteLine("Server is running, press any key to exit...");
Console.ReadKey();
}
public static void OnConnected(int id)
{
Console.WriteLine("A client is connected!");
}
static ConnectionCallback callback;
}
Why "OnConnected" is never be called? What's wrong!
A lot of things could be wrong depending on what SetCallback() is supposed
to do, but only one thing is demonstrably wrong in this code alone. The
documentation for .GetFunctionPointerForDelegate() explicitly warns:

"You must manually keep the delegate from being collected by the garbage
collector from managed code. The garbage collector does not track reference
to unmanaged code."

The delegate in "callback" becomes unreachable (and hence eligible for GC)
on the line after SetCallback() is called. Just because you assigned it to a
member doesn't mean it remains reachable. I have no idea if it actually is
garbage collected before the code behind SetCallback() does its thing, but
to keep it alive, you need to insert a call to GC.KeepAlive(callback) at the
end of Main().

You can detect this particular scenario (callbacks on garbage-collected
delegates) with MDAs. See http://msdn.microsoft.com/library/d21c150d.
--
J.
Loading...