我有一个静态库(对于iOS为* .a),其中包含一些我需要分配给C#回调的函数。该代码可以在没有回调的情况下正常运行,但是当我将委托添加到结构中时,它将失败并显示以下错误:

ArgumentException: The specified structure must be blittable or have
layout information. Parameter name: structure at
FMOD_Listener.LoadPlugins () [0x00000] in <filename unknown>:0  at
FMOD_Listener.Initialize () [0x00000] in <filename unknown>:0
(Filename: currently not available on il2cpp Line: -1)


这是本机代码(C):

extern "C" {
    typedef void (F_CALLBACK *basic_callback)  (int *value1);

    typedef struct telephone
    {
        int area_code;
        int number;
        basic_callback  basic_callbck;
    } TELEPHONE;

    F_DECLSPEC F_DLLEXPORT void F_STDCALL AigooRegisterPhone(TELEPHONE **telephone);

    void F_CALLBACK aigoo_basic_callback(int *value1)
    {
        *value1 = *value1 * 10 ;
    }

    F_DECLSPEC F_DLLEXPORT void F_STDCALL AigooRegisterPhone(TELEPHONE **telephone)
    {
        TELEPHONE* myPhone = new TELEPHONE ();
        myPhone->area_code = 929;
        myPhone->number = 823;
        myPhone->basic_callbck = aigoo_basic_callback;
        *telephone = myPhone;
    }
}


这是托管端C#:

public delegate void basic_callback (ref int value1);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TELEPHONE
{
    public int area_code;
    public int number;
    public basic_callback               basic_callbck;
}

public class FMODPlugInHandler {

    [DllImport ("__Internal")]
    public static extern void AigooRegisterPhone(out IntPtr TelephonePtr);

}

public class FMOD_Listener : MonoBehaviour
{

...

    void LoadPlugins()
    {

        int plugin_result = 0;

        if (Application.platform == RuntimePlatform.IPhonePlayer) {

            IntPtr PhoneIntPtr;
            FMODPlugInHandler.AigooRegisterPhone(out PhoneIntPtr);
            plugin_result = 823823823;
            myLog = "plugin_result = " + plugin_result + " PhoneIntPtr: " + PhoneIntPtr;
            if (PhoneIntPtr != IntPtr.Zero){
                TELEPHONE MyPhone = (TELEPHONE)Marshal.PtrToStructure(PhoneIntPtr, typeof(TELEPHONE));
                plugin_result = 123456;
                myLog = "result = " + plugin_result + " number: " + MyPhone.number ;

                int int_cs = 2;
                plugin_result = MyPhone.basic_callbck(ref int_cs);
                myLog = "result = " + plugin_result + " number: " + MyPhone.number + " int_cs: " + int_cs;
            }
        }
    }
...
}

最佳答案

从MSDN


  您可以将此属性应用于类或结构。
  公共语言运行时控制托管内存中类或结构的数据字段的物理布局。但是,如果要将类型传递给非托管代码,则可以使用StructLayoutAttribute属性来控制类型的非托管布局。将该属性与LayoutKind.Sequential一起使用,以强制成员按照它们出现的顺序顺序进行布局。对于,LayoutKind.Sequential同时控制托管内存中的布局和非托管内存中的布局。对于不可拆分类型,当类或结构封送为非托管代码时,它控制布局,但不控制托管内存中的布局。将该属性与LayoutKind.Explicit一起使用,以控制每个数据成员的精确位置。这会影响可托管和不可托管类型的托管布局和非托管布局。使用LayoutKind.Explicit要求您使用FieldOffsetAttribute属性指示类型中每个字段的位置。
  
  默认情况下,C#,Visual Basic和C ++编译器将顺序布局值应用于结构。对于类,必须显式应用LayoutKind.Sequential值。 Tlbimp.exe(类型库导入器)还应用了StructLayoutAttribute属性;导入类型库时,它将始终应用LayoutKind.Sequential值。


我认为您的代码必须是

StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct TELEPHONE
{
    [FieldOffset(0)]
    public int area_code;
    [FieldOffset(4)]
    public int number;
    [FieldOffset(8)]
    public basic_callback               basic_callbck;
}

10-04 17:28