Предотвращение GC от принятия моего делегата в C#

Я возвращаюсь к сообщению, которое я сделал давно, поскольку я только что вернулся к работе с этим WRG305API.dll.

С ссылкой на: вызов функций C++, содержащих обратные вызовы в C#

Я пытался написать приложение, которое взаимодействует с этой DLL, и спасибо ребятам, которые помогли мне тогда. Я смог заставить его работать в течение короткого периода времени.

Я был остановлен этой раздражающей проблемой, которая заявлена ​​как:

A callback was made on a garbage collected delegate of type 'WinFFT!WinFFT.winradioIO.winRadioIOWrapper+CallbackFunc::Invoke'. This may cause application crashes ...

Вот код оболочки:

using System;
using System.Collections.Generic;
using System.Text;

using System.Runtime.InteropServices;

namespace WinFFT.winradioIO
{
    class winRadioIOWrapper
    {
        private const string APIDLL_PATH = "WRG305API.dll";

        public delegate void CallbackFunc(IntPtr p);

        public CallbackFunc mycallback;

        [StructLayout(LayoutKind.Sequential)]
        public struct Features
        {
            public uint feature;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct RadioInfo
        {
            public uint length;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
            public string serialNumber;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
            public string productName;
            public UInt64 minFrequency;
            public UInt64 maxFrequency;
            public Features feature;
        }

        [DllImport(APIDLL_PATH)]
        public static extern int OpenRadioDevice(int deviceNumber);

        [DllImport(APIDLL_PATH)]
        public static extern bool CloseRadioDevice(int radioHandle);

        [DllImport(APIDLL_PATH)]
        public static extern int GetRadioList(ref RadioInfo info, int bufferSize, ref int infoSize);

        [DllImport(APIDLL_PATH)]
        public static extern bool IsDeviceConnected(int radioHandle);

        [DllImport(APIDLL_PATH)]
        public static extern bool GetInfo(int radioHandle, ref RadioInfo info);

        [DllImport(APIDLL_PATH)]
        public static extern int GetFrequency(int radioHandle);

        [DllImport(APIDLL_PATH)]
        public static extern bool SetFrequency(int radioHandle, int frequency);

        [DllImport(APIDLL_PATH)]
        private static extern bool CodecStart(int hRadio, CallbackFunc func, IntPtr CallbackTarget);

        [DllImport(APIDLL_PATH)]
        private static extern uint CodecRead(int hRadio, byte[] Buf, uint Size);

        [DllImport(APIDLL_PATH)]
        private static extern bool CodecStop(int hRadio);


        public static bool startIFStream(int radioHandle)
        {


            bool bStarted = CodecStart(radioHandle, MyCallbackFunc, IntPtr.Zero);
            return bStarted;
        }

        // Note: this method will be called from a different thread!
        private static void MyCallbackFunc(IntPtr pData)
        {
            // Sophisticated work goes here...
        }

        public static void readIFStreamBlock(int radioHandle, byte[] streamDumpLocation, uint blockSize)
        {
            CodecRead(radioHandle, streamDumpLocation, blockSize);
        }

        public static bool stopIFStream(int radioHandle)
        {
            bool bStoped = CodecStop(radioHandle);
            return bStoped;
        }
    }
}

Вот мой уровень класса интерфейса Application, который предоставляет дружественные методы для использования интерфейса dll:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace WinFFT.winradioIO
{
    class radioInterface
    {
        enum DeviceStatus { Disconnected, Connected, Unknown };
        private static DeviceStatus deviceStatus = DeviceStatus.Unknown;

        public static string radioName, serialNumber;
        public static int minimumFreq, maximumFreq;

        static int radioHandle;

        static radioInterface()
        {
            InitializeDeviceConnection();
        }

        public static void duffMethod(IntPtr ptr)
        {
        }

        private static void InitializeDeviceConnection()
        {
            winRadioIOWrapper.CloseRadioDevice(radioHandle);

            deviceStatus = DeviceStatus.Disconnected;

            // Get Radio Info
            winRadioIOWrapper.RadioInfo radioInfo = new winRadioIOWrapper.RadioInfo();
            int aStructSize = Marshal.SizeOf(radioInfo), anInfoSize = 0;
            radioInfo.length = (uint)aStructSize;

            if (winRadioIOWrapper.GetRadioList(ref radioInfo, aStructSize, ref anInfoSize) == 1)
            {
                radioName = radioInfo.productName;
                serialNumber = radioInfo.serialNumber;

                minimumFreq = (int)radioInfo.minFrequency;
                maximumFreq = (int)radioInfo.maxFrequency;
            }

            // Open device
            radioHandle = winRadioIOWrapper.OpenRadioDevice(0);

            CheckDeviceConnection();
        }

        private static void CheckDeviceConnection()
        {
            bool anIsDeviceConnected = winRadioIOWrapper.IsDeviceConnected(radioHandle);

            if (deviceStatus == DeviceStatus.Unknown ||
                deviceStatus == DeviceStatus.Disconnected && anIsDeviceConnected ||
                deviceStatus == DeviceStatus.Connected && !anIsDeviceConnected)
            {
                if (anIsDeviceConnected)
                {
                    deviceStatus = DeviceStatus.Connected;

                    winRadioIOWrapper.startIFStream(radioHandle);

                }
                else
                {
                    winRadioIOWrapper.CloseRadioDevice(radioHandle);

                    deviceStatus = DeviceStatus.Disconnected;
                }
            }
        }

        public static void ReadIFStream(ref byte[] bufferLocation)
        {
            winRadioIOWrapper.readIFStreamBlock(radioHandle, bufferLocation, (uint)bufferLocation.Length);
        }

        public static void SetFreq(int valueInHz)
        {
            winRadioIOWrapper.SetFrequency(radioHandle, valueInHz);
        }

        public static void ShutDownRadio()
        {
            winRadioIOWrapper.CloseRadioDevice(radioHandle);
        }

    }
}

Я понимаю, почему AVIDeveloper выбрал путь, он великолепен для непрерывной потоковой передачи данных из WinRadio Reciever (для чего и предназначена DLL), но функция CodecRead в DLL позволяет копировать указанное количество байтов из радиомодуля. буфер. Это путь, по которому я пошел, поскольку я хочу контролировать, как регулярно я беру данные, и, следовательно, почему я оставил функцию делегата в покое. Но с этим, как это происходит в настоящее время, я теряю делегата в обертке для GC, и я действительно озадачен тем, как предотвратить это.

Заранее спасибо, ребята.

1 ответ

Решение

Сохраните делегата как поле.

private static CallbackFunc _callback = new CallbackFunc(MyCallbackFunc);

public static bool startIFStream(int radioHandle)
{


   bool bStarted = CodecStart(radioHandle, _callback, IntPtr.Zero);
   return bStarted;
}
Другие вопросы по тегам