I've read solutions to similar problems, but I'm having trouble getting them to work in my case. I'm getting the following error on a C# .NET 4.0 project running in Visual Studio 2010:
CallbackOnCollectedDelegate was detected Message: A callback was made on a garbage collected delegate of type 'VLCMTest!VLCMTest.Data+VCECLB_GrabFrame_CallbackEx::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
Here is my situation: I have a background thread that is notified when a frame of data is collected.
protected AutoResetEvent frameGrabbed;
public event EventHandler<DataFrameInfo> FrameGrabbedEvent;
private void DataCollectionThread()
{
while (true)
{
frameGrabbed.WaitOne();
lock (locker)
{
FrameGrabbedEvent(this, new DataFrameInfo(lastFrame.BufferIndex, lastFrame.FrameNumber, lastFrame.FrameTimestamp));
}
}
}
The DataFrameInfo class stores some information about the frame (index into the frame buffers, frame number, and timestamp). I create an instance of this class and pass it to the main thread so the data can be displayed. The code in the main thread looks like this:
// Delegate for the Invoke call,
// make it static to prevent a problem with garbage collection
delegate void GetFrameDelegate(DataFrameInfo frameInfo);
private static GetFrameDelegate d;
/// <summary>
/// Did we just receive a frame?
/// </summary>
/// <param name="source"></param>
/// <param name="args"></param>
void frameGrabbed(object source, DataFrameInfo args)
{
if (this.InvokeRequired)
{
// It's on a different thread, so use Invoke.
d = new GetFrameDelegate(GetFrame);
this.Invoke(d, new object[] { args });
return;
}
// Get the Frame
GetFrame(args);
}
private void GetFrame(DataFrameInfo frameInfo)
{
// Call Display Frame
Debug.WriteLine("Frame: The bufferIndex is " + frameInfo.BufferIndex);
Debug.WriteLine("Frame: The number is " + frameInfo.FrameNumber);
Debug.WriteLine("Frame: The timestamp is " + frameInfo.FrameTimestamp / 1000);
}
The FrameGrabbedEvent is connected to the frameGrabbed() function. Since frameGrabbed() is being called from another thread, Invoke should be required. Then for now, I'm just trying to dump out the frame details before I work on displaying the data.
Interestingly enough, the program will run fine for a while. As soon as I move the main window of the program around the desktop, the error appears almost instantly. I must be changing the timing just enough that an object is getting garbage collected before it's used. It seemed like the solution most suggested was to make the delegate static, but that's not working for me.
Update
Sounds like I missed some relevant code. Below is the code that causes the frameGrabbed event. Essentially it's an interrupt handler that is called by a DLL that I'm using.
I declare the following:
// Function pointer used by StartGrabEx
public delegate void GrabFrame_CallbackEx(IntPtr userData, ref FrameInfoEx frameInfo);
Then I start the data collection with:
public void Start()
{
// Start grabbing frames
isGrabRunning = true;
GrabFrame_CallbackEx callback = new GrabFrame_CallbackEx(GrabCallback);
StartGrabEx(callback);
}
The GrabCallback function then looks like this:
FrameInfo lastFrame;
private void GrabCallback(IntPtr userData, ref FrameInfoEx frameInfo)
{
// Are we grabbing frames?
if (!isGrabRunning)
{
return;
}
lock (locker)
{
lastFrame = new DataFrameInfo(bufferIndex, frameInfo.number, frameInfo.timestamp);
}
// We've captured a frame, notify the DataCollectionThread
frameGrabbed.Set();
}
Looking over this update, perhaps the problem is with lastFrame, although I thought I changed the code to update lastFrame in GrabCallback (rather than using new) and that still failed.
Update #2
Perhaps I should also mention that the DataCollectionThread is declared as such:
DataCollectionThread = new Thread(DataCollectionThread );
DataCollectionThread .Name = "DataCollectionThread ";
DataCollectionThread .IsBackground = true;
DataCollectionThread .Start();