简体   繁体   English

.Net CLR 如何处理 P/Invoke 调用返回的 memory?

[英]How .Net CLR deals with memory returned from P/Invoke call?

I have a.Net 4.6 C# GUI app that uses a native DLL.我有一个使用本机 DLL 的.Net 4.6 C# GUI 应用程序。 I would like to process data in the native code and return memory allocated on the native side back to C#.我想在本机代码中处理数据并将本机端分配的 memory 返回给 C#。

I've read various questions on StackOverflow and links from around the web but there are some missing pieces I want to ensure, and potentially gather all the relevant interop answers in one place.我已经阅读了有关 StackOverflow 的各种问题以及来自 web 的链接,但我想确保有一些缺失的部分,并可能将所有相关的互操作答案收集在一个地方。

Case 1:情况1:

[DllImport("Native.dll")]
public static extern void GetStringA(
    [MarshalAs(UnmanagedType.LPStr)]
    out string str);
  • This one as I understand from the following documentation will be copied first and the original buffer will be freed using CoTaskMemFree .我从以下文档中了解到的这个将首先被复制,原始缓冲区将使用CoTaskMemFree释放。

https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices
Windows Specific For [Out] strings the CLR will use CoTaskMemFree by default to free strings or SysStringFree for strings that are marked as UnmanagedType.BSTR . Windows 特定[Out]字符串,CLR 默认使用CoTaskMemFree释放字符串,或SysStringFree标记为UnmanagedType.BSTR的字符串。

Case 2:案例二:

[DllImport("Native.dll")]
public static extern void GetStringW(
    [MarshalAs(UnmanagedType.LPWStr)]
    out string str);
  • Will CLR call CoTaskMemFree on str or is it going to use the existing buffer and call CoTaskMemFree on a later GC cycle? CLR 会在str上调用CoTaskMemFree ,还是会使用现有缓冲区并在以后的 GC 周期中调用CoTaskMemFree
    (Notice the LPWStr ) (注意LPWStr

Case 3:案例3:

[DllImport("Native.dll")]
public static extern void GetStringA(
    [MarshalAs(UnmanagedType.LPStr, SizeParamIndex = 1)]
    out string str,
    out int length);
  • Will CLR take SizeParamIndex into account while marshalling back or just look for null termination? CLR 会在编组返回时考虑SizeParamIndex还是只查找 null 终止?

Case 4:案例4:

[DllImport("Native.dll")]
public static extern void GetInts(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    out int[] array,
    out int length);
  • Will CLR call CoTaskMemFree on array or is it going to use the existing buffer and call CoTaskMemFree on a later GC cycle? CLR 会在array上调用CoTaskMemFree ,还是会使用现有缓冲区并在以后的 GC 周期中调用CoTaskMemFree Documentation talks about CoTaskMem* functions for strings but I couldn't see info on whether it handles LPArray the same way.文档讨论了字符串的CoTaskMem*函数,但我看不到有关它是否以相同方式处理LPArray的信息。
  • Will CLR take SizeParamIndex into account here while marshalling back or is it only effective for input parameters? CLR 在编组返回时会在此处考虑SizeParamIndex还是仅对输入参数有效?

Case 5:案例5:

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string str;
    [MarshalAs(UnmanagedType.LPArray)]
    public int[] ints;
}

[DllImport("Native.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GetStructs(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    out MyStruct[] array,
    out int length);
  • Can I count on CLR to handle complex types properly the same way it handles simple types?我可以指望 CLR 像处理简单类型一样正确处理复杂类型吗?
    Is it going to recursively marshal and copy/reuse reference types the way I naturally expect?它会以我自然期望的方式递归编组和复制/重用引用类型吗?

TLDR; TLDR; how do I handle memory management & ownership fully correct while using native interop?如何在使用本机互操作时完全正确地处理 memory 管理和所有权? (FYI I author the native DLL as well as the GUI app.) (仅供参考,我编写了本机 DLL 以及 GUI 应用程序。)

Case 1:情况1:

The key point you are missing is the docuementation for CoTaskMemFree您缺少的关键点是CoTaskMemFree的文档

Frees a block of task memory previously allocated through a call to the CoTaskMemAlloc or CoTaskMemRealloc function.释放之前通过调用CoTaskMemAllocCoTaskMemRealloc function分配的任务 memory 块。

If your function does not allocate strings that way (for example it uses new or malloc ) then the memory does not get freed and will leak.如果您的 function 没有以这种方式分配字符串(例如,它使用newmalloc ),那么 memory 不会被释放并会泄漏。 You must make sure to free the memory buffer using the same mechanism that is used to allocate it.必须确保使用分配它的相同机制释放 memory 缓冲区。

The marshaller does not magically know what allocation strategy you used.编组器不会神奇地知道您使用了什么分配策略。 Ideally your C++ code would provide a FreeMemory function.理想情况下,您的 C++ 代码将提供FreeMemory function。

Case 2:案例二:

CoTaskMemFree is called immediately on the return of the function, after the pointer is copied into the out string .在将指针复制到out string之后,在 function 返回时立即调用CoTaskMemFree The marshaller does not get involved at all once your function has finished.一旦您的 function 完成,编组器就不会参与其中。

Case 3:案例3:

As far as I am aware, SizeParamIndex is only used for arrays, not strings, so null-termination will be used.据我所知, SizeParamIndex仅用于 arrays,而不是字符串,因此将使用空终止。 Instead you can marshal this as a byte[] and copy it out afterwards.相反,您可以将其编组为byte[]并在之后将其复制出来。

[DllImport("Native.dll")]
public static extern void GetStringA(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    out byte[] str,
    out int length);

Case 4:案例4:

No, the marshaller will not free the array at all.不,编组器根本不会释放数组。 You need to free the array yourself.您需要自己释放阵列。

Case 5:案例5:

Yes, the marshaller will handle nested structs.是的,编组器将处理嵌套结构。 But there are some things that cannot be done.但是有些事情是做不到的。 For example, SizeParamIndex doe not work on struct members or on arrays inside structs.例如, SizeParamIndex不适用于结构成员或结构内部的 arrays。 SizeConst still works, as does LPArray . SizeConst仍然有效, LPArray也是如此。

I suggest you specify the CharSet in your specific example.我建议您在具体示例中指定CharSet


I would suggest firstly, that you reconsider the reasons you are using C++ in the first place, and whether it is possible to write that code in C#.我首先建议您重新考虑首先使用 C++ 的原因,以及是否可以在 C# 中编写该代码。

Secondly, if possible use BSTR and SAFEARRAY , both of which the marshaller can fully handle in all cases, including nested arrays and arrays of strings.其次,如果可能的话,使用BSTRSAFEARRAY ,这两种情况编组器都可以完全处理,包括字符串的嵌套 arrays 和 arrays 。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM