从头开始,对不起这个怪异的标题,但我真的不知道该如何用简短的词来描述这个问题。
我正在尝试使用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元素都会在不同的索引上崩溃。
我的问题是在复制数组然后释放它以供其他进程覆盖之前,是否有任何方法可以锁定一定范围的内存?
我能够解决这个问题,这受到@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);