gpt4 book ai didi

c# - 跨平台 C 代码和防止垃圾回收

转载 作者:行者123 更新时间:2023-11-30 20:21:49 27 4
gpt4 key购买 nike

我有一组需要在 ARM 目标、C++ 和 C# 中使用的 C 函数。我可以成功地将 C 封装到一个 C++ DLL 中,然后再封装到一个 C# DLL 中,并使用我已成功绑定(bind)的所有 C 函数。但是,我有一个调试功能,我希望能够打印到 C# GUI 并且它使用的委托(delegate)正在被垃圾收集,而不是在持续时间内留在原地。

Managed Debugging Assistant 'CallbackOnCollectedDelegate' has detected a problem in 'C:\utm\pc\utm_win32_app\bin\Debug\utm_win32_app.vshost.exe'.

Additional Information: A callback was made on a garbage collected delegate of type 'utm_dll_wrapper_cs!MessageCodec.MessageCodec_dll+guiPrintToConsoleCallback:: 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.

这是使用和设置回调 mp_guiPrintToConsole 的 C 代码片段:

#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
}

这是 C++ 代码,与 C 代码一起内置到 DLL 中,可以从 C# 调用并传入函数指针 printToConsole:

void msInitDll (void (*printToConsole) (const char *))
{
initDll (printToConsole);
}

这是调用 msInitDll()、传入 guiPrintToConsole() 并定义委托(delegate) onConsoleTrace 的 C# DLL 代码片段,我猜这是正在消失的东西:

[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);
}

我在这里查看了各种答案,最有希望的似乎是在 C# 代码中创建一个静态变量:

static GCHandle gch;

...然后使用它来引用 C# bindDll() 函数中的 onConsoleTrace:

gch = GCHandle.Alloc(onConsoleTrace);

但是,这对我没有任何好处。我已经尝试过其他一些尝试将事物声明为静态,但似乎没有什么能让我到达我想去的地方。谁能建议另一种解决问题的方法?我有一个需要修复的错误,事实证明缺少任何调试非常烦人。

罗布

最佳答案

以下行使用了一些语法糖:

msInitDll(guiPrintToConsole);

完整的语法是:

msInitDll(new guiPrintToConsoleCallback(guiPrintToConsole));

希望现在您明白为什么委托(delegate)可以被垃圾回收了。

一个简单的解决方法:

var callback = new guiPrintToConsoleCallback(guiPrintToConsole);
msInitDll(callback);
// ... some other code
GC.KeepAlive(callback);

现在保证委托(delegate)GC.KeepAlive 调用之前一直存活。

但您很可能需要委托(delegate)存活更长时间。正如错误消息所说,只需保留对它的引用。如果您在整个 C# 应用程序生命周期内需要它,请将 callback 本地转换为类中的静态字段。静态字段被视为 GC 根,因为它们的值始终可以访问。

关于c# - 跨平台 C 代码和防止垃圾回收,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33052882/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com