Can I set the size attribute of StructLayout at runtime? - c#

Im trying to use SendInput to simulate keyboard presses in my app and want to support both 32-bit and 64-bit.
I've determined that for this to work, I need to have 2 different INPUT structs as such
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk; // Virtual Key Code
public ushort wScan; // Scan Code
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit, Size = 28)]
public struct INPUT32
{
[FieldOffset(0)]
public uint type; // eg. INPUT_KEYBOARD
[FieldOffset(4)]
public KEYBDINPUT ki;
}
[StructLayout(LayoutKind.Explicit, Size = 40)]
public struct INPUT64
{
[FieldOffset(0)]
public uint type; // eg. INPUT_KEYBOARD
[FieldOffset(8)]
public KEYBDINPUT ki;
}
I wanted to know if there was a way to set the StructLayout size and FieldOffsets at runtime so I could use just one INPUT struct and determine the size and fieldoffset depending on the machine.
I have tried the code below but I would like to know if the same is possible at runtime instead of compile time.
#if _M_IX86
[StructLayout(LayoutKind.Explicit, Size = 28)]
#else
[StructLayout(LayoutKind.Explicit, Size = 40)]
#endif
public struct INPUT
{
[FieldOffset(0)]
public uint type; // eg. INPUT_KEYBOARD
#if _M_IX86
[FieldOffset(4)]
#else
[FieldOffset(8)]
#endif
public KEYBDINPUT ki;
}

Unfortunately, no.
Attributes are "fused" to the type at compile-time, which is why all values passed to an attribute constructor must be constants.
And at runtime you can't modify the attributes attached to the type. You can grab a copy and modify its values, but the actual attribute attached to the type will remain unchanged, so you can't "trick" mscorlib code into seeing your changes instead of the original, either.

You could always have 2 structs and determine which one to use at runtime.
With a proper design you could limit code duplication to a few lines. (Plus having the structures twice.)

Related

Send Shift/Alt/Ctrl in SendInput

I'm writing a controller program, which simulates keystrokes to any active application. I found the SendInput method like this(I only care for keyboard, so irrelevant definitions omitted):
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public Int32 type;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public Int16 wVk;
public Int16 wScan;
public Int32 dwFlags;
public Int32 time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll")]
public static extern UInt32 SendInput(UInt32 nInputs, ref INPUT pInputs, int cbSize);
I know the wVk and wScan can be assigned by a System.Windows.Forms.Keys enumation value. I also looked at the values, and found Shift=65536, Control=131072, Alt=262144(the value of all keys is 1-254). I also know those three values should be in wScan(corresponding 1,2,4) and they are modifier state flags. But three questions:
For example, I want to send a left Ctrl key to the active application, must I pass both wVk==LControlKey and wScan==Control right? What will happen if I only pass the first and not the second? And should I pass wVk==LControlKey; wScan==Control; dwFlags==0(key down dwFlags), and wVk==LControlKey; wScan==0; dwFlags==2(key up dwFlags)?
in Keys enumation there exist LControlKey, RControlKey, ControlKey, what's the third one mean?
in Keys enumation there exist Control keys and Shift keys, but why not Alt keys? So isn't it possible to send an Alt keystroke by SendInput method?

Cannot declare pointer to non-unmanaged type

I'm trying to learn how to write a wrapper around a DLL and I've hit a bit of a road-block. I've got a struct declared as such:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_Surface
{
public readonly UInt32 flags;
public readonly SDL_PixelFormat* format;
public readonly int w, h;
public readonly int pitch;
public void* pixels;
/// <summary>Application data associated with the surface</summary>
public void* userdata;
/// <summary>information needed for surfaces requiring locks</summary>
public readonly int locked;
public readonly void* lock_data;
/// <summary>clipping information</summary>
public readonly SDL_Rect clip_rect;
/// <summary>info for fast blit mapping to other surfaces</summary>
private SDL_BlitMap *map; // <--- Cannot take the address of, get the size of, or declare a pointer to a managed type
/// <summary>Reference count -- used when freeing surface</summary>
public int refcount;
}
When I try to compile my project, it gives the above error.
But you will notice above it, I do have a pointer to another struct. I'm trying to figure out what the difference between these two structs is that makes one work but the other doesn't, but I'm not sure; they're both unsafe structs. They are as follows:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_PixelFormat
{
UInt32 format;
SDL_Palette *palette;
byte BitsPerPixel;
byte BytesPerPixel;
fixed byte padding [2];
UInt32 Rmask;
UInt32 Gmask;
UInt32 Bmask;
UInt32 Amask;
byte Rloss;
byte Gloss;
byte Bloss;
byte Aloss;
byte Rshift;
byte Gshift;
byte Bshift;
byte Ashift;
int refcount;
SDL_PixelFormat *next;
}
unsafe internal delegate int SDL_blit(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect);
[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_BlitMap
{
SDL_Surface* dst;
int identity;
SDL_blit blit;
void* data;
SDL_BlitInfo info;
/* the version count matches the destination; mismatch indicates
an invalid mapping */
UInt32 dst_palette_version;
UInt32 src_palette_version;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_Rect
{
int x, y;
int w, h;
}
So what do I have to change to make this compile?
I believe it's the reference to SDL_blit in SDL_BlitMap that's causing the problem. I've declared it as a delegate; is there something else I should be declaring it as? It's defined as this, in C:
typedef int (*SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect,
struct SDL_Surface * dst, SDL_Rect * dstrect);
Any struct that contains a managed type cannot have its address taken.
Delegates are a reference type, therefore they are also a managed type.
This means that an SDL_Blitmap is a managed type because it contains a managed reference to an SDL_blit, thus you cannot get a pointer to it without fixing it first.
If the function you are trying to invoke is already available in the dll, I suggest you have a look at the DllImportAttribute.(http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute(v=vs.100).aspx)
By combining a public static extern declaration with a DllImportAttribute, you can invoke any global function declared in the dll you are interop-ing.
Alternatively you'll need to create a C/C++ side function that takes a function pointer and invokes it, which could get messy.
What if you don't rely on unsafe code? It may affect affect performance though if your code is performance/speed critical. Something along the lines of this:
[StructLayout(LayoutKind.Sequential)]
struct SDL_PixelFormat
{
UInt32 format;
IntPtr palettePtr;
byte BitsPerPixel;
byte BytesPerPixel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
byte padding[];
UInt32 Rmask;
UInt32 Gmask;
UInt32 Bmask;
UInt32 Amask;
byte Rloss;
byte Gloss;
byte Bloss;
byte Aloss;
byte Rshift;
byte Gshift;
byte Bshift;
byte Ashift;
int refcount;
IntPtr nextPtr;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_Surface
{
public readonly UInt32 flags;
public readonly IntPtr format;
public readonly int w, h;
public readonly int pitch;
public IntPtr pixels;
/// <summary>Application data associated with the surface</summary>
public IntPtr userdata;
/// <summary>information needed for surfaces requiring locks</summary>
public readonly int locked;
public readonly IntPtr lock_data;
/// <summary>clipping information</summary>
public readonly SDL_Rect clip_rect;
/// <summary>info for fast blit mapping to other surfaces</summary>
private IntPtr mapPtr;
/// <summary>Reference count -- used when freeing surface</summary>
public int refcount;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_BlitMap
{
IntPtr dstPtr;
int identity;
SDL_blit blit;
IntPtr data;
SDL_BlitInfo info;
/* the version count matches the destination; mismatch indicates
an invalid mapping */
UInt32 dst_palette_version;
UInt32 src_palette_version;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_Rect
{
int x, y;
int w, h;
}
[UnmanagedFunctionPointer(CallingConvention.ToBeAdded)]
internal delegate int SDL_blit(ref SDL_Surface src, ref SDL_Rect srcrect, ref SDL_Surface dst, ref SDL_Rect dstrect);
When you need to read any structure pointers:
var palette = (SDL_Palette) Marshal.PtrToStructure(surface.palettePtr, typeof (SDL_Pallete));
Otherwise, if you wish to stick to your current code, try declaring the function pointer as an IntPtr instead of a delegate and create the delegate during runtime using Marshal.GetDelegateForFunctionPointer.

Volatile and LayoutKind.Explicit on struct

I've been looking all over both SO and MSDN, but have not been able to find anything mentioning the combination of a struct with LayoutKind.Explicit (which basically creates a C-style union in C#) that has a volatile field that overlaps several other fields, here's my code:
[StructLayout(LayoutKind.Explicit)]
public struct ThreadContextId
{
[FieldOffset(0)]
public byte ServerNumber;
[FieldOffset(1)]
public byte ThreadNumber;
[FieldOffset(0)]
public short Packed;
}
[StructLayout(LayoutKind.Explicit)]
public struct ThreadObjectId
{
[FieldOffset(0)]
public ThreadContextId ThreadContextId;
[FieldOffset(2)]
public ushort ObjectId;
[FieldOffset(0)]
public volatile uint Packed;
}
As you can see it's the last field in the ThreadObjectId struct which overlays both the previously defined struct and the ushort in the ThreadObjectId struct. Will this work "as expect", by which I mean how a volatile uint would work on a normal sequential struct or a reference type.
I won't be amazed if I'm proved wrong, but my understanding is:
Reads and writes of Packed would have volatile semantics in the situations it would with an automatic layout (e.g. not if via ref, but would most of the time).
Reads and writes of ThreadContextId and ObjectId would not have volatile semantics.
Therefore, it would be much like if some accesses to a non-volatile field were from normal assignment and some via Thread.VolatileRead and Thread.VolatileWrite.

Can't get around compile error about fixed sized buffers in C#

I'm trying to create some structures in C# to mimic ones from some C++ Microsoft header files. My code is as follows:
[StructLayout(LayoutKind.Sequential)]
unsafe public struct _NotifyData
{
fixed uint adwData[2];
public struct Data
{
uint cbBuf;
IntPtr pBuff;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO_DATA
{
public ushort Type;
public ushort Field;
public uint Reserved;
public uint Id;
public _NotifyData NotifyData;
}
[StructLayout(LayoutKind.Sequential)]
unsafe public struct PRINTER_NOTIFY_INFO
{
public uint Version;
public uint Flags;
public uint Count;
fixed PRINTER_NOTIFY_INFO_DATA aData[1]; //Compiler complains here!!!
}
The compiler complains about my aData[1] variable in my declaration of struct PRINTER_NOTIFY_INFO. I've encountered a handfull of these and adding fixed to the variable in question and unsafe to the structure declaration seemed to work. Except for this structure that is. The error I get is this:
Fixed size buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double
Now I can see that the type I'm using is not one of the listed types, but according to this, putting unsafe in front of the struct declaration should allow me to use types other than the ones listed. For some reason it is not letting me.
I have a couple of suggestions.
First of all _NotifyData is a union and should look like this:
[StructLayout(LayoutKind.Explicit)]
public struct _NotifyData
{
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
public uint[] adwData;
[FieldOffset(0)]
public struct Data
{
uint cbBuf;
IntPtr pBuff;
}
}
Secondly, PRINTER_NOTIFY_INFO simply can't be handled by the P/invoke marshaller. You will have to use manual marshalling, i.e. Marshal.PtrToStructure() to get anywhere. The documentation for the ppPrinterNotifyInfo parameter of FindNextPrinterChangeNotification() states:
A pointer to a pointer variable that receives a pointer to a system-allocated, read-only buffer. Call the FreePrinterNotifyInfo function to free the buffer when you are finished with it. This parameter can be NULL if no information is required.
You should pass an IntPtr as an out parameter and then use Marshal.PtrToStructure() to read out the contents into your own data structures. Something like this:
IntPtr PrinterNotifyInfo;
FindNextPrinterChangeNotification(..., out PrinterNotifyInfo);
IntPtr pCount = PrinterNotifyInfo + 2*Marshal.SizeOf(typeof(uint));
uint Count = (uint)Marshal.ReadInt32(pCount);
IntPtr pData = pCount + Marshal.SizeOf(typeof(uint));
for (int i=0; i<Count; i++)
{
PRINTER_NOTIFY_INFO_DATA Data = (PRINTER_NOTIFY_INFO_DATA)Marshal.PtrToStructure(pData, typeof(PRINTER_NOTIFY_INFO_DATA));
pData += Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO_DATA));
}
I've not attempted to compile this but hopefully it gets the idea across.
You just can't declare fixed-size array of custom structs in C#, as the error message and the page you linked to (I suggest you reread it) say.
EDIT: Removed incorrect info. See David Heffernan's answer for a way to solve this.

How to convert a C++ Struct with Union into C#?

Guys I am having difficulties on retrieving struct member values after calling a function in the DLL. I tried to convert the C++ codes into C# but I’m not sure if it is correct or not. Please help me understand my mistakes here (if there is) and how to correct.
My problem here is I can’t correctly retrieved the values of the INNER STRUCTS (Union) after I called the ReceiveMessage function from the DLL. Like for example m_objMsg.MsgData.StartReq.MsgID is always 0.
But when I try to use the C++ .exe program, the MsgID has a correct value. (not 0)
C++ Code:
extern int ReceiveMessage(SESSION, int, Msg*);
typedef struct
{
char SubsId[15];
int Level;
char Options[12];
} ConxReq;
typedef struct
{
char MsgId[25];
} StartReq;
typedef struct
{
long Length;
short Type;
union
{
ConxReq oConxReq;
StartReq oStartReq;
} Data;
} Msg;
/////////////////////////////////////////////////////
Msg oMsg;
int rc=ReceiveMessage(Session, 0, &oMsg);
switch(rc)
{
case 0:
switch(oMsg.Type)
{
case 0: // ConxReq
…
break;
case 1: // StartReq
…
break;
…
}
And here is my attempt to convert this into c#:
[DllImport("MyDLL.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
protected static extern Int32 ReceiveMessage(IntPtr session,
Int32 nTimeOut,
[MarshalAs(UnmanagedType.Struct)] ref Msg ptrMsg);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public StartReq oStartReq;
}
Msg m_objMsg = new Msg();
m_objMsg.MsgData = new Data();
m_objMsg.MsgData.oConxReq = new ConxReq();
m_objMsg.MsgData.oStartReq = new StartReq();
int rc = ReceiveMessage(m_Session, nTimeOut, ref m_objMsg);
then the SWITCH Condition
And If I add this struct inside the UNION for c++ and c#...
I've got an error stating the "... incorrectly align" or "...overlapped..."
c++
ConxNack oConxNack;
typedef struct
{
int Reason;
} ConxNack;
[StructLayout(LayoutKind.Sequential)]
public struct ConxNack
{
public int nReason;
}
[FieldOffset(0)]
public ConxNack oConxNack;
Thank you so much in advance for your time and help...
Akash is right, have a look here: http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3
Another option is create two structs and use an appropriate cast once you know which type it is.
hth
Mario
In C++, we know that all members of UNION shared the same memory chunk and can only have one member of an object at a time.
In order to implement this in C#, we need to use the LayoutKind to Explicit and set all the starting point of each member to 0.
In my previous example, An error message is displayed stating that the offset of an object type is incorrectly aligned or overlapped by a non-object type.
Answer is we cannot set all the members to FieldOffSet to 0 since it is not allowed to combine the reference type with the value type.
- Thanks to the explanation of Hans Passant
What I did is to create a copy of the UNION Member Structs and change the type of all the String Member Variables to bytes.
I used bytes since this is a value type so I can put this struct into FieldOffSet(0).
Take note, i adjust the FieldOffSet of the next member variable so i can still get the same size of my string variable.
And also for the struct size since i have byte member at the last.
Thanks to Akash Kava and Mario The Spoon for giving me an idea and providing me a useful link.
After calling the function in the DLL and passed this Struct Obj (ref m_objMsg) as a paramter, I need to extract the values.
One way is to have a pointer that points to the address of the struct in the unmanaged memory and convert this pointer a new
Struct with the corresponding member variables (my original structs).
NEW STRUCTS (BYTES)
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 31)]
public struct ConxReq
{
[FieldOffSet(0)]
public byteSubsId;
[FieldOffSet(15)]
public Int32 Level;
[FieldOffSet(19)]
public byte Options;
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ConxNack
{
[FieldOffSet(0)]
public int nReason;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 25)]
public struct StartReq
{
[FieldOffSet(0)]
public byte MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public ConxNack oConxNack;
[FieldOffset(0)]
public StartReq oStartReq;
}
////////////////////////////////////////////////////////////////
MY ORIGINAL STRUCTS
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyConxNack
{
public int nReason;
}
///////////////////////////////////////////////////////////////
Since I have a Msg.Type, i know what kind of struct (type) I could cast the object.
Like for example
ReceiveMessage(m_Session, nTimeOut, ref oMsg);
switch (oMsg.Type)
{
case 0: // ConxReq
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(oMsg.MsgData.ConxReq); // use the new struct (bytes)
Marshal.StructureToPtr(oMsg.MsgData.ConxReq, ptr, false);
MyConxReq oMyConxReq = new MyConxReq;
oMyConxReq = (MyConxReq) Marshal.PtrToStructure(ptr, typeof(MyConxReq)); // convert it to the original struct
Marshal.FreeHGlobal(ptr);
Then you can use now the oMyConxReq object to acccess the member variables directly.
Please let me know if you have other or better way to do this...
Kindly advise if what I did is correct or if I missed something.
Thank you so much!!! :)
You have to use StructLayout(LayoutKind.Explicit) and FieldOffsets to make union.

Categories

Resources