natbro / UnityPlugin

example of callbacks, recurring callbacks, buffer marshaling from native-code to Unity/C#
103 stars 9 forks source link

accessing local variables in C# #1

Closed aswinthomas closed 9 years ago

aswinthomas commented 9 years ago

Will it be possible to 'set' local variables from inside the C# callback?

I get the following when I use any kind of variable. Could this be because I am storing the address of the delegate inside the 'C' file for later use?

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object
natbro commented 9 years ago

without looking at your exact code, i'd just say "probably" :) as I'm not sure exactly what you're trying to do. in the context of the example project, what i think you're looking to do is create a "recurring callback" type of call where when you call up you are setting local members.

if you'd like to post an example of what's going wrong i'd be happy to give you some more specific feedback.

aswinthomas commented 9 years ago

Sorry about that. I dont actually have the exact code at the moment since I gave up and switched to polling. Basically I used the _onetime_callback with a float argument. The only difference was this: 1) Inside the 'C' code, I make a copy of the C# function

SIMPLE_CALLBACK callback_copy;
void reply_during_call(SIMPLE_CALLBACK callback) {
  if (callback) {
    callback_copy=callback;
  }
}

void some_other_task(){
  float pi=3.14;
  callback_copy(pi);
}

2) Inside the C# code, I try to set a local variable

float my_arg;
void _onetime_callback(float arg) {
  my_arg = arg; // throws error
  Console.WriteLine(arg); // this works well if the above line is commented
}
natbro commented 9 years ago

gotcha. no worries, i understand now. what you want in this case is to use the _recurring_callback example at https://github.com/natbro/UnityPlugin/blob/master/UnityPluginTest/Assets/Scripts/UnityPlugin.cs#L37-L48. what was happening when you received the NULL reference exception is that the little wrapper object that you were holding onto was working - it was getting you back into C# at the correct method, but the instance value was no longer valid (because you didn't have it pinned down in a member variable). using _recurring_callback and/or polling where you hold onto the delegate object (which refers to the "this" or instance object in a steady way) will allow it to work. good luck!

aswinthomas commented 9 years ago

Perfect! Thank you

Following is my code that passes a struct. Maybe useful for others. C code:

typedef struct {
  float steer;
  float motor;
  float brake;
  int gear;
} ControlData;

ControlData *cd = new ControlData();

typedef void (*CONTROL_CALLBACK)(ControlData *cd);
CONTROL_CALLBACK controlCallbackFunction;

extern "C" int setControlCallback(CONTROL_CALLBACK callback) {
  if (callback) {
    printf("Setup controlCallbackFunction\n");
    controlCallbackFunction = callback;
  }

void task() {
  //populate *cd
  controlCallbackFunction(cd);
}

C# code

private delegate void ControlCallbackDelegate(ref ControlStruct value);
private ControlCallbackDelegate controlCallbackHolder;
[DllImport ("comms_interface")]
private static extern int setControlCallback(ControlCallbackDelegate callback);

[StructLayout(LayoutKind.Sequential)]
public struct ControlStruct
 {
  [MarshalAs(UnmanagedType.R4)]
  public float targetSteer;
  [MarshalAs(UnmanagedType.R4)]
  public float targetMotor;
  [MarshalAs(UnmanagedType.R4)]
  public float targetBrake;
  [MarshalAs(UnmanagedType.I4)]
  public Int32 targetGear;
 }

void Awake() {
  controlCallbackHolder = new ControlCallbackDelegate(controlCallback);
  setControlCallback(controlCallbackHolder);
}

void controlCallback(ref ControlStruct value) {
        Console.WriteLine(value.targetSteer+" "+value.targetMotor+" "+
value.targetBrake+" "+value.targetGear);
}
natbro commented 9 years ago

cool, thanks for sharing & super glad it's working for you!