2015-10-05 31 views
0

我有一个静态库(* .a for iOS),其中包含一些我需要分配给C#回调的函数。该代码工作正常,没有回调,但是当我将委托添加到结构时,出现以下错误:如何设置DLL的代理/回调(C和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; 
      } 
     }   
    } 
... 
} 

回答

0

以下是基于@Josh Peterson建议的工作代码。 C代码是一样的。只改变了C#端。

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

... 


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; 
       IntPtr basic_callbck_intptr = MyPhone.basic_callbck_intptr; 
       basic_callback basic_callbck = Marshal.GetDelegateForFunctionPointer(basic_callbck_intptr, typeof(basic_callback)) as basic_callback; 

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

      } 
     } 
0

从MSDN

You can apply this attribute to classes or structures. The common language runtime controls the physical layout of the data fields of a class or structure in managed memory. However, if you want to pass the type to unmanaged code, you can use the StructLayoutAttribute attribute to control the unmanaged layout of the type. Use the attribute with LayoutKind.Sequential to force the members to be laid out sequentially in the order they appear. For , LayoutKind.Sequential controls both the layout in managed memory and the layout in unmanaged memory. For non-blittable types, it controls the layout when the class or structure is marshaled to unmanaged code, but does not control the layout in managed memory. Use the attribute with LayoutKind.Explicit to control the precise position of each data member. This affects both managed and unmanaged layout, for both blittable and non-blittable types. Using LayoutKind.Explicit requires that you use the FieldOffsetAttribute attribute to indicate the position of each field within the type.

C#, Visual Basic, and C++ compilers apply the Sequential layout value to structures by default. For classes, you must apply the LayoutKind.Sequential value explicitly. The Tlbimp.exe (Type Library Importer) also applies the StructLayoutAttribute attribute; it always applies the LayoutKind.Sequential value when it imports a type library.

我觉得你的代码必须是

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; 
} 
+0

嗨@LPs,感谢您的回复。我刚刚尝试了你的建议。有同样的错误:'ArgumentException:指定的结构必须是blittable或有布局信息。 参数名称:结构 在FMOD_Listener.LoadPlugins()[0x00000]在<文件名未知>:0 在FMOD_Listener.Initialize()[0x00000]在<文件名未知>:0 (文件名:当前不可用的il2cpp线: -1)' –

0

这里的问题是,委托是不是blittable类型(搜索关于此page“代表”),所以这个错误开始发生当你将一个委托添加到结构中时。

错误的第一部分:

The specified structure must be blittable or have layout information.

是这里最重要的。错误的“布局信息”部分可以忽略。

最好的选择可能是通过一个输出参数的委托作为单独的参数到AigooRegisterPhone或在TELEPHONE结构使用一个IntPtr代替basic_callback类型。在后面的例子中,你可以调用Marshal.GetDelegateForFunctionPointer从本机函数指针获取一个C#委托。

+0

嗨@JoshPeterson我会尝试你的两个修复,并让你知道它是如何去的。感谢您的回复。 –

+0

嗨@JoshPeterson IntPtr解决方案工作,我更喜欢这个选项,因为我得到保持相同的结构,只需将其更改为IntPtr。发布下面的解决方案代码。 –