簡體   English   中英

如何從DLL設置委托/回調(C和C#之間的互操作)

[英]How to setup delegate/callback from DLL (interop between C and C#)

我有一個靜態庫(對於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;
}

這里的問題是委托不是可復制的類型(在此頁面上搜索“ delegate”),因此,當您將委托添加到結構中時,就會開始發生此錯誤。

錯誤的第一部分:

指定的結構必須是可漂白的或具有布局信息。

這里重要的是 錯誤的“布局信息”部分可以忽略。

最好的選擇可能是將委托的out參數作為單獨的參數傳遞給AigooRegisterPhone或者在TELEPHONE結構中使用IntPtr而不是basic_callback類型。 在后一種情況下,可以調用Marshal.GetDelegateForFunctionPointer以從本機函數指針獲取C#委托。

這是基於@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; 

            }
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM