Search Unity

How to setup delegate/callback from DLL (interaction between C and C#)

Discussion in 'Scripting' started by cchacon, Oct 5, 2015.

  1. cchacon

    cchacon

    Joined:
    Sep 20, 2015
    Posts:
    18
    Hi Everyone,

    I have a static library (*.a for iOS) that contains some functions that I need to assign to a delegate on C#. I'm implementing the code below but I'm getting this error:

    Here is the native code (C):

    Code (Csharp):
    1. extern "C" {
    2.     typedef void (F_CALLBACK *basic_callback)  (int *value1);
    3.  
    4.     typedef struct telephone
    5.     {
    6.         int area_code;
    7.         int number;
    8.         basic_callback  basic_callbck;
    9.     } TELEPHONE;
    10.  
    11.     F_DECLSPEC F_DLLEXPORT void F_STDCALL AigooRegisterPhone(TELEPHONE **telephone);
    12.  
    13.     void F_CALLBACK aigoo_basic_callback(int *value1)
    14.     {
    15.         *value1 = *value1 * 10 ;
    16.     }
    17.  
    18.     F_DECLSPEC F_DLLEXPORT void F_STDCALL AigooRegisterPhone(TELEPHONE **telephone)
    19.     {
    20.         TELEPHONE* myPhone = new TELEPHONE ();
    21.         myPhone->area_code = 929;
    22.         myPhone->number = 823;
    23.         myPhone->basic_callbck = aigoo_basic_callback;
    24.         *telephone = myPhone;
    25.     }
    26. }
    This is the managed side C#:
    Code (CSharp):
    1. public delegate void basic_callback (ref int value1);
    2.  
    3. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    4. public struct TELEPHONE
    5. {
    6.     public int area_code;
    7.     public int number;
    8.     public basic_callback               basic_callbck;
    9. }
    10.  
    11. public class FMODPlugInHandler {
    12.  
    13.     [DllImport ("__Internal")]
    14.     public static extern void AigooRegisterPhone(out IntPtr TelephonePtr);
    15.  
    16. }
    17.  
    18. public class FMOD_Listener : MonoBehaviour
    19. {
    20.  
    21. ...
    22.  
    23.     void LoadPlugins()
    24.     {
    25.  
    26.         int plugin_result = 0;
    27.  
    28.         if (Application.platform == RuntimePlatform.IPhonePlayer) {
    29.  
    30.             IntPtr PhoneIntPtr;
    31.             FMODPlugInHandler.AigooRegisterPhone(out PhoneIntPtr);
    32.             plugin_result = 823823823;
    33.             myLog = "plugin_result = " + plugin_result + " PhoneIntPtr: " + PhoneIntPtr;
    34.             if (PhoneIntPtr != IntPtr.Zero){
    35.                 TELEPHONE MyPhone = (TELEPHONE)Marshal.PtrToStructure(PhoneIntPtr, typeof(TELEPHONE));
    36.                 plugin_result = 123456;
    37.                 myLog = "result = " + plugin_result + " number: " + MyPhone.number ;
    38.  
    39.                 int int_cs = 2;
    40.                 plugin_result = MyPhone.basic_callbck(ref int_cs);
    41.                 myLog = "result = " + plugin_result + " number: " + MyPhone.number + " int_cs: " + int_cs;
    42.             }
    43.         }        
    44.     }
    45. ...
    46. }
     
    Last edited: Oct 5, 2015
  2. cchacon

    cchacon

    Joined:
    Sep 20, 2015
    Posts:
    18
    anybody?
     
  3. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,422
    Hey @cchacon.

    I have a similar thing working in my app, but I really strapped for time right now (to look at your problem properly).

    I suspect it could be the signature of the callback having a 'ref int' parameter. try doing it first with no parameter and see if that works.

    I doubt a ref is blittable (just guessing here though).
     
  4. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    @larku is right on the money. ref and out are not supported on marshalled parameters. Return the modified struct in stead or store and return with a getter.
     
  5. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    When you are marshalling between managed and unmanaged memory, you are never dealing with the same references - hence there is no way that ref or out could work.

    The exception is when you instantiate or access objects via the static System.Runtime.InteropServices.Marshal API. Still no ref or out though - you just communicate by passing IntPtr pointers to unmanaged memory.
     
    cchacon and landon912 like this.
  6. cchacon

    cchacon

    Joined:
    Sep 20, 2015
    Posts:
    18
    larku likes this.
  7. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    I wouldn't recommend that design, but if it works for you, that is grand :)
     
  8. cchacon

    cchacon

    Joined:
    Sep 20, 2015
    Posts:
    18
    Hi @AngryAnt, I'm very interested to hear why you wouldnt recommend that approach. I definitely want to make it in the correct way. So I would be glad to hear your advice.
     
  9. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    I would as much as possible rely on the automatic marshalling capabilities of mono / .net and only consider Marshal API use for edge cases - where other designs don't work or you have a valid (data supported) performance concern.

    Concretely in this case, that means my earlier recommendation of passing back the modified struct as a return value or storing it for later retrieval via a getter (in case you wish to use the return value for error messages).