I am registering a managed delegate to an unmanaged DLL for asynchronous
callback. I understand the garbage collector will free the managed resource
if they go out of scope but with the information I have read, it is unclear
to me if the garbage collector will move the delegate (e.g.
cableInterruptC allbackEventHan dler) if it is still referenced thereby
invalidating the callback performed by the unmanaged code. I would greatly
appreciate some information regarding this topic. I have included some code
that will illustrate what I am doing. In my actual code I am implementing
the Dispose Pattern. Sorry, it is a bit lengthy.
[btapi.h] (partial)
#ifdef __cplusplus
#define BT_EXTERN extern "C"
#else
#define BT_EXTERN extern
#endif
#if !defined(BT973) && !defined(WINVER ) && !defined(BT1590 1) \
&& !defined(BT1599 1) && !defined(BT1790 3) && !defined(BT1390 8)
#define BT_METHOD
#else
/* Windows requires __stdcall between the return type and the function
name- so we can't just hide it in BT_EXTERN. */
#define BT_METHOD __stdcall
#endif
BT_EXTERN bt_error_t
BT_METHOD bt_icbr_install (
bt_desc_t btd_p,
bt_irq_t irq_type,
bt_icbr_t *icbr_p,
void *param_p,
bt_data32_t vector);
BT_EXTERN bt_error_t
BT_METHOD bt_icbr_remove(
bt_desc_t btd_p,
bt_irq_t irq_type,
bt_icbr_t *icbr_p);
[C#]
using System;
using System.Runtime. InteropServices ;
namespace SbsTechnologies .BusAdapter
{
public enum ErrorStatus : int
{
Success,
IOError,
DeviceDoesNotEx ist,
OutOfMemory,
}
public enum InterruptType : int
{
Overflow,
Error,
Programmed,
Cable,
}
public enum InterruptCallba ckType : int
{
Error = InterruptType.E rror,
Programmed = InterruptType.P rogrammed,
Cable = InterruptType.C able,
}
public class MirrorApiEventA rgs : EventArgs
{
private InterruptType interruptType;
private uint vector;
public MirrorApiEventA rgs(InterruptTy pe interruptType, uint vector) : base()
{
this.interruptT ype = interruptType;
this.vector = vector;
}
public InterruptType InterruptType
{
get
{
return this.interruptT ype;
}
}
public uint Vector
{
get
{
return this.vector;
}
}
}
internal delegate void InterruptCallba ckEventHandler( IntPtr parameter,
InterruptType interruptType, uint vector);
public delegate void MirrorApiEventH andler(object sender,
MirrorApiEventA rgs e);
internal sealed class MirrorApiIntero p
{
private sealed class UnsafeNativeMet hods
{
[DllImport(@"bit 3_984.dll", EntryPoint = "bt_icbr_instal l")]
public static extern ErrorStatus bt_icbr_install (
IntPtr btd_p,
InterruptCallba ckType irq_type,
InterruptCallba ckEventHandler icbr_p,
IntPtr param_p,
uint vector
);
[DllImport(@"bit 3_984.dll", EntryPoint = "bt_icbr_remove ")]
public static extern ErrorStatus bt_icbr_remove(
IntPtr btd_p,
InterruptCallba ckType irq_type,
InterruptCallba ckEventHandler icbr_p
);
}
internal static void InstallInterrup tCallbackRoutin e(IntPtr handle,
InterruptCallba ckType interruptCallba ckType, InterruptCallba ckEventHandler
interruptCallba ckEventHandler, IntPtr param, uint vector)
{
UnsafeNativeMet hods.bt_icbr_in stall(handle, interruptCallba ckType,
interruptCallba ckEventHandler, param, vector);
}
internal static void RemoveInterrupt CallbackRoutine (IntPtr handle,
InterruptCallba ckType interruptCallba ckType, InterruptCallba ckEventHandler
interruptCallba ckEventHandler)
{
UnsafeNativeMet hods.bt_icbr_re move(handle, interruptCallba ckType,
interruptCallba ckEventHandler) ;
}
}
public class MirrorApiInterr upt
{
private IntPtr handle;
private uint vector;
private InterruptCallba ckEventHandler cableInterruptC allbackEventHan dler;
private InterruptCallba ckEventHandler errorInterruptC allbackEventHan dler;
private InterruptCallba ckEventHandler
programmedInter ruptCallbackEve ntHandler;
public event MirrorApiEventH andler Cable;
public event MirrorApiEventH andler Error;
public event MirrorApiEventH andler Programmed;
public MirrorApiInterr upt(IntPtr handle, uint vector)
{
this.handle = handle;
this.vector = vector;
cableInterruptC allbackEventHan dler = new
InterruptCallba ckEventHandler( this.CableInter ruptCallback);
errorInterruptC allbackEventHan dler = new
InterruptCallba ckEventHandler( this.ErrorInter ruptCallback);
programmedInter ruptCallbackEve ntHandler = new
InterruptCallba ckEventHandler( this.Programmed InterruptCallba ck);
MirrorApiIntero p.InstallInterr uptCallbackRout ine(this.handle ,
InterruptCallba ckType.Cable, cableInterruptC allbackEventHan dler, IntPtr.Zero,
this.vector);
MirrorApiIntero p.InstallInterr uptCallbackRout ine(this.handle ,
InterruptCallba ckType.Error, errorInterruptC allbackEventHan dler, IntPtr.Zero,
this.vector);
MirrorApiIntero p.InstallInterr uptCallbackRout ine(this.handle ,
InterruptCallba ckType.Programm ed, programmedInter ruptCallbackEve ntHandler,
IntPtr.Zero, this.vector);
}
~MirrorApiInter rupt()
{
MirrorApiIntero p.RemoveInterru ptCallbackRouti ne(this.handle,
InterruptCallba ckType.Cable, cableInterruptC allbackEventHan dler);
}
private void CableInterruptC allback(IntPtr parameter, InterruptType
interruptType, uint vector)
{
this.OnCable(ne w MirrorApiEventA rgs(interruptTy pe, vector));
}
private void ErrorInterruptC allback(IntPtr parameter, InterruptType
interruptType, uint vector)
{
this.OnError(ne w MirrorApiEventA rgs(interruptTy pe, vector));
}
private void ProgrammedInter ruptCallback(In tPtr parameter, InterruptType
interruptType, uint vector)
{
this.OnProgramm ed(new MirrorApiEventA rgs(interruptTy pe, vector));
}
protected virtual void OnCable(MirrorA piEventArgs e)
{
if (this.Cable != null)
{
this.Cable(this , e);
}
}
protected virtual void OnError(MirrorA piEventArgs e)
{
if (this.Error != null)
{
this.Error(this , e);
}
}
protected virtual void OnProgrammed(Mi rrorApiEventArg s e)
{
if (this.Programme d != null)
{
this.Programmed (this, e);
}
}
}
}
callback. I understand the garbage collector will free the managed resource
if they go out of scope but with the information I have read, it is unclear
to me if the garbage collector will move the delegate (e.g.
cableInterruptC allbackEventHan dler) if it is still referenced thereby
invalidating the callback performed by the unmanaged code. I would greatly
appreciate some information regarding this topic. I have included some code
that will illustrate what I am doing. In my actual code I am implementing
the Dispose Pattern. Sorry, it is a bit lengthy.
[btapi.h] (partial)
#ifdef __cplusplus
#define BT_EXTERN extern "C"
#else
#define BT_EXTERN extern
#endif
#if !defined(BT973) && !defined(WINVER ) && !defined(BT1590 1) \
&& !defined(BT1599 1) && !defined(BT1790 3) && !defined(BT1390 8)
#define BT_METHOD
#else
/* Windows requires __stdcall between the return type and the function
name- so we can't just hide it in BT_EXTERN. */
#define BT_METHOD __stdcall
#endif
BT_EXTERN bt_error_t
BT_METHOD bt_icbr_install (
bt_desc_t btd_p,
bt_irq_t irq_type,
bt_icbr_t *icbr_p,
void *param_p,
bt_data32_t vector);
BT_EXTERN bt_error_t
BT_METHOD bt_icbr_remove(
bt_desc_t btd_p,
bt_irq_t irq_type,
bt_icbr_t *icbr_p);
[C#]
using System;
using System.Runtime. InteropServices ;
namespace SbsTechnologies .BusAdapter
{
public enum ErrorStatus : int
{
Success,
IOError,
DeviceDoesNotEx ist,
OutOfMemory,
}
public enum InterruptType : int
{
Overflow,
Error,
Programmed,
Cable,
}
public enum InterruptCallba ckType : int
{
Error = InterruptType.E rror,
Programmed = InterruptType.P rogrammed,
Cable = InterruptType.C able,
}
public class MirrorApiEventA rgs : EventArgs
{
private InterruptType interruptType;
private uint vector;
public MirrorApiEventA rgs(InterruptTy pe interruptType, uint vector) : base()
{
this.interruptT ype = interruptType;
this.vector = vector;
}
public InterruptType InterruptType
{
get
{
return this.interruptT ype;
}
}
public uint Vector
{
get
{
return this.vector;
}
}
}
internal delegate void InterruptCallba ckEventHandler( IntPtr parameter,
InterruptType interruptType, uint vector);
public delegate void MirrorApiEventH andler(object sender,
MirrorApiEventA rgs e);
internal sealed class MirrorApiIntero p
{
private sealed class UnsafeNativeMet hods
{
[DllImport(@"bit 3_984.dll", EntryPoint = "bt_icbr_instal l")]
public static extern ErrorStatus bt_icbr_install (
IntPtr btd_p,
InterruptCallba ckType irq_type,
InterruptCallba ckEventHandler icbr_p,
IntPtr param_p,
uint vector
);
[DllImport(@"bit 3_984.dll", EntryPoint = "bt_icbr_remove ")]
public static extern ErrorStatus bt_icbr_remove(
IntPtr btd_p,
InterruptCallba ckType irq_type,
InterruptCallba ckEventHandler icbr_p
);
}
internal static void InstallInterrup tCallbackRoutin e(IntPtr handle,
InterruptCallba ckType interruptCallba ckType, InterruptCallba ckEventHandler
interruptCallba ckEventHandler, IntPtr param, uint vector)
{
UnsafeNativeMet hods.bt_icbr_in stall(handle, interruptCallba ckType,
interruptCallba ckEventHandler, param, vector);
}
internal static void RemoveInterrupt CallbackRoutine (IntPtr handle,
InterruptCallba ckType interruptCallba ckType, InterruptCallba ckEventHandler
interruptCallba ckEventHandler)
{
UnsafeNativeMet hods.bt_icbr_re move(handle, interruptCallba ckType,
interruptCallba ckEventHandler) ;
}
}
public class MirrorApiInterr upt
{
private IntPtr handle;
private uint vector;
private InterruptCallba ckEventHandler cableInterruptC allbackEventHan dler;
private InterruptCallba ckEventHandler errorInterruptC allbackEventHan dler;
private InterruptCallba ckEventHandler
programmedInter ruptCallbackEve ntHandler;
public event MirrorApiEventH andler Cable;
public event MirrorApiEventH andler Error;
public event MirrorApiEventH andler Programmed;
public MirrorApiInterr upt(IntPtr handle, uint vector)
{
this.handle = handle;
this.vector = vector;
cableInterruptC allbackEventHan dler = new
InterruptCallba ckEventHandler( this.CableInter ruptCallback);
errorInterruptC allbackEventHan dler = new
InterruptCallba ckEventHandler( this.ErrorInter ruptCallback);
programmedInter ruptCallbackEve ntHandler = new
InterruptCallba ckEventHandler( this.Programmed InterruptCallba ck);
MirrorApiIntero p.InstallInterr uptCallbackRout ine(this.handle ,
InterruptCallba ckType.Cable, cableInterruptC allbackEventHan dler, IntPtr.Zero,
this.vector);
MirrorApiIntero p.InstallInterr uptCallbackRout ine(this.handle ,
InterruptCallba ckType.Error, errorInterruptC allbackEventHan dler, IntPtr.Zero,
this.vector);
MirrorApiIntero p.InstallInterr uptCallbackRout ine(this.handle ,
InterruptCallba ckType.Programm ed, programmedInter ruptCallbackEve ntHandler,
IntPtr.Zero, this.vector);
}
~MirrorApiInter rupt()
{
MirrorApiIntero p.RemoveInterru ptCallbackRouti ne(this.handle,
InterruptCallba ckType.Cable, cableInterruptC allbackEventHan dler);
}
private void CableInterruptC allback(IntPtr parameter, InterruptType
interruptType, uint vector)
{
this.OnCable(ne w MirrorApiEventA rgs(interruptTy pe, vector));
}
private void ErrorInterruptC allback(IntPtr parameter, InterruptType
interruptType, uint vector)
{
this.OnError(ne w MirrorApiEventA rgs(interruptTy pe, vector));
}
private void ProgrammedInter ruptCallback(In tPtr parameter, InterruptType
interruptType, uint vector)
{
this.OnProgramm ed(new MirrorApiEventA rgs(interruptTy pe, vector));
}
protected virtual void OnCable(MirrorA piEventArgs e)
{
if (this.Cable != null)
{
this.Cable(this , e);
}
}
protected virtual void OnError(MirrorA piEventArgs e)
{
if (this.Error != null)
{
this.Error(this , e);
}
}
protected virtual void OnProgrammed(Mi rrorApiEventArg s e)
{
if (this.Programme d != null)
{
this.Programmed (this, e);
}
}
}
}
Comment