Нужно ли вызывать Marshal.DestroyStructure после копирования структуры в байт []? - PullRequest
0 голосов
/ 05 мая 2018

На самом деле у меня много вопросов, поэтому я стараюсь их подробно описать, спасибо за ваше терпение.

Я хочу написать несколько общих методов для переноса нативных API, например, ReadProcessMemory и WriteProcessMemory.

Возьмите WriteProcessMemory, например, я нашел несколько способов сделать это:

1.GCHandle

// Write memory: Non-Array
var lengthInBytes = (IntPtr)Marshal.SizeOf<T>();
var tHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
    // Call native api;
}
finally
{
    tHandle.Free();
}

// Write memory: Array
var lengthInBytes = (IntPtr)(data.Length * Marshal.SizeOf<T>());
var tHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
     // Call native api;
}
finally
{
    tHandle.Free();
}

Этот способ очень прост, но единственная проблема - объект, который должен быть закреплен, должен быть встроенным типом значения (кроме фиксированного массива, но тип элемента в массиве подчиняется тем же требованиям),. Поэтому я нашел другой способ: Marshal.AllocHGlobal метод.

2. Члены класса маршала

//Write memory: Non-Array
var lengthInBytes = Marshal.SizeOf<T>();
var memPtr = Marshal.AllocHGlobal(lengthInBytes);
try
{
    Marshal.StructureToPtr<T>(data, memPtr, false);
    // Call native API
}
finally
{
    Marshal.DestroyStructure<T>(memPtr);
    Marshal.FreeHGlobal(memPtr);
}

// Write memory: Array
var perSize = Marshal.SizeOf<T>();
var arrayLength = data.Length;
var lengthInBytes = arrayLength * perSize;
var memPtr = Marshal.AllocHGlobal(lengthInBytes);
var baseAdr = memPtr;
try
{
    foreach (var item in data)
    {
        Marshal.StructureToPtr<T>(item, baseAdr, false);
        baseAdr += perSize;
    }
    // call native API.
}
finally
{
    for (int i = 0; i < arrayLength; i++, baseAdr -= perSize)
    {
        Marshal.DestroyStructure<T>(baseAdr);
    }
    Marshal.FreeHGlobal(memPtr);
}

Этот способ выделяет память, копирует данные и даже перемещает массив дважды, но он поддерживает ссылочные типы, и я был удовлетворен этим, но потом у меня возникла идея: как насчет использования Byte [] вместо IntPtr?

В частности, метод extern:

[DllImport("Kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Boolean WriteProcessMemory([In] HandleRef hProcess, [In]IntPtr lpBaseAddress, [In, MarshalAs(UnmanagedType.LPArray)] Byte[] lpBuffer, [In, MarshalAs(UnmanagedType.SysInt)]IntPtr nSize, [Out, Optional, MarshalAs(UnmanagedType.SysInt)] out IntPtr lpNumberOfBytesWritten);

И код в методе:

var perSize = Marshal.SizeOf<T>();
var buffer = new Byte[perSize];
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var pBuffer = handle.AddrOfPinnedObject();
try
{
    Marshal.StructureToPtr<T>(data, pBuffer, false);

    //Call native API.

}
finally
{
   // Question!!!!!!!!!!!!!!
    Marshal.DestroyStructure<T>(pBuffer);
    handle.Free();
}

Итак, возникает вопрос: нужно ли вызывать метод Marshal.DestroyStructure?

MSDN говорит

StructureToPtr (T, IntPtr, Boolean) копирует содержимое структуры на заранее выделенный блок памяти, на который указывает параметр ptr. Если структура содержит ссылочные типы, которые маршалируют к COM-интерфейсу указатели (интерфейсы, классы без макета и System.Object), управляемые объекты поддерживаются счетчиками ссылок. Все остальные ссылочные типы (например, строки и массивы) маршалируются в копии. Чтобы освободить эти управляемые или неуправляемые объекты, вы должны вызвать метод DestroyStructure (IntPtr) перед освобождением памяти блок.

И этот метод представляет собой «данные Marshals из управляемого объекта указанного типа в неуправляемый блок памяти

Но буфер переменных - это Byte [] (в управляемой памяти), поэтому, если я не вызову DestroyStructure, может ли GC освободить ссылочные типы в T? (Кстати, в моем тесте этот способ намного быстрее, чем второй, хотя метод Marshal.DestroyStructure также вызывался ...)

И еще вопросы, будут ли еще способы получить указатель универсального T? А если есть ошибка в решении выше?

...