[数组元素获取内存覆盖导致访问冲突异常

问题描述 投票:-2回答:1

从头开始,对不起这个怪异的标题,但我真的不知道该如何用简短的词来描述这个问题。

我正在尝试使用pinvoke方法包装c ++ DLL。我有这个功能:

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数组可以具有动态长度。

现在使用:

[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。如果我将elt变量的SizeConst设置为5038848,则CopyMemory会毫无例外地传递

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

我也尝试通过遍历循环来复制数组。每次,按索引访问elt元素都会在不同的索引上崩溃。

我的问题是在复制数组然后释放它以供其他进程覆盖之前,是否有任何方法可以锁定一定范围的内存?

c# c++ .net pinvoke
1个回答
-1
投票

我能够解决这个问题,这受到@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);
© www.soinside.com 2019 - 2024. All rights reserved.