CallbackOnCollectedDelegate was detected

Рейтинг: 1Ответов: 1Опубликовано: 13.11.2014

Помогите решить проблему. Есть PCI плата и библиотека для упраления PCI-Dask.dll.

Есть пневмосистема, которая состоит из 24 пневмопушек. На каждой пневмопушке установлен датчик срабатывания.

Есть форма с 24 кнопками и 24 textboxами. При нажатии одной из 24 кнопок срабатывает соответствующая пневмопушка. А в один из 24 textbox'ов выводится количество выстрелов от соответствующей пневмопушки. Но возникает следующая ошибка.

CallbackOnCollectedDelegate was detected

Message: A callback was made on a garbage collected delegate of type 'Pci7442!Pci7442.Card+Multidelegate::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.

Причем возникает не сразу, а после примерно 40 нажатий на различные кнопки.
Также было замечено, что при стрельбе с отсутствием воздуха (датчики не срабатывают в этом случае), проблема не возникает.

Возможно, проблема методе GetDataFromDigitalInput().

public class Pci7442
{
    [DllImport("PCI-Dask.dll")]
    public static extern short DI_ReadLine (ushort cardNumber, ushort port, ushort line, out ushort state);
    [DllImport("PCI-Dask.dll")]
    public static extern short DI_ReadPort (ushort cardNumber, ushort port, out uint value);

    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_GetCOSLatchDataInt32(ushort wCardNumber, byte port, out uint cosLData);

    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_INT_EventMessage(ushort cardNumber, short intMode, long windowHandle, long message, MulticastDelegate callbackAddr);

    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_INT1_EventMessage(ushort cardNumber, short int1Mode, uint windowHandle, uint message, MulticastDelegate callbackAddr);
    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_INT2_EventMessage(ushort cardNumber, short int2Mode, long windowHandle, long message, MulticastDelegate callbackAddr);
    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_LinesConfig(ushort wCardNumber, ushort wPort, ushort wLinesdirmap);
    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_LineConfig(ushort wCardNumber, ushort wPort, ushort wLine, ushort wDirection);
    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_PortConfig (ushort cardNumber, ushort port, ushort direction);
    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_SetDualInterrupt (ushort cardNumber, short int1Mode, short int2Mode, long hEvent);
    [DllImport("PCI-Dask.dll")]
    public static extern short DIO_SetCOSInterrupt32(ushort wCardNumber, byte port, uint ctl, out long hEvent, bool bManualReset);
    [DllImport("PCI-Dask.dll")]
    public static extern short DO_ReadLine (ushort cardNumber, ushort port, ushort line, out ushort value);
    [DllImport("PCI-Dask.dll")]
    public static extern short DO_ReadPort (ushort cardNumber, ushort port, out uint value);

    [DllImport("PCI-Dask.dll")]
    public static extern short DO_WriteLine (ushort cardNumber, ushort port, ushort line, ushort value);

    [DllImport("PCI-Dask.dll")]
    public static extern short DO_WritePort (ushort cardNumber, ushort port, uint value);

    }
}

Есть UserControl, который состоит из 24 кнопок, при нажатии одной из кнопок выполняется btn_Click, _numberOfPort - определяет порядковый номер пневмопушки. При использовании linesControl.ShotLineOutput(CardPci7442, line, _numberOfPort); вместо Task.Run(() => linesControl.ShotLineOutput(CardPci7442, line, _numberOfPort)); данная проблема не возникает, но зависает форма:

public partial class ButtonControl : UserControl
{
        public ButtonControl()
        {
            InitializeComponent();
        }

        private const ushort CardPci7442 = 0;
        private readonly Dictionary<string, ushort> _tableOfLine = new Dictionary<string, ushort>();
        private ushort _numberOfPort;
        public void SetNumberOfPort(ushort numberOfPort)
        {
            _numberOfPort = numberOfPort;
        }

        private void btn_Click(object sender, EventArgs e)
        {
                var linesControl = new LineControl();
                var cklickedButton = (Button)sender;
                var line = _tableOfLine[cklickedButton.Tag.ToString()];

               // linesControl.ShotLineOutput(CardPci7442, line, _numberOfPort);
                Task.Run(() => linesControl.ShotLineOutput(CardPci7442, line, _numberOfPort));

         }
    }

    class LineControl:ILineControl
    {
        public void ShotLineOutput(ushort numberOfCard, ushort numberOfLine, ushort port)
        {
            for (var i = 0; i < 5; i++)
            {
                try
                {
                    Pci7442.DO_WriteLine(numberOfCard, port, numberOfLine, Pci7442.Low);
                    Thread.Sleep(70);
                    Pci7442.DO_WriteLine(numberOfCard, port, numberOfLine, Pci7442.High);
                    Thread.Sleep(70);

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }

            }
        }
  }

На каждой пневмопушке установлен датчик, который срабатывает при выстреле и возникает вызов GetDataFromDigitalInput; cosLData - число, которое определяет, какой из 24 датчиков сработал:

class Card : ICard
    {
        public delegate void Multidelegate();
        private const ushort CardPci7442 = 0;

      public void InitCosInterrupt()
        {
            long hEvent;
            Multidelegate callbackAddr = GetDataFromDigitalInput;
        }

        private uint _cosLData;
        private void SetCosLData(uint value)
        {
            _cosLData = value;
        }
        public uint GetCosLData()
        {
            return _cosLData;
        }

        private void GetDataFromDigitalInput()
        {
            uint cosLData;
            Pci7442.DIO_GetCOSLatchDataInt32(CardPci7442, 0, out cosLData);
            SetCosLData(cosLData);
            GC.KeepAlive(this);
        }
    }

В основной форме создается 24 кнопки и 24 текстовых поля, каждую мс срабатывает таймер, который преобразует полученное с PCI число _card.GetCosLData() в номер пневмопушки.

public partial class Pci7442Form : Form
    {
        private readonly ICard _card = new Card();
        private const ushort CardPci7442 = 0;
        public Pci7442Form()
        {
            InitializeComponent();
            _card.RegisterCard(CardPci7442);
            Thread.Sleep(100);
            _card.InitCosInterrupt();

            btnControl1.CreateButtons();
            btnControl1.SetNumberOfPort(0);

        }

        private void GetCountOfShoots_Tick(object sender, EventArgs e)
        {

                var aRandom = new Random();
                var numberOfSensor = Math.Log(_card.GetCosLData(), 2);
                if (!double.IsNegativeInfinity(numberOfSensor))
                {
                    var t = txBxUserControl1.TextBoxsArray[Convert.ToInt32(numberOfSensor)];
                     //var t = txBxUserControl1[aRandom.Next(0, 24)];
                    t.Value++;
                }
         }

     }
}

Ответы

▲ 1

Ваша проблема - в вызовах DIO_INT_EventMessage, DIO_INT1_EventMessage или DIO_INT2_EventMessage. Посмотрите, какой делегат туда передается. Делегат - это тоже объект .NET, и как и все объекты он может быть собран сборщиком мусора. Но сборщик мусора не видит, используется делегат неуправляемым кодом или нет. Поэтому надо хранить ссылку на переданный в неуправляемый код делегат в течении всего времени, когда этот делегат может быть вызван.