Quantcast
Channel: Common Language Runtime Internals and Architecture forum
Viewing all articles
Browse latest Browse all 1710

passing a float value by reference to unmanaged code to be used in callbacks: I'm missing something ...

$
0
0

Conside the 'thought-experiment' straight C routine:

void setToThree(float *v)
{
  if (v == NULL) return;
  *v = 3.0;
}

This won't care (in C) if you pass a pointer to a float variable or to an array of 1 float:

float v_value;
float v_array[1];

setToThree(&v_value) -> v_value = 3.0;
setToThree(v_array) -> v_array[0] = 3.0; // or maybe 3.000000000000004

Let's put this in a DLL along with the ability to fire a callback at some interval.  The idea being, I want to be able to update from this callback some VB.NET Single variable whenever the callback is fired (still a thought experiment here).

So assume some C# .NET dll that does the interop and that might have a class 'Three': 

public class Three
{  private object threeObject = null;
    private float threeFloat;
    private IntPtr threePtr;
    private GCHandle gch;

    public delegate void TimedCallback();

    private TimedCallback vbCallback; 

    public void SetToThree(ref float v)
    {
      threeObject = v;
      threeFloat = v;
      unsafe { fixed (float* p = &v) threePtr = new IntPtr((void*)p); }
      gch = GCHandle.Alloc(v, GCHandleType.Pinned);
      native.setToThree(ref threeFloat);
    }

    public unsafe void nativeTimedCallback() // to be passed to the C DLL
    {
      if (threeObject != null)
      {
        native.setToThree(ref threeFloat);
      //  *(float*)gch.AddrOfPinnedObject().ToPointer() = threeFloat;
        *(float *)threePtr = threeFloat;
      }

     if (vbCallback != null) vbCallback();
  }
  public StartThreeCallback(ref float v,TimedCallback cb) 
  {

     SetToThree(v);

     vbCallback = cb;
  }
}

Of course without the callback business, just calling SetToThree() without all the pinning, etc. would do the trick.  But continuing with my dilemma, suppose in VB you call the StartThreeCallback() and try to do something like:

  Dim fv As Single
  Delegate Sub vbcbDelegate()
  Public Sub vbcb()
    If sumcurrent.InvokeRequired = True Then
      Invoke(New vbcbDelegate(AddressOf vbcb))
      Exit Sub
    End If
    label1.Text = "value: " + Str(fv)
    fv = 4.0
  End Sub

I want to see label1 always show me '3' and never '4'. Assuming there's a timer calling the callback every second, this actually works for a few rounds and then just stays put at '4'.   Something seems to have moved (in spite of the 'pinned'), but at least nothing ever crashes.  If I use the (commented out in nativeTimedCallback) 'AddrOfPinnedObject' variant, it never works (always see a '4')!  In the debugger I see that the "fixed (float* p = &v) threePtr = new IntPtr((void*)p);" way of getting a pointer gives a different IntPtr than AddrOfPinnedObject().  Hmmm....

If I make the overloads to handle a float[] value and use a Dim v(0) As Single in VB it always works (and 'AddrOfPinnedObject' is the way to go here)! 

What am I missing concerning updating a single variable passed by reference from an unmanaged callback?

 Thanks in advance!


Viewing all articles
Browse latest Browse all 1710


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>