【问题标题】:P/Invoke method with struct using union使用联合的结构的 P/Invoke 方法
【发布时间】:2015-07-22 01:22:31
【问题描述】:

我正在 C# 中围绕原生 Windows 生物识别框架构建一个托管包装器,用于访问指纹传感器等生物识别传感器。

我无法将此方法与 P/Invoke 一起使用:WinBioIdentify

HRESULT WINAPI WinBioIdentify(
    _In_      WINBIO_SESSION_HANDLE    SessionHandle,
    _Out_opt_ WINBIO_UNIT_ID           *UnitId,
    _Out_opt_ WINBIO_IDENTITY          *Identity,
    _Out_opt_ WINBIO_BIOMETRIC_SUBTYPE *SubFactor,
    _Out_opt_ WINBIO_REJECT_DETAIL     *RejectDetail
);

问题在于WINBIO_IDENTITY 结构,因为它包含一个联合:

typedef struct _WINBIO_IDENTITY {
    WINBIO_IDENTITY_TYPE Type;
    union {
        ULONG  Null;
        ULONG  Wildcard;
        GUID   TemplateGuid;
        struct {
            ULONG Size;
            UCHAR Data[SECURITY_MAX_SID_SIZE]; // the constant is 68
        } AccountSid;
    } Value;
} WINBIO_IDENTITY;

这是我尝试过的:

[StructLayout(LayoutKind.Explicit, Size = 76)]
public struct WinBioIdentity
{
    [FieldOffset(0)]
    public WinBioIdentityType Type;

    [FieldOffset(4)]
    public int Null;

    [FieldOffset(4)]
    public int Wildcard;

    [FieldOffset(4)]
    public Guid TemplateGuid;

    [FieldOffset(4)]
    public int AccountSidSize;

    [FieldOffset(8)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 68)]
    public byte[] AccountSid;
}

[DllImport("winbio.dll", EntryPoint = "WinBioIdentify")]
private extern static WinBioErrorCode Identify(
    WinBioSessionHandle sessionHandle,
    out int unitId,
    out WinBioIdentity identity,
    out WinBioBiometricSubType subFactor,
    out WinBioRejectDetail rejectDetail);

public static int Identify(
    WinBioSessionHandle sessionHandle,
    out WinBioIdentity identity,
    out WinBioBiometricSubType subFactor,
    out WinBioRejectDetail rejectDetail)
{
    int unitId;
    var code = Identify(sessionHandle, out unitId, out identity, out subFactor, out rejectDetail);
    WinBioException.ThrowOnError(code, "WinBioIdentify failed");
    return unitId;
}

在这种形式中,它会崩溃并出现 TypeLoadException,抱怨 WinBioIdentity 结构在偏移量 8 处包含未对齐的字段。如果我忽略最后一个字段,它可以工作,但是当然最重要的数据丢失了。

非常感谢任何帮助如何处理这种情况。

【问题讨论】:

  • 将联合放在它自己的类型中,因此对所有成员使用 FieldOffset(0)。这让编译器可以正确布局包含的结构。而且您需要为该数组使用一个固定数组,以使该类型成为 blittable。

标签: c# interop pinvoke


【解决方案1】:

这种结构中的 Guid 是麻烦制造者。它有 16 个字节长,因此与 byte[] 重叠。 CLR 不允许这种重叠,它会阻止垃圾收集器可靠地识别数组对象引用。 了解是否需要收集数组非常重要。 GC 无法判断结构中是否包含 Guid 或数组。

您必须放弃 byte[] 并将其替换为值类型,以便 GC 不必处理可能损坏的对象引用。 C# 语言有 fixed 关键字来声明这种值类型:

[StructLayout(LayoutKind.Explicit)]
unsafe public struct WinBioIdentity {
    //...
    [FieldOffset(8)]
    public fixed byte AccountSid[68];
}

注意必需的unsafe 关键字。项目 > 属性 > 构建 > 允许不安全代码选项。实际上这是非常不安全的,您需要在开始使用该结构之前在代码中添加一个断言,这样您就可以确保不会发生内存损坏:

System.Diagnostics.Debug.Assert(Marshal.SizeOf(typeof(WinBioIdentity)) == 76);

【讨论】:

    猜你喜欢
    • 2012-01-21
    • 1970-01-01
    • 2010-11-18
    • 2013-07-07
    • 2012-10-28
    • 1970-01-01
    • 1970-01-01
    • 2011-07-04
    • 1970-01-01
    相关资源
    最近更新 更多