P/Invoke 结构使用联合的方法
P/Invoke method with struct using union
我正在 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 处包含一个未对齐的字段。如果我省略最后一个字段它可以工作,但是最重要的数据丢失了,当然。
非常感谢任何帮助如何处理这种情况。
此结构中的 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
关键字。 Project > Properties > Build > Allow unsafe code 选项。它实际上非常不安全,您需要在开始使用该结构之前在您的代码中放置一个断言,这样您就可以确保不会发生内存损坏:
System.Diagnostics.Debug.Assert(Marshal.SizeOf(typeof(WinBioIdentity)) == 76);
我正在 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 处包含一个未对齐的字段。如果我省略最后一个字段它可以工作,但是最重要的数据丢失了,当然。
非常感谢任何帮助如何处理这种情况。
此结构中的 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
关键字。 Project > Properties > Build > Allow unsafe code 选项。它实际上非常不安全,您需要在开始使用该结构之前在您的代码中放置一个断言,这样您就可以确保不会发生内存损坏:
System.Diagnostics.Debug.Assert(Marshal.SizeOf(typeof(WinBioIdentity)) == 76);