I was trying to write a C# equivalent for ACCESS_DENIED_ACE struct as defined in MSDN:
typedef struct _ACCESS_DENIED_ACE {
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD SidStart;
} ACCESS_DENIED_ACE, *PACCESS_DENIED_ACE;
Where SidStart is the first DWORD of a trustee's SID. The remaining bytes of the SID are stored in contiguous memory after the SidStart member.
I have seen examples where its used like (PSID) &accessAllowedAce->SidStart as in,
if ( EqualSid (pSid, (PSID) &accessDeniedAce->SidStart) )
{
//
}
Now how can I write the C# StructLayout for this and how can I use it in EqualSid function, also explain how your solution works.
Start with the sub structs.
[StructLayout(LayoutKind.Sequential)]
struct ACE_HEADER
{
byte AceType;
byte AceFlags;
uint AceSize;
}
DWORD = uint
[Flags]
enum ACCESS_MASK : uint
{
// ...
}
Its a flag based enum struct to be represented as 32 bits (i.e. multiple states can be toggled).
You'll need to define the bits as documented.
[StructLayout(LayoutKind.Sequential)]
struct ACCESS_DENIED_ACE
{
ACE_HEADER Header;
ACCESS_MASK Mask;
uint SidStart;
}
Simple structure now.
When I get the IntPtr for ACCESS_DENIED_ACE (assume deniedAceIntPtr), I can get the IntPtr for SID in the ACCESS_DENIED_ACE by adding the offset of the SidStart
IntPtr tempSid = IntPtr.Add(deniedAceIntPtr, 8);
Related
I am trying to marshal a hid_device_info struct in C#, but I can't figure out how to translate the wchar_t* strings to managed C# strings. I have tried all possible values in the MarshalAs attribute, but all of them returned the first character only and nothing else.
I have tried replacing all the wide strings with pointers so I can manually look at them, this is the struct that I have so far:
public struct HidDeviceInfo
{
public IntPtr path; // This one marshals fine because it's just a regular char_t*
public ushort vendor_id;
public ushort product_id;
public IntPtr serial_number; // wchar_t*
public ushort release_number;
public IntPtr manufacturer_string; // wchar_t*
public IntPtr product_string; // wchar_t*
public ushort usage_page;
public ushort usage;
public int interface_number;
public IntPtr next;
}
When I manually iterate through one of the pointers (serial_number for example), I can see that all the characters have 4 bytes (1 ascii byte followed by 3 zeros). I have tried all the possible Marshal.PtrToString... methods, but none of them are able to retrieve the full string.
I have a suspicion that the strings are being treated as 2 byte characters since I can't specify the character width anywhere in C#, and this is why it stops after the first character. Of course, by knowing this, I could easily write my own string marshaler, but I feel like there must be an existing solution and I'm overlooking something obvious.
This struct is coming from a P/Invoked function and Marshal.PtrToStructure:
[DllImport(LibUsbName, CharSet = CharSet.Unicode)]
public static extern IntPtr hid_enumerate(ushort vendorId, ushort productId);
I've also tried all the possible CharSet values.
This can't be a character type mismatch, as it was in this question, because I've tried all possible combinations of different character types.
I ended up writing this method that works fine for me, but only if all character are ASCII and the char width is guaranteed to be 4 bytes.
private static string ToUcs4String(this IntPtr ptr)
{
var builder = new StringBuilder();
var buffer = new byte[4];
while (true)
{
Marshal.Copy(ptr, buffer, 0, 4);
if (buffer[0] == 0)
break;
builder.Append((char) buffer[0]);
ptr += 4;
}
return builder.ToString();
}
I am converting c/c++ structures into C# standards.
C/C++ Structures:
typedef struct _sta_conn_info{
STA_CONNECT_STATE connect_state;//Enum
STA_ASSOC_STATE assoc_state;//Enum
unsigned char bssid[6];
unsigned char ssid[34];
unsigned long channel;
enum mode mode;//Enum
unsigned long signalStrength;
unsigned long noiseLevel;
STA_AUTH_ALG auth_alg;//enum
STA_ENCRYPT_ALG encrypt_alg;//enum
}STA_CONN_INFO;
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
PTCHAR ptcDeviceName;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Respective C# structures:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _sta_conn_info
{
public _sta_connect_state connect_state;
public _sta_assoc_state assoc_state;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public char[] bssid ;//= new char[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public char[] ssid ;//= new char[34]
public uint channel;
public mode mode;
public uint signalStrength;
public uint noiseLevel;
public _sta_auth_alg auth_alg;
public _sta_encrypt_alg encrypt_alg;
}
QUERY STRUCT:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
public byte[] Data;
};
I converted the data types using this >>>reference
Marshal.SizeOf() is working in WIN CE. I tested it.
If my structure conversion is fine then definitely Marshal.SizeOf() will work to get the size of the structure, but it is throwing exceptions and returning error code 87 in DeviceIoControl() API.
Can anyone clarify me about the conversions and let me know If I did anything wrong.
For bssid and ssid the C++ declarations are:
unsigned char bssid[6];
unsigned char ssid[34];
Now, unsigned char is a single byte and is typically used for byte arrays rather than text. So the C# should be:
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public byte[] bssid ;//= new byte[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public byte[] ssid ;//= new byte[34]
Your use of char in the C# is not correct because char is two bytes wide in C#.
In _NDISUIO_QUERY_OID where you have
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = sizeof(uint))]
public byte[] Data;
I believe that you need to use ByValArray rather than ByValTStr. But as we have discussed in many of your recent questions, the exact meaning of this member is unclear. Is it really a fixed length byte array, or is it a variable length buffer? Do you have sample C++ code that works? That would settle the debate once and for all.
OK, from the header nuiouser.h header file I have this:
//
// Structure to go with IOCTL_NDISUIO_QUERY_OID_VALUE.
// The Data part is of variable length, determined by
// the input buffer length passed to DeviceIoControl.
//
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
#ifdef UNDER_CE
//
// In CE land app is allowed to query without having to do
// IOCTL_NDISUIO_OPEN_DEVICE
// Hence the device name to query argument needed..
// For app that does IOCTL_NDISUIO_OPEN_DEVICE this argument
// is then not necessary..
//
PTCHAR ptcDeviceName;
#endif
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Which tells you conclusively that Data is variable length. You'll need to allocate the struct with AllocHGlobal and do all the marshalling by hand I am afraid.
The API I am using has something like this:
int simpletran(LPSTRUCT req)
{
printf("%d", req->length);
}
typedef unsigned long ULONG;
typdef struct _st {
ULONG length;
}STRUCT, *LPSTRUCT;
My C# version of it:
[DllImport(LINUXLIB, CallingConvention=CallingConvention.Cdecl)]
public static extern simpletran(STRUCT req);
class STRUCT
{
public UInt32 length;
}
STRUCT st = new STRUCT();
st.length = (UInt32)100;
simpletran(st);
When I call the unmanaged function I get some long negative values like -31245665!!!
I am using C# mono on a Linux machine.
I haven't tested any of this, so it may need some changes, but here's what I see off hand.
First off, STRUCT should be declared as follows:
struct STRUCT
{
public UInt32 length;
}
Notice that we changed from class to struct so that the memory layout of the object is known and matches what the C code is expecting.
Update
After a bit of further consideration, there's an easier way of declaring and calling the method, I'll leave the original answer below for another way of doing it.
Your P/Invoke signature should be:
[DllImport(LINUXLIB, CallingConvention=CallingConvention.Cdecl)]
public static extern int simpletran(ref STRUCT req);
Notice that we changed STRUCT to ref STRUCT since STRUCT is a value type and the C code requires a pointer to that structure.
And you'd call it like this:
STRUCT st = new STRUCT();
st.length = (UInt32)100;
simpletran(ref st);
Original
[DllImport(LINUXLIB, CallingConvention=CallingConvention.Cdecl)]
public static extern int simpletran(IntPtr req);
Notice that we changed STRUCT to IntPtr since STRUCT has to be a value type and the C code requires a pointer to that structure.
And you'd call it like this:
STRUCT st = new STRUCT();
st.length = (UInt32)100;
IntPtr ptr = Marshal.AllocHGlobal(sizeof(STRUCT));
Marshal.StructureToPtr(st, ptr, false);
simpletran(ptr);
Marshal.FreeHGlobal(ptr);
Adding the extra steps in between creating the instance and calling the method for allocating a chunk of unmanaged memory to store the value of the struct in (Marshal.AllocHGlobal) and copying the value of st into that memory with Marshal.StructureToPtr(...). Be sure to free the allocated memory after the call with a call to Marshal.FreeHGlobal
I have a C++ function in a DLL which takes a pointer to a struct, JPInfo, which in the function is filled with data received from a server, the layout of the C++ struct is as seen below:
typedef struct JP
{
unsigned char type;
DWORD value;
} JP;
typedef struct JPInfo
{
JP jps[3];
_int16 ConT;
_int16 CallT;
unsigned char ret;
unsigned char count;
unsigned char JPOffset;
unsigned char JPPeriod;
} JPInfo;
The function is exported in the DLL like so:
__declspec(dllexport) DWORD __stdcall GetJPInfo(JPInfo* jpi, DWORD time);
The function takes a pointer to a JPInfo struct, I have tried to emulate this struct in C#
[StructLayout(LayoutKind.Sequential, Size = 5), Serializable]
public struct JP
{
byte type;
int value;
}
[StructLayout(LayoutKind.Sequential,Size=23),Serializable]
public struct JPInfo
{
JP[] jps;
Int16 ConT;
Int16 CallT;
byte ret;
byte count;
byte JPOffset;
byte JPPeriod;
}
I attempt to call the function from C# like so:
[DllImport("DLLImp.dll")]
unsafe public static extern int GetJP(ref JPInfo jpi, int time);
// then in main...
JPInfo jpi = new JPInfo;
GetJackpotValues(ref jpi, 4000);
I get an unhandled exception of type "System.ExecutionEngineException". I can't have a fixed size array of JP structs in my JPInfo struct, so I don't know how to approach this.
Thanks.
Have you tried removing your Size attributes on your structs? I haven't had to specify a Size when doing something similar. For your array properties, try attributing them like:
[StructLayout(LayoutKind.Sequential)]
public struct JPInfo
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
JP[] jps;
Int16 ConT;
Int16 CallT;
byte ret;
byte count;
byte JPOffset;
byte JPPeriod;
}
Assuming that the C++ structs are packed, your C# structs should look like this:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct JP
{
byte type;
uint value;
}
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct JPInfo
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
JP[] jps;
Int16 ConT;
Int16 CallT;
byte ret;
byte count;
byte JPOffset;
byte JPPeriod;
}
On the other hand, if they are not packed then remove the Pack parameter to the StructLayout attribute. You should look for a #pragma pack statement in the C++ header file to understand whether or not the C++ structs are packed.
I'm guessing that the C++ structs are packed because you said that they are mapped onto data received from the server.
Your import should be like so:
[DllImport("DLLImp.dll")]
public static extern uint GetJP(ref JPInfo jpi, uint time);
A DWORD translates to uint rather than int and there is no need for unsafe code here.
I have such a C++ structure:
typedef struct _FILE_OP_BLOCK
{
unsigned short fid; // objective file ID
unsigned short offset; // operating offset
unsigned char len; // buffer length(update)
// read length(read)
unsigned char buff[240];
} FILE_OP_BLOCK;
And now I want to map it in .Net. The tricky thing is that the I should pass a 2 byte array for fid, and integer for len, even though in C# fid is an unsigned short and len is an unsigned char
I wonder whether my structure ( in C#) below is correct?
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Auto)]
public struct File_OP_Block
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] fid;
public ushort offset;
public byte length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 240)]
public char[] buff;
}
Your CharSet property on the [DllImport] attribute is definitely wrong, you need CharSet.Ansi to get the P/Invoke marshaller to convert it to a char[]. Declare the buff member as a string for easier usage. While declaring the fid member as a byte[] isn't wrong, I really don't see the point of it. That the unmanaged code copies a char[] into it is an implementation detail. Thus:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct File_OP_Block
{
public ushort fid;
public ushort offset;
public byte length;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 240)]
public string buff;
}
Given that the C++ short data type is actually two bytes, a two byte array should work. The integer sizes in C/C++ are not strictly defined, so the standard only says that a short is at least two bytes.
The C# char data type is a 16 bit unicode character, so that doesn't match the C++ char data type which is an 8 bit data type. You either need an attribute to specify how the characters are encoded into bytes, or use a byte array.
You might need an attribute to specify the packing, so that there is no padding between the members.