c# - Cross-platform C code and preventing garbage collection -
i have set of c functions need use on arm target, in c++ , in c#. can wrap c c++ dll , c# dll , use c functions i've bound successfully. however, have debug function want able print c# gui , delegate uses being garbage collected rather left in place duration.
managed debugging assistant 'callbackoncollecteddelegate' has detected problem in 'c:\utm\pc\utm_win32_app\bin\debug\utm_win32_app.vshost.exe'.
additional information: callback made on garbage collected delegate of type 'utm_dll_wrapper_cs!messagecodec.messagecodec_dll+guiprinttoconsolecallback:: invoke'. may cause application crashes, corruption , data loss. when passing delegates unmanaged code, must kept alive managed application until guaranteed never called.
here's snippet of c code uses , sets callback mp_guiprinttoconsole
:
#ifdef win32 static void (* mp_guiprinttoconsole) (const char*) = null; void logmsg (const char * pformat, ...) { char buffer[max_debug_message_len]; va_list args; va_start (args, pformat); vsnprintf (buffer, sizeof (buffer), pformat, args); va_end (args); #ifdef win32 if (mp_guiprinttoconsole) { (*mp_guiprinttoconsole) (buffer); } #else // must on arm printf (buffer); #endif } void initdll (void (*guiprinttoconsole) (const char *)) { #ifdef win32 mp_guiprinttoconsole = guiprinttoconsole; // signal gui we're done initialisation logmsg ("ready.\r\n"); #endif }
here's c++ code, built dll along c code, can called c# , passes in function pointer printtoconsole
:
void msinitdll (void (*printtoconsole) (const char *)) { initdll (printtoconsole); }
here's snippet code c# dll calls msinitdll()
, passing in guiprinttoconsole()
, , defines delegate onconsoletrace
, guess thing disappearing:
[unmanagedfunctionpointer (callingconvention.cdecl)] public delegate void _msinitdll([marshalas (unmanagedtype.functionptr)] guiprinttoconsolecallback callbackpointer); public _msinitdll msinitdll; public delegate void consoletrace(string data); public event consoletrace onconsoletrace; public void guiprinttoconsole(stringbuilder data) { if (onconsoletrace != null) { onconsoletrace (data.tostring ()); } } public void binddll(string dlllocation) { intptr ptrdll = loadlibrary (dlllocation); if (ptrdll == intptr.zero) throw new exception (string.format ("cannot find {0}", dlllocation)); //... // other dll function bindings here //... msinitdll = (_msinitdll)binditem(ptrdll, "msinitdll", typeof(_msinitdll)); msinitdll(guiprinttoconsole); }
i've looked @ various answers here , promising seemed to create static variable in c# code:
static gchandle gch;
...and use reference onconsoletrace
in c# binddll()
function:
gch = gchandle.alloc(onconsoletrace);
however, doesn't me good. i've tried few other attempts @ declaring things static nothing seems me want be. can suggest approach fixing problem? have bug need fix , lack of debug proving quite annoying.
rob
the following line uses syntactic sugar:
msinitdll(guiprinttoconsole);
the full syntax is:
msinitdll(new guiprinttoconsolecallback(guiprinttoconsole));
hopefully see why delegate can garbage-collected.
one simple workaround:
var callback = new guiprinttoconsolecallback(guiprinttoconsole); msinitdll(callback); // ... other code gc.keepalive(callback);
now delegate guaranteed alive up gc.keepalive
call.
but need delegate stay alive longer. error message says, keep reference it. if need full c# app lifetime duration, turn callback
local static field in class. static fields treated gc roots values reachable.
Comments
Post a Comment