Native plugin with IL2CPP throws ArgumentException

I’m trying to have one of my plugins working in IL2CPP but they keep throwing me an exception.

ArgumentException: Type BE_CONFIG cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.

This plugin works fine in Mono 2.x.

The Type in question here is:

  [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi), Serializable]
  public class BE_CONFIG
  {
  // encoding formats
  public const uint BE_CONFIG_MP3    = 0;
  public const uint BE_CONFIG_LAME = 256;

  public uint   dwConfig;
  public Format format;

  public BE_CONFIG(WaveFormat format, uint MpeBitRate)
  {
  this.dwConfig = BE_CONFIG_LAME;
  this.format = new Format(format, MpeBitRate);
  }
  public BE_CONFIG(WaveFormat format)
  : this(format, 128)
  {
  }
  }

Is anyone else having this kind of issues?

Hi,

could you post type definitions for Format and WaveFormat types?

Yes of course. Should have done it in the first place.
Thanks in advance.

BE_CONFIG

  [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi), Serializable]
  public class BE_CONFIG
  {
    // encoding formats
    public const uint BE_CONFIG_MP3     = 0;
    public const uint BE_CONFIG_LAME = 256;

    public uint    dwConfig;
    public Format format;

    public BE_CONFIG(WaveFormat format, uint MpeBitRate)
    {
      this.dwConfig = BE_CONFIG_LAME;
      this.format = new Format(format, MpeBitRate);
    }
    public BE_CONFIG(WaveFormat format)
      : this(format, 128)
    {
    }
  }

Format

  [StructLayout(LayoutKind.Explicit), Serializable]
  public class Format
  {
    [FieldOffset(0)]
    public MP3 mp3;
    [FieldOffset(0)]
    public LHV1 lhv1;
    [FieldOffset(0)]
    public ACC acc;

  public Format(WaveFormat format, uint MpeBitRate)
  { .. }
  }

MP3

  [StructLayout(LayoutKind.Sequential), Serializable]
  public struct    MP3 //BE_CONFIG_MP3
  {
    public uint   dwSampleRate;        // 48000, 44100 and 32000 allowed
    public byte      byMode;            // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO
    public ushort    wBitrate;        // 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 allowed
    public int      bPrivate;       
    public int      bCRC;
    public int      bCopyright;
    public int      bOriginal;
  }

ACC

  [StructLayout(LayoutKind.Sequential), Serializable]
  public struct    ACC
  {
    public uint    dwSampleRate;
    public byte    byMode;
    public ushort    wBitrate;
    public byte    byEncodingMethod;
  }

LHV1

[StructLayout(LayoutKind.Sequential, Size=327), Serializable]
  public struct LHV1 // BE_CONFIG_LAME LAME header version 1
  {
    public const uint MPEG1    = 1;
    public const uint MPEG2    = 0;

    // STRUCTURE INFORMATION
    public uint            dwStructVersion;   
    public uint            dwStructSize;
    // BASIC ENCODER SETTINGS
    public uint            dwSampleRate;        // SAMPLERATE OF INPUT FILE
    public uint            dwReSampleRate;        // DOWNSAMPLERATE, 0=ENCODER DECIDES 
    public MpegMode            nMode;                // STEREO, MONO
    public uint            dwBitrate;            // CBR bitrate, VBR min bitrate
    public uint            dwMaxBitrate;        // CBR ignored, VBR Max bitrate
    public LAME_QUALITY_PRESET            nPreset;            // Quality preset
    public uint            dwMpegVersion;        // MPEG-1 OR MPEG-2
    public uint            dwPsyModel;            // FUTURE USE, SET TO 0
    public uint            dwEmphasis;            // FUTURE USE, SET TO 0
    // BIT STREAM SETTINGS
    public int            bPrivate;            // Set Private Bit (TRUE/FALSE)
    public int            bCRC;                // Insert CRC (TRUE/FALSE)
    public int            bCopyright;            // Set Copyright Bit (TRUE/FALSE)
    public int            bOriginal;            // Set Original Bit (TRUE/FALSE)
    // VBR STUFF
    public int            bWriteVBRHeader;    // WRITE XING VBR HEADER (TRUE/FALSE)
    public int            bEnableVBR;            // USE VBR ENCODING (TRUE/FALSE)
    public int            nVBRQuality;        // VBR QUALITY 0..9
    public uint            dwVbrAbr_bps;        // Use ABR in stead of nVBRQuality
    public VBRMETHOD        nVbrMethod;
    public int            bNoRes;                // Disable Bit resorvoir (TRUE/FALSE)
    // MISC SETTINGS
    public int            bStrictIso;            // Use strict ISO encoding rules (TRUE/FALSE)
    public ushort        nQuality;            // Quality Setting, HIGH BYTE should be NOT LOW byte, otherwhise quality=5
  public LHV1(WaveFormat format, uint MpeBitRate)
  { ... }
  }

Where MpegMode, LAME_QUALITY_PRESET and VBRMETHOD are enums

WaveFormat

    [StructLayout(LayoutKind.Sequential)]
    public class WaveFormat
    {
        public short wFormatTag;
        public short nChannels;
        public int nSamplesPerSec;
        public int nAvgBytesPerSec;
        public short nBlockAlign;
        public short wBitsPerSample;
        public short cbSize;

        public WaveFormat(int rate, int bits, int channels)
        { .. }
}

Right, so I potentially see two issues here:

  1. We currently do not support LayoutKind.Explicit for marshaling. A fix for this is expected to land in 4.6.3p1, which is scheduled for next week;
  2. I’m not entirely sure right now, but I don’t think it’s legal to have fields that are classes (rather than structs/string/arrays). I believe this is true even for Mono, while .NET marshals them as IDispatch, which is only relevant to Windows. I may be mistaken on this one, I’d have to double check.

Anyway, what’s the native definitions for those structures? Are you marshaling it by passing through P/Invoke, or in some other way? It should be possible to workaround these limitations, especially if the plugin was made to be invoked from C#.

1 Like

Yes i’m marshaling it by passing through P/Invoke and it is invoked from C# to C.
The native definition is is part of Lame as this is a plugin to encode mp3.

    typedef struct    {
        DWORD    dwConfig;            // BE_CONFIG_XXXXX
                                // Currently only BE_CONFIG_MP3 is supported
        union    {
          
            struct    {
              
                DWORD    dwSampleRate;
                BYTE    byMode;           
                WORD    wBitrate;      
                BOOL    bPrivate;
                BOOL    bCRC;
                BOOL    bCopyright;
                BOOL    bOriginal;
              
            } mp3;                    // BE_CONFIG_MP3
          
            struct
            {
                // STRUCTURE INFORMATION
                DWORD            dwStructVersion;
                DWORD            dwStructSize;
              
                // BASIC ENCODER SETTINGS
                DWORD            dwSampleRate;        
                DWORD            dwReSampleRate;        
                LONG            nMode;               
                DWORD            dwBitrate;            
                DWORD            dwMaxBitrate;     
                LONG            nPreset;            
                DWORD            dwMpegVersion;       
                DWORD            dwPsyModel;            
                DWORD            dwEmphasis;           
              
                // BIT STREAM SETTINGS
                BOOL            bPrivate;            
                BOOL            bCRC;               
                BOOL            bCopyright;         
                BOOL            bOriginal;            
              
                // VBR STUFF
                BOOL            bWriteVBRHeader;   
                BOOL            bEnableVBR;            
                INT                nVBRQuality;        
                DWORD            dwVbrAbr_bps;    
                VBRMETHOD        nVbrMethod;
                BOOL            bNoRes;                
              
                // MISC SETTINGS
                BOOL            bStrictIso;            
                WORD            nQuality;            
              
                // FUTURE USE, SET TO 0, align strucutre to 331 bytes
                BYTE            btReserved[255-4*sizeof(DWORD) - sizeof( WORD )];
              
            } LHV1;                    // LAME header version 1
          
            struct    {
              
                DWORD    dwSampleRate;
                BYTE    byMode;
                WORD    wBitrate;
                BYTE    byEncodingMethod;
              
            } aac;
          
        } format;
      
    } BE_CONFIG, *PBE_CONFIG;

That doesn’t sound so bad at all after all. I’d create a separete BE_CONFIG class for each the types: MP3, LHV and AAC. Also, the comment in the native struct suggests that only MP3 is supported, and if that’s the case, you’d need only create one such class:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi), Serializable]
public class BE_CONFIG_MP3
{
    // encoding formats
    public const uint BE_CONFIG_MP3    = 0;
    public const uint BE_CONFIG_LAME = 256;

    public uint   dwConfig;
    public MP3 mp3;  // Just declare MP3 field inline

    public BE_CONFIG(WaveFormat format, uint MpeBitRate)
    {
        this.dwConfig = BE_CONFIG_LAME;
        this.format = /* ... */
    }
 
    public BE_CONFIG(WaveFormat format)
        : this(format, 128)
    {
    }
}

This should marshal just fine :).

1 Like

Thank you very much.
Yes bypassing the class where it was LayoutKind.Explicit did the trick. In fact i don’t think i’ll ever need that class and should go straight.
Appreciate it a lot. Thanks again and congrats on the great job you guys do it at Unity.