【问题标题】:How to setup delegate/callback from DLL (interop between C and C#)如何从 DLL 设置委托/回调(C 和 C# 之间的互操作)
【发布时间】:2015-10-05 05:46:01
【问题描述】:

我有一个静态库(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;
            }
        }        
    }
...
}

【问题讨论】:

    标签: c# ios c dll callback


    【解决方案1】:

    来自 MSDN

    您可以将此属性应用于类或结构。 公共语言运行库控制托管内存中类或结构的数据字段的物理布局。但是,如果要将类型传递给非托管代码,则可以使用 StructLayoutAttribute 特性来控制类型的非托管布局。使用带有 LayoutKind.Sequential 的属性来强制成员按照它们出现的顺序依次排列。对于 , LayoutKind.Sequential 控制托管内存中的布局和非托管内存中的布局。对于非 blittable 类型,当类或结构编组为非托管代码时,它控制布局,但不控制托管内存中的布局。 使用 LayoutKind.Explicit 属性来控制每个数据成员的精确位置。对于 blittable 和非 blittable 类型,这会影响托管和非托管布局。使用 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;
    }
    

    【讨论】:

    • 嗨@LPs,感谢您的回复。我刚试过你的建议。得到同样的错误:ArgumentException: The specified structure must be blittable or have layout information. Parameter name: structure at FMOD_Listener.LoadPlugins () [0x00000] in &lt;filename unknown&gt;:0 at FMOD_Listener.Initialize () [0x00000] in &lt;filename unknown&gt;:0 (Filename: currently not available on il2cpp Line: -1)
    【解决方案2】:

    这里的问题是委托不是 blittable 类型(在此 page 上搜索“委托”),因此当您将委托添加到结构时开始出现此错误。

    第一部分的错误:

    指定的结构必须是 blittable 或具有 布局信息。

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

    最好的选择可能是将委托的 out 参数作为单独的参数传递给 AigooRegisterPhone 或在 TELEPHONE 结构中使用 IntPtr 而不是 basic_callback 类型。在后一种情况下,您可以调用 Marshal.GetDelegateForFunctionPointer 从本地函数指针中获取 C# 委托。

    【讨论】:

    • 嗨@JoshPeterson,我会尝试您的两个修复程序,并告诉您进展如何。感谢您的回复。
    • 嗨@JoshPeterson IntPtr 解决方案有效,我更喜欢该选项,因为我可以保持相同的结构,只需将其更改为 IntPtr。下面贴出解决方案代码。
    【解决方案3】:

    这是基于@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; 
    
                }
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-02
      • 1970-01-01
      • 2019-10-27
      • 2017-10-26
      相关资源
      最近更新 更多