2015-07-21 57 views
1

我正在用C#圍繞原生Windows生物統計框架構建一個託管包裝,該包裝用於訪問指紋傳感器等生物識別傳感器。P /結合使用union的調用方法

我得到這個方法問題的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; 
} 

在這種形式,它具有TypeLoadE崩潰xception抱怨WinBioIdentity結構在偏移8處包含未對齊的字段。如果我忽略了最後一個字段的工作原理,那麼最重要的數據當然會丟失。

任何幫助如何處理這種情況非常感謝。

+0

把工會在其自己的類型等爲所有成員使用FieldOffset(0)。這可以讓編譯器正確地佈局包含的結構。你需要使用一個固定的數組來使這個類型變爲blittable。 –

回答

3

在這個結構中的Guid是麻煩製造者。它長16個字節,因此與字節[]重疊。 CLR不允許這種重疊,它可以防止垃圾回收器可靠地識別數組對象引用。 Very重要的是要知道數組是否需要收集。 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);