我有一个名为rawbits的IntPtr,它指向一个10MB的数据数组,16位值。我需要从中返回一个托管的ushort数组。以下代码有效,但我想摆脱一个额外的BlockCopy。 Marshal.Copy不支持ushort。我能做什么? (仅供参考:rawbits由视频framegrabber卡填充到非托管内存中)
public const int width = 2056;
public const int height = 2048;
public const int depth = 2;
public System.IntPtr rawbits;
public ushort[] bits()
{
ushort[] output = new ushort[width * height];
short[] temp = new short[width * height];
Marshal.Copy(rawbits, temp, 0, width * height);
System.Buffer.BlockCopy(temp, 0, output, 0, width * height * depth);
return output;
}
以下问题中提出的建议没有帮助。 (编译错误)。
C# Marshal.Copy Intptr to 16 bit managed unsigned integer array
[顺便说一句,短阵列中确实有无符号的16位数据。 Marshal.Copy()不尊重标志,这就是我想要的。但我宁愿不只是假装短[]是一个ushort []]
答案 0 :(得分:8)
选项1 - 致电CopyMemory
:
[DllImport("kernel32.dll", SetLastError = false)]
static extern void CopyMemory(IntPtr destination, IntPtr source, UIntPtr length);
public static void Copy<T>(IntPtr source, T[] destination, int startIndex, int length)
where T : struct
{
var gch = GCHandle.Alloc(destination, GCHandleType.Pinned);
try
{
var targetPtr = Marshal.UnsafeAddrOfPinnedArrayElement(destination, startIndex);
var bytesToCopy = Marshal.SizeOf(typeof(T)) * length;
CopyMemory(targetPtr, source, (UIntPtr)bytesToCopy);
}
finally
{
gch.Free();
}
}
不便携,但性能不错。
选项2 - unsafe
和指针:
public static void Copy(IntPtr source, ushort[] destination, int startIndex, int length)
{
unsafe
{
var sourcePtr = (ushort*)source;
for(int i = startIndex; i < startIndex + length; ++i)
{
destination[i] = *sourcePtr++;
}
}
}
需要在项目构建属性中启用unsafe
选项。
选项3 - 反思(只是为了好玩,不在生产中使用):
Marshal
类在内部对所有CopyToManaged(IntPtr, object, int, int)
重载使用Copy(IntPtr, <array>, int, int)
方法(至少在.NET 4.5中)。使用反射我们可以直接调用该方法:
private static readonly Action<IntPtr, object, int, int> _copyToManaged =
GetCopyToManagedMethod();
private static Action<IntPtr, object, int, int> GetCopyToManagedMethod()
{
var method = typeof(Marshal).GetMethod("CopyToManaged",
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
return (Action<IntPtr, object, int, int>)method.CreateDelegate(
typeof(Action<IntPtr, object, int, int>), null);
}
public static void Copy<T>(IntPtr source, T[] destination, int startIndex, int length)
where T : struct
{
_copyToManaged(source, destination, startIndex, length);
}
由于可以更改Marshal
类内部,因此此方法不可靠且不应使用,但此实现可能最接近其他Marshal.Copy()
方法重载。
答案 1 :(得分:0)
好像你自己做了额外的转换(短[]到ushort [],你基本上已经做过了),或者通过不安全的关键字做自己的mem复制。
还有第三种选择:创建自定义结构。
struct MyMagicalStruct
{
// todo: set SizeConst correct
[MarshalAs(UnmanagedType.ByValArray, SizeConst=width*height)]
public ushort[] Test123;
}
您还必须使用Marshal.PtrToStructure<MyMagicalStruct>(yourPtr)
..
答案 2 :(得分:0)
在现代.NET中,您也许可以使用范围而不是数组来工作,然后事情变得有趣起来:
public unsafe Span<ushort> bits()
=> new Span<short>(rawbits.ToPointer(), width * height);
这是零副本,但是不传播unsafe
-使用者可以使用托管的“安全”代码访问范围。它的工作原理很像数组,包括边界省略等-但可以与非托管内存通信。
如果呼叫者不需要(或不应该)更改值,则可以使用ReadOnlySpan<T>
。
注意:如果您使用unsafe
来构建跨度并弄错了内容(例如长度),则它仍会在调用方上爆炸,因此是“安全的”而不是安全的。