使用union的P / Invoke方法

时间:2015-07-21 03:00:34

标签: c# interop pinvoke

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

我在使用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处的未对齐字段。如果我遗漏了最后一个字段,那么它可以正常工作,但当然缺少最重要的数据。

非常感谢任何帮助如何处理此案例。

1 个答案:

答案 0 :(得分:3)

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

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

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

请注意所需的unsafe关键字。项目>属性>构建>允许不安全的代码选项。它实际上是非常不安全的,你需要在开始使用struct之前在代码中放置一个断言,这样你就可以确定不会发生内存损坏:

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