Элементы массива получают переопределение памяти, приводящее к исключению нарушения доступа - PullRequest
0 голосов
/ 21 марта 2020

С самого начала, извините за странный заголовок, но я действительно не знаю, как описать эту проблему короткой фразой.

Я пытаюсь обернуть DLL-библиотеку c ++ с помощью метода pinvoke. У меня есть эта функция:

C++ header:
int32_t __cdecl ShowAllCharacters(Uint32Array *Image);

C#:
[DllImport(@"x86\OCR.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ShowAllCharacters(ref IntPtr image);

Где изображение IntPtr приводит к следующей структуре:

C++ header:
typedef struct {
    int32_t dimSizes[2];
    uint32_t elt[1];
} Uint32ArrayBase;
typedef Uint32ArrayBase **Uint32Array;

C#:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct Uint32Array
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public int[] dimSizes;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public uint[] elt;
}

В основном функция возвращает структуру Uint32Array, которая представляет массив элементов uint. В Uint32Array dimSizes - это длина массива (для получения размера необходимо умножить элементы dimSizes), а elt - первый элемент массива. Это означает, что этот массив uint может иметь динамическую c длину.

Теперь я использую:

[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);

unsafe public static void ShowAllCharacters()
{
    IntPtr ptr = IntPtr.Zero;
    OCRPinvoke.ShowAllCharacters(ref ptr);

    IntPtr imgPP = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr));
    Uint32Array img = (Uint32Array)Marshal.PtrToStructure(imgPP, typeof(Uint32Array));

    uint[] dest = new uint[img.dimSizes[1] * img.dimSizes[0]];

    fixed (uint* arrPtr = img.elt)
    {
        fixed (uint* destPtr = dest)
        {
            CopyMemory((IntPtr)destPtr, (IntPtr)arrPtr, (uint)dest.Length * sizeof(uint)); // Access violation reading location 
        }
    }
}

Я предполагаю, что эта ошибка происходит из-за переопределения памяти, прежде чем я смог скопируйте массив uint в управляемый массив. Почему? Я знаю по факту, что в некоторых условиях размер массива 'elt' должен быть 5038848. Если я устанавливаю SizeConst переменной elt на 5038848, CopyMemory проходит без исключения

internal struct Uint32Array
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public int[] dimSizes;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5038848)]
    public uint[] elt;
}

Я также пытаюсь скопировать массив путем итерации по l oop. Каждый раз при доступе к элементам elt по индексу происходит сбой в другом индексе.

Мой вопрос: есть ли способ заблокировать некоторый диапазон памяти до тех пор, пока я не скопирую свой массив и не отпущу его для переопределения другими процессами?

1 Ответ

0 голосов
/ 21 марта 2020

Я смог решить эту проблему, вдохновленный @David Heffernan. Он спрашивает, почему маршалинг вручную?

Зная, что Uint32Array является последовательным, мы можем читать каждое значение непосредственно из указателей, а затем использовать Marshal.Copy для получения окончательного управляемого массива:

IntPtr ptr = IntPtr.Zero;
OCRPinvoke.ShowAllCharacters(ref ptr);
imagePtr = Marshal.ReadIntPtr(ptr);

int height = Marshal.ReadInt32(ptr);
int width = Marshal.ReadInt32(ptr + sizeof(int));

int[] img = new int[width * height];
Marshal.Copy(ptr + sizeof(int) * 2, img, 0, img.Length);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...