Необъяснимая утечка памяти с Marshal.StructureToPtr

Я разрабатываю приложение, которое включает в себя взаимодействие между C ++ и C # через оболочку C ++ / CLR.

У меня проблемы со следующей операцией, которая вызывает утечку памяти:

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

(ПРИМЕЧАНИЕ: я понимаю, что на самом деле я ничего не делаю с «данными», так что это лишнее.)

Использование памяти продолжает расти, пока приложение не падает из-за нехватки памяти в системе. Когда я удаляю этот код, этого не происходит. Это не сборщик мусора, так как а) он должен собирать, когда системе не хватает памяти и б) я пытался принудительно сделать это с помощью GC.Collect ().

Фактически, я сузил утечку до команды StructureToPtr.

Я не могу установить третий параметр на «true», так как память была выделена собственным C ++, и C # считает эту «защищенную» память, которая не может быть освобождена.

Я проверил, что заполненная структура данных не повреждена, имеет действительные данные и имеет тот же размер, что и эквивалентная собственная структура.

На мой взгляд, это то, что должно происходить:

  1. Моя нативная структура, на которую ссылается ptr, распределяется и копируется в управляемую структуру «data»

  2. Управляющая структура копируется обратно в ту же память, на которую ссылается 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;
}

3

Решение

У вас есть указатели на строки в вашей структуре. Эти указатели назначаются в вашем неуправляемом коде (используя new wchar_t[<a number>]), право? При маршалинге этих указателей на управляемый код маршалер создает управляемую строку и заполняет их содержимым неуправляемых символьных массивов. При маршалинге их обратно в неуправляемый код маршалер копирует весь структурный контент, включая символьные указатели, присваивая им новые значения (выделяя память для каждой строки, используя CoTaskMemAlloc()). Это то, что третий параметр Marshal.StructureToPtr для. Если установлено значение true, маршалер пытается освободить память, указанную символьными указателями (используя CoTaskMemFree()). Если вы выделили память для указателей символов, используя new оператор, вы не можете установить для этого параметра значение true, а при маршалинге обратно в неуправляемый вы теряете указатели на выделенную память (маршаллер перезаписывает их новыми значениями). А поскольку вы не освобождаете память, выделенную маршалером, вы в конечном итоге получаете утечку памяти.

Лучший вариант справиться с этой ситуацией:

Маршал струны как указатели и использование 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);
4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]