Marshal.AllocHGlobalによって割り当てられたメモリをゼロにする方法?
-
18-09-2019 - |
質問
私はMarshal.AllocHGlobal
を経由して自分のアプリケーションでいくつかのアンマネージメモリを割り当てるしています。私はこの場所にバイトのセットをコピーしstruct
介して再度メモリを解放する前にMarshal.FreeHGlobal
にメモリの得られたセグメントを変換しています。
ここでの方法です。
public static T Deserialize<T>(byte[] messageBytes, int start, int length)
where T : struct
{
if (start + length > messageBytes.Length)
throw new ArgumentOutOfRangeException();
int typeSize = Marshal.SizeOf(typeof(T));
int bytesToCopy = Math.Min(typeSize, length);
IntPtr targetBytes = Marshal.AllocHGlobal(typeSize);
Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy);
if (length < typeSize)
{
// Zero out additional bytes at the end of the struct
}
T item = (T)Marshal.PtrToStructure(targetBytes, typeof(T));
Marshal.FreeHGlobal(targetBytes);
return item;
}
このは、ほとんどの部分のために働く私はstruct
の大きさが必要とされたよりも少ないバイトを持っている場合は、しかし、その後、「ランダム」の値は、最後のフィールド(Iがターゲット構造体にLayoutKind.Sequential
を使用しています)に割り当てられます。私は可能な限り効率的にこれらのぶら下がっフィールドをゼロにしたいと思います。
コンテキストの場合、このコードは、Linux上のC ++から送信された高周波マルチキャストメッセージをデシリアライズされます。
ここで失敗したテストケースである:
// Give only one byte, which is too few for the struct
var s3 = MessageSerializer.Deserialize<S3>(new[] { (byte)0x21 });
Assert.AreEqual(0x21, s3.Byte);
Assert.AreEqual(0x0000, s3.Int); // hanging field should be zero, but isn't
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct S3
{
public byte Byte;
public int Int;
}
このテストを実行すると、繰り返し第アサートは異なる値とするたびに失敗します。
<時間>編集
最後に、私はhref="https://stackoverflow.com/questions/1486999/how-to-zero-out-memory-allocated-by-marshal-allochglobal/1487056#1487056"> leppie年代unsafeを行くとstackalloc
を使用しての提案する。これは、(より大きなメッセージが大きな利益を見た)メッセージのサイズに応じて、50%と100%の間からバイト必要に応じてゼロにしたアレイ、および改善されたスループットを割り当てられます。
最後の方法が似てしまっます:
public static T Deserialize<T>(byte[] messageBytes, int startIndex, int length)
where T : struct
{
if (length <= 0)
throw new ArgumentOutOfRangeException("length", length, "Must be greater than zero.");
if (startIndex < 0)
throw new ArgumentOutOfRangeException("startIndex", startIndex, "Must be greater than or equal to zero.");
if (startIndex + length > messageBytes.Length)
throw new ArgumentOutOfRangeException("length", length, "startIndex + length must be <= messageBytes.Length");
int typeSize = Marshal.SizeOf(typeof(T));
unsafe
{
byte* basePtr = stackalloc byte[typeSize];
byte* b = basePtr;
int end = startIndex + Math.Min(length, typeSize);
for (int srcPos = startIndex; srcPos < end; srcPos++)
*b++ = messageBytes[srcPos];
return (T)Marshal.PtrToStructure(new IntPtr(basePtr), typeof(T));
}
}
残念ながら、これはまだ、ターゲット型にバイトを変換するためにMarshal.PtrToStructure
を呼び出す必要があります。
解決
なぜちょうどstart + length
がtypesize
内にあるかどうかをチェックしませんか?
ところで:私はちょうどここunsafe
を移動して、追加のメモリをゼロにするためのforループを使用します。
あまりにもあなたにはるかに安全とstackalloc
よりも高速であるAllocGlobal
を使用することの利点を与えること。
他のヒント
[DllImport("kernel32.dll")]
static extern void RtlZeroMemory(IntPtr dst, int length);
...
RtlZeroMemory(targetBytes, typeSize);
これは、Windows上で正常に動作します:
namespace KernelPInvoke
{
/// <summary>
/// Implements some of the C functions declared in string.h
/// </summary>
public static class MemoryWrapper
{
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
static extern void CopyMemory(IntPtr destination, IntPtr source, uint length);
[DllImport("kernel32.dll", EntryPoint = "MoveMemory", SetLastError = false)]
static extern void MoveMemory(IntPtr destination, IntPtr source, uint length);
[DllImport("kernel32.dll", EntryPoint = "RtlFillMemory", SetLastError = false)]
static extern void FillMemory(IntPtr destination, uint length, byte fill);
}
var ptr = Marshal.AllocHGlobal(size);
try
{
MemoryWrapper.FillMemory(ptr, size, 0);
// further work...
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
ジョン・シーゲルがに言ったようにはい、あなたはMarshal.WriteByteを使用して、それをゼロにすることができます。
次の例では、Iは、構造体をコピーする前にバッファをゼロ
if (start + length > messageBytes.Length)
throw new ArgumentOutOfRangeException();
int typeSize = Marshal.SizeOf(typeof(T));
int bytesToCopy = Math.Min(typeSize, length);
IntPtr targetBytes = Marshal.AllocHGlobal(typeSize);
//zero out buffer
for(int i=0; i < typeSize; i++)
{
Marshal.WriteByte(targetBytes, i, 0);
}
Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy);
私は前にC#でこのようなものをやったことがないが、私は、MSDNでMarshal.WriteByte(のIntPtr、のInt32、バイト)を発見しました。それを試してみてください。
for(int i=0; i < buffSize / 8; i += 8 )
{
Marshal.WriteInt64(buffer, i, 0x00);
}
for(int i= buffSize % 8 ; i < -1 ; i-- )
{
Marshal.WriteByte (buffer, buffSize - i, 0x00);
}
私は、あなたが64ビットwrightsの代わりに、(あなたはまだ最後の数バイトのために必要な)8ビットwrightsを使用して、数倍速くなるためにそれを見つけるだろうと思います。
私はあなたが望んでいない、または他の道を行くことができない場合は、バッファをゼロにするための最良の方法は、これだと思います:
for(int i=0; i<buffSize; i++)
{
Marshal.WriteByte(buffer, i, 0x00);
}