提问人:Reddog 提问时间:10/4/2008 最后编辑:Reddog 更新时间:11/3/2008 访问量:4517
在非托管代码中处理托管委托
Handling Managed Delegates in Unmanaged code
问:
我知道我可以在技术上做到这一点,但我想实施最干净的解决方案。情况如下:
我有一个托管库,它包装了一个非托管的 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 委托的形式提供,该委托是从我的托管库的某个客户端传入的。
答:
2赞
Lou Franco
10/4/2008
#1
如果声明为正确,则 .NET 可以自动将委托转换为指向函数的指针。有两点需要注意
- 必须构建 C 函数 STDCALL
- 指向函数的指针不计为对对象的引用,因此必须安排保留引用,以便基础对象不会被垃圾回收
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);
评论