2017-01-08 11 views
0

У меня есть следующая структура, что я пытаюсь выстроить в C#:Marshalling String (но не Byte Array) Внутри Struct Причины AccessViolationException

  • Длина (4 байта)
  • версии (4 байта)
  • MachineID (16 байт)

проблема у меня является получение MachineID как string в C#. В структуре я указываю [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 24)] наверху и [MarshalAs(UnmanagedType.LPStr, SizeConst = 16)] для MachineID. Но когда я это делаю, я получаю System.AccessViolationException.

Если я меняю MachineID на byte[] и указываю [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)], он отлично работает.

Ниже приведен код, который работает с byte[], но не string. Форма MachineID в форме ASCII составляет ABCDEFGHIJKLMNO.

using System; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Text; 

public class Test 
{ 
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 24)] 
    struct Valid 
    { 
     public uint Length; 
     public uint Version; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
     public byte[] MachineId; 
    } 

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 24)] 
    struct Invalid 
    { 
     public uint Length; 
     public uint Version; 
     [MarshalAs(UnmanagedType.LPStr, SizeConst = 16)] 
     public string MachineId; 
    } 

    public static void Main() 
    { 
     var bytes = new byte[] 
     { 
      0x58, 0, 0, 0, 
      0, 0, 0, 0, 
      0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0 
     }; 

     var pinnedBuffer = GCHandle.Alloc(bytes, GCHandleType.Pinned); 
     var ptr = pinnedBuffer.AddrOfPinnedObject(); 

     // Works! 
     Debug.Assert(bytes.Length == Marshal.SizeOf<Valid>()); 

     var trackerData1 = Marshal.PtrToStructure<Valid>(ptr); 

     Console.WriteLine("Length: {0}", trackerData1.Length); 
     Console.WriteLine("Version: {0}", trackerData1.Version); 
     Console.WriteLine("MachineId: {0}", Encoding.ASCII.GetString(trackerData1.MachineId)); 

     // Doesnt work! 
     Debug.Assert(bytes.Length == Marshal.SizeOf<Invalid>()); 

     // Throws System.AccessViolationException 
     var trackerData2 = Marshal.PtrToStructure<Invalid>(ptr); 

     Console.WriteLine("Length: {0}", trackerData2.Length); 
     Console.WriteLine("Version: {0}", trackerData2.Version); 
     Console.WriteLine("MachineId: {0}", trackerData2.MachineId); 

     pinnedBuffer.Free(); 

     Console.In.ReadLine(); 
    } 
} 

Если вас run this code in IDEOne, вы увидите, что он бросает System.AccessViolationException, когда он пытается выстроить в string, и это не только мой компьютер.

Что мне любопытно, почему это работает для байтового массива, но не для строки?

ответ

0

Вы должны мобилизовывать его как ByValTStr для этого работы:

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] 
public string MachineId; 

Вот the example.