问题描述
我正在为我的C一个DLL包装++库,从C#调用。此包装应该也有从库中调用,并在C#中实现的回调函数。这些函数具有例如性病::矢量< unsigned char型>作为输出参数。我不知道如何使这一点。如何通过一个回调函数传递未知大小的从C#和C ++的缓冲?
I'm writing a DLL wrapper for my C++ library, to be called from C#. This wrapper should also have callback functions called from the library and implemented in C#. These functions have for instance std::vector<unsigned char> as output parameters. I don't know how to make this. How do I pass a buffer of unknown size from C# to C++ via a callback function?
让我们以这个例子
CallbackFunction FunctionImplementedInCSharp;
void FunctionCalledFromLib(const std::vector<unsigned char>& input, std::vector<unsigned char>& output)
{
// Here FunctionImplementedInCSharp (C# delegate) should somehow be called
}
void RegisterFunction(CallbackFunction f)
{
FunctionImplementedInCSharp = f;
}
应该如何 callbackFunction参数
定义和里面是什么FunctionCalledFromLib的代码?
How should CallbackFunction
be defined and what is the code inside FunctionCalledFromLib?
的事情之一,是愚蠢的我的是:我怎么删除由C#里面的C ++代码创建了一个缓冲
One of the things that dumb me is: how do I delete a buffer created by C# inside C++ code?
推荐答案
有一些事情你应该知道的。第一个是,如果你正在调用从非托管代码的.NET委托,那么除非你遵循一些非常狭窄的限制,你将在疼痛。
There are some things you should be aware of. The first is that if you are calling a .NET delegate from unmanaged code, then unless you follow some pretty narrow constraints, you will be in for pain.
理想情况下,可以创建一个委托在C#将它传递到托管代码,这将封的函数指针,守住它,只要你喜欢,然后用无不良影响调用它。在.NET文档中是这么说的。
Ideally, you can create a delegate in C# pass it into managed code, marshal it into a function pointer, hold onto it for as long as you like, then call it with no ill effects. The .NET documentation says so.
我可以告诉你,这是不正确的。最终,你的代表或其thunk的部分将被回收,当你调用从非托管代码的函数指针,你会被发送到遗忘。我不在乎微软说什么,我跟着他们的处方信,看着函数指针得到变成垃圾,尤其是在服务器端代码屁股。
I can tell you that this is simply not true. Eventually, part of your delegate or its thunk will get garbage collected and when you call the function pointer from unmanaged code you will get sent into oblivion. I don't care what Microsoft says, I've followed their prescription to the letter and watched function pointers get turned into garbage, especially in server-side code behinds.
鉴于此,使用函数指针的最有效的方法是这样的:
Given that, the most effective way to use function pointers is thus:
- C#代码调用非托管代码,传递代表
- 非托管代码编组委托到函数指针。
- 非托管代码做了一些工作,可能调用函数指针。
- 非托管代码下降到函数指针的所有引用。
- 非托管代码返回到托管代码。
- C# code calls unmanaged code, passing in delegate.
- Unmanaged code marshals the delegate to a function pointer.
- Unmanaged code does some work, possible calling the function pointer.
- Unmanaged code drops all references to the function pointer.
- Unmanaged code returns to managed code.
鉴于此,假设我们在C#中的以下内容:
Given that, suppose we have the following in C#:
public void PerformTrick(MyManagedDelegate delegate)
{
APIGlue.CallIntoUnamangedCode(delegate);
}
,然后在托管C ++(不是的):
static CallIntoUnmanagedCode(MyManagedDelegate *delegate)
{
MyManagedDelegate __pin *pinnedDelegate = delegate;
SOME_CALLBACK_PTR p = Marshal::GetFunctionPointerForDelegate(pinnedDelegate);
CallDeepIntoUnmanagedCode(p); // this will call p
}
我没有这个最近用C ++做的/ CLI - 语法是不同的 - 我认为它最终看起来是这样的:
I haven't done this recently in C++/CLI - the syntax is different - I think it ends up looking like this:
// This is declared in a class
static CallIntoUnamangedCode(MyManagedDelegate ^delegate)
{
pin_ptr<MyManagedDelegate ^> pinnedDelegate = &delegate;
SOME_CALLBACK_PTR p = Marshal::GetFunctionPointerForDelegate(pinnedDelegate);
CallDeepIntoUnmanagedCode(p); // This will call p
}
当您退出该程序中,锁定被释放
When you exit this routines, the pinning gets released.
当你真的需要有函数指针徘徊了一会儿打电话之前,我已经做了在C ++ / CLI以下内容:
When you really, really need to have function pointers hanging around for a while before calling, I have done the following in C++/CLI:
- 做了一个哈希表是从int类型的地图 - >委托
- 制造注册/注销例程添加新的代表进入哈希表,颠簸了散列int类型的计数器。
- 提出,注册非托管代码从寄存器调用一个int一个静态的非托管回调例程。当本程序,它调用回托管代码中说:找到代表以<有关; int>的并调用它的这些论据。
什么情况是,代表不具有做过渡的thunk了,因为他们暗示。他们可以自由地挂在地狱周围由 GC 的移动。当他们被调用,委托将由得到固定,并根据需要释放。我也看到了这种方法失败,特别是在代码在一开始的时候是静态注册的回调,并希望他们留身边的时间结束的情况下。我已经看到了这个失败的ASP.NET代码背后以及为工作服务器端代码通过。这是相当令人不安,但要解决它的办法就是重构你的API,允许迟到(R)结合函数调用。
What happens is that the delegates don't have thunks that do transitions anymore since they're implied. They're free to hang around in limbo being moved by the GC as needed. When they get called, the delegate will get pinned by the CLR and released as needed. I have also seen this method fail, particularly in the case of code that statically registers callbacks at the beginning of time and expects them to stay around to the end of time. I've seen this fail in ASP.NET code behind as well as server side code for Silverlight working through WCF. It's rather unnerving, but the way to fix it is to refactor your API to allow late(r) binding to function calls.
要给你当这种意志的例子发生 - 假设你有一个包括这样的功能库:
To give you an example of when this will happen - suppose you have a library that includes a function like this:
typedef void * (*f_AllocPtr) (size_t nBytes);
typedef void *t_AllocCookie;
extern void RegisterAllocFunction(f_AllocPtr allocPtr, t_AllocCookie cookie);
和期望是,当你调用分配内存的API,这将指向脱入提供 f_AllocPtr
。信不信由你,你可以在C#写这个。这是甜蜜的:
and the expectation is that when you call an API that allocates memory, it will be vectored off into the supplied f_AllocPtr
. Believe it or not, you can write this in C#. It's sweet:
public IntPtr ManagedAllocMemory(long nBytes)
{
byte[] data = new byte[nBytes];
GCHandle dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
unsafe {
fixed (byte *b = &data[0]) {
dataPtr = new IntPtr(b);
RegisterPointerHandleAndArray(dataPtr, dataHandle, data);
return dataPtr;
}
}
}
RegisterPointerHandleAndArray充塞三重离开妥善保管。当相应的自由被调用的方式,你可以这样做:
RegisterPointerHandleAndArray stuffs the triplet away for safe keeping. That way when the corresponding free gets called, you can do this:
public void ManagedFreeMemory(IntPtr dataPointer)
{
GCHandle dataHandle;
byte[] data;
if (TryUnregister(dataPointer, out dataHandle, out data)) {
dataHandle.Free();
// do anything with data? I dunno...
}
}
当然,这是愚蠢的因为分配的内存,现在固定在GC堆,将它分段到地狱 - 但问题是,这是可行的。
And of course this is stupid because allocated memory is now pinned in the GC heap and will fragment it to hell - but the point is that it's doable.
但同样,我亲自看到了这一点失败,除非实际指针短暂的。这通常意味着你的包装API,这样当你调用到完成某项特定任务,它注册的回调,做任务,然后再换回调了常规。
But again, I have personally seen this fail unless the actual pointers are short lived. This typically means wrapping your API, so that when you call into a routine that accomplishes a specific task, it registers callbacks, does the task, and then pulls the callbacks out.
这篇关于实现对C ++ DLL的回调C#函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!