C# struct behavior based on architecture - c#

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;
}

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).

P/Invoke method with struct using union

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);

Using a C struct in C#

I am trying to port a windows-only USB driver to Linux by switching from hid.dll and other windows-only-stuff to the cross-platform hidapi.
hidapi.h contains the following definitions:
struct hid_device_info {
char *path;
unsigned short vendor_id;
unsigned short product_id;
wchar_t *serial_number;
unsigned short release_number;
wchar_t *manufacturer_string;
wchar_t *product_string;
unsigned short usage_page;
unsigned short usage;
int interface_number;
struct hid_device_info *next;
};
and
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
Please see the link above for the code with comments.
The driver is written in C#, so I would like to connect to the hidapi library and use these directly. I have managed to get access to hid_enumerate by doing this:
[DllImport ("/usr/local/lib/libhidapi-hidraw.so.0")]
internal static extern IntPtr hid_enumerate (ushort vendor_id, ushort product_id);
Calling hid_enumerate later then gives me a pointer. However, I am unsure how to proceed from here. I would like to turn this into a LinkedList of hid_device_info objects, or some other structure in C#.
I am quite new to C#, but have a lot of programming experience.
Could you please point me in the right direction?
Here's what I ended up doing. I made a struct in C# like this:
[StructLayout(LayoutKind.Sequential)]
private struct hid_device_info {
[MarshalAs(UnmanagedType.LPStr)]
public String path;
public ushort vendor_id;
public ushort product_id;
[MarshalAs(UnmanagedType.LPWStr)]
public String serial_number;
public ushort release_number;
[MarshalAs(UnmanagedType.LPWStr)]
public String manufacturer_string;
[MarshalAs(UnmanagedType.LPWStr)]
public String product_string;
public ushort usage_page;
public ushort usage;
public int interface_number;
public IntPtr next;
};
and when calling hid_enumerate, I did this to turn the pointer into an instance of the struct:
List<hid_device_info> list = new List<HidDeviceManager.hid_device_info> ();
IntPtr pDev = hid_enumerate (0, 0);
while (pDev != IntPtr.Zero) {
hid_device_info dev = new hid_device_info();
dev = (hid_device_info)Marshal.PtrToStructure (pDev, typeof(hid_device_info));
list.Add (dev);
pDev = dev.next;
}
It works well.

Calling C function with struct in C# - what am I doing wrong?

I thought this one was fairly straight forward but still trying to understand all of this and having some issues.
I don't know much about the C function b/c i've been given limited information.
Here is the function call in C:
int GetCard(CardInfo card);
Here is the request structure:
typedef struct _tCardInfo
{
char CardNumber[80];
char isExist;
} TCardInfo, *pTCardInfo;
I want to pass the card number to see if it exists.
So in C# I did the following:
public struct CardInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string cardNumber;
public byte isExist;
}
[DllImport("card.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int GetCardInfo(ref CardInfo cardInfo);
Then in the c# method:
CardInfo cardInfo = new CardInfo();
cardInfo.cardNumber = "1234567890";
int success = GetCardInfo (ref cardInfo);
The good thing about the DLL that I'm calling is it generates a log file.
When I execute the call, the log tells me that I'm hitting the DLL but it is not passing the card number which then sets a message saying the card number was not passed.
Any help is greatly appreciated.
Thanks!
The problem is that you're requesting TChar marshaling, but the DLL requires 8-byte characters. Change the C struct to wchar_t.
Also, use Visual Studio to set a breakpoint in your DLL, and actually inspect the data when it comes in! Visual Studio can debug across .NET/native boundaries, which is super cool!
Try to add attribute StructLayout for struct
[StructLayout(LayoutKind.Sequential)]
public struct CardInfo
{
...
Try to create the .Net struct like this:
[StructLayout(LayoutKind.Sequential)]
public struct CardInfo
{
[MarshalAs(UnmanagedType.AnsiBStr, SizeConst = 80)]
public string cardNumber;
[MarshalAs(UnmanagedType.I1)]
public sbyte isExist;
}
And for the function declaration: try not to use the CallingConvention and CharSet in the DLL import, and use the [In, Out] attributes before the parameter. Like this:
[DllImport("card.dll")]
public static extern int GetCardInfo([In, Out] CardInfo cardInfo);

Convert C Union to C# (Incorrectly Aligned)

I want to call the DhcpGetClientInfo API from C# but I have a question on conversion of this C struct to C#:
typedef struct _DHCP_CLIENT_SEARCH_INFO {
DHCP_SEARCH_INFO_TYPE SearchType;
union {
DHCP_IP_ADDRESS ClientIpAddress;
DHCP_CLIENT_UID ClientHardwareAddress;
LPWSTR ClientName;
} SearchInfo;
} DHCP_SEARCH_INFO, *LPDHCP_SEARCH_INFO;
I think the Correct conversion is this:
[StructLayout(LayoutKind.Explicit, Size=12)]
public struct DHCP_SEARCH_INFO
{
[FieldOffset(0)]
public DHCP_SEARCH_INFO_TYPE SearchType;
[FieldOffset(4)]
public DHCP_IP_ADDRESS ClientIpAddress;
[FieldOffset(4)]
public DHCP_BINARY_DATA ClientHardwareAddress;
[FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)]
public string ClientName;
};
But that gives an System.TypeLoadException: Additional information: Could not load type 'Dhcpsapi.DHCP_SEARCH_INFO' from assembly 'ConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field.
This is the conversion of the other types in case you want to compile:
public enum DHCP_SEARCH_INFO_TYPE : uint
{
DhcpClientIpAddress = 0,
DhcpClientHardwareAddress = 1,
DhcpClientName = 2
};
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_BINARY_DATA
{
public uint DataLength;
public IntPtr Data;
};
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_IP_ADDRESS
{
public UInt32 IPAddress;
}
EDIT:
I verified sizeof and offsets in C:
#pragma comment(lib,"Dhcpsapi.lib")
int _tmain(int argc, _TCHAR* argv[])
{
DHCP_SEARCH_INFO si;
printf("sizeof(DHCP_SEARCH_INFO)=%d\n", sizeof(DHCP_SEARCH_INFO));
printf("ClientIpAddress offset=%d\n", (PBYTE)&si.SearchInfo.ClientIpAddress - (PBYTE)&si);
printf("ClientHardwareAddress offset=%d\n", (PBYTE)&si.SearchInfo.ClientHardwareAddress - (PBYTE)&si);
printf("ClientName offset=%d\n", (PBYTE)&si.SearchInfo.ClientName - (PBYTE)&si);
return 0;
}
Output is:
sizeof(DHCP_SEARCH_INFO)=12
ClientIpAddress offset=4
ClientHardwareAddress offset=4
ClientName offset=4
EDIT:
Based on Camford's answer I declared the struct as below. Using sizeof should make it correct for x64 as well.
[StructLayout(LayoutKind.Explicit, Size=12)]
public struct DHCP_SEARCH_INFO
{
[FieldOffset(0)]
public DHCP_SEARCH_INFO_TYPE SearchType;
[FieldOffset(sizeof(DHCP_SEARCH_INFO_TYPE))]
public DHCP_IP_ADDRESS ClientIpAddress;
[FieldOffset(sizeof(DHCP_SEARCH_INFO_TYPE))]
public IntPtr ClientName;
[FieldOffset(sizeof(DHCP_SEARCH_INFO_TYPE))]
public DHCP_BINARY_DATA ClientHardwareAddress;
};
The way you are simulating the union is correct as far as I can tell. The exception you are getting is likely related to thestring object in your struct. I tried to build your code in a test project. With string in the struct, I get the same exception as you do. With an IntPtr replacing the string, I don't get any exceptions. Whether the call to DhcpGetClientInfo is going to work or not, I have no idea. You can use Marshal.StringToHGlobalUni to get an IntPtr for your string.
[StructLayout(LayoutKind.Explicit)]
public struct SearchInfo
{
[FieldOffset(0)]
public DHCP_IP_ADDRESS ClientIpAddress;
[FieldOffset(0)]
public DHCP_BINARY_DATA ClientHardwareAddress;
[FieldOffset(0)]
public IntPtr ClientName; //LPWSTR
}
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_SEARCH_INFO
{
public DHCP_SEARCH_INFO_TYPE SearchType;
public SearchInfo SearchInfo;
}
Edit: I guess this means that simulating a union in C# has similar requirement to the union in C++. In C++ you can only have POD types in a union. In C# you can probably only have struct types.
Updated: Thanks to DavidHeffernan for pointing out a better way of laying out structs with unions inside. You can read his explanation below.
I believe that the best thing to do is to add get/set methods to... guess what... get/set the correct bits in a variable. I believe that you'll have to wrap that too
Something like:
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_SEARCH_INFO
{
public DHCP_SEARCH_INFO_TYPE SearchType;
public ulong Complex;
};
public struct DHCP_SEARCH_INFO_WRAP
{
public DHCP_SEARCH_INFO_TYPE SearchType;
private ulong Complex;
public DHCP_IP_ADDRESS ClientIpAddress
{
get
{
return BitConverter.ToInt32(BitConverter.GetBytes(Complex),0);
}
set
{
byte[] orig = BitConverter.GetBytes(Complex);
byte[] chng = BitConverter.GetBytes(value);
Array.Copy(chng,0,orig,0,4);
}
}
public DHCP_SEARCH_INFO_WRAP(ref DHCP_SEARCH_INFO var)
{
this.SearchType = var.SearchType;
this.Complex = var.Complex;
}
};
I wrote this directly here and didn't test it.
EDIT:
Camford enlightened me that you can have unions in C#, I was wrong. But as it seem that you can only emulate the primitive types I stand by my solution

Categories