使用 Win32API:结构中的联合生成 TypeLoadException

Use Win32API: unions within structures generate TypeLoadException

我想导入 Win32 的外部函数API。

API(C 语言)中的代码如下所示:

typedef struct _BLUETOOTH_ADDRESS {
  union {
    BTH_ADDR ullLong;
    BYTE     rgBytes[6];
  };
} BLUETOOTH_ADDRESS;

我的 C# 实现如下所示:

[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct BLUETOOTH_ADDRESS
{
    [FieldOffset(0)]
    public ulong ullLong;

    [FieldOffset(2)]
    public byte[] rgBytes;
};

问题是:我一创建结构,它就抛出一个 TypeLoadException,错误代码: System.TypeLoadException: "Could not load type 'BLUETOOTH_ADDRESS' from assembly 'BleLab, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 2 that is incorrectly aligned or overlapped by a non-object field."

您是否知道解决这个问题或问题出在哪里?

此致

编辑: 忘记提供调用方式:

var ba = new Win32API.BLUETOOTH_ADDRESS();
ba.rgBytes = new byte[6];

尝试将其声明为联合确实没有意义。它是一个无符号的 64 位类型。只需使用 ulong 而不是结构。

如果您永远不需要显示地址,那么您只需要挑出该地址的前 6 个字节 ulong。蓝牙地址是一个 48 位值,因此是 6 个字节。

但出于您的目的,尝试在用于互操作的类型中表达这种细微差别不会有任何收获。这就是为什么我建议使用 ulong 进行互操作,并在必要时作为单独的操作挑选出有意义的字节。

为帮助您理解错误,请注意以下事项:

  1. C 联合类型的所有成员都有从偏移量 0 开始的重叠存储,因此 C# 结构中成员 rgBytes 的偏移量应使用 [FieldOffset(0)],而不是 [FieldOffset(2)].

  2. C联合体中rgBytes成员的类型是固定的6字节数组。在您的 C# 结构中,它是 byte[] 数组类型。 C# 中的(普通)数组类型是 "reference type",可以将其视为 C 指针。也就是说,该对象只是持有对堆上某个值的引用(指针)。

  3. 您可以使用fixed关键字创建固定数组,如下所示:

    fixed byte rgBytes[6];

    固定数组是不安全的代码,所以上面需要声明unsafe。它也在您的结构中声明为 public,因此 rgBytes 成员的完整声明可以如下所示:

    public unsafe fixed byte rgBytes[6];

将它们放在一起给出了 BLUETOOTH_ADDRESS 的以下 C# 声明:

[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct BLUETOOTH_ADDRESS
{
    [FieldOffset(0)]
    public ulong ullLong;

    [FieldOffset(0)]
    public unsafe fixed byte rgBytes[6];
};

您可以省略 Size = 8 部分。

根据 David Heffernan 的回答,您最好只使用 ulong,尤其是因为这样可以避免任何 "unsafe" 代码。