我正在尝试将登录扩展到具有C ++ \ CLI包装器的本机库。在下面的代码中,当我最终在稍后尝试使用System.ExecutionEngineException
的行中调用某些内容时,只会抛出m_logging_callback
。
我需要固定代表吗?如果可以,我可以在MyCLIClass
的生命周期内执行此操作,因为您无法将本机对象存储在托管类中吗?
我的C#代码:
public class NativeLogger
{
private readonly ILogger<NativeLogger> _logger;
public NativeLogger(ILogger<NativeLogger> logger)
{
_logger = logger;
}
public void LogMessage(string message, string module_name)
{
_logger.Error(new[] { LogCategory.Native }, $"Error in module {module_name}. Error: {message}");
}
}
我的C ++代码:
class NativeInstance
{
private:
std::function<void(std::string, std::string)> m_logging_callback;
public:
void SetLoggingCallback(std::function<void(std::string, std::string)> logging_callback)
}
void
NativeInstance::SetLoggingCallback(
std::function<void(std::string, std::string)> logging_callback)
{
m_logging_callback = logging_callback;
}
void
NativeInstance::NativeFunction()
{
m_logging_callback("Logging Message", "NativeInstance::NativeFunction");
}
C ++ \ CLI包装器:
delegate void CallbackPointer(std::string name, std::string module_name);
typedef void(*UnmanagedPointer)(std::string name, std::string module_name);
void MyCLIClass::log_callback(std::string message, std::string module_name)
{
m_logger->LogMessage(
gcnew System::String(message.c_str()),
gcnew System::String(module_name.c_str()));
}
/******************************************************************************/
MyCLIClass::ctor(Utilities::Logging::NativeLogger^ logger)
{
auto native = CreateNativeInstance();
m_native = gcnew native_shared_ptr<Native::NativeInstance>(native);
m_logger = logger;
CallbackPointer^ managed = gcnew CallbackPointer(this, &MyCLIClass::log_callback);
IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(managed);
UnmanagedPointer UnmanagedCallback = static_cast<UnmanagedPointer>(stubPointer.ToPointer());
m_native->SetLoggingCallback(UnmanagedCallback);
}
... more functions - some of which will call NativeInstance::NativeFunction()
似乎我只是迷惑了自己,而我的错误是非常基本的。
根据此博客:https://devblogs.microsoft.com/cbrumme/asynchronous-operations-pinning/
沿着同一行,可以将托管委托编组为非托管代码,在此将它们作为非托管函数指针公开。调用这些指针将执行从非托管到托管的转换;通话习惯的改变;输入正确的AppDomain;以及任何必要的论点封送处理。显然,非托管函数指针必须引用固定地址。如果GC重新定位,那将是一场灾难!这导致许多应用程序为委托创建固定句柄。这完全没有必要。非托管函数指针实际上是指我们动态生成以执行过渡和封送处理的本机代码存根。此存根存在于GC堆之外的固定内存中。
但是,应用程序负责以某种方式延长委托的寿命,直到不再受非托管代码的调用为止。本机代码存根的生存期与委托的生存期直接相关。收集到委托后,通过非托管函数指针进行的后续调用将崩溃或破坏进程。在我们的最新版本中,我们添加了一个“客户调试探针”,它使您可以清晰地检测到代码中的这个普遍存在的错误。如果您在开发过程中尚未开始使用客户调试探针,请看一下!
因此,我只需要将CallbackPointer存储在我的CLI类中。
CallbackPointer^ managed = gcnew CallbackPointer(this, &MyCLIClass::log_callback);
成为
m_callback_ptr = gcnew CallbackPointer(this, &ModeLibInstance::log_callback);