从非托管 C DLL 转换混合类型 return 数据?
Converting mixed type return data from an unmanaged C DLL?
我正在编写一个包装器 C# DLL 以与供应商提供的用 C 编写的非托管 DLL 进行交互。在大多数情况下,一切都很顺利,但是在提供的文档中有一些功能如下所示:
int get_value(int unit, int id, int* value)
我的问题是 int* value
是一个混合了 int 和 float 的数组...文档说明哪个是哪个,即第一个值是 int
,第二个和第三个是 float
,等等。如果我专门将 DLL value
调用为 int
或 float
,我会取得一些成功。 return 是我正在寻找的一些数据,但仅限于一种类型。例如,这有点有效:
[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
用 [NaN, value1, value2] return 编辑,因为 0 索引实际上是一个整数,它没有被正确解释。我试图在非托管 DLL 中使用 IntPtr[] 作为 return value
,然后使用 Marshal.Copy()
从内存位置获取数据,但这并不顺利。这是一个有前途的方法吗?如果是这样,我不确定要使用哪个重载,一个例子会很有帮助!
int* value
is an array with a mixture of int's and float's...
这严重违反了 strict aliasing rule 并且会 引发未定义的行为 。在 C 中,您必须改用联合。文档应该是这样的
union Value
{
int i;
float f;
};
int get_value(int unit, int id, union Value* value);
忽略 supplier.dll C# 中的原始 UB 你可以用 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;
}
获取数组后,使用正确的字段即可得到期望值
var ret = GetValue(unit, id, out Value[] value);
var f0 = value[0].f;
var i1 = value[1].i;
如果你真的想在没有联合的情况下这样做,那么你必须像这样获得浮点数的二进制表示
float f0 = value[0];
int i1 = BitConverter.SingleToInt32Bits(value[1]);
int i2 = BitConverter.SingleToInt32Bits(value[2]);
value[1] = BitConverter.Int32BitsToSingle(f0 + 0.123f);
但是如果数组总是有 3 个这样的项目,那么为什么 return 数组而不是结构?
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) {...}
我正在编写一个包装器 C# DLL 以与供应商提供的用 C 编写的非托管 DLL 进行交互。在大多数情况下,一切都很顺利,但是在提供的文档中有一些功能如下所示:
int get_value(int unit, int id, int* value)
我的问题是 int* value
是一个混合了 int 和 float 的数组...文档说明哪个是哪个,即第一个值是 int
,第二个和第三个是 float
,等等。如果我专门将 DLL value
调用为 int
或 float
,我会取得一些成功。 return 是我正在寻找的一些数据,但仅限于一种类型。例如,这有点有效:
[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
用 [NaN, value1, value2] return 编辑,因为 0 索引实际上是一个整数,它没有被正确解释。我试图在非托管 DLL 中使用 IntPtr[] 作为 return value
,然后使用 Marshal.Copy()
从内存位置获取数据,但这并不顺利。这是一个有前途的方法吗?如果是这样,我不确定要使用哪个重载,一个例子会很有帮助!
int* value
is an array with a mixture of int's and float's...
这严重违反了 strict aliasing rule 并且会 引发未定义的行为 。在 C 中,您必须改用联合。文档应该是这样的
union Value
{
int i;
float f;
};
int get_value(int unit, int id, union Value* value);
忽略 supplier.dll C# 中的原始 UB 你可以用 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;
}
获取数组后,使用正确的字段即可得到期望值
var ret = GetValue(unit, id, out Value[] value);
var f0 = value[0].f;
var i1 = value[1].i;
如果你真的想在没有联合的情况下这样做,那么你必须像这样获得浮点数的二进制表示
float f0 = value[0];
int i1 = BitConverter.SingleToInt32Bits(value[1]);
int i2 = BitConverter.SingleToInt32Bits(value[2]);
value[1] = BitConverter.Int32BitsToSingle(f0 + 0.123f);
但是如果数组总是有 3 个这样的项目,那么为什么 return 数组而不是结构?
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) {...}