Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9304

C# unsafeポインタ TIPS 17連発

$
0
0

初めに

本記事は @gushwellさんの C#リフレクションTIPS 55連発に触発されて書きました。

C#リフレクションTIPS 55連発は、薄っすらとしか覚えていないリフレクションが網羅的にまとめられており、その便利さのおかげで 余計に リフレクションを覚えられなくなってしまった良記事です:thumbsup_tone1:

私は業務で画像を扱うことが多く、P/Invoke でメモリを受け渡したり、ポインタ越しにメモリを操作するのですが、その度に過去のコードを探ったり、ぐぐったりしています。

ポインタに関わらず大体のことは ぐぐれば出てくるのですが、手間なので、未来の自分のため unsafeポインタ についてまとめました。

前提

これから示すコード(のほとんど)は、unsafe コンパイラオプションの有効化 と 下記usingディレクティブ が前提となっています。

動作は .NET Core 3.1 で確認しています。

usingSystem;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;

型変換

System.IntPtr と void* は相互に変換できます。

1. Convert IntPtr -> Pointer

unsafe{// IntPtr intPtrvoid*pointer=intPtr.ToPointer();}

2. Convert Pointer -> IntPtr

unsafe{// void* pointerIntPtrintPtr0=newIntPtr(pointer);IntPtrintPtr1=(IntPtr)pointer;// どちらも同じ}

3. Convert StackData -> Pointer

どちらでも結果は同じなので、お好みで使えば良いと思います。

byteb;unsafe{byte*pointer=&b;}
byteb=0x00;unsafe{void*pointer=Unsafe.AsPointer<byte>(refb);}

4. Convert Pointer -> ref T

unsafe{// void* pointerrefbyteb=refUnsafe.AsRef<byte>(pointer);}

5. Convert Array -> Pointer

マネージドオブジェクトはヒープ領域で管理されているので、GCの再配置を防ぐため fixed を使う必要があります。

byte[]array=newbyte[size];unsafe{fixed(byte*ptr=array){}}

読み込み

6. Read from IntPtr

ポインタから値を読み込む。

// unsafe不要byteb=Marshal.ReadByte(intPtr);shorts=Marshal.ReadInt16(intPtr);inti=Marshal.ReadInt32(intPtr);longl=Marshal.ReadInt64(intPtr);MyStructst=Marshal.PtrToStructure<MyStruct>(intPtr);

7. Read from Pointer

ポインタから値を読み込む。

unsafe{// void* pointerbyteb=Unsafe.Read<byte>(pointer);// アライメントを考慮しない版MyStructs=Unsafe.ReadUnaligned<MyStruct>(pointer);}

書き込み

8. Write to IntPtr

ポインタに値を書き込む。

// unsafe不要Marshal.WriteByte(intPtr,0x01);Marshal.WriteInt16(intPtr,0x0123);Marshal.WriteInt32(intPtr,0x0123_4567);Marshal.WriteInt64(intPtr,0x0123_4567_89ab_cdef);Marshal.StructureToPtr<MyStruct>(myStruct,intPtr,fDeleteOld:false);

9. Write to Pointer

ポインタに値を書き込む。

Write() と Copy() が提供されていますが、参照渡しできる Copy() の方がパフォーマンスが良さそうです。

unsafe{// void* pointerUnsafe.Write<byte>(pointer0,0xff);// アライメントを考慮しない版Unsafe.WriteUnaligned<MyStruct>(pointer1,myStruct);byteb=0x00;Unsafe.Copy<byte>(pointer2,refb);}

10. Fill Pointer

ポインタから指定サイズ分だけ、値(byte)を書き込む。

unsafe{// void* pointerUnsafe.InitBlock(pointer,0x00,(uint)size);// アライメントを考慮しない版Unsafe.InitBlockUnaligned(pointer,0x00,(uint)size);}

コピー

11. Copy Pointer -> Pointer

Unsafe

unsafe{// void* srcPointer / void* destPointerUnsafe.CopyBlock(srcPointer,destPointer,(uint)size);// アライメントを考慮しない版Unsafe.CopyBlockUnaligned(srcPointer,destPointer,(uint)size);}

Buffer

Unsafeクラスには存在しない書き込み先メモリの使用可能なバイト数を設定する引数が存在しますが、使う場面が分からないので、上の Unsafe.CopyBlock() を使っておけば良さそうです。

unsafe{// void* srcPointer / void* destPointerBuffer.MemoryCopy(srcPointer,destPointer,size,size);}

12. Copy IntPtr -> IntPtr

動作は速いが、マルチプラットフォームで動作しません。

RtlMoveMemory() @kernel32.dll

// unsafe不要[DllImport("kernel32.dll",EntryPoint="RtlMoveMemory",SetLastError=false)]privatestaticexternvoidRtlMoveMemory(IntPtrdest,IntPtrsrc,[MarshalAs(UnmanagedType.U4)]intlength);RtlMoveMemory(destIntPtr,srcIntPtr,length);

memcpy @msvcrt.dll

// unsafe不要[DllImport("msvcrt.dll",EntryPoint="memcpy",SetLastError=false)]privatestaticexternIntPtrmemcpy(IntPtrdest,IntPtrsrc,UIntPtrcount);_=memcpy(destIntPtr,srcIntPtr,(UIntPtr)length);

13. Copy IntPtr -> Array

// unsafe不要Marshal.Copy(srcIntPtr,destArray,startIndex:0,destArray.Length);

14. Copy Array -> IntPtr

// unsafe不要Marshal.Copy(srcArray,startIndex:0,destIntPtr,srcArray.Length);

その他

15. アンマネージドメモリの確保

AllocCoTaskMem() を使う方が良さげです。

AllocHGlobalとAllocCoTaskMem どちらを使うべきか? - Qiita

Marshl.AllocCoTaskMem()

// unsafe不要IntPtrintPtr=Marshal.AllocCoTaskMem(allocSize);// do somethingMarshal.FreeCoTaskMem(intPtr);

Marshal.AllocHGlobal()

// unsafe不要IntPtrintPtr=Marshal.AllocHGlobal(allocSize);// do somethingMarshal.FreeHGlobal(intPtr);

16. スタックメモリのポインタ取得

unsafe{byte*pointer=stackallocbyte[size];}

17. Span化

unsafe{// void* pointervarspan=newSpan<byte>(pointer,length);varroSpan=newReadOnlySpan<byte>(pointer,length);}

終わりに

30連発くらいにはなるかと思っていましたが、遠く及びませんでした…


Viewing all articles
Browse latest Browse all 9304

Trending Articles