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!