Кастомный маршалер

229
04 февраля 2019, 18:40

Судя по документации, не совсем ясно что должен вообще реализовывать интерфейс ICustomMarshaler.

Хотелось бы описать маршалер для преобразования массива си строк в управляемый массив строк и обратно.

Есть ли какие-либо примеры или подсказки как это сделать правильно?

Дополнительные классы преобразования описал, вот только неясность осталась с данным интерфейсом.

public class CString : IDisposable
{
    private bool _isDisposed;
    public CString(string str)
    {
        byte[] tmpArr = Encoding.UTF8.GetBytes(str);
        Handle = Marshal.AllocHGlobal(tmpArr.Length + 1);
        Marshal.Copy(tmpArr, 0, Handle, tmpArr.Length);
        Marshal.WriteByte(Handle, tmpArr.Length, (byte) '\0');
        GC.KeepAlive(this);
    }
    public CString(IntPtr unmanagedStrPtr)
    {
        Handle = unmanagedStrPtr;
    }
    public IntPtr Handle { get; }
    public char this[int idx] => (char) Marshal.ReadByte(Handle, idx);
    public void Dispose()
    {
        if (_isDisposed) return;
        Marshal.FreeHGlobal(Handle);
        _isDisposed = true;
    }
    public static implicit operator CString(string str)
    {
        return new CString(str);
    }
    public static implicit operator CString(IntPtr strPtr)
    {
        return new CString(strPtr);
    }
    public static implicit operator string(CString str)
    {
        int size = 0;
        while (Marshal.ReadByte(str.Handle, size) != '\0') size++;
        byte[] arrBytes = new byte[size];
        Marshal.Copy(str.Handle, arrBytes, 0, size);
        return Encoding.UTF8.GetString(arrBytes);
    }
    ~CString()
    {
        Dispose();
        GC.SuppressFinalize(this);
    }
}
public class CStringArray : IDisposable
{
    public IntPtr Handle { get; }
    private CString[] _arrStrings;
    private IntPtr[] _arrPtr;
    private bool _isDisposed;
    public int Length => _arrPtr.Length;
    public CStringArray(string[] arrayStrings)
    {
        _arrStrings = new CString[arrayStrings.Length];
        _arrPtr = new IntPtr[arrayStrings.Length];
        for (int i = 0; i < arrayStrings.Length; i++)
        {
            _arrStrings[i] = arrayStrings[i];
        }
        Handle = Marshal.AllocHGlobal(IntPtr.Size * arrayStrings.Length);
        for (int i = 0; i < _arrPtr.Length; i++)
        {
            _arrPtr[i] = _arrStrings[i].Handle;
        }
        Marshal.Copy(_arrPtr, 0, Handle, _arrPtr.Length);
        GC.KeepAlive(this);
    }
    public CStringArray(IntPtr unmanagedPtr, int length)
    {
        _arrPtr = new IntPtr[length];
        _arrStrings = new CString[length];
        for (int i = 0; i < length; i++)
        {
            _arrPtr[i] = Marshal.ReadIntPtr(unmanagedPtr, IntPtr.Size * i);
            _arrStrings[i] = new CString(_arrPtr[i]);
        }
    }
    public CString this[int idx] => _arrStrings[idx];
    public static implicit operator string[](CStringArray array)
    {
        string[] arr = new string[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            arr[i] = array[i];
        }
        return arr;
    }
    public static implicit operator CStringArray(string[] strings)
    {
        return new CStringArray(strings);
    }
    public void Dispose()
    {
        if (_isDisposed)
        {
            return;
        }
        for (int i = 0; i < _arrStrings.Length; i++)
        {
            _arrStrings[i].Dispose();
        }
        _arrStrings = null;
        _arrPtr = null;
        _isDisposed = true;
    }
    ~CStringArray()
    {
        Dispose();
        GC.SuppressFinalize(this);
    }
}

На данный момент описал его так:

public class CStringMarshaler : ICustomMarshaler
{
    static CStringMarshaler StaticInstance;
    public void CleanUpManagedData(object ManagedObj)
    {
        if (ManagedObj is CString data)
        {
            data.Dispose();
        }
        else
        {
            throw new BadArgumentException("ManagedObj is not CString");
        }
    }
    public void CleanUpNativeData(IntPtr pNativeData)
    {
        Marshal.FreeHGlobal(pNativeData);
    }
    public int GetNativeDataSize()
    {
        return -1;
    }
    public IntPtr MarshalManagedToNative(object ManagedObj)
    {
        return new CString((string)ManagedObj).Handle;
    }
    public object MarshalNativeToManaged(IntPtr pNativeData)
    {
        return new CString(pNativeData);
    }
    public static ICustomMarshaler GetInstance(string cookie)
    {
        if (StaticInstance == null)
        {
            return StaticInstance = new CStringMarshaler();
        }
        return StaticInstance;
    }
}

Это работает, но не уверен в правильности реализации.

READ ALSO
C# OpenGL Проблема при перетаскивании объектов

C# OpenGL Проблема при перетаскивании объектов

Почему при перетаскивании объекта, он перетаскивается не конкретно с курсором, а слегка обтекая его (попытался отобразить на скриншотах...

215
Поворот объекта через transform.Rotate

Поворот объекта через transform.Rotate

Как повернуть объект по направлению к другому через transformRotate только по оси у?

207
Репозиторий &ldquo;два-в-одном&rdquo;

Репозиторий “два-в-одном”

Есть некоторый сервис, который дёргает два репозитория (первый - к сущности Order, второй - к сущности OrderPosition) и возвращает уже собранный заказ...

254
Как эффективно затереть данные

Как эффективно затереть данные

Задача: повредить файл, для обеспечения невозможности его чтения(И удачного восстановления если его удалить программным способом, на той...

395