title: Preventing GC from taking my delegate in C#
body: <p>I'm referring back to a post i made a long time ago as i've only just got back to working with this WRG305API.dll.</p>
  
  <p>With referral to: <a href="https://stackoverflow.com/questions/9659303/calling-c-functions-containing-callbacks-in-c-sharp">calling C++ functions containing callbacks in C#</a></p>
  
  <p>Ive been trying to write an app that interfaces with this DLL and thanks to the guys that helped me back then. I've been able to get it working for a short period of time.</p>
  
  <p>Ive been stopped by this annoying issue which is stated as:</p>
  
  <pre><code>A callback was made on a garbage collected delegate of type 'WinFFT!WinFFT.winradioIO.winRadioIOWrapper+CallbackFunc::Invoke'. This may cause application crashes ...
  </code></pre>
  
  <p>Here is the wrapper code:</p>
  
  <pre><code>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;
          }
      }
  }
  </code></pre>
  
  <p>Here is my Application interfacing class layer which provides the friendly methods to use the dll interface:</p>
  
  <pre><code>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 &amp;&amp; anIsDeviceConnected ||
                  deviceStatus == DeviceStatus.Connected &amp;&amp; !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);
          }
  
      }
  }
  </code></pre>
  
  <p>I Understand why AVIDeveloper chose the path, its is brilliant for continuously streaming out the data from the WinRadio Reciever (which is what the DLL is for), but the function CodecRead in the DLL allows one to copy a specified number of bytes from the radio's buffer. This is the path I went as I want to control how regularly I take the data and hence why I left the delegate function alone. But with this as it is at the moment, I am loosing the delegate in the wrapper to the GC and I am really stumped on how to prevent this.</p>
  
  <p>Thanks in Advance guys.</p>