I am writing a wrapper C# DLL to interface with a supplier provided unmanaged DLL written in C. For the most part everything is smooth, however there are some functions that in the provided documentation look like this:
int get_value(int unit, int id, int* value)
My issue is that int* value
is an array with a mixture of int's and float's... The documentation states which is which, i.e. that the first value is an int
, the second and third are float
, etc.. I have some success if i specifically call out the DLL value
as int
or float
. That returns some of the data i'm looking for, but only for the one type. For example, this kinda works:
[DllImport("supplier.dll")]
public static extern int get_value(int unit, int id, float* value)
public static int GetValue(int unit, int id, out float[] value)
{
float[] value = new float[3];
int ret = get_value(unit, id, value);
return ret;
}
value
is returned with [NaN, value1, value2] because the 0 index is actually an integer and it's not interpreted correctly. I have attempted to use an IntPtr[] as the return value
in the unmanaged DLL and then use Marshal.Copy()
to get the data from the memory locations, but this hasn't gone well. Is that a promising approach? If so, I'm not sure which overload to use, an example would be very helpful!
int* value
is an array with a mixture of int's and float's...
That's a serious violation of the strict aliasing rule and will invoke undefined behavior. In C you'll have to use a union instead. The documentation should be like this
union Value
{
int i;
float f;
};
int get_value(int unit, int id, union Value* value);
Disregarding the original UB in supplier.dll C# you can simulate the similar feature with FieldOffset
[StructLayout(LayoutKind.Explicit)]
public struct Value
{
[FieldOffset(0)] public int i;
[FieldOffset(0)] public float f;
}
[DllImport("supplier.dll")]
public static extern int get_value(int unit, int id, out Value[] value);
public static int GetValue(int unit, int id, out Value[] value)
{
float[] value = new float[3];
int ret = get_value(unit, id, value);
return ret;
}
After obtaining the array, just use the correct field to get the expected value
var ret = GetValue(unit, id, out Value[] value);
var f0 = value[0].f;
var i1 = value[1].i;
If you really want to do that without a union then you must get the float's binary representation like this
float f0 = value[0];
int i1 = BitConverter.SingleToInt32Bits(value[1]);
int i2 = BitConverter.SingleToInt32Bits(value[2]);
value[1] = BitConverter.Int32BitsToSingle(f0 + 0.123f);
But if the array always has 3 items like that then why return an array instead of a struct?
public struct Values
{
public float f0;
public int i1;
public int i2;
}
[DllImport("supplier.dll")]
public static extern int get_value(int unit, int id, out Values values);
public static int GetValue(int unit, int id, out Values values) {...}