系统相关
首页 > 系统相关> > c#-无法解释的内存泄漏与Marshal.StructureToPtr

c#-无法解释的内存泄漏与Marshal.StructureToPtr

作者:互联网

我正在开发一个涉及通过C/C++LR包装器从本机C到C#互操作的应用程序.

我在执行以下操作时遇到了麻烦,这会导致内存泄漏:

MyObject data = (MyObject)Marshal.PtrToStructure(ptr, typeof(MyObject));
Marshal.StructureToPtr(data, ptr, false);

(注意:我意识到我目前实际上并未在为“数据”做任何事情,所以这是多余的.)

内存使用率持续上升,直到由于系统内存不足而使应用程序崩溃.当我删除此代码时,这不会发生.它不是垃圾收集器,因为a)它应该在系统内存不足时收集,并且b)我尝试使用GC.Collect()强制它.

实际上,我已将泄漏范围缩小到StructureToPtr命令.

由于内存是由本机C分配的,因此我无法将第三个参数设置为“ true”,并且C#认为无法释放此“受保护”的内存.

我检查了填充的Data结构是否完好,具有有效数据,并且与等效的本机结构大小相同.

在我看来,这是应该发生的事情:

>将ptr引用的本机结构整理并复制到“数据”托管结构中
>将管理结构复制回ptr引用的相同内存.

我看不到这怎么会导致内存泄漏,因为它的结构大小完全一样,被复制回相同的内存空间.但是很明显,删除代码会堵塞漏洞.

这里有我不正确理解的机械师吗?

编辑:根据要求,这是“ MyObject”的声明.

C#:

[StructLayout(LayoutKind.Sequential)]
public struct MyObject
{
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamOne;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamTwo;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamThree;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFour;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFive;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamSix;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamSeven;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamEight;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamNine;
    public Vector2f ParamTen;
    public Vector2f ParamEleven;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamTwelve;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamThirteen;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamFourteen;
    public IntPtr ParamFifteen;
    public IntPtr ParamSixteen;
}

C :

struct MyObject
{
    public:
    bool ParamOne;
    bool ParamTwo;      
    bool ParamThree;
    bool ParamFour;
    bool ParamFive;
    bool ParamSix;
    float ParamSeven;
    float ParamEight;
    float ParamNine;
    Vector2f ParamTen;
    Vector2f ParamEleven;
    wchar_t * ParamTwelve;
    wchar_t * ParamThirteen;
    wchar_t * ParamFourteen;
    void * ParamFifteen; 
    void * ParamSixteen;
};

Vector2f的定义如下:

[StructLayout(LayoutKind.Sequential)]
public struct Vector2f
{
    [MarshalAs(UnmanagedType.R4)]
    float x;
    [MarshalAs(UnmanagedType.R4)]
    float y;
}

解决方法:

您的结构中有指向字符串的指针.这些指针是在您的非托管代码中分配的(使用新的wchar_t [< a>]]),对吗?当封送那些指向托管代码的指针时,封送处理程序创建托管字符串,并用非托管字符数组的内容填充它们.将其封送回非托管代码时,封送处理程序将复制整个结构内容(包括字符指针),并为其分配新值(使用CoTaskMemAlloc()为每个字符串分配内存).这就是Marshal.StructureToPtr的第三个参数.如果将其设置为true,则封送程序将尝试释放由字符指针指向的内存(使用CoTaskMemFree()).如果使用new运算符为字符指针分配了内存,则无法将该参数设置为true,并且通过编组回unmanaged,将丢失指向已分配内存的指针(marshaller用新值覆盖它们).并且由于您没有取消分配封送处理程序分配的内存,所以最终会导致内存泄漏.

处理这种情况的最佳选择是:

将字符串编组为指针,并使用Marshal.PtrToStringUni()将它们转换为字符串:

[StructLayout(LayoutKind.Sequential)]
public struct MyObject
{
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamOne;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamTwo;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamThree;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFour;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFive;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamSix;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamSeven;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamEight;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamNine;
    public Vector2f ParamTen;
    public Vector2f ParamEleven;
    public IntPtr ParamTwelve;    // <-- These are your strings
    public IntPtr ParamThirteen;  // <--
    public IntPtr ParamFourteen;  // <--
    public IntPtr ParamFifteen;
    public IntPtr ParamSixteen;
}

和封送处理:

    MyObject data = (MyObject)Marshal.PtrToStructure(ptr, typeof(MyObject));
    var str1 = Marshal.PtrToStringUni(data.ParamTwelve);
    var str2 = Marshal.PtrToStringUni(data.ParamThirteen);
    var str3 = Marshal.PtrToStringUni(data.ParamFourteen);
    Marshal.StructureToPtr(data, ptr, false);

标签:memory,interop,marshalling,c,c-4
来源: https://codeday.me/bug/20191031/1976707.html