C #에서 byte []에서 IntPtr을 얻는 방법


127

C #에서 매개 변수 byte[]를 사용하는 메서드에 a 를 전달하고 싶습니다. IntPtr가능합니까?


자세한 내용을 알려 주시겠습니까? 왜 하시겠습니까?
Grzenio

2
예를 들어 DirectShow API를 사용하여 VideoRenderer에서 데이터를 가져 오려면이 기능이 필요합니다.이 GCHandle방법 을 사용해야합니다 fixed. : P :))
Cipi

테라 바이트 단위의 데이터를 전송하는 경우에는 이것이 필요하며 추가 사본을 피하려고합니다. 상상력을 발휘 해봐.
Brain2000

답변:


93

배열에 IntPtr을 가져 오는 것에 대해 확실하지 않지만 Mashal.Copy를 사용하여 관리되지 않는 코드와 함께 사용할 데이터를 복사 할 수 있습니다.

IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);

또는 하나의 속성으로 구조체를 선언 한 다음 Marshal.PtrToStructure를 사용할 수 있지만 여전히 관리되지 않는 메모리를 할당해야합니다.

편집 : 또한 Tyalis가 지적했듯이 안전하지 않은 코드가 옵션 인 경우 고정 을 사용할 수도 있습니다


명확히하기 위해 Marshal.Copy과부하에는 시작 색인이 필요합니다. 전화 :Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon

@ user65157의 답변과 같이 새 메모리를 만들지 않고 IntPtr을 얻는 것이 좋습니다.
Lin

208

또 다른 방법,

GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();

1
@Cipi Eric Lipperts의 다른 게시물 중 하나에 GC를 사용하는 대신 고정 키워드가 있어야합니다
goodguys_activate

고마워 잘 작동하고 아주 쉽습니다. 나는 GCHandles와 원수에 능숙하지 않다. 누군가가 원수와 어떤 GCHandle 그리고 어떤 것을 사용하는지 플러스를 말할 수 있습니까? 감사합니다
piggy

1
누군가 Eric Lippert 게시물에 대한 참조를 제공해 주시겠습니까?
Cameron

3
@piggy : Marshal의 단점은 데이터 사본을 만들어야한다는 것입니다 (오래 걸릴 수 있으며 메모리가 낭비 될 수 있음)
Riki

6
@ makerofthings7 Lippert가 GC 대신 '고정'을 사용한다고 말하지 않는다 [객체 고정], 나는 GC를 사용하여 객체를 무기한으로 고정하지 말고 고정 사용하지 말라고 믿습니다. 어느 쪽이든 마찬가지입니다.
카메론

129

이것은 작동하지만 안전하지 않은 상황에서 사용해야합니다.

byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
    IntPtr ptr = (IntPtr)p;
    // do you stuff here
}

고정 블록에서 포인터를 사용해야합니다. gc는 더 이상 고정 블록에 있지 않으면 객체를 이동할 수 있습니다.


16
데이터에 액세스하기 위해 추가 메모리를 할당하지 않아도되므로이 답변이 마음에
듭니다.

3
큰 바이트 []를 사용하는 경우 가장 좋은 방법입니다. 사본은 너무 많은 오버 헤드가
goodguys_activate


13

다음은 @ user65157의 답변을 변형 한 것입니다 (BTW의 경우 +1).

고정 된 개체에 대한 IDisposable 래퍼를 만들었습니다.

class AutoPinner : IDisposable
{
   GCHandle _pinnedArray;
   public AutoPinner(Object obj)
   {
      _pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
   }
   public static implicit operator IntPtr(AutoPinner ap)
   {
      return ap._pinnedArray.AddrOfPinnedObject(); 
   }
   public void Dispose()
   {
      _pinnedArray.Free();
   }
}

다음과 같이 사용하십시오 :

using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
   UnmanagedIntPtr = ap;  // Use the operator to retrieve the IntPtr
   //do your stuff
}

나는 이것이 Free () 호출하는 것을 잊지 않는 좋은 방법이라는 것을 알았습니다. :)


4
SafeHandle에서 AutoPinner를 파생시키는 방법을 조사해보십시오. 해당 클래스는 동시성 및 보안 "gotchas"를 고려하고 권장되는 IDisposable 패턴을 권장 / 사용하기 때문입니다.
kkahl

0

원수. 복사는 작동하지만 속도가 느립니다. for 루프에서 바이트를 복사하는 것이 더 빠릅니다. 더 빠른 방법은 바이트 배열을 ulong 배열로 캐스트하고 바이트 배열에 맞는만큼 ulong을 복사 한 다음 남은 7 바이트 (8 바이트가 정렬되지 않은 트레일)를 복사하는 것입니다. Tyalis의 답변에서 위에서 제안한 것처럼 고정 배열로 바이트 배열을 고정하는 것이 가장 빠릅니다.


-1
IntPtr GetIntPtr(Byte[] byteBuf)
{
    IntPtr ptr = Marshal.AllocHGlobal(byteBuf.Length);
    for (int i = 0; i < byteBuf.Length; i++)
    {
       Marshal.WriteByte(ptr, i, byteBuf[i]);
    }
    return ptr;
}

이것은 허용 된 답변의보다 비효율적 인 복제본입니다. 왜 한 번에 하나씩 대신 바이트 단위로 복사합니까?
BDL

내 코드의 허용되는 답변에서 오류를 발견했습니다. C #에서 C ++ dll 함수를 호출했습니다. C ++에서 사용되는 char * 매개 변수이므로 허용되는 방법을 사용했습니다. [DllImport ( "MyDll.dll", EntryPoint = "functionName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]) 이상하지만, 허용 된 메소드를 사용할 때 올바른 IntPtr을 얻을 수 없습니다. 일부 버퍼가 손상되었습니다. 일부 버퍼는 유니 코드로 표시되었습니다. 그래서 저는이 방법을 사용했습니다
nexdev

-6

경우에 따라 IntPtr의 경우 Int32 유형 (또는 Int64)을 사용할 수 있습니다. 가능하면 또 다른 유용한 클래스는 BitConverter입니다. 예를 들어 BitConverter.ToInt32를 사용할 수 있습니다.


14
포인터 대신 Int32 또는 Int64를 사용해서는 안됩니다 . 코드를 다른 플랫폼 (32 비트-> 64 비트)으로 이식해야하는 경우 모든 종류의 문제가 발생합니다.
xxbbcc 2011

5
아니요, Int32포인터를 정확하고 안전하게 사용할 수있는 유효한 경우는 없습니다 . 이것은 몇 년 전에 행한 나쁜 관행이며 모든 종류의 이식 문제로 이어집니다. 심지어는 Int64이미 증가 128 비트 아키텍처와 포인터의 크기가 있기 때문에 안전하지 않습니다. 포인터는 포인터로만 표시되어야합니다.
xxbbcc

1
문제없이 .NET CF 프로젝트에서 사용했지만 물론 다른 시스템으로 이식하려고하면 문제가 발생하지만 이식 할 코드는 없습니다.
Alejandro Mezcua 10

일부 코드는 이식 될 계획이 없지만 상황이 매우 빠르게 변경 될 수 있습니다. CF에서의 사용법이 정당하더라도 (잘 모르겠습니다) 일반적인 질문에 대해서는 여전히 나쁜 조언입니다. 포인터에 int/ 를 사용하는 유일한 시나리오는 사용 long된 언어에 개념이없는 경우입니다 (예 : VB6). C #은 포인터를 지원하며 포인터 를 대신 IntPtr사용할 필요가 전혀 없습니다 int. 답변에 명확한 경고와 잠재적 문제에 대한 설명을 추가하면 내 -1을 제거합니다.
xxbbcc 2016

3
글쎄, 당신 매우 구체적인 마샬링 코드의 사용법을 C #에서 포인터 사용에 관한 일반적인 질문과 비교하고 있습니다. 정상적인 C #에서는 이것이 유효한 시나리오가 없습니다. 나는 그것이 오래된 질문이라는 것을 알고 있지만 IntPtr에 메모리를 할당하는 방법을 찾아 그 문제를 발견했기 때문에 투표를 거부했습니다. 다른 사람들도 이것을 볼 수 있습니다. 나는 사람들이 미래에 곤경에 처했을 때 쉽게 문제를 해결하는 데 어려움을 겪고 있다고 생각하기 때문에 조언이 매우 위험하다고 생각합니다.
xxbbcc
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.