dllimport : c code affect a struct passed by reference (c#) - c#

I've got a c dll that contains an exposed function, that takes three parameters :
int ParseInput(char* opt_path, char* input, SENNA_RESULT_ARRAY* result);
I want to call this from C#, which actually works. The problem is that the result struct is not affected.
Here is the structure defined in c code :
typedef struct RESULT_
{
char* word;
int pos_start;
int pos_end;
char* pos;
char* chk;
char* ner;
char* psg;
} RESULT;
typedef struct RESULT_ARRAY_
{
int size;
RESULT* Results;
} RESULT_ARRAY;
and my c# code :
[StructLayout(LayoutKind.Sequential)]
public struct SENNA_RESULT
{
[MarshalAs(UnmanagedType.LPStr)]
public string word;
[MarshalAs(UnmanagedType.I4)]
public int pos_start;
[MarshalAs(UnmanagedType.I4)]
public int pos_end;
[MarshalAs(UnmanagedType.LPStr)]
public string pos;
[MarshalAs(UnmanagedType.LPStr)]
public string chk;
[MarshalAs(UnmanagedType.LPStr)]
public string ner;
[MarshalAs(UnmanagedType.LPStr)]
public string psg;
}
[StructLayout(LayoutKind.Sequential)]
public struct SENNA_RESULT_ARRAY
{
public SENNA_RESULT[] Results;
public int size;
}
[DllImport("Senna-32.dll", CharSet = CharSet.Ansi)]
static extern int Parse(string msg, string stream, ref SENNA_RESULT_ARRAY results);
Parse(#"path", "sentence", ref result_array)
I've tried many things, like :
1-use classes instead of struct without ref keyword
2-use a pointer instead of passing a struct
Each time i got a different error like
array is not of the specified type
low level error( corrupted heap)
even if i don't specify the array in the first struct, the size member has not the correct value (the C code prints the value in the console)
Any advice ?
Thanks

Consider using code below.
[StructLayout(LayoutKind.Sequential)]
public struct SENNA_RESULT
{
public IntPtr word;
public int pos_start;
public int pos_end;
public IntPtr pos;
public IntPtr chk;
public IntPtr ner;
public IntPtr psg;
}
[StructLayout(LayoutKind.Sequential)]
public struct SENNA_RESULT_ARRAY
{
public IntPtr Results;
public int size;
}
[DllImport("Senna-32.dll")]
static extern int Parse(string msg, string stream, out SENNA_RESULT_ARRAY results);
Here is usage example
SENNA_RESULT_ARRAY array = new SENNA_RESULT_ARRAY();
int result = Parse("path", "sentence", out array);
if (result == SUCCESS && array.Results != IntPtr.Zero)
{
for (int index = 0; index < array.size; index++)
{
IntPtr offset = (IntPtr)((int)array.Results + index * Marshal.SizeOf(typeof(SENNA_RESULT)));
SENNA_RESULT senna = (SENNA_RESULT)Marshal.PtrToStructure(offset, typeof(SENNA_RESULT));
}
}
I hope that you understand that it is just idea. Make sure that code is working properly and then improve it to make it more comfotable to use. I talking about replacing IntPtr with string.

Related

C# DllImport : AccessViolationException when calling a vkCreateInstance

I'm trying to create a Vulkan wrapper in C#, but I have some problems when I call a function. I rewrote the vulkan.h header as follows :
public static class Vk {
[StructLayout(LayoutKind.Sequential)] public class Instance { }
public enum Result {
...
}
public enum StructureType {
...
}
[StructLayout(LayoutKind.Sequential)] public class ApplicationInfo {
public StructureType sType;
public IntPtr pNext;
public string pApplicationName;
public uint applicationVersion;
public string pEngineName;
public uint engineVersion;
public uint apiVersion;
}
[StructLayout(LayoutKind.Sequential)] public class InstanceCreateInfo {
public StructureType sType;
public IntPtr pNext;
public uint flags_VkInstanceCreateFlags;
public ApplicationInfo pApplicationInfo;
public uint enabledLayerCount;
public string[] ppEnabledLayerNames;
public uint enabledExtensionCount;
public string[] ppEnabledExtensionNames;
}
[DllImport("vulkan-1.dll", EntryPoint = "vkCreateInstance")]
public extern static Result CreateInstance(
InstanceCreateInfo pCreateInfo,
IntPtr AllocationCallbacks_pAllocator,
out IntPtr pInstance_Instance);
}
The original declaration in C of this function is
VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance);
Now when I call my function, I'm doing like this :
Vk.InstanceCreateInfo instance_create_info = new Vk.InstanceCreateInfo();
...
IntPtr hinstance;
Vk.Result result = Vk.CreateInstance(instance_create_info, IntPtr.Zero, out hinstance); <-- error AccessViolationException
I don't understand where is my problem, because it seems to be a valid solution : StackOverflow : AccessViolationException when calling vkEnumeratePhysicalDevices via pInvoke from c#.
I tried by initializing my IntPtr hinstance with
Marshal.AllocHGlobal(Marshal.SizeOf<Vk.Instance>());
I also tried to "convert" my instance_create_info to another IntPtr with Marshal.StructureToPtr(...); and I tried to pass instance_create_info and instance by the ref keyword. Obviously, nothing worked.
Any idea ?
EDIT :
The native function is used as follows :
//Definition
typedef struct VkApplicationInfo {
VkStructureType sType;
const void* pNext;
const char* pApplicationName;
uint32_t applicationVersion;
const char* pEngineName;
uint32_t engineVersion;
uint32_t apiVersion;
} VkApplicationInfo;`
typedef struct VkInstanceCreateInfo {
VkStructureType sType;
const void* pNext;
VkInstanceCreateFlags flags;
const VkApplicationInfo* pApplicationInfo;
uint32_t enabledLayerCount;
const char* const* ppEnabledLayerNames;
uint32_t enabledExtensionCount;
const char* const* ppEnabledExtensionNames;
} VkInstanceCreateInfo;
#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
VK_DEFINE_HANDLE(VkInstance)
//Code
VkApplicationInfo application_info{};
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.apiVersion = VK_API_VERSION;
application_info.applicationVersion = VK_MAKE_VERSION( 1, 0, 0 );
application_info.pApplicationName = "";
application_info.engineVersion = VK_MAKE_VERSION( 1, 0, 0 );
application_info.pEngineName = "";
VkInstanceCreateInfo instance_create_info{};
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_create_info.pApplicationInfo = &application_info;
instance_create_info.enabledLayerCount = 0
instance_create_info.ppEnabledLayerNames = nullptr
instance_create_info.enabledExtensionCount = 0
instance_create_info.ppEnabledExtensionNames = nullptr
VkInstance _instance = nullptr;
assert( !vkCreateInstance( &instance_create_info, nullptr, &_instance ) );
it seems that you are trying to do something similar this guy made a wrapper for the c#. Can be useful for you. Go to Source/SharpVulkan/Generated/Functions.
public static unsafe Instance CreateInstance(ref InstanceCreateInfo createInfo, AllocationCallbacks* allocator = null)
{
Instance instance;
fixed (InstanceCreateInfo* __createInfo__ = &createInfo)
{
vkCreateInstance(__createInfo__, allocator, &instance).CheckError();
}
return instance;
}
[DllImport("vulkan-1.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern unsafe Result vkCreateInstance(InstanceCreateInfo* createInfo, AllocationCallbacks* allocator, Instance* instance);
internal static unsafe void EnumerateInstanceExtensionProperties(byte* layerName, ref uint propertyCount, ExtensionProperties* properties)
{
fixed (uint* __propertyCount__ = &propertyCount)
{
vkEnumerateInstanceExtensionProperties(layerName, __propertyCount__, properties).CheckError();
}
}
Hope it is helpfull :)
remove out keyword for third argument or declare it as corresponding struct.

How to marshall out void* argument which can be one of two structs from interface?

I have an unmanaged interface I'm trying to marshal and use in C#.
And there is a function I'm not sure how to marshal correctly:
IDataInfo :
public IUnknown {
...
STDMETHOD_(BOOL, GetDataPackInfo) (UINT packIndex, void* pPackExtendedInfo) = 0;
...
}
The void* can be one of two different structures:
struct DataExtendedInfoArchive {
WORD Size;
BOOL Archived;
UINT SignalLength;
BYTE Captured;
};
struct DataExtendedInfoStorage {
WORD Size;
FLOAT SignalFreq;
UINT SignalLength;
CHAR Code[4];
};
I implement those in C# like this:
[StructLayout(LayoutKind.Sequential)]
public struct TrackExtendedInfoAudio
{
int Size;
[MarshalAs(UnmanagedType.Bool)]
bool Archived;
uint SignalLength;
byte Captured;
}
[StructLayout(LayoutKind.Sequential)]
public struct TrackExtendedInfoVideo
{
public int Size;
public double SignalFreq;
public uint SignalLength;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public StringBuilder Code;
}
The problem is I don't exactly understand what I'm going to get in void* pPackExtendedInfo and how to handle it and therefore don't know how to write a correct marshaling signature for this function.
The managed function signature (minus attributes and decorations) should be:
// make sure that the return is marshalled as UnmanagedTypes.Boolean.
bool GetDataPackInfo(uint packIndex, IntPtr pPackExtendedInfo);
To unpack the struct, first you need to determine which one you're working with. Fortunately the first member of each is a size parameter, which will give a clue as to the size of the struct. To read that size, then unpack the structure:
IntPtr ptr; // this is the pointer passed to your callback.
int cbSize = Marshal.ReadInt32(ptr, 0);
if (cbSize = Marshal.SizeOf(TrackExtendedInfoAudio))
{
TrackExtendedInfoAudio s = Marshal.PtrToStructure(ptr, typeof(TrackExtendedInfoAudio))
as TrackExtendedInfoAudio;
// Processing...
}
else if (cbSize == Marshal.SizeOf(TrackExtendedInfoVideo))
{
TrackExtendedInfoVideo s = Marshal.PtrToStructure(ptr, typeof(TrackExtendedInfoVideo))
as TrackExtendedInfoVideo;
// Processing...
}
else
{
// unknown struct
}
You could marshal void* as IntPtr:
private static extern bool GetDataPackInfo(uint packIndex, [In,Out] IntPtr pPackExtendedInfo);
And copy structure using one of the Marshal.PtrToStructure (and Marshal.StructureToPtr) methods:
IntPtr p = IntPtr.Zero;
GetDataPackInfo(..., p);
TrackExtendedInfoAudio audioInfo = Marshal.PtrToStructure<TrackExtendedInfoAudio>(p);
or
TrackExtendedInfoVideo videoInfo = Marshal.PtrToStructure<TrackExtendedInfoVideo>(p);
PS. And I'm not sure the StringBuilder is an appropriate marshaling for the CHAR Code[4];.

How to allocate memory for an array of structure in C#

Structure 1:
typedef struct _wfs_cdm_cu_info
{
USHORT usTellerID;
USHORT usCount;
LPWFSCDMCASHUNIT * lppList;
} WFSCDMCUINFO, * LPWFSCDMCUINFO;
Structure 2:
typedef struct _wfs_cdm_cashunit
{
USHORT usNumber;
USHORT usType;
LPSTR lpszCashUnitName;
CHAR cUnitID[5];
CHAR cCurrencyID[3];
ULONG ulValues;
ULONG ulInitialCount;
ULONG ulCount;
ULONG ulRejectCount;
ULONG ulMinimum;
ULONG ulMaximum;
BOOL bAppLock;
USHORT usStatus;
USHORT usNumPhysicalCUs;
LPWFSCDMPHCU * lppPhysical;
} WFSCDMCASHUNIT, * LPWFSCDMCASHUNIT;
Structure 3:
typedef struct _wfs_cdm_physicalcu
{
LPSTR lpPhysicalPositionName;
CHAR cUnitID[5];
ULONG ulInitialCount;
ULONG ulCount;
ULONG ulRejectCount;
ULONG ulMaximum;
USHORT usPStatus;
BOOL bHardwareSensor;
} WFSCDMPHCU, * LPWFSCDMPHCU;
C# structure:-
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi, Pack = 1)]
public struct WFSCDMPHCU { [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string lpPhysicalPositionName;[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=5)]
public string cUnitID;
public uint ulInitialCount;
public uint ulCount;
public uint ulRejectCount;
public uint ulMaximum;
public ushort usPStatus;
public int bHardwareSensor;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi, Pack = 1)]
public struct WFSCDMCASHUNIT {
public ushort usNumber;
public ushort usType; [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string lpszCashUnitName;[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=5)]
public string cUnitID; [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=3)]
public string cCurrencyID;
public uint ulValues;
public uint ulInitialCount;
public uint ulCount;
public uint ulRejectCount;
public uint ulMinimum;
public uint ulMaximum;
public int bAppLock;
public ushort usStatus;
public ushort usNumPhysicalCUs;
public System.IntPtr lppPhysical;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)]
public struct WFSCDMCUINFO {
public ushort usTellerID;
public ushort usCount;
public System.IntPtr lppList;
}
DLLImport
[DllImport(#"Dispenser.dll")]
public static extern int CDM_SetCashUnit(out WFSCDMCUINFO cuinfo);
1)My main problem is How should I marshall or allocate memory for this structure to send data from C# to C++ ,the second and third structure being array of structure???
2)If I use pointer how efficient it would be.
3)If C++/CLI wrapper be used then how I could access it through C#.
It is been a long time I'm working and I m yet to figure out how should I fill the array of structures in C# .
Follwing code is what i try to figure out...
Marshal Code:
Facing an error of "to define an extension non generic static class"
public static IntPtr GetIntPtr(this object obj) {
try {
var handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
var thread = new Thread(() => {
Thread.Sleep(20000);
handle.Free();
});
thread.Start();
return handle.AddrOfPinnedObject();
} catch (ArgumentException) {
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
Marshal.StructureToPtr(obj, ptr, false);
return ptr;
}
}
public static T FromIntPtr<T>(this IntPtr ptr) {
if (ptr == IntPtr.Zero)
return default(T);
return (T) Marshal.PtrToStructure(ptr, typeof (T));
}
A link of C++ code, of how I have called the function is given.link
This is my structure mapping in c#:
[StructLayout(LayoutKind.Sequential)]
public struct Message
{
public uint MsgId;
public uint DLC;
public uint Interval;
public uint Handle;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] Data;
};
C++ here:
struct Message {
unsigned int MsgId;
unsigned int DLC;
unsigned int Interval;
unsigned int Handle;
unsigned char Data[64];
} ;
My method in c++ which needs such a structure as a parameter:
extern "C" __declspec(dllexport) int _stdcall MessageWrapper( Message *msg)
Here is how i call this method from C#:
Message frame = new Message();
frame.MsgId = (uint)MsgId;
frame.DLC = (uint)Dlc;
frame.Interval = (uint)Interval;
frame.Data = new byte[64];
int rawsize = Marshal.SizeOf(frame);
IntPtr frameBuffer = Marshal.AllocHGlobal(rawsize);
Marshal.StructureToPtr(frame, frameBuffer, false);
Call the method here:
int response = HwWrapper.MessageWrapper(frameBuffer);
You may have some modifications made in c++ and you can read them back here in c#:
frame = (Message)(Marshal.PtrToStructure(frameBuffer, typeof(Message)));
Marshal.FreeHGlobal(frameBuffer);

Error looping through IntPtr to build array of struct

I'm having a problem building an array of structs from an IntPtr in another struct.
This structure is returned by a Windows API I'm using:
public struct DFS_INFO_9 {
[MarshalAs(UnmanagedType.LPWStr)]
public string EntryPath;
[MarshalAs(UnmanagedType.LPWStr)]
public string Comment;
public DFS_VOLUME_STATE State;
public UInt32 Timeout;
public Guid Guid;
public UInt32 PropertyFlags;
public UInt32 MetadataSize;
public UInt32 SdLengthReserved;
public IntPtr pSecurityDescriptor;
public UInt32 NumberOfStorages;
public IntPtr Storage;
}
[DllImport("netapi32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int NetDfsEnum([MarshalAs(UnmanagedType.LPWStr)]string DfsName, int Level, int PrefMaxLen, out IntPtr Buffer, [MarshalAs(UnmanagedType.I4)]out int EntriesRead, [MarshalAs(UnmanagedType.I4)]ref int ResumeHandle);
I'm trying to get the array of DFS_STORAGE_INFO_1s referenced by IntPtr Storage.
Here's that structure (if it matters):
public struct DFS_STORAGE_INFO_1 {
public DFS_STORAGE_STATE State;
[MarshalAs(UnmanagedType.LPWStr)]
public string ServerName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ShareName;
public IntPtr TargetPriority;
}
So far this code has been working to get the DFS_INFO_9s with only one storage, but fails when trying to marshal the second item in the array.
DFS_INFO_9 info = GetInfoFromWinApi();
List<DFS_STORAGE_INFO_1> Storages = new List<DFS_STORAGE_INFO_1>();
for (int i = 0; i < info.NumberOfStorages; i++) {
IntPtr pStorage = new IntPtr(info.Storage.ToInt64() + i * Marshal.SizeOf(typeof(DFS_STORAGE_INFO_1)));
DFS_STORAGE_INFO_1 storage = (DFS_STORAGE_INFO_1)Marshal.PtrToStructure(pStorage, typeof(DFS_STORAGE_INFO_1));
Storages.Add(storage);
}
I'm getting a FatalExecutionEngineError that spits out error code 0x0000005 (Access Denied). I'm assuming the size of the DFS_STORAGE_INFO_1 is being miscalculated causing the marshal to attempt to access memory outside that allocated for the array. But this is happening on i = 1, when there might be 7 storages to get through. Maybe my thinking is flawed, but I have no idea how to rectify this.
The actual definition of DFS_STORAGE_INFO_1 is this:
public struct DFS_STORAGE_INFO_1 {
public DFS_STORAGE_STATE State;
[MarshalAs(UnmanagedType.LPWStr)]
public string ServerName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ShareName;
DFS_TARGET_PRIORITY TargetPriority;
}
TargetPriority is a structure, defined here:
public struct DFS_TARGET_PRIORITY {
public DFS_TARGET_PRIORITY_CLASS TargetPriorityClass;
public UInt16 TargetPriorityRank;
public UInt16 Reserved;
}
public enum DFS_TARGET_PRIORITY_CLASS {
DfsInvalidPriorityClass = -1,
DfsSiteCostNormalPriorityClass = 0,
DfsGlobalHighPriorityClass = 1,
DfsSiteCostHighPriorityClass = 2,
DfsSiteCostLowPriorityClass = 3,
DfsGlobalLowPriorityClass = 4
}
As for the FatalExecutionEngineError, I believe that the size of the structure DFS_STORAGE_INFO_1 was being miscalculated since it was defined incorrectly. When trying to convert the pointers to the structures they referenced, the next index was wrong because the size was off. When converting the block of memory, it presumably referenced a block that shouldn't have been accessible, throwing the "Access Denied (0x0000005)" error.

What am I doing wrong with this use of StructLayout( LayoutKind.Explicit ) when calling a PInvoke struct with union?

The following is a complete program. It works fine as long as you don't uncomment the '#define BROKEN' at the top. The break is due to a PInvoke failing to marshal a union correctly. The INPUT_RECORD structure in question has a number of substructures that might be used depending on the value in EventType.
What I don't understand is that when I define only the single child structure of KEY_EVENT_RECORD it works with the explicit declaration at offset 4. But when I add the other structures at the same offset the structure's content get's totally hosed.
//UNCOMMENT THIS LINE TO BREAK IT:
//#define BROKEN
using System;
using System.Runtime.InteropServices;
class ConIOBroken
{
static void Main()
{
int nRead = 0;
IntPtr handle = GetStdHandle(-10 /*STD_INPUT_HANDLE*/);
Console.Write("Press the letter: 'a': ");
INPUT_RECORD record = new INPUT_RECORD();
do
{
ReadConsoleInputW(handle, ref record, 1, ref nRead);
} while (record.EventType != 0x0001/*KEY_EVENT*/);
Assert.AreEqual((short)0x0001, record.EventType);
Assert.AreEqual(true, record.KeyEvent.bKeyDown);
Assert.AreEqual(0x00000000, record.KeyEvent.dwControlKeyState & ~0x00000020);//strip num-lock and test
Assert.AreEqual('a', record.KeyEvent.UnicodeChar);
Assert.AreEqual((short)0x0001, record.KeyEvent.wRepeatCount);
Assert.AreEqual((short)0x0041, record.KeyEvent.wVirtualKeyCode);
Assert.AreEqual((short)0x001e, record.KeyEvent.wVirtualScanCode);
}
static class Assert { public static void AreEqual(object x, object y) { if (!x.Equals(y)) throw new ApplicationException(); } }
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ReadConsoleInputW(IntPtr hConsoleInput, ref INPUT_RECORD lpBuffer, int nLength, ref int lpNumberOfEventsRead);
[StructLayout(LayoutKind.Explicit)]
public struct INPUT_RECORD
{
[FieldOffset(0)]
public short EventType;
//union {
[FieldOffset(4)]
public KEY_EVENT_RECORD KeyEvent;
#if BROKEN
[FieldOffset(4)]
public MOUSE_EVENT_RECORD MouseEvent;
[FieldOffset(4)]
public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
[FieldOffset(4)]
public MENU_EVENT_RECORD MenuEvent;
[FieldOffset(4)]
public FOCUS_EVENT_RECORD FocusEvent;
//}
#endif
}
[StructLayout(LayoutKind.Sequential)]
public struct KEY_EVENT_RECORD
{
public bool bKeyDown;
public short wRepeatCount;
public short wVirtualKeyCode;
public short wVirtualScanCode;
public char UnicodeChar;
public int dwControlKeyState;
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSE_EVENT_RECORD
{
public COORD dwMousePosition;
public int dwButtonState;
public int dwControlKeyState;
public int dwEventFlags;
};
[StructLayout(LayoutKind.Sequential)]
public struct WINDOW_BUFFER_SIZE_RECORD
{
public COORD dwSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct MENU_EVENT_RECORD
{
public int dwCommandId;
}
[StructLayout(LayoutKind.Sequential)]
public struct FOCUS_EVENT_RECORD
{
public bool bSetFocus;
}
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short X;
public short Y;
}
}
UPDATE:
For those worried about the struct declarations themselves:
bool is treated as a 32-bit value
the reason for offset(4) on the data is to allow for the 32-bit structure alignment which prevents the union from beginning at offset 2.
Again, my problem isn't making PInvoke work at all, it's trying to figure out why these additional structures (supposedly at the same offset) are fowling up the data by simply adding them.
I believe it will work if you make bSetFocus and dwCommandId of type uint.
In the future, check out the PInvoke wiki for the proper signatures. It's usually accurate or, at the very least, a good starting point.
//UNCOMMENT THIS LINE TO BREAK IT:
//#define BROKEN
using System;
using System.Runtime.InteropServices;
class ConIOBroken
{
static void Main()
{
int nRead = 0;
IntPtr handle = GetStdHandle(-10 /STD_INPUT_HANDLE/);
Console.Write("Press the letter: 'a': ");
INPUT_RECORD record = new INPUT_RECORD();
do
{
ReadConsoleInputW(handle, ref record, 1, ref nRead);
} while (record.EventType != 0x0001/*KEY_EVENT*/);
Assert.AreEqual((short)0x0001, record.EventType);
Assert.AreEqual(1u, record.KeyEvent.bKeyDown);
Assert.AreEqual(0x00000000, record.KeyEvent.dwControlKeyState & ~0x00000020);//strip num-lock and test
Assert.AreEqual('a', record.KeyEvent.UnicodeChar);
Assert.AreEqual((short)0x0001, record.KeyEvent.wRepeatCount);
Assert.AreEqual((short)0x0041, record.KeyEvent.wVirtualKeyCode);
Assert.AreEqual((short)0x001e, record.KeyEvent.wVirtualScanCode);
return;
}
static class Assert { public static void AreEqual(object x, object y) { if (!x.Equals(y)) throw new ApplicationException(); } }
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ReadConsoleInputW(IntPtr hConsoleInput, ref INPUT_RECORD lpBuffer, int nLength, ref int lpNumberOfEventsRead);
[StructLayout(LayoutKind.Explicit)]
public struct INPUT_RECORD
{
[FieldOffset(0)]
public short EventType;
//union {
[FieldOffset(4)]
public KEY_EVENT_RECORD KeyEvent;
[FieldOffset(4)]
public MOUSE_EVENT_RECORD MouseEvent;
[FieldOffset(4)]
public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
[FieldOffset(4)]
public MENU_EVENT_RECORD MenuEvent;
[FieldOffset(4)]
public FOCUS_EVENT_RECORD FocusEvent;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEY_EVENT_RECORD
{
public uint bKeyDown;
public short wRepeatCount;
public short wVirtualKeyCode;
public short wVirtualScanCode;
public char UnicodeChar;
public int dwControlKeyState;
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSE_EVENT_RECORD
{
public COORD dwMousePosition;
public int dwButtonState;
public int dwControlKeyState;
public int dwEventFlags;
};
[StructLayout(LayoutKind.Sequential)]
public struct WINDOW_BUFFER_SIZE_RECORD
{
public COORD dwSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct MENU_EVENT_RECORD
{
public int dwCommandId;
}
[StructLayout(LayoutKind.Sequential)]
public struct FOCUS_EVENT_RECORD
{
public uint bSetFocus;
}
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short X;
public short Y;
}
}
1) public bool bKeyDown should be Int32 bKeyDown because it's a BOOL (4bytes) in c++
2) public bool bSetFocus should be Int32
Just a thought, what happens if you declare the largest field last? Maybe P/Invoke is copying up to the last field, which ends before earlier fields.
Try adding a manually calculated Size field to the StructLayout attribute, like this:
[StructLayout(LayoutKind.Explicit, Size=...)]
The original code with the bool contained 13 bytes, starting at FieldOffset(4) ...
The MOUSE_EVENT_RECORD starting at the same offset conatianed 16 bytes starting at the same offset.
When you changed the bool (1byte) to an uint(4bytes) you made up the 3 bytes.

Categories