具有 P/Invoke 和指针的泛型
Generics with P/Invoke and pointers
我正在使用具有回调函数的 C 接口,其中软件在指针上工作。
在 C# 端,我有以下代码用于将指针复制到托管数组并返回,例如对于 float *
。
class FloatPointer
{
readonly unsafe float* _pointer;
public uint Size { get; set; }
public float[] Array { get; set; }
public unsafe void CopyToPointer ()
{
int length = (int)Math.Min (Size, Array.Length);
Marshal.Copy (Array, 0, (IntPtr)_pointer, length);
for (int i = length; i < Size; i++) {
_pointer [i] = 0;
}
}
unsafe float[] CopyFromPointer ()
{
float[] result = new float[Size];
Marshal.Copy ((IntPtr)_pointer, result, 0, (int)Size);
return result;
}
public unsafe FloatPointer (float* pointer, uint size)
{
_pointer = pointer;
Size = size;
Array = CopyFromPointer ();
}
}
因为我必须为 unsigned char*
、uint*
等做同样的事情,所以我正在考虑为此使用通用的 class,class Pointer<T>: where T: ValueType
。
不幸的是,该约束是不可能的。
如果我将其更改为不受约束的通用 class,那么 Marshal.Copy
会告诉我它不知道 T[]
。
有什么方法可以创建这个通用的 class?
我相信 class 会如您所愿。它具有从数组转换为指针和返回的方法,以及作为泛型类型参数。
public class StructPointer<T> where T : struct
{
/// <summary>
/// Pointer
/// </summary>
public IntPtr Pointer { get; set; }
/// <summary>
/// Number of elements in pointer
/// </summary>
public int PointerElementCount { get; set; }
/// <summary>
/// Array
/// </summary>
public T[] Array { get; set; }
private int sizeOfT;
/// <summary>
/// Copy Array to Pointer
/// </summary>
public void ArrayToPointer()
{
if (Array == null || Pointer == IntPtr.Zero)
{
return;
}
int length = (int)Math.Min(PointerElementCount, Array.Length);
int byteCount = length * sizeOfT;
GCHandle handle = GCHandle.Alloc(Array, GCHandleType.Pinned);
unsafe
{
for (int i = 0; i < byteCount; i++)
{
*(((byte*)Pointer) + i) = *(((byte*)handle.AddrOfPinnedObject()) + i);
}
}
handle.Free();
if (PointerElementCount > Array.Length)
{
unsafe
{
int byteCount2 = byteCount + (sizeOfT * (PointerElementCount - Array.Length));
for (int i = byteCount; i < byteCount2; i++)
{
*(((byte*)Pointer) + i) = 0;
}
}
}
}
/// <summary>
/// Copy Pointer to Array
/// </summary>
public void ArrayFromPointer()
{
if (Pointer == IntPtr.Zero)
{
return;
}
Array = new T[PointerElementCount];
GCHandle handle = GCHandle.Alloc(Array, GCHandleType.Pinned);
int byteCount = Array.Length * sizeOfT;
unsafe
{
for (int i = 0; i < byteCount; i++)
{
*(((byte*)handle.AddrOfPinnedObject()) + i) = *(((byte*)Pointer) + i);
}
}
handle.Free();
}
public StructPointer()
{
sizeOfT = Marshal.SizeOf(typeof(T));
}
/// <summary>
/// Constructor. Copies pointer to Array.
/// </summary>
/// <param name="pointer">Pointer</param>
/// <param name="length">Number of elements in pointer</param>
public StructPointer(IntPtr pointer, int length)
{
sizeOfT = Marshal.SizeOf(typeof(T));
Pointer = pointer;
PointerElementCount = length; // number of elements in pointer, not number of bytes
ArrayFromPointer();
}
}
我正在使用具有回调函数的 C 接口,其中软件在指针上工作。
在 C# 端,我有以下代码用于将指针复制到托管数组并返回,例如对于 float *
。
class FloatPointer
{
readonly unsafe float* _pointer;
public uint Size { get; set; }
public float[] Array { get; set; }
public unsafe void CopyToPointer ()
{
int length = (int)Math.Min (Size, Array.Length);
Marshal.Copy (Array, 0, (IntPtr)_pointer, length);
for (int i = length; i < Size; i++) {
_pointer [i] = 0;
}
}
unsafe float[] CopyFromPointer ()
{
float[] result = new float[Size];
Marshal.Copy ((IntPtr)_pointer, result, 0, (int)Size);
return result;
}
public unsafe FloatPointer (float* pointer, uint size)
{
_pointer = pointer;
Size = size;
Array = CopyFromPointer ();
}
}
因为我必须为 unsigned char*
、uint*
等做同样的事情,所以我正在考虑为此使用通用的 class,class Pointer<T>: where T: ValueType
。
不幸的是,该约束是不可能的。
如果我将其更改为不受约束的通用 class,那么 Marshal.Copy
会告诉我它不知道 T[]
。
有什么方法可以创建这个通用的 class?
我相信 class 会如您所愿。它具有从数组转换为指针和返回的方法,以及作为泛型类型参数。
public class StructPointer<T> where T : struct
{
/// <summary>
/// Pointer
/// </summary>
public IntPtr Pointer { get; set; }
/// <summary>
/// Number of elements in pointer
/// </summary>
public int PointerElementCount { get; set; }
/// <summary>
/// Array
/// </summary>
public T[] Array { get; set; }
private int sizeOfT;
/// <summary>
/// Copy Array to Pointer
/// </summary>
public void ArrayToPointer()
{
if (Array == null || Pointer == IntPtr.Zero)
{
return;
}
int length = (int)Math.Min(PointerElementCount, Array.Length);
int byteCount = length * sizeOfT;
GCHandle handle = GCHandle.Alloc(Array, GCHandleType.Pinned);
unsafe
{
for (int i = 0; i < byteCount; i++)
{
*(((byte*)Pointer) + i) = *(((byte*)handle.AddrOfPinnedObject()) + i);
}
}
handle.Free();
if (PointerElementCount > Array.Length)
{
unsafe
{
int byteCount2 = byteCount + (sizeOfT * (PointerElementCount - Array.Length));
for (int i = byteCount; i < byteCount2; i++)
{
*(((byte*)Pointer) + i) = 0;
}
}
}
}
/// <summary>
/// Copy Pointer to Array
/// </summary>
public void ArrayFromPointer()
{
if (Pointer == IntPtr.Zero)
{
return;
}
Array = new T[PointerElementCount];
GCHandle handle = GCHandle.Alloc(Array, GCHandleType.Pinned);
int byteCount = Array.Length * sizeOfT;
unsafe
{
for (int i = 0; i < byteCount; i++)
{
*(((byte*)handle.AddrOfPinnedObject()) + i) = *(((byte*)Pointer) + i);
}
}
handle.Free();
}
public StructPointer()
{
sizeOfT = Marshal.SizeOf(typeof(T));
}
/// <summary>
/// Constructor. Copies pointer to Array.
/// </summary>
/// <param name="pointer">Pointer</param>
/// <param name="length">Number of elements in pointer</param>
public StructPointer(IntPtr pointer, int length)
{
sizeOfT = Marshal.SizeOf(typeof(T));
Pointer = pointer;
PointerElementCount = length; // number of elements in pointer, not number of bytes
ArrayFromPointer();
}
}