1

我已经阅读了类似问题的解决方案,但我无法让它们在我的情况下工作。在 Visual Studio 2010 中运行的 C# .NET 4.0 项目中出现以下错误:

检测到 CallbackOnCollectedDelegate 消息:对类型为“VLCMTest!VLCMTest.Data+VCECLB_GrabFrame_CallbackEx::Invoke”的垃圾收集委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。将委托传递给非托管代码时,托管应用程序必须使它们保持活动状态,直到保证它们永远不会被调用。

这是我的情况:我有一个后台线程,当收集到一帧数据时会收到通知。

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));
            }
        }
    }

DataFrameInfo 类存储有关帧的一些信息(帧缓冲区的索引、帧号和时间戳)。我创建了这个类的一个实例并将其传递给主线程,以便显示数据。主线程中的代码如下所示:

    // 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);
    }

FrameGrabbedEvent 连接到 frameGrabbed() 函数。由于 frameGrabbed() 是从另一个线程调用的,因此应该需要 Invoke。那么现在,我只是想在显示数据之前转储框架细节。

有趣的是,该程序将运行一段时间。一旦我在桌面上移动程序的主窗口,错误几乎立即出现。我必须更改足够的时间,以使对象在使用之前得到垃圾收集。似乎大多数建议的解决方案是使委托静态,但这对我不起作用。

更新

听起来我错过了一些相关的代码。下面是导致 frameGrabbed 事件的代码。本质上,它是一个由我正在使用的 DLL 调用的中断处理程序。

我声明如下:

// Function pointer used by StartGrabEx
public delegate void GrabFrame_CallbackEx(IntPtr userData, ref FrameInfoEx frameInfo);

然后我开始数据收集:

    public void Start()
    {

        // Start grabbing frames
        isGrabRunning = true;
        GrabFrame_CallbackEx callback = new GrabFrame_CallbackEx(GrabCallback);
        StartGrabEx(callback);
    }

GrabCallback 函数如下所示:

    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();
    }

查看此更新,问题可能出在 lastFrame 上,尽管我认为我更改了代码以更新 GrabCallback 中的 lastFrame(而不是使用 new),但仍然失败。

更新#2

也许我还应该提到 DataCollectionThread 是这样声明的:

DataCollectionThread = new Thread(DataCollectionThread );
DataCollectionThread .Name = "DataCollectionThread ";
DataCollectionThread .IsBackground = true;
DataCollectionThread .Start();
4

1 回答 1

2

正如 Hans Passant 所说,问题出在 Start() 方法中的回调变量上。我将其更改为以下内容:

private GrabFrame_CallbackEx callback;

public void Start()
{

    // Start grabbing frames
    isGrabRunning = true;
    callback = new GrabFrame_CallbackEx(GrabCallback);
    StartGrabEx(callback);
}

现在一切正常!

于 2012-10-26T01:44:46.103 回答