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

Handling Managed Delegates in Unmanaged code

提问人:Reddog 提问时间:10/4/2008 最后编辑:Reddog 更新时间:11/3/2008 访问量:4517

问:

我知道我可以在技术上做到这一点,但我想实施最干净的解决方案。情况如下:

我有一个托管库,它包装了一个非托管的 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 委托的形式提供,该委托是从我的托管库的某个客户端传入的。

.NET 回调 托管 C++

评论


答:

2赞 Lou Franco 10/4/2008 #1

如果声明为正确,则 .NET 可以自动将委托转换为指向函数的指针。有两点需要注意

  1. 必须构建 C 函数 STDCALL
  2. 指向函数的指针不计为对对象的引用,因此必须安排保留引用,以便基础对象不会被垃圾回收

http://www.codeproject.com/KB/mcpp/FuncPtrDelegate.aspx?display=Print

评论

1赞 galets 5/26/2012
感谢您的第二条评论。这终于解释了为什么我总是在随机时间得到 SEH
0赞 Lou Franco 5/26/2012
我很幸运,在我使用 C# 的第一天就有人告诉我这一点,所以我一直都知道这一点。他知道我即将编写一个 C#/C++ 桥接。
2赞 Rob Walker 10/4/2008 #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,也可以将回调传递给您可以使用的用户提供的变量。

评论

0赞 Reddog 10/4/2008
因此,从本质上讲,我会让一个非托管函数指针调用一个非托管函数,然后我会让该非托管函数调用委托,该委托已作为成员变量存储在我的托管类中?
0赞 Rob Walker 10/4/2008
从本质上讲,是的。您需要弄清楚 StringCallback 函数如何访问托管委托。上面的代码片段只是使其成为全局变量。StringCallback 还可以处理类型之间的转换,例如 std::string 和 String^
0赞 Rasmus Faber 11/3/2008 #3

你想做这样的事情:

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