在非托管代码中处理托管委托

4
我知道我可以在技术上实现这个,但我想要实现最简洁的解决方案。情况如下:
我有一个托管库,它包装了一个非托管的C风格库。我目前正在包装的C风格库功能涉及一组字符串的处理。该库的客户端代码可以提供一个委托,以便在列表处理过程中,如果遇到“无效”场景,则库可以通过此委托回调到客户端,并允许他们选择要使用的策略(抛出异常、替换无效字符等)。
我理想情况下想要的是,所有的托管C++都隔离在一个函数中,然后能够调用一个仅接受非托管参数的单独函数,以便所有的本地C++和非托管代码都隔离在那个点上。为这个非托管代码提供回调机制对我来说是个棘手的问题。

#pragma managed
public delegate string InvalidStringFilter(int lineNumber, string text);
...
public IList<Result> DoListProcessing(IList<string> listToProcess, InvalidStringFilter filter)
{
  // Managed code goes here, translate parameters etc.
}

#pragma unmanaged
// This should be the only function that actually touches the C-library directly
std::vector<NativeResult> ProcessList(std::vector<char*> list, ?? callback);

在这段代码中,我希望将所有C库访问保留在ProcessList中,但在处理过程中,它需要执行回调操作,这个回调操作是以InvalidStringFilter委托的形式提供的,该委托从我的托管库的某个客户端传递进来。
3个回答

2

1
感谢您的第二条评论。这终于解释了为什么我会在随机时间收到SEH错误。 - galets
我很幸运,在我接触C#的第一天就有人告诉我这个,所以我一直都知道。他知道我即将要写一个C#/C++桥接程序。 - Lou Franco

2

如果我理解问题正确,您需要在C++/CLI程序集中声明一个非托管回调函数,作为C库和托管委托之间的桥梁。


#pragma managed
public delegate string InvalidStringFilter(int lineNumber, string text);

...
static InvalidStringFilter sFilter;

public IList<Result> DoListProcessing(IList<string> listToProcess, InvalidStringFilter filter)
{
  // Managed code goes here, translate parameters etc.
  SFilter = filter;
}

#pragma unmanaged

void StringCallback(???)
{
  sFilter(????);
}

// This should be the only function that actually touches the C-library directly
std::vector<NativeResult> ProcessList(std::vector<char*> list, StringCallback);

按照现有的代码编写,显然不具备线程安全性。如果需要线程安全性,则需要使用其他机制来查找正确的托管委托回调函数,例如ThreadStatic或者回调函数传递一个用户提供的变量以供使用。


所以基本上我会有一个未托管的函数指针调用一个未托管的函数,然后我会让那个未托管的函数调用委托,在我的托管类中作为成员变量存储? - Reddog
基本上是这样。你需要弄清楚StringCallback函数如何访问托管委托。上面的代码片段只是将其设置为全局变量。StringCallback还可以处理类型转换,例如std::string和String^之间的转换。 - Rob Walker

0
你想要做类似这样的事情:
typedef void (__stdcall *w_InvalidStringFilter) (int lineNumber, string message);

GCHandle handle = GCHandle::Alloc(InvalidStringFilter);
w_InvalidStringFilter callback = 
  static_cast<w_InvalidStringFilter>(
    Marshal::GetFunctionPointerForDelegate(InvalidStringFilter).ToPointer()
  );

std::vector<NativeResult> res = ProcessList(list, callback);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接