Rob*_*Rob 2 c# delegates cross-platform
我有一组C函数,我需要在ARM目标,C++和C#中使用.我可以成功地将C包装成C++ DLL,然后成为C#DLL并使用我已成功绑定的所有C函数.但是,我有一个调试功能,我希望能够打印到C#GUI,它使用的委托是垃圾收集而不是留在原地.
托管调试助手'CallbackOnCollectedDelegate'在'C:\ utm\pc\utm_win32_app\bin\Debug\utm_win32_app.vshost.exe'中检测到问题.
附加信息:对类型为'utm_dll_wrapper_cs!MessageCodec.MessageCodec_dll + guiPrintToConsoleCallback :: Invoke'的垃圾回收委托进行了回调.这可能会导致应用程序崩溃,损坏和数据丢失.将委托传递给非托管代码时,托管应用程序必须将它们保持活动状态,直到确保它们永远不会被调用.
以下是使用和设置回调的C代码片段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 be on ARM
printf (buffer);
#endif
}
void initDll (void (*guiPrintToConsole) (const char *))
{
#ifdef WIN32
mp_guiPrintToConsole = guiPrintToConsole;
// This is the signal to the GUI that we're done with initialisation
logMsg ("ready.\r\n");
#endif
}
Run Code Online (Sandbox Code Playgroud)
这是C++代码,它与C代码一起构建在DLL中,可以从C#调用并传入函数指针printToConsole:
void msInitDll (void (*printToConsole) (const char *))
{
initDll (printToConsole);
}
Run Code Online (Sandbox Code Playgroud)
这是来自C#DLL的代码片段,它调用msInitDll(),传入guiPrintToConsole()和定义委托onConsoleTrace,我猜这是消失的东西:
[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));
//...
// All the other DLL function bindings are here
//...
msInitDll = (_msInitDll)bindItem(ptrDll, "msInitDll", typeof(_msInitDll));
msInitDll(guiPrintToConsole);
}
Run Code Online (Sandbox Code Playgroud)
我在这里看了各种答案,最有希望的似乎是在C#代码中创建一个静态变量:
static GCHandle gch;
...然后用它来引用onConsoleTraceC#bindDll()函数:
gch = GCHandle.Alloc(onConsoleTrace);
但是,这对我没有任何好处.我尝试了一些其他尝试静态的尝试,但似乎没有什么能让我想到的地方.任何人都可以建议另一种方法来解决问题吗?我有一个我需要修复的错误,缺少任何调试都证明非常烦人.
抢
以下行使用一些语法糖:
msInitDll(guiPrintToConsole);
Run Code Online (Sandbox Code Playgroud)
完整语法是:
msInitDll(new guiPrintToConsoleCallback(guiPrintToConsole));
Run Code Online (Sandbox Code Playgroud)
希望你现在明白为什么代表可以收集垃圾.
一个简单的解决方法:
var callback = new guiPrintToConsoleCallback(guiPrintToConsole);
msInitDll(callback);
// ... some other code
GC.KeepAlive(callback);
Run Code Online (Sandbox Code Playgroud)
现在委托保证是活到GC.KeepAlive呼叫.
但你很可能需要代表才能保持活力更长时间.正如错误消息所示,只需保留对它的引用即可.如果您需要完整的C#应用程序生命周期,请将callback本地语言转换为类中的静态字段.静态字段被视为GC根,因为它们的值始终可以访问.
| 归档时间: |
|
| 查看次数: |
595 次 |
| 最近记录: |