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

在这个片段中,我想在 ProcessList 中保留所有 C 库访问,但是在处理过程中,它需要做回调,并且这个回调以 InvalidStringFilter 委托的形式提供,该委托是从一些客户端传入的我的托管图书馆。

4

3 回答 3

2

如果声明正确,.NET 可以将委托自动转换为指向函数的指针。有两个警告

  1. C 函数必须建立 STDCALL
  2. 指向函数的指针不计为对对象的引用,因此您必须安排保留引用,以免底层对象被垃圾收集

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

于 2008-10-03T19:50:49.627 回答
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,或者回调可能会传递给您可以使用的用户提供的变量。

于 2008-10-03T19:52:15.707 回答
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);
于 2008-11-03T12:18:12.120 回答