函数在 .NET Framework 2.0 中有效,但在任何其他版本中无效

Function works in .NET framework 2.0 but not in any other version

在 .NET 5(以及 .NET core 3 和 3.1)上,大约 5 秒后进行调试时,代码会抛出 System.ExecutionEngineException,这似乎永远不会弹出,因为据我所知,它已经过时了来自搜索。

.NET Framework >2(例如 4.8 或 4.7.2)上的相同代码同样可以工作大约 5 秒,然后抛出以下异常:

Managed Debugging Assistant 'CallbackOnCollectedDelegate' : 'A callback was made on a garbage collected delegate of type 'SoundCheck!SoundCheck.CHCNetSDK+VOICEDATACALLBACKV30::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.'

但在 .NET Framework 2 上它可以神奇地工作,没有任何问题。

据我所知,我应该以某种方式使垃圾收集停止并且不收集此方法。但是我对此很陌生。我不知道该如何处理。

我使用的是海康威视SDKhttps://www.hikvision.com/en/support/download/sdk/

代码:

Dll导入:

public delegate void VOICEDATACALLBACKV30(int lVoiceComHandle, IntPtr pRecvDataBuffer, uint dwBufSize, byte byAudioFlag, System.IntPtr pUser);

[DllImport(@"..\bin\HCNetSDK.dll")]
public static extern int NET_DVR_StartVoiceCom_V30(int lUserID, uint dwVoiceChan, bool bNeedCBNoEncData, VOICEDATACALLBACKV30 fVoiceDataCallBack, IntPtr pUser);

开始按钮:

   private void btnVioceTalk_Click(object sender, EventArgs e)
        {
            if (m_bTalk == false)
            {
               
                CHCNetSDK.VOICEDATACALLBACKV30 VoiceData = new CHCNetSDK.VOICEDATACALLBACKV30(VoiceDataCallBack);

                lVoiceComHandle = CHCNetSDK.NET_DVR_StartVoiceCom_V30(m_lUserID, 1, true, VoiceData, IntPtr.Zero);
                

                if (lVoiceComHandle < 0)
                {
                    iLastErr = CHCNetSDK.NET_DVR_GetLastError();
                    str = "NET_DVR_StartVoiceCom_V30 failed, error code= " + iLastErr;
                    MessageBox.Show(str);
                    return;
                }
                else
                {
                    btnVioceTalk.Text = "Stop Talk";
                    m_bTalk = true;
                }
            }
            else
            {
               
                if (!CHCNetSDK.NET_DVR_StopVoiceCom(lVoiceComHandle))
                {
                    iLastErr = CHCNetSDK.NET_DVR_GetLastError();
                    str = "NET_DVR_StopVoiceCom failed, error code= " + iLastErr;
                    MessageBox.Show(str);
                    return;
                }
                else
                {
                    btnVioceTalk.Text = "Start Talk";
                    m_bTalk = false;
                }
            }
        }

回调函数:

   public void VoiceDataCallBack(int lVoiceComHandle, IntPtr pRecvDataBuffer, uint dwBufSize, byte byAudioFlag, System.IntPtr pUser)
    {
        byte[] sString = new byte[dwBufSize];
        Marshal.Copy(pRecvDataBuffer, sString, 0, (Int32)dwBufSize);

        if (byAudioFlag ==0)
        {
         
            string str = "sound1.pcm";
            FileStream fs = new FileStream(str, FileMode.Create);
            int iLen = (int)dwBufSize;
            fs.Write(sString, 0, iLen);
            fs.Close();
        }
        if (byAudioFlag == 1)
        {
            
            string str = "sound2.pcm";
            FileStream fs = new FileStream(str, FileMode.Create);
            int iLen = (int)dwBufSize;
            fs.Write(sString, 0, iLen);
            fs.Close();
        }

    }

据我了解,您声明了委托类型 VOICEDATACALLBACKV30 并且您有一个实现该签名的方法。这是方法 VoiceDataCallBack
在行 CHCNetSDK.VOICEDATACALLBACKV30 VoiceData = new CHCNetSDK.VOICEDATACALLBACKV30(VoiceDataCallBack); 您在事件处理程序中实例化该回调,因此它的托管内存分配在 堆栈 上“存在”了很短的时间。
然后你将它传递给一些 SDK 函数。
似乎非托管 SDK 函数继续与该委托一起工作,明显更长的时间,并为该委托
保留一个非托管 reference/pointer。 但是您的 .NET 代码已经在不久后对其进行了垃圾回收,因为它不再在托管代码中引用。
因此,当 SDK 在收集其托管内存后第一次调用回调时,它会崩溃。

因此需要在托管代码中保留一个引用,只需将其分配给一个字段,以使其在 上保持活动状态。所以它不是GC'ed。

 //declare field
 private CHCNetSDK.VOICEDATACALLBACKV30 _voiceData;
 .... 
 //inside 'btnVioceTalk_Click' event handler
 if (_voiceData == null) {
    _voiceData = new CHCNetSDK.VOICEDATACALLBACKV30(VoiceDataCallBack);
 }

.NET Framework 2 可能有另一个 GC implementation/algorithm。