2012-08-22 30 views
0

我正在使用Idael Cardoso的示例Win32Functions Class與CD-Drive進行交互。我想讀取托盤中cd的ISRC代碼。首先一些代碼 - 這是Win32Functions類 - 我添加了希望正確的代碼,用於讀取CD的Q_CHANNEL,這裏找到的一些C++結構(http://msdn.microsoft.com/en-us/library/windows/hardware/ff567601%28v=vs.85%29.aspx),並試圖將它們融入到C#中。最後,我添加的DeviceIoControl的呼籲讀取軌道的ISRC碼:PInvoke用於讀取ISRC的DeviceIOControl不返回數據

namespace MyISRCReader 
    { 
    /// <summary> 
    /// Wrapper class for Win32 functions and structures needed to handle CD. 
    /// </summary> 
    internal class Win32Functions 
    { 
    public enum DriveTypes:uint 
    { 
     DRIVE_UNKNOWN = 0, 
     DRIVE_NO_ROOT_DIR, 
     DRIVE_REMOVABLE, 
     DRIVE_FIXED, 
     DRIVE_REMOTE, 
     DRIVE_CDROM, 
     DRIVE_RAMDISK 
    }; 

    [System.Runtime.InteropServices.DllImport("Kernel32.dll")] 
    public extern static DriveTypes GetDriveType(string drive); 

    //DesiredAccess values 
    public const uint GENERIC_READ  = 0x80000000; 
    public const uint GENERIC_WRITE  = 0x40000000; 
    public const uint GENERIC_EXECUTE = 0x20000000; 
    public const uint GENERIC_ALL  = 0x10000000; 

    //Share constants 
    public const uint FILE_SHARE_READ = 0x00000001; 
    public const uint FILE_SHARE_WRITE = 0x00000002; 
    public const uint FILE_SHARE_DELETE = 0x00000004; 

    //CreationDisposition constants 
    public const uint CREATE_NEW  = 1; 
    public const uint CREATE_ALWAYS  = 2; 
    public const uint OPEN_EXISTING  = 3; 
    public const uint OPEN_ALWAYS  = 4; 
    public const uint TRUNCATE_EXISTING = 5; 

    /// <summary> 
    /// Win32 CreateFile function, look for complete information at Platform SDK 
    /// </summary> 
    /// <param name="FileName">In order to read CD data FileName must be "\\.\\D:" where D is the CDROM drive letter</param> 
    /// <param name="DesiredAccess">Must be GENERIC_READ for CDROMs others access flags are not important in this case</param> 
    /// <param name="ShareMode">O means exlusive access, FILE_SHARE_READ allow open the CDROM</param> 
    /// <param name="lpSecurityAttributes">See Platform SDK documentation for details. NULL pointer could be enough</param> 
    /// <param name="CreationDisposition">Must be OPEN_EXISTING for CDROM drives</param> 
    /// <param name="dwFlagsAndAttributes">0 in fine for this case</param> 
    /// <param name="hTemplateFile">NULL handle in this case</param> 
    /// <returns>INVALID_HANDLE_VALUE on error or the handle to file if success</returns> 
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
    public extern static IntPtr CreateFile(string FileName, uint DesiredAccess, 
     uint ShareMode, IntPtr lpSecurityAttributes, 
     uint CreationDisposition, uint dwFlagsAndAttributes, 
     IntPtr hTemplateFile); 

    /// <summary> 
    /// The CloseHandle function closes an open object handle. 
    /// </summary> 
    /// <param name="hObject">Handle to an open object.</param> 
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns> 
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
    public extern static int CloseHandle(IntPtr hObject); 

    public const uint IOCTL_CDROM_READ_TOC   = 0x00024000; 
    public const uint IOCTL_STORAGE_CHECK_VERIFY = 0x002D4800; 
    public const uint IOCTL_CDROM_RAW_READ   = 0x0002403E; 
    public const uint IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804; 
    public const uint IOCTL_STORAGE_EJECT_MEDIA = 0x002D4808; 
    public const uint IOCTL_STORAGE_LOAD_MEDIA  = 0x002D480C; 
    public const uint IOCTL_CDROM_READ_Q_CHANNEL = 0x0002402C; // found here: http://mu97bot.googlecode.com/svn-history/r2/trunk/Include/WinAPIEx.au3 
                    // and here: http://www.ioctls.net/ 


    /// <summary> 
    /// Most general form of DeviceIoControl Win32 function 
    /// </summary> 
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"/></param> 
    /// <param name="IoControlCode">Code of DeviceIoControl operation</param> 
    /// <param name="lpInBuffer">Pointer to a buffer that contains the data required to perform the operation.</param> 
    /// <param name="InBufferSize">Size of the buffer pointed to by lpInBuffer, in bytes.</param> 
    /// <param name="lpOutBuffer">Pointer to a buffer that receives the operation's output data.</param> 
    /// <param name="nOutBufferSize">Size of the buffer pointed to by lpOutBuffer, in bytes.</param> 
    /// <param name="lpBytesReturned">Receives the size, in bytes, of the data stored into the buffer pointed to by lpOutBuffer. </param> 
    /// <param name="lpOverlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param> 
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns> 
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
    public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode, 
     IntPtr lpInBuffer, uint InBufferSize, 
     IntPtr lpOutBuffer, uint nOutBufferSize, 
     ref uint lpBytesReturned, 
     IntPtr lpOverlapped); 

    [ StructLayout(LayoutKind.Sequential)] 
    public struct TRACK_DATA 
    { 
     public byte Reserved; 
     private byte BitMapped; 
     public byte Control 
     { 
     get 
     { 
      return (byte)(BitMapped & 0x0F); 
     } 
     set 
     { 
      BitMapped = (byte)((BitMapped & 0xF0) | (value & (byte)0x0F)); 
     } 
     } 
     public byte Adr 
     { 
     get 
     { 
      return (byte)((BitMapped & (byte)0xF0) >> 4); 
     } 
     set 
     { 
      BitMapped = (byte)((BitMapped & (byte)0x0F) | (value << 4)); 
     } 
     } 
     public byte TrackNumber; 
     public byte Reserved1; 
     /// <summary> 
     /// Don't use array to avoid array creation 
     /// </summary> 
     public byte Address_0; 
     public byte Address_1; 
     public byte Address_2; 
     public byte Address_3; 
    }; 

    public const int MAXIMUM_NUMBER_TRACKS = 100; 

    [ StructLayout(LayoutKind.Sequential)] 
    public class TrackDataList 
    { 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst=MAXIMUM_NUMBER_TRACKS*8)] 
     private byte[] Data; 
     public TRACK_DATA this [int Index] 
     { 
     get 
     { 
      if ((Index < 0) | (Index >= MAXIMUM_NUMBER_TRACKS)) 
      { 
      throw new IndexOutOfRangeException(); 
      } 
      TRACK_DATA res; 
      GCHandle handle = GCHandle.Alloc(Data, GCHandleType.Pinned); 
      try 
      { 
      IntPtr buffer = handle.AddrOfPinnedObject(); 
      buffer = (IntPtr)(buffer.ToInt32() + (Index*Marshal.SizeOf(typeof(TRACK_DATA)))); 
      res = (TRACK_DATA)Marshal.PtrToStructure(buffer, typeof(TRACK_DATA)); 
      } 
      finally 
      { 
      handle.Free(); 
      } 
      return res; 
     } 
     } 
     public TrackDataList() 
     { 
     Data = new byte[MAXIMUM_NUMBER_TRACKS*Marshal.SizeOf(typeof(TRACK_DATA))]; 
     } 
    } 

    //Struktur für den Input Parameter: 
    public const uint IOCTL_CDROM_TRACK_ISRC = 0x3; 

    [StructLayout(LayoutKind.Sequential)] 
    public class _CDROM_SUB_Q_DATA_FORMAT 
    { 
     public byte Format; 
     public byte Track; 
    } 
    //Strukturen für den Output Parameter 
    [StructLayout(LayoutKind.Explicit)] 
    public class _SUB_Q_CHANNEL_DATA 
    { 
     [FieldOffset(0)] 
     public _SUB_Q_CURRENT_POSITION  CurrentPosition; 
     [FieldOffset(0)] 
     public _SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog; 
     [FieldOffset(0)] 
     public _SUB_Q_TRACK_ISRC TrackIsrc; 
    } 


    [StructLayout(LayoutKind.Sequential)] 
    public class _SUB_Q_CURRENT_POSITION 
    { 
     public _SUB_Q_HEADER Header; 
     public byte   FormatCode; 
     //private byte  BitMapped; 
     public byte BitMapped; 
     //public byte Control 
     //{ 
     // get 
     // { 
     //  return (byte)(BitMapped & 0x0F); 
     // } 
     // set 
     // { 
     //  BitMapped = (byte)((BitMapped & 0xF0) | (value & (byte)0x0F)); 
     // } 
     //} 
     //public byte Adr 
     //{ 
     // get 
     // { 
     //  return (byte)((BitMapped & (byte)0xF0) >> 4); 
     // } 
     // set 
     // { 
     //  BitMapped = (byte)((BitMapped & (byte)0x0F) | (value << 4)); 
     // } 
     //} 
     public byte   TrackNumber; 
     public byte   IndexNumber; 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)] 
     public byte[]  AbsoluteAddress; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)] 
     public byte[]  TrackRelativeAddress; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public class _SUB_Q_MEDIA_CATALOG_NUMBER 
    { 
     _SUB_Q_HEADER Header; 
     byte  FormatCode; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)] 
     public byte[]  Reserved; 
     //private byte BitMapped; 
     public byte BitMapped; 
     //public byte Reserved1 
     //{ 
     // get 
     // { 
     //  return (byte)(BitMapped & 0xFE); 
     // } 
     // set 
     // { 
     //  BitMapped = (byte)((BitMapped & 0x01) | (value & (byte)0xFE)); 
     // } 
     //} 
     //public byte Mcval 
     //{ 
     // get 
     // { 
     //  return (byte)((BitMapped & (byte)0x01) >> 7); 
     // } 
     // set 
     // { 
     //  BitMapped = (byte)((BitMapped & (byte)0xFE) | (value << 7)); 
     // } 
     //} 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst=15)] 
     public byte[] MediaCatalog; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public class _SUB_Q_TRACK_ISRC 
    { 

     public _SUB_Q_HEADER Header; 
     public byte FormatCode; 
     public byte Reserved0; 
     public byte Track; 
     public byte Reserved1; 
     //private byte BitMapped; 
     public byte BitMapped; 
     //public byte Reserved2 
     //{ 
     // get 
     // { 
     //  return (byte)(BitMapped & 0xFE); 
     // } 
     // set 
     // { 
     //  BitMapped = (byte)((BitMapped & 0x01) | (value & (byte)0xFE)); 
     // } 
     //} 
     //public byte Tcval 
     //{ 
     // get 
     // { 
     //  return (byte)((BitMapped & (byte)0x01) >> 7); 
     // } 
     // set 
     // { 
     //  BitMapped = (byte)((BitMapped & (byte)0xFE) | (value << 7)); 
     // } 
     //} 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst=15)] 
     public byte[]  TrackIsrc; 

    } 

    [StructLayout(LayoutKind.Sequential)] 
    public class _SUB_Q_HEADER 
    { 
     public byte Reserved; 
     public byte AudioStatus; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)] 
     public byte[] DataLength; 
    } 



    [StructLayout(LayoutKind.Sequential)] 
    public class CDROM_TOC 
    { 
     public ushort Length; 
     public byte FirstTrack = 0; 
     public byte LastTrack = 0; 

     public TrackDataList TrackData; 

     public CDROM_TOC() 
     { 
     TrackData = new TrackDataList(); 
     Length = (ushort)Marshal.SizeOf(this); 
     } 
    } 

    [ StructLayout(LayoutKind.Sequential)] 
    public class PREVENT_MEDIA_REMOVAL 
    { 
     public byte PreventMediaRemoval = 0; 
    } 



    public enum TRACK_MODE_TYPE { YellowMode2, XAForm2, CDDA } 
    [ StructLayout(LayoutKind.Sequential)] 
     public class RAW_READ_INFO 
    { 
     public long DiskOffset = 0; 
     public uint SectorCount = 0; 
     public TRACK_MODE_TYPE TrackMode = TRACK_MODE_TYPE.CDDA; 
    } 

    /// <summary> 
    /// Overload version of DeviceIOControl to read the TOC (Table of contents) 
    /// </summary> 
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"/></param> 
    /// <param name="IoControlCode">Must be IOCTL_CDROM_READ_TOC for this overload version</param> 
    /// <param name="InBuffer">Must be <code>IntPtr.Zero</code> for this overload version </param> 
    /// <param name="InBufferSize">Must be 0 for this overload version</param> 
    /// <param name="OutTOC">TOC object that receive the CDROM TOC</param> 
    /// <param name="OutBufferSize">Must be <code>(UInt32)Marshal.SizeOf(CDROM_TOC)</code> for this overload version</param> 
    /// <param name="BytesReturned">Receives the size, in bytes, of the data stored into OutTOC</param> 
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param> 
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns> 
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
    public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode, 
     IntPtr InBuffer, uint InBufferSize, 
     [Out] CDROM_TOC OutTOC, uint OutBufferSize, 
     ref uint BytesReturned, 
     IntPtr Overlapped); 

    /// <summary> 
    /// Overload version of DeviceIOControl to lock/unlock the CD 
    /// </summary> 
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"/></param> 
    /// <param name="IoControlCode">Must be IOCTL_STORAGE_MEDIA_REMOVAL for this overload version</param> 
    /// <param name="InMediaRemoval">Set the lock/unlock state</param> 
    /// <param name="InBufferSize">Must be <code>(UInt32)Marshal.SizeOf(PREVENT_MEDIA_REMOVAL)</code> for this overload version</param> 
    /// <param name="OutBuffer">Must be <code>IntPtr.Zero</code> for this overload version </param> 
    /// <param name="OutBufferSize">Must be 0 for this overload version</param> 
    /// <param name="BytesReturned">A "dummy" varible in this case</param> 
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param> 
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns> 
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
    public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode, 
     [In] PREVENT_MEDIA_REMOVAL InMediaRemoval, uint InBufferSize, 
     IntPtr OutBuffer, uint OutBufferSize, 
     ref uint BytesReturned, 
     IntPtr Overlapped); 

    /// <summary> 
    /// Overload version of DeviceIOControl to read digital data 
    /// </summary> 
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"</param> 
    /// <param name="IoControlCode">Must be IOCTL_CDROM_RAW_READ for this overload version</param> 
    /// <param name="rri">RAW_READ_INFO structure</param> 
    /// <param name="InBufferSize">Size of RAW_READ_INFO structure</param> 
    /// <param name="OutBuffer">Buffer that will receive the data to be read</param> 
    /// <param name="OutBufferSize">Size of the buffer</param> 
    /// <param name="BytesReturned">Receives the size, in bytes, of the data stored into OutBuffer</param> 
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param> 
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns> 
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
    public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode, 
     [In] RAW_READ_INFO rri, uint InBufferSize, 
     [In, Out] byte[] OutBuffer, uint OutBufferSize, 
     ref uint BytesReturned, 
     IntPtr Overlapped); 

    /// <summary> 
    /// Overload Version of DeviceIOControl to read ISRC Code from CD for special track 
    /// </summary> 
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="MyISRCReader.Win32Functions.CreateFile"</param> 
    /// <param name="IoControlCode">Must be IOCTL_CDROM_READ_Q_CHANNEL</param> 
    /// <param name="fmt">_CDROM_SUB_Q_DATA_FORMAT structure </param> 
    /// <param name="InBufferSize">Size of _CDROM_SUB_Q_DATA_FORMAT</param> 
    /// <param name="OutData">_SUB_Q_CHANNEL_DATA structure</param> 
    /// <param name="OutBufferSize">Size of _SUB_Q_CHANNEL_DATA buffer</param> 
    /// <param name="BytesReturned">Receives the size, in bytes, of the data stored into OutBuffer</param> 
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param> 
    /// <returns>0 if the function fails and nonzero if the function succeeded</returns> 
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError = true)] 
    public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode, 
     [In] _CDROM_SUB_Q_DATA_FORMAT fmt, uint InBufferSize, 
     [In, Out] _SUB_Q_CHANNEL_DATA OutData, uint OutBufferSize, 
     ref uint BytesReturned, 
     IntPtr Overlapped); 
    } 
} 

這是我簡單的試驗計劃 - 我打開的句柄CD,讓我的CD的正確軌道計數。我的ISRC-Read之後的錯誤代碼是1,這意味着「錯誤的功能」。輸出結構數據全部爲0 ...

namespace MyISRCReader 
{  
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IntPtr cdHandle = IntPtr.Zero; 
      char Drive = 'D'; 
      Win32Functions.CDROM_TOC Toc = new Win32Functions.CDROM_TOC(); 
      bool TocValid; 

      if (Win32Functions.GetDriveType(Drive + ":\\") == Win32Functions.DriveTypes.DRIVE_CDROM) 
      { 
       cdHandle = Win32Functions.CreateFile("\\\\.\\" + Drive + ':', Win32Functions.GENERIC_READ, Win32Functions.FILE_SHARE_READ, IntPtr.Zero, Win32Functions.OPEN_EXISTING, 0, IntPtr.Zero); 
       if (((int)cdHandle != -1) && ((int)cdHandle != 0)) 
       { 
        uint Dummy = 0; 
        Win32Functions.DeviceIoControl(cdHandle, Win32Functions.IOCTL_STORAGE_LOAD_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, ref Dummy, IntPtr.Zero); 
        uint BytesRead = 0; 
        TocValid = Win32Functions.DeviceIoControl(cdHandle, Win32Functions.IOCTL_CDROM_READ_TOC, IntPtr.Zero, 0, Toc, (uint)Marshal.SizeOf(Toc), ref BytesRead, IntPtr.Zero) != 0; 
        if (TocValid) 
        { 
         Console.WriteLine("track count: " + (Toc.LastTrack - Toc.FirstTrack + 1)); 

         Win32Functions._CDROM_SUB_Q_DATA_FORMAT format = new Win32Functions._CDROM_SUB_Q_DATA_FORMAT(); 
         Win32Functions._SUB_Q_CHANNEL_DATA data = new Win32Functions._SUB_Q_CHANNEL_DATA(); 

         format.Format = (byte)Win32Functions.IOCTL_CDROM_TRACK_ISRC; 
         format.Track = (byte)2; 

         Console.WriteLine("Last Win32 Error beforde ISRC read: " + Marshal.GetLastWin32Error().ToString()); 

         int erg = Win32Functions.DeviceIoControl(cdHandle, 
                 Win32Functions.IOCTL_CDROM_TRACK_ISRC, 
                 format, 
                 (uint)Marshal.SizeOf(format), 
                 data, 
                 (uint)Marshal.SizeOf(data), 
                 ref Dummy, 
                 IntPtr.Zero); 

         Console.WriteLine("Last Win32 error: " + Marshal.GetLastWin32Error().ToString()); 
         if (erg == 0) 
         { 
          //Reading ISRC failed 
          Console.WriteLine("Could not read ISRC!"); 
         } 
         else 
         { 
          //Reading ISRC succeeded 
          Console.WriteLine("Tracknumber: " + data.TrackIsrc.Track + " - ISRC: " + data.TrackIsrc.TrackIsrc[data.TrackIsrc.Track].ToString()); 
         } 
        } 
        else 
        { 
         Console.WriteLine("Toc invalid"); 
        } 

       } 
       else 
       { 
        Console.WriteLine("Handle Error"); 
       } 
      } 
      Console.Write("Eject media? (y/n)"); 
      var ch = Console.ReadKey(); 
      if (ch.KeyChar == 'y' || ch.KeyChar=='Y') 
      { 
       uint Dummy = 0; 
       Win32Functions.DeviceIoControl(cdHandle, Win32Functions.IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, ref Dummy, IntPtr.Zero); 
      } 
      Console.ReadKey(); 
     } 
    } 
} 

PInvoke調用和結構(類)是否正確? Win32Functions中的註釋部分(模擬C++結構)是模擬C++位域的替代方法。我不知道如何調試,或者我可以找到錯誤...有人有一個想法?

回答

0

那麼一種可能是,當你調用下面一行:你傳遞格式

int erg = Win32Functions.DeviceIoControl(cdHandle, 
             Win32Functions.IOCTL_CDROM_TRACK_ISRC, 
             format, 
             (uint)Marshal.SizeOf(format), 
             data, 
             (uint)Marshal.SizeOf(data), 
             ref Dummy, 
             IntPtr.Zero); 

,並根據dllimport的聲明該公司預計IntPtrs爲他們當數據對象直接。

[System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
public extern static int DeviceIoControl(IntPtr hDevice, 
             uint IoControlCode, 
             IntPtr lpInBuffer, 
             uint InBufferSize, 
             IntPtr lpOutBuffer, 
             uint nOutBufferSize, 
             ref uint lpBytesReturned, 
             IntPtr lpOverlapped); 

現在我可能會預期某種訪問衝突異常,而不是你得到的東西,但它是一個開始的地方。

另外,你是否確實知道當你獲得格式和數據的大小時,它會返回正確的大小?我看你們還定義爲

[StructLayout(LayoutKind.Sequential)] 
public class _CDROM_SUB_Q_DATA_FORMAT 
{ 
    public byte Format; 
    public byte Track; 
} 

現在你所期望的尺寸將是2個字節的其中之一,但它實際上可以出來爲8,因爲默認情況下它會帶大小,以便每個字段從4字節邊界開始。 您可以將Pack = 1添加到屬性以確保它使用最小字節數。但是你應該仔細檢查大小,並且它們與C++代碼中定義的內容相對應。