Getting a complex struct from C++ to C# - c#

Days ago I made this question: Passing a complex Struct (with inner array of struct) from C# to C++
Fortunately it as been answered and the code seems to work.
Now I need to do the oposite case, I mean, I need to get the struct from the C++ dll to my C# code.
I was researching in this site, trying the use of the IntPtr type, but didnt work. Then as my struct in C# was defined correctly I tried to use an out reference.
As a sumary, gonna repost the structs defined in both languages
typedef struct _ImParam
{
UINT Format;
UINT Resolution;
UINT ColorDepth;
} IM_PARAM;
typedef struct _sValues
{
UINT Xpos;
UINT Ypos;
UINT Width;
UINT Height;
BOOL Milli;
} S_VALUES;
typedef struct _sProperties
{
BOOL Enable;
S_VALUES Properties;
} S_PROPERTIES;
typedef struct _DevParam
{
BOOL Enable;
UINT Font;
char Symbol;
IM_PARAM Image1;
IM_PARAM Image2;
S_PROPERTIES Properties[10];
UINT FeedMode;
} DevParam;
// more code, comments, etc. etc.
// The function I want to use
BOOL GetParameters( DWORD ID, DevParam *dParam );
This is how I build the structs in C#
[StructLayout(LayoutKind.Sequential)]
public struct ImParam
{
public uint Format;
public uint Resolution;
public uint ColorDepth;
public ImParam(uint n)
{
Format = n;
Resolution = 300;
ColorDepth = 256;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sValues
{
public uint Xpos;
public uint Ypos;
public uint Width;
public uint Height;
public bool Milli;
public sValues(uint n)
{
Xpos = n;
Ypos = n;
Width = n;
Height = n;
Milli = false;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sProperties
{
public bool Enable;
public sValues Properties;
public sProperties(int n)
{
Enable = false;
Properties = new sValues(n);
}
};
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
public bool Enable;
public uint Font;
public char Symbol;
public ImParam Image1;
public ImParam Image2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public sProperties[] Properties;
public uint FeedMode;
public DeviceParameters(int n)
{
Enable = true;
Font = 0;
Symbol = '?';
Image1 = new ImParam(3);
Image2 = new ImParam(3);
Properties = new sProperties[10];
for(int i = 0; i < 10; i++)
Properties[i] = new sProperties(n);
FeedMode = 1;
}
};
// This is the method imported from the C++ dll
[DllImport(path, EntryPoint = "?GetParameters##YGHKPAU_DevParam###Z")]
public static extern bool GetParameters(int ID, out DevParam dParam);
And here's the call
// Already got ID from somewhere else
DevParam DP;
bool res = Class1.GetParameters(ID, out DP);
Console.WriteLine("Result: " + res);
The code seems to work, since I'm getting a "true" as result. The problem is it's getting wrong values in the structs members, placing the default ones (always 0 the numbers, always false the booleans), even if I use the SetParam(..) method before (and I know that one works because when I change the image format scanner decreases scanning speed).
What am I missing?
Note: I dont have source code of the .dll
Edited:
Been trying with these modifications:
// At the dll wrapper class
[DllImport(path, EntryPoint = "?GetParameters##YGHKPAU_DevParam###Z")]
public static extern bool GetParameters(int ID, ref IntPtr dParam);
// At main
int size = Marshal.SizeOf(typeof(DevParam));
IntPtr Ptr = Marshal.AllocHGlobal(size);
bool res = Class1.GetParameters(ID, ref Ptr);
Console.WriteLine("Result: " + res);
var test = Marshal.PtrToStructure(Ptr, typeof(DevParam));
// No idea what I'll do here
Marshal.FreeHGlobal(Ptr);
If I try to print "test", it should give an adress, since it's a pointer, but it's null. Neither know how can I extract the data from the struct.
Any ideas?

Solved!
// At the dll wrapper class
[DllImport(path, EntryPoint = "?GetParameters##YGHKPAU_DevParam###Z")]
public static extern bool GetParameters(int ID, IntPtr dParam);
// At main
int size = Marshal.SizeOf(typeof(DevParam));
IntPtr Ptr = Marshal.AllocHGlobal(size);
bool res = Class1.GetParameters(ID, Ptr);
DevParam test = (DevParam)Marshal.PtrToStructure(Ptr, typeof(DevParam));
// For testing purpoises, previously changed the default values with another method
Console.WriteLine(test.Enable);
Marshal.FreeHGlobal(Ptr);
Had to remove the ref keyword.

Related

Trouble converting function from vmm.dll (C) to vmmsharp.dll (C#). (DMA) (PCILEECH) (C#)

I am attempting to bring over a function from vmmdll.h to vmmsharp.cs. In this case, I am trying to bring over Map_GetPool.
You can see here my implementation to C#:
//Please view the images:
Bringing function over
internal static uint VMMDLL_POOLMAP_FLAG_ALL = 0;
internal static uint VMMDLL_POOLMAP_FLAG_BIG = 1;
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
internal unsafe struct VMMDLL_MAP_POOLENTRYTAG
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] internal char[] szTag;
//szTag struct
/*internal uint dwTag;
internal uint _Filler;
internal uint cEntry;
internal uint iTag2Map;*/
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
internal unsafe struct VMMDLL_MAP_POOLENTRY
{
internal ulong va;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] internal char[] szTag;
//szTag struct
/*internal uint dwTag;
internal byte _ReservedZero;
internal byte fAlloc;
internal byte tpPool;
internal byte tpSS;*/
internal uint cb;
internal uint _Filler;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
internal unsafe struct VMMDLL_MAP_POOL
{
internal uint dwVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] internal uint[] _Reserved1;
internal uint cbTotal;
internal uint* piTag2Map;
internal VMMDLL_MAP_POOLENTRYTAG* pTag;
internal uint cTag;
internal uint cMap;
internal VMMDLL_MAP_POOLENTRY[] pMap;
}
[DllImport("vmm.dll", EntryPoint = "VMMDLL_Map_GetPoolEx")]
internal static extern unsafe bool VMMDLL_Map_GetPoolEx(
byte* pNetMap,
ref uint pcbNetMap);
Implementing it into c#
//My map pool
public unsafe struct MAP_POOL
{
public uint dwVersion;
public uint[] _Reserved1;
//public unsafe fixed uint _Reserved1[6];
public uint cbTotal;
public uint[] piTag2Map;
public MAP_POOLENTRYTAG* pTag;
public uint cTag;
public uint cMap;
public MAP_POOLENTRY[] pMap;
}
//szTag[5] -> POOLENTRY 4th tpPool MAP_POOL_TYPE 5th tpSS MAP_POOL_TYPE_SUBSEGMENT
public enum MAP_POOL_TYPE
{
MAP_POOL_TYPE_Unknown = 0,
MAP_POOL_TYPE_NonPagedPool = 1,
MAP_POOL_TYPE_NonPagedPoolNx = 2,
MAP_POOL_TYPE_PagedPool = 3
}
public enum MAP_POOL_TYPE_SUBSEGMENT
{
VMM_MAP_POOL_TYPE_SUBSEGMENT_UNKNOWN = 0,
VMM_MAP_POOL_TYPE_SUBSEGMENT_NA = 1,
VMM_MAP_POOL_TYPE_SUBSEGMENT_BIG = 2,
VMM_MAP_POOL_TYPE_SUBSEGMENT_LARGE = 3,
VMM_MAP_POOL_TYPE_SUBSEGMENT_VS = 4,
VMM_MAP_POOL_TYPE_SUBSEGMENT_LFH = 5
}
public unsafe struct MAP_POOLENTRYTAG
{
//union
public char[] szTag;
/*
* struct {
DWORD dwTag;
DWORD _Filler;
DWORD cEntry;
DWORD iTag2Map;
};
*/
}
public unsafe struct MAP_POOLENTRY
{
public ulong va;
public char[] szTag;
//public unsafe fixed char szTag[5];
public uint cb;
public uint _Filler;
}
Function
public static unsafe MAP_POOLENTRY[] Map_GetPoolEx()
{
bool result;
uint cb = 0;
int cbMAP = System.Runtime.InteropServices.Marshal.SizeOf(typeof(vmmi.VMMDLL_MAP_POOL));
int cbENTRY = System.Runtime.InteropServices.Marshal.SizeOf(typeof(vmmi.VMMDLL_MAP_POOLENTRY));
result = vmmi.VMMDLL_Map_GetPoolEx(null, ref cb);
if (!result || (cb == 0)) { return new MAP_POOLENTRY[0]; }
fixed (byte* pb = new byte[cb])
{
result = vmmi.VMMDLL_Map_GetPoolEx(pb, ref cb);
if (!result) { return new MAP_POOLENTRY[0]; }
vmmi.VMMDLL_MAP_POOL pm = Marshal.PtrToStructure<vmmi.VMMDLL_MAP_POOL>((System.IntPtr)pb);
if (pm.dwVersion != vmmi.VMMDLL_MAP_PHYSMEM_VERSION) { return new MAP_POOLENTRY[0]; }
MAP_POOLENTRY[] m = new MAP_POOLENTRY[pm.cMap];
for (int i = 0; i < pm.cMap; i++)
{
vmmi.VMMDLL_MAP_POOLENTRY n = Marshal.PtrToStructure<vmmi.VMMDLL_MAP_POOLENTRY>((System.IntPtr)(pb + cbMAP + i * cbENTRY));
MAP_POOLENTRY e;
e.va = n.va;
e.cb = n.cb;
e.szTag = n.szTag;
//m[i] = e;
}
return m;
}
}
As you can see, the pointer PVMMDLL -> VMMDLL* throws an error stating it can't do that.
For the function, for some reason, e is undefined even though it is indeed defined. Also, is my overall setup correct?
Thanks, I appreciate any help.
Here are the links again to the implementations if needed (This doesn't have my implementation in it, but it has other implementations for similar functions):
vmmdll: https://github.com/ufrisk/MemProcFS/blob/master/vmm/vmmdll.h#L1506
vmmsharp: https://github.com/ufrisk/MemProcFS/blob/master/vmmsharp/vmmsharp.cs

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];.

dllimport : c code affect a struct passed by reference (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.

C# Union Structure Marshalling

I'm trying to integrate Video4Linux in my managed application. Indeed I've declared all required structures and relative ioctl. In this question I present two ioctl: SetFormat and GetFormat; while the former is working well (like the other dozen I actually use), the latter is returning me bad memory behavior.
The GetFormat ioctl is actually performing, but as soon the application is accessing to the ioctl parameter (either debugger or my application itself), it always crashes with following stack:
System.NullReferenceException: ...
at System.String.mempy4(...) in <filename unknown>:0
at System.String.memcpy(...) in <filename unknown>:0
at Derm.Platform.Video4Linux.ControlGetFormat(...) in <...>
...
I had some investigation on p/invoking ioctl, and again, I cannot understand why my application is crashing. I suspect it's becuase structure memory layout, but I cannot explain why SetFormat is working while GetFormat don't (since they use the very same parameter).
I have to P/Invoke a ioctl routine that receive/return the structure v4l2_format:
struct v4l2_format {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
__u8 raw_data[200]; /* user-defined */
} fmt;
};
struct v4l2_pix_format {
__u32 width;
__u32 height;
__u32 pixelformat;
enum v4l2_field field;
__u32 bytesperline; /* for padding, zero if unused */
__u32 sizeimage;
enum v4l2_colorspace colorspace;
__u32 priv; /* private data, depends on pixelformat */
};
I've traduced the structure v4l2_format using the following form
[StructLayout(LayoutKind.Explicit, Pack = 4, CharSet = CharSet.Ansi)]
public struct Format
{
public Format(BufferType bufferType)
{
Type = bufferType;
Pixmap = new PixmapFormat();
RawData = new byte[RawDataLength];
}
public Format(BufferType bufferType, PixmapFormat pixmapFormat)
{
Type = bufferType;
Pixmap = pixmapFormat;
RawData = new byte[RawDataLength];
}
[FieldOffset(0)]
public BufferType Type;
[FieldOffset(4)]
public PixmapFormat Pixmap;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst=RawDataLength)]
public byte[] RawData;
public const int RawDataLength = 200;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct PixmapFormat
{
public PixmapFormat(uint width, uint height, PixelFormatCode pixelFormat)
{
Width = width;
Height = height;
PixelFormat = pixelFormat;
Field = Video4Linux.BufferField.Any;
BytesPerLine = 0;
SizeImage = 0;
Colorspace = Video4Linux.PixelColorspace.None;
Private = 0;
}
public UInt32 Width;
public UInt32 Height;
public PixelFormatCode PixelFormat;
public BufferField Field;
public UInt32 BytesPerLine;
public UInt32 SizeImage;
public PixelColorspace Colorspace;
public UInt32 Private;
}
And finally, here is the methods calling ioctl:
public static void ControlGetFormat(IntPtr fd, BufferType pixmapType, out PixmapFormat pixmapFormat)
{
if (fd == IntPtr.Zero)
throw new ArgumentException("invalid file descriptor", "fd");
Format format = new Format(pixmapType);
int result = IoCtrlGetFormat(fd, GetFormat.ControlCode, ref format);
if (result < 0)
ThrowExceptionOnError();
pixmapFormat = format.Pixmap;
}
private static readonly IoCtrlRequest GetFormat = new IoCtrlRequest(IoCtrlDirection.Read | IoCtrlDirection.Write, 'V', 4, typeof(Format));
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
private static extern int IoCtrlGetFormat(IntPtr fd, Int32 code, ref Format argument);
public static void ControlSetFormat(IntPtr fd, BufferType pixmapType, ref PixmapFormat pixmapFormat)
{
if (fd == IntPtr.Zero)
throw new ArgumentException("invalid file descriptor", "fd");
PixmapFormat pixmapFormatCopy = pixmapFormat;
Format format = new Format(pixmapType, pixmapFormatCopy);
int result = IoCtrlSetFormat(fd, SetFormat.ControlCode, ref format);
if (result < 0)
ThrowExceptionOnError();
pixmapFormat = format.Pixmap;
}
private static readonly IoCtrlRequest SetFormat = new IoCtrlRequest(IoCtrlDirection.Read | IoCtrlDirection.Write, 'V', 5, typeof(Format));
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
private static extern int IoCtrlSetFormat(IntPtr fd, Int32 code, ref Format argument);
The problem has been solved by forcing the structure size to the actual one (204, in my case), and remove the array field RawData (as suggested by David Heffernan).
Indeed:
[StructLayout(LayoutKind.Explicit, Pack = 4, CharSet = CharSet.Ansi, Size = 204)]
public struct Format
{
public Format(BufferType bufferType)
{
Type = bufferType;
Pixmap = new PixmapFormat();
}
public Format(BufferType bufferType, PixmapFormat pixmapFormat)
{
Type = bufferType;
Pixmap = pixmapFormat;
}
[FieldOffset(0)]
public BufferType Type;
[FieldOffset(4)]
public PixmapFormat Pixmap;
}
Of course, Marshal.SizeOf(typeof(Format)) == 204.

Marshaling an array inside a Struct with methods using C# P/Invoke

I wrote in C a struct like that
struct IMAGE {
unsigned int x, y;
unsigned char **data;
};
Could anybody please tell me how to marshall this struct to use in C#?
my solution does not work.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class IMAGE
{
public UInt32 x;
public UInt32 y;
public byte[][] data;
};
Managed arrays are different than pointers. A managed array requires the size of the array, and if you're trying to marshal a struct, it requires a fixed size to marshal directly.
You can use the SizeConst parameter of the MarshalAs attribute to set the size of data when it gets marshaled.
But I'm guessing that x and y are the dimensions of the image and that the size of data depends on those variables. The best solution here is to marshal it over as an IntPtr and access the data when you need it:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class IMAGE
{
public UInt32 x;
public UInt32 y;
private IntPtr data;
public byte[][] Data
{
get
{
byte[][] newData = new byte[y][];
for(int i = 0; i < y; i++)
{
newData[i] = new byte[x];
Marshal.Copy(new IntPtr(data.ToInt64() + (i * x)), newData[i], 0, x);
}
return newData;
}
set
{
for (int i = 0; i < value.Length; i++)
{
Marshal.Copy(value[i], 0, new IntPtr(data.ToInt64() + (i * x)), value[i].Length);
}
}
}
}
If you are allowed to use unsafe code, you can change the IntPtr to a byte** and work with it directly.
With the setter, you'll probably want to verify the dimensions of the value before you blindly write to unmanaged memory.
My guess is that it is:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class IMAGE
{
public UInt32 x;
public UInt32 y;
public ref IntPtr data;
};
A very handy reference is the p/invoke cheatsheet.

Categories