【问题标题】:Converting mixed type return data from an unmanaged C DLL?从非托管 C DLL 转换混合类型返回数据?
【发布时间】:2021-05-30 15:42:23
【问题描述】:

我正在编写一个包装 C# DLL 来与供应商提供的用 C 编写的非托管 DLL 进行接口。在大多数情况下,一切都很顺利,但是在提供的文档中有些函数看起来像这样:

int get_value(int unit, int id, int* value)

我的问题是int* value 是一个混合了int 和float 的数组...文档说明了哪个,即第一个值是int,第二个和第三个是float,等等。如果我专门将 DLL value 称为 intfloat,我会取得一些成功。这会返回一些我正在寻找的数据,但仅限于一种类型。例如,这有点工作:

[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] 一起返回,因为 0 索引实际上是一个整数并且它没有被正确解释。我尝试使用 IntPtr[] 作为非托管 DLL 中的返回 value,然后使用 Marshal.Copy() 从内存位置获取数据,但这并不顺利。这是一个有前途的方法吗?如果是这样,我不确定要使用哪个重载,举个例子会很有帮助!

【问题讨论】:

  • 您可以通过使用LayoutKind.Explicit (docs.microsoft.com/en-us/dotnet/api/…) 创建struct 来创建类似于C union 的东西。使用这些数组,也许??
  • 要么使用显式结构路由,要么只使用IntPtr(并进行指针数学运算)而不是IntPtr[]。我必须说,这就是我讨厌 C 的原因,这应该被声明为 struct*void*

标签: c# c++ marshalling wrapper dllimport


【解决方案1】:

int* value 是一个混合了 int 和 float 的数组...

这是对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 个这样的项,那为什么要返回数组而不是结构呢?

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) {...}

【讨论】:

  • 很好的答案。 BitConverter 方法有效,但结构更优雅。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
  • 2013-01-07
  • 2019-01-08
相关资源
最近更新 更多