包装非托管 c++ 在托管包装中

发布于 2024-07-12 04:12:58 字数 320 浏览 11 评论 0原文

我有一个非托管 C++ 库。 我想公开 .NET 应用程序的功能。 有一个特殊的函数我不知道如何处理:

typedef void (free_fn*) (void*); void put(void *data, free_fn deallocation_function);

这个想法是将动态分配的缓冲区传递给函数并提供释放函数。 该库将异步处理数据,并在不再需要数据时释放缓冲区:

void *p = malloc (100); ...填充缓冲区... put(p,自由);

我如何向 .NET 应用程序公开此类内容?

I have an unmanaged C++ library. I would like to expose the functionality for .NET applications. There's one partucular function I am not sure how to handle:

typedef void (free_fn*) (void*);
void put (void *data, free_fn deallocation_function);

The idea is that you pass dynamically allocated buffer to the function and supply a deallocation function. The library will process the data asynchronously and will release the buffer later on when data is no longer needed:

void *p = malloc (100);
... fill in the buffer...
put (p, free);

How can I expose this kind of thing to .NET applications?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

梦醒时光 2024-07-19 04:12:58

执行此操作时要非常小心。 .NET 确实非常希望其对象在进入非托管例程时被固定,并在退出时取消固定。 如果您的非托管代码保留了一个指针值,该值已固定在其中,那么内存很可能会被移动或垃圾收集或两者兼而有之。

对于编组到函数指针的委托尤其如此(请相信我 - 我发现编组的委托正在对我进行垃圾收集 - 我让 Microsoft 的人员为我验证了这一点)。 此问题的最终解决方案是将委托的副本存储在与唯一事务 ID 配对的静态表中,然后创建一个非托管函数,该函数在调用时通过事务 ID 在表中查找委托,然后执行它。 它很丑陋,如果我有其他选择,我会使用它。

这是在您的情况下执行此操作的最佳方法 - 由于您的非托管代码使用设置它并且忘记它的模型,那么您应该使您的 API 更块。 在托管 C++ 中创建一个包装器,通过非托管例程分配内存,将数据复制到其中,然后将其与指向非托管释放器的指针一起传递。

Be very careful when you do this. .NET really, really wants to have its objects be pinned on the way into an unmanaged routine and unpinned on the way out. If your unmanaged code holds onto a pointer value, that had been pinned on the way in then there is very real chance that the memory will be moved or garbage collected or both.

This is especially the case with delegates marshalled to function pointers (trust me on this - I found that marshaled delegates were being garbage collected on me - I had people at Microsoft verify that for me). The ultimate solution to this problem is to stash away copies of your delegates in a static table paired with a unique transaction id, then create an unmanaged function that when called looks up the delegate in the table via transaction id then executes it. It's ugly and if I had another choice, I would've used it.

Here's the best way to do this in your case - since your unmanaged code uses a set it and forget it model, then you should make your API chunkier. Create an wrapper in managed C++ that allocates memory via an unmanaged routine, copies your data into it and then passes it on along with a pointer to an unmanaged deallocator.

东走西顾 2024-07-19 04:12:58

一般来说,您的库的 .NET 使用者不会将动态创建的数组传递给您的函数。 据我所知,.NET 中的所有容器都是垃圾收集的。

无论如何,您需要为非托管代码创建一个托管包装器。 有很多关于此的教程和文章,这里是一个开始与

在为未修改的代码编写 .NET 包装器时,我发现您希望更多地关注保留功能,而不是使每个函数都可以在 .NET 中访问。 在您的示例中,最好让托管包装器将数组复制到非托管内存中,并在库内执行您需要的任何操作。 这样,您就不必对托管内存进行任何固定或将托管内存编组到非托管内存来规避 .NET 运行时的垃圾收集。 然而,如何实现托管包装器实际上取决于该函数的目的是什么。

如果您确实想在 .NET 中实现此功能,则需要查看 .NET 中的 Marshal 类,用于控制非托管代码中的托管内存。

对于回调函数,您首先需要创建可以在托管代码中分配的 .NET 委托。 然后,您需要在库内部创建一个非托管自由函数,该函数由 put 函数的非托管版本调用。 然后,如果用户分配了托管委托,则该非托管自由函数将负责调用托管委托。

In general, .NET consumers of your library won't be passing dynamically created arrays to your functions. As far as I know, all containers in .NET are garbage collected.

Regardless, you will need to make a managed wrapper for your unmanaged code. There are many tutorials and articles on this, here is one to start with.

When writing .NET wrappers for unamanged code, I've found that you want to concentrate more on preserving functionality than on making every function accessible in .NET. In your example, it may be better to just have the managed wrapper copy the array into unmanaged memory and perform whatever operations you need to inside the library. This way you don't have to do any pinning of managed memory or Marshalling of managed to unmanaged memory in order to circumvent the .NET runtime's garbage collection. However, how you implement the managed wrapper really depends on what the purpose of that function is.

If you really want to implement this function for function in .NET, you will need to look at the Marshal class in .NET for taking control of managed memory in unmanaged code.

For your callback function, you will first need to create .NET delegates that can be assigned in managed code. You will then need to make an unmanaged free function internal to your library that is called by the unmanaged version of the put function. This unmanaged free function will then be responsible for calling the managed delegate, if the user assigned one.

猛虎独行 2024-07-19 04:12:58

您绝对不想固定托管缓冲区,因为尝试在非托管代码中释放它似乎是通往疯狂的最短路线。 如果您无法在完全托管的代码中重写这部分,那么最好的选择是在包装器中复制数据,或者从托管世界中完全隐藏缓冲区管理。

如果您有勇气(和受虐狂的耐力),您可以将缓冲区固定在包装器中,然后传入取消固定缓冲区的托管函数的编组委托。 但是,我不建议这样做。 不得不做几个托管包装器让我明白了公开绝对最小非托管功能的价值,即使这意味着您必须在托管代码中重写一些东西。 跨越这一边界就像过去从东德到西德一样容易,更不用说性能上的冲击了。

You definitely don't want to pin the managed buffer, as trying to deallocate it in unmanaged code seems like the shortest route to madness. If you can't rewrite this portion in fully managed code, your best bet is either going to be making a copy of the data in the wrapper, or completely hiding the buffer management from the managed world.

If you had the guts (and the masochistic stamina) you could pin the buffer in the wrapper, then pass in the marshaled delegate of a managed function that unpins the buffer. However, I wouldn't suggest it. Having had to do a couple of managed wrappers has taught me the value of exposing the absolute minimum unmanaged functionality, even if it means you have to rewrite some things in managed code. Crossing that boundary is about as easy as going from East Germany to West Germany used to be, to say nothing of the performance hits.

心碎的声音 2024-07-19 04:12:58

大多数回复建议应将数据从托管缓冲区复制到非托管缓冲区。 你具体会怎么做? 下面的实现可以吗?

void managed_put (byte data_ __gc[], size_t size_)
{
    //  Pin the data
    byte __pin *tmp_data = &data_[0];

    //  Copy data to the unmanaged buffer.
    void *data = malloc (size_);
    memcpy (data, (byte*) tmp_data, size_);

    //  Forward the call
    put (data, size_, free);
}

Most replies suggest that the data should be copied from managed buffer to unmanaged buffer. How exactly would you do that? Is following implementation OK?

void managed_put (byte data_ __gc[], size_t size_)
{
    //  Pin the data
    byte __pin *tmp_data = &data_[0];

    //  Copy data to the unmanaged buffer.
    void *data = malloc (size_);
    memcpy (data, (byte*) tmp_data, size_);

    //  Forward the call
    put (data, size_, free);
}
风流物 2024-07-19 04:12:58

之前的一些发帖者一直在使用 MC++,但它已被弃用。 C++/CLI 是更优雅的解决方案。

最好的互操作技术是隐式互操作,而不是显式互操作。 我相信还没有人对此发表评论。 但是,它使您能够从托管<->本机编组类型,如果您对类型定义或结构布局进行更改,则不会导致重大更改(显式互操作会造成这种情况)。

这篇维基百科文章记录了一些差异,是获取更多信息的良好起点。

P/Invoke(显式和隐式)

此外,该网站 marshal-as.net 有一些关于这种新方法的示例和信息(同样,更理想,因为它如果重新定义本机结构,不会破坏您的代码)。

Some of the previous poster's have been using MC++, which is deprecated. C++/CLI is far more elegant of a solution.

The BEST, technique for interop, is implicit interop, not explicit. I dont believe anybody has commented on this yet. However, it gives you the ability to marshal your types from managed<->native where if you make a change to your type definition or structure layout, it will not result in a breaking change (which explicit interop does).

This wikiepedia article documents some of the differences and is a good starting point for further information.

P/Invoke (explicit and implicit)

Also, the site marshal-as.net has some examples and information as to this newer method (again, more ideal as it will not break your code if the a native struct is re-defined).

平安喜乐 2024-07-19 04:12:58

您必须为函数本身提供托管包装器(如果您想传递托管函数,则必须使用非托管包装器)。 否则,将非托管函数指针视为托管世界中的不透明句柄。

You'd have to have managed wrappers for the functions themselves (or unmanaged wrappers if you want to pass in managed functions). Or else, treat the unmanaged function pointers as opaque handles in the managed world.

吝吻 2024-07-19 04:12:58

既然你提到它是异步的,我就会这样做。
.Net 公开的函数仅接受数据,但不接受委托。 您的代码将固定数据和函数指针传递给一个函数,该函数将简单地取消固定数据。 这将内存清理工作留给 GC,但确保在异步部分完成之前它不会清理内存。

Since you mentioned it was asyncronous, I'd do it this way.
The .Net exposed function only takes the data but doesn't take a delegate. Your code passes the pinned data and a function pointer to a function that will simply unpin the data. This leaves the memory cleanup to the GC, but makes sure the it won't clean it up till the asyncronous part is done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文