P/Invoke method with struct using union - c#

I am building a managed wrapper in C# around the native Windows Biometric Framework, which is used to access biometric sensors like fingerprint sensors.
I have problems getting this method to work with 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
);
The problem is the WINBIO_IDENTITY struct because it contains a union:
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;
Here is what I tried:
[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;
}
In this form it crashes with a TypeLoadException complaining that the WinBioIdentity struct contains a misaligned field at offset 8. If I leave out that last field it works, but then the most important data is missing, of course.
Any help how to handle this case is very much appreciated.

The Guid in this structure is the trouble-maker. It is 16 bytes long and therefore overlaps the byte[]. The CLR disallows this kind of overlap, it prevents the garbage collector from identifying the array object reference reliably. Very important to know whether or not the array needs to be collected. The GC has no way to find out if the structure contains the Guid or the array.
You must give up on byte[] and substitute it with a value type so the GC does not have to deal with a possibly broken object reference. The C# language has the fixed keyword to declare such kind of value types:
[StructLayout(LayoutKind.Explicit)]
unsafe public struct WinBioIdentity {
//...
[FieldOffset(8)]
public fixed byte AccountSid[68];
}
Note the required unsafe keyword. Project > Properties > Build > Allow unsafe code option. It is in fact pretty unsafe, you'll want to put an assert in your code before you start using the struct so you can be sure no memory corruption can occur:
System.Diagnostics.Debug.Assert(Marshal.SizeOf(typeof(WinBioIdentity)) == 76);

Related

How to define MAPINAMEID structure in C# in 32/64-bit compatible way?

In C, MAPINAMEID is defined as:
typedef struct _MAPINAMEID
{
LPGUID lpguid;
ULONG ulKind;
union {
LONG lID;
LPWSTR lpwstrName;
} Kind;
} MAPINAMEID, FAR * LPMAPINAMEID;
In C#, my definition is:
[StructLayout(LayoutKind.Explicit)]
private struct MAPINAMEID
{
[FieldOffset(0)]
public IntPtr lpguid;
[FieldOffset(8)]
public uint ulKind;
[FieldOffset(16)]
public int lID;
[FieldOffset(16)]
public IntPtr lpwstrName;
};
Obviously, it works in 64-bit mode only, with 32-bit I need different offset values. Unfortunately, FieldOffset attribute does not allow using computable values (like IntPtr.Size). Is there a platform-independent way to specify offset (or somehow else tell the compiler that I want lID and lpwstrName to share the same offset?
You can declare it like that:
[StructLayout(LayoutKind.Sequential)]
private struct MAPINAMEID
{
public IntPtr lpguid;
public uint ulKind;
public IntPtr lpwstrName; // or lID
};
And use IntPtr 32-bit conversion to switch between lpwstrName and lId when needed (LONG and ULONG are 32-bit).

C# struct behavior based on architecture

I have a question regarding either feeding multiple structs into a method depending on compile architecture,
or about properly laying out the Thread_Basic_Information struct so I can just use a single struct for the same method regardless of x64/x86 (see for reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684283(v=vs.85).aspx)
I have confirmed it working fine on x86, and if I manually switch out the struct's it will also work for x64.
However I've recently tried to change my approach from just copy-pasting a ton of functions to trying to recycle my code.
I'm having an extremely difficult time trying to find either precise information on the struct in question which I can use in C# to make it compatible regardless of x86/x64, or feeding 2 different struct's into the same method depending on architecture.
Perhaps StructLayout pack size? however I am not familiar with that attribute.
I'm hoping there might be a way I can generic this, such as if IntPtr.Size == 8 then use 64bit struct, else use 32bit struct. However short of copy-pasting code with a minor rename, I'm hoping there may be a way to do this with generics?
Code:
Method used to create the thread:
public IRemoteThread Create(IntPtr address, bool isStarted = true)
{
//Create the thread
var thr = ThreadHelper.CreateRemoteThread(Process.Handle, address, IntPtr.Zero, ThreadCreationFlags.Suspended)
//Acquire desired information from the thread
var ret = ThreadHelper.NtQueryInformationThread(thr);
// Do other stuff
return result;
}
Support method for querying the thread info we desire:
public static ThreadBasicInformation NtQueryInformationThread(SafeMemoryHandle threadHandle)
{
// Check if the handle is valid
HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle");
// Create a structure to store thread info
var info = new ThreadBasicInformation();
// Get the thread info
var ret = Nt.NtQueryInformationThread(threadHandle, 0, ref info, MarshalType<ThreadBasicInformation>.Size,
IntPtr.Zero);
// If the function succeeded
if (ret == 0)
return info;
// Else, couldn't get the thread info, throws an exception
throw new ApplicationException($"Couldn't get the information from the thread, error code '{ret}'.");
}
32bit struct used in the above method:
[StructLayout(LayoutKind.Sequential)]
public struct ThreadBasicInformation
{
public uint ExitStatus;
public IntPtr TebBaseAdress;
public int ProcessId;
public int ThreadId;
public uint AffinityMask;
public uint Priority;
public uint BasePriority;
}
x64 variant of the same struct
[StructLayout(LayoutKind.Explicit)]
public struct ThreadBasicInformation64
{
[FieldOffset(0)]
public uint ExitStatus;
[FieldOffset(8)]
public IntPtr TebBaseAdress;
[FieldOffset(16)]
public int ProcessId;
[FieldOffset(24)]
public int ThreadId;
[FieldOffset(32)]
public uint AffinityMask;
[FieldOffset(40)]
public uint Priority;
[FieldOffset(44)]
public uint BasePriority;
}
EDIT:
C declaration I found:
typedef LONG KPRIORITY;
typedef struct _CLIENT_ID {
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID;
typedef CLIENT_ID *PCLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION
{
NTSTATUS ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
KPRIORITY Priority;
KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
The premise of your question is wrong because you have mistranslated the structs. Once you translate them correctly you will find that you can use a single declaration of the struct in your C# code and the compiler will lay it out correctly for all target architectures.
[StructLayout(LayoutKind.Sequential)]
public struct CLIENT_ID
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential)]
public struct THREAD_BASIC_INFORMATION
{
public int ExitStatus;
public IntPtr TebBaseAdress;
public CLIENT_ID ClientId;
public IntPtr AffinityMask;
public int Priority;
public int BasePriority;
}

How use Struct Array in C#

I am a new C# user. I have a C/C++ struct below:
typedef struct
{
float x;
float y;
}Point2f;
typedef struct
{
int id;
unsigned char charcode;
}CharResultInfo;
typedef struct
{
int strlength;
unsigned char strval[1024];
CharResultInfo charinfo[1024];
}StringResultInfo;
typedef struct
{
int threshold;
int polarity;
bool inverted;
}Diagnotices;
typedef struct
{
Point2f regioncenter;
StringResultInfo stringinfo;
Diagnotics diagnotics;
}SingleOutResult;
I use C# to define the same struct like below:
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct Point2f
{
public double x;
public double y;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public unsafe struct DF_TAdvOCRCharResultInfo
{
public Int32 id;
public char charcode;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public unsafe struct DF_TAdvOCRStringResultInfo
{
public int strlength;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string strval;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.Struct)]
public CharResultInfo[] charinfo;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public unsafe struct Diagnotics
{
public Int32 polarity;
[MarshalAsAttribute(UnmanagedType.I1)]
public bool inverted;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public unsafe struct OutResult
{
public Point2f regioncenter;
public StringResultInfo stringinfo;
public Diagnotics diagnotics;
}
However, When I used below in C# project:
OutResult *pResult = (OutResult *)inputparam; //inputparam input from C/C++ dll
the Complier output:
error CS0208: Cannot take the address of, get the size of, or declare
a pointer to a managed type ('***.OutResult')
My Question is why Struct Pointer cannot used and how to fixed?
A pointer cannot point to a reference or to a struct that contains references, because an object reference can be garbage collected even if a pointer is pointing to it. The garbage collector does not keep track of whether an object is being pointed to by any pointer types.
https://msdn.microsoft.com/en-us/library/y31yhkeb.aspx
Essentially because C# is a managed language it needs to track all the references to the object in order to know when to GC it. If you declare a pointer to the Diagnostics object in your OutResult then the GC wouldn't be aware of the new reference and could later dispose of your object whilst you are using it.
To fix that I would personally steer clear of pointers unless you absolutely must use them. I'm not sure what your overall program is but if you simply want to pass around references then make OutResult a reference type (class) rather than a value type (struct). C# is a managed language and so it's always best to try and stick to a managed context, particulally if you're still a beginner as you say.
public class OutResult
{
public Point2f regioncenter;
public StringResultInfo stringinfo;
public Diagnotics diagnotics;
}

Writing complex structure to memory-mapped-file

I try to write following struct to a memory mapped file, but I still have problem with the array (writing throws exception that the struct can not contain reference)
[StructLayout(LayoutKind.Explicit)]
struct IndexEntry {
[FieldOffset(0)]
public byte key;
[FieldOffset(4)]
public int lastValueIdx;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.PART_ENTRY_SIZE)]
public long[] values;
}
I use this calling for writing:
UnmanagedMemoryAccessor.Write<IndexEntry>(0, ref entry);
Can you please tell me, what am I doing wrong? Thx
The solution of this is using the fixed size array and unsafe code. So the struct should look like this:
[StructLayout(LayoutKind.Explicit)]
unsafe struct IndexEntry {
[FieldOffset(0)]
public byte key;
[FieldOffset(1)]
public int lastValueIdx;
[FieldOffset(5)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.PART_ENTRY_SIZE)]
public fixed long values[Constants.PART_ENTRY_SIZE];
}
Note that the program (or a single project containing that struct) must be compiled with the "/unasfe" option and the array must be then accessed like this:
fixed(long* arr = this.values) {
// Setting value
arr[index] = value;
}
unsafe {
// Getting value
x = obj.values[index];
}
Then the UnmanagedMemoryAccessor.Write<T>(...) and UnmanagedMemoryAccessor.Read<T>(...) functions work perfectly.

How to avoid copying during PInvoke marshalling when struct contains array of integers?

I have a "C" struct which is defined as:
typedef unsigned char tUI8;
typedef struct
{
tUI8 Mode;
tUI8 Data[16];
} TestStruct;
And a function which takes a pointer to this structure and fill the data:
void FillTest(tUI8 Mode, TestStruct *s);
To PInvoke to this function, I wrote the C# code as:
[StructLayout(LayoutKind.Sequential)]
struct TestStruct
{
public byte Mode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Data;
}
static class NativeTest
{
[DllImport("Native.dll")]
public static extern void FillTest(byte mode, ref TestStruct s);
}
This works but I suspect that during the PInvoke marshaller is copying the struct during call and return instead of pinning it. I can say that because even if I don't initialize the struct, it works fine.
TestStruct s;
//here s.Data == null!
NativeTest.FillTest(10, ref s); //<<< I expected an error here
//here s.Data points to a valid byte[] of length 16
Console.WriteLine(BitConverter.ToString(s.Data));
My question is how can I define a PInvoke signature, using either struct or class, which avoid copying the data during marshalling?
I suspect you want a fixed size buffer, which will inline the data in your struct:
[StructLayout(LayoutKind.Sequential)]
unsafe struct TestStruct
{
public byte Mode;
public fixed byte Data[16];
}
You should now be able to pass that by reference directly to your unmanaged code. (You'll also need to explicitly allow unsafe code.)
I don't know what attributes you'll then need to use for marshalling, if any, but it's worth a try...
This can be done without using unsafe code.
By marshalling as ByValArray
[StructLayout(LayoutKind.Sequential)]
struct TestStruct
{
public byte Mode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Data;
}

Categories