I would like to marshal a C struct with a variable-length array back to C# but so far I can't get anything better than a pointer-to-struct representation and a pointer to float.
Unmanaged representation:
typedef float smpl_t;
typedef struct {
uint_t length; /**< length of buffer */
smpl_t *data; /**< data vector of length ::fvec_t.length */
} fvec_t;
Managed representation:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct fvec_t1
{
public uint length;
public float* data;
}
[DllImport("libaubio-4.dll", EntryPoint = "new_fvec", PreserveSig = true, CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.Cdecl)]
public static extern unsafe fvec_t1* new_fvec1(uint length);
What I would like is to have a .NET style array, where data would be float[] but if I do change the struct to the form below I do get Cannot take the address of, get the size of, or declare a pointer to a managed type in the external function above.
[StructLayout(LayoutKind.Sequential)]
public unsafe struct fvec_t1
{
public uint length;
public float[] data;
}
Apparently, it is not possible to a have a variable-length array marshalled back as-is, is this correct or is it there still a way to achieve this ?
short answer
you can't marshal variable length array as an array , because Without knowing the size, the interop marshalling service cannot marshal the array elements
but if you know the size it will be like below:
int arr[15]
you will be able to marshal it like this:
[MarshalAs(UnmanagedType.LPArray, SizeConst=15)] int[] arr
if you don't know the length of the array and this is what you want
you can convert it to intprt and deal with inptr but first you need to create 2 structs
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct fvec_t1
{
public uint whatever;
public int[] data;
}
the other one like below:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct fvec_t2{
public uint whatever;
}
create a function to initialize the array like below
private static int[] ReturnIntArray()
{
int [] myInt = new int[30];
for (int i = 0; i < myInt.length; i++)
{
myInt[i] = i + 1;
}
return myInt;
}
instantiate the first struct
fvec_t1 instance = new fvec_t1();
instance.whatever=10;
instance.data= ReturnIntArray();
instantiate the second struct
fvec_t2 instance1 = new fvec_t2();
instance1.whatever = instance.whatever
dynamically allocate space for fvec_t2 struct with extended space for data array
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(fvec_t2)) + Instance.data.Length);
Transfer the existing field values of fvec_t2 to memory space pointed to by ptr
Marshal.StructureToPtr(instance1, ptr, true);
Calculate the offset of data array field which should be at the end of an fvec_t2
struct
int offset = Marshal.SizeOf(typeof(fvec_t2));
get memory address of data array field based on the offset.
IntPtr address = new IntPtr(ptr.ToInt32() + offset);
copy data to ptr
Marshal.Copy(instance.data, 0, address, instance.data.Length);
do the call
bool success = dllfunction(ptr);
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
In the above case I'd definitely use the SizeParamIndex.
[StructLayout(LayoutKind.Sequential)]
public struct fvec_t1
{
uint length;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] float[] data;
}
Good luck.
Related
From the beginning, sorry for the weird title, but i really don't know how to describe this problem in short phrase.
I'm trying to wrapp a c++ DLL using pinvoke method. I have this function:
C++ header:
int32_t __cdecl ShowAllCharacters(Uint32Array *Image);
C#:
[DllImport(#"x86\OCR.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ShowAllCharacters(ref IntPtr image);
Where IntPtr image leads to following struct:
C++ header:
typedef struct {
int32_t dimSizes[2];
uint32_t elt[1];
} Uint32ArrayBase;
typedef Uint32ArrayBase **Uint32Array;
C#:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct Uint32Array
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public int[] dimSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public uint[] elt;
}
Basically function return Uint32Array struct, which represents array of uint elements. In Uint32Array, dimSizes is array length (dimSizes elements need to be multiplied to receive size) and elt is the first element of the array. That means, this uint array can have dynamic length.
Now my usage:
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);
unsafe public static void ShowAllCharacters()
{
IntPtr ptr = IntPtr.Zero;
OCRPinvoke.ShowAllCharacters(ref ptr);
IntPtr imgPP = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr));
Uint32Array img = (Uint32Array)Marshal.PtrToStructure(imgPP, typeof(Uint32Array));
uint[] dest = new uint[img.dimSizes[1] * img.dimSizes[0]];
fixed (uint* arrPtr = img.elt)
{
fixed (uint* destPtr = dest)
{
CopyMemory((IntPtr)destPtr, (IntPtr)arrPtr, (uint)dest.Length * sizeof(uint)); // Access violation reading location
}
}
}
My assumption is that this error is due to memory override before i'm able to copy uint array to managed array. Why? I know by fact that in some conditions, size of 'elt' array should be 5038848. If i'm setting SizeConst of the elt variable to 5038848, CopyMemory pass without exception
internal struct Uint32Array
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public int[] dimSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5038848)]
public uint[] elt;
}
I have also try to copy array by iterating through loop. Every time, accessing elt elements by index crashes on different index.
My question is if there is any way to lock some range of memory until i copy my array and then release it to being override by other processes?
I was able to solve this problem, inspired by #David Heffernan. He ask why marshaling manually?
Knowing that Uint32Array is sequential, we can read every value directly from pointers and then use Marshal.Copy to receive final managed array:
IntPtr ptr = IntPtr.Zero;
OCRPinvoke.ShowAllCharacters(ref ptr);
imagePtr = Marshal.ReadIntPtr(ptr);
int height = Marshal.ReadInt32(ptr);
int width = Marshal.ReadInt32(ptr + sizeof(int));
int[] img = new int[width * height];
Marshal.Copy(ptr + sizeof(int) * 2, img, 0, img.Length);
Following my other question here, I have been able to share string from C++ to C# thanks to this community.
However, I need to go one level above and I need to share chained structures from C++ to C# using memory mapping.
In an example scenario:
My C++ structures:
struct STRUCT_2
{
char Name[260];
};
struct STRUCT_1
{
void Init()
{
this->Count = 0;
this->Index = 0;
}
DWORD Count;
DWORD Index;
STRUCT_2 Table[256];
};
And me trying to "transfer" it to C#:
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_2
{
[FieldOffset(0)]
public fixed char Name[260];
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_1
{
void Init()
{
this.Count = 0;
this.Index = 0;
}
[FieldOffset(0)]
public uint Count;
[FieldOffset(0)]
public uint Index;
[FieldOffset(100)]
[MarshalAs(UnmanagedType.LPStruct, SizeConst = 256)]
public STRUCT_2 Table;
}
This works partially, basically I can see the values from Count and Index, however I cannot see values or even get them in STRUCT_2.
I have tried to change:
public STRUCT_2[] Table;
But then the compiler tells me:
"The specified Type must be a struct containing no references."
So my question would be, how can you read structures within structures using MemoryMappedFile in C# ?
Advice, thoughts or examples are very welcomed.
Update:
Complete testable code in C#:
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_2
{
[FieldOffset(0)]
public fixed byte Name[260];
// Fix thanks to Ben Voigt
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct STRUCT_1
{
void Init()
{
this.Count = 0;
this.Index = 0;
}
[FieldOffset(0)]
public uint Count;
[FieldOffset(0)]
public uint Index;
[FieldOffset(100)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public STRUCT_2[] Table;
}
static void Main(string[] args)
{
MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();
CustomSecurity.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>("everyone", MemoryMappedFileRights.FullControl, System.Security.AccessControl.AccessControlType.Allow));
var mappedFile = MemoryMappedFile.CreateOrOpen("Local\\STRUCT_MAPPING", 1024, MemoryMappedFileAccess.ReadWriteExecute, MemoryMappedFileOptions.None, CustomSecurity, System.IO.HandleInheritability.Inheritable);
using (var accessor = mappedFile.CreateViewAccessor())
{
STRUCT_1 data;
accessor.Read<STRUCT_1>(0, out data); // ERROR !
//The specified Type must be a struct containing no references.
Console.WriteLine(data.Count);
Console.WriteLine(data.Index);
}
}
Check this out.
Tested on Visual Studio 2017, Windows 7 x64.
Write and Read data works find.
It's also good study for me.
Take time on this.
Godspeed!
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct STRUCT_2
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] Name;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
public struct STRUCT_1
{
void Init()
{
this.Count = 0;
this.Index = 0;
}
public uint Count;
public uint Index;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] //! array size of 10.
public STRUCT_2 [] Table;
}
static void test3()
{
MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();
CustomSecurity.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>
( "everyone"
, MemoryMappedFileRights.FullControl
, System.Security.AccessControl.AccessControlType.Allow));
using (var mappedFile = MemoryMappedFile.CreateOrOpen("Local\\STRUCT_MAPPING"
, 10 * 1024
, MemoryMappedFileAccess.ReadWriteExecute
, MemoryMappedFileOptions.None
, CustomSecurity
, System.IO.HandleInheritability.Inheritable))
{
using (var accessor = mappedFile.CreateViewAccessor())
{
//! test setting.
int table_count = 5;
//! write data.
STRUCT_1 write_data;
write_data.Index = 1;
write_data.Count = 2;
write_data.Table = new STRUCT_2[10];
for (int i = 0; i < 10; i++)
{
write_data.Table[i].Name = new byte[260];
write_data.Table[i].Name[0] = (byte)i;
}
//! ----------------------------
// Get size of struct
int size = Marshal.SizeOf(typeof(STRUCT_1));
byte[] data = new byte[size];
// Initialize unmanaged memory.
IntPtr p = Marshal.AllocHGlobal(size);
// Copy struct to unmanaged memory.
Marshal.StructureToPtr(write_data, p, false);
// Copy from unmanaged memory to byte array.
Marshal.Copy(p, data, 0, size);
// Write to memory mapped file.
accessor.WriteArray<byte>(0, data, 0, data.Length);
// Free unmanaged memory.
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
//! ----------------------------------------------
STRUCT_1 read_data;
size = Marshal.SizeOf(typeof(STRUCT_1));
data = new byte[size];
// Initialize unmanaged memory.
p = Marshal.AllocHGlobal(size);
// Read from memory mapped file.
accessor.ReadArray<byte>(0, data, 0, data.Length);
// Copy from byte array to unmanaged memory.
Marshal.Copy(data, 0, p, size);
// Copy unmanaged memory to struct.
read_data = (STRUCT_1)Marshal.PtrToStructure(p, typeof(STRUCT_1));
// Free unmanaged memory.
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
Console.WriteLine(read_data.Index);
Console.WriteLine(read_data.Count);
}
}
}
I have some C++ dll with struct description and some methods:
struct MSG_STRUCT {
unsigned long dataSize;
unsigned char* data;
}
And function for example:
unsigned long ReadMsg( unsigned long msgId, MSG_STRUCT* readMsg)
{
readMsg->dataSize = someDataSize;
readMsg->data = someData;
}
So I want to call this function from C#:
[StructLayout(LayoutKind.Sequential)]
struct MSG_STRUCT
{
UInt32 dataSize;
byte[] data;
}
[DllImport("mydll.dll")]
public static Int32 ReadMsg( UInt32 msgId, ref MSG_STRUCT readMsg);
So I tried to call C# function like:
var readMsg = new MSG_STRUCT();
readMsg.data = new byte[4128];
Int32 res = ReadMsg( someMsgId, ref readMsg);
But I didn't get smth normal in data.
I also tried to call ReadMsg with IntPrt type parameter, but Marshal.PtrToStructure gave me AccessViolationException sometimes.
I don't have any ideas how to pass a pointer to MSG_STRUCT from C# to C++ and receive the result as filled MSG_STRUCT.data
The final solutionthat worked for me:
I used a part of solution offered by xanatos:
I set CallingConvention = CallingConvention.Cdecl for my DllImport function.
I found out that I also need to change:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4128)]
public byte[] Data;
Thanks everyone for your help
You could try with:
[StructLayout(LayoutKind.Sequential)]
public struct MSG_STRUCT
{
int dataSize;
IntPtr data;
public byte[] GetData()
{
var bytes = new byte[dataSize];
Marshal.Copy(data, bytes, 0, dataSize);
return bytes;
}
}
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint ReadMsg(uint msgId, ref MSG_STRUCT readMsg);
and then:
MSG_STRUCT msg = new MSG_STRUCT();
uint res = ReadMsg(123, ref msg);
byte[] bytes = msg.GetData();
Your C function is reassigning the data pointer, so you have to marshal it back to C#. The easiest way (for me) is to simply pass a IntPtr and do some explicit Marshal.Copy(...).
An alternative is to have data a byte[], but then in C-side you have to memcpy(readMsg->data, someData, someDataSize) instead of simply assigning readMsg->data = someData.
Try to change attribute from
[StructLayout(LayoutKind.Sequential)]
to
[StructLayout(LayoutKind.Sequential, Pack=X)]
Where X is 1,2,4,8 ..
Default packing in c++ is 8, so try to set Pack = 8
i use p/invoke to return an array of "DN_OPstruct"s from my unmanaged code:
struct DN_OPstruct {
const char* TargetNode_Identifier;
const char* Name;
int TargetNode_NamespaceIndex;
...
};
EXTERN_C UA_EXPORT_WRAPPER_IMPORT int getOpToArr(const char* _rootGuid, DN_OPstruct ** array, int * arraySizeInElements){
std::list<UA_Ref_and_TargetNode> uaList;
uaList = getLisT(...)
*arraySizeInElements = uaList.size();
int bytesToAlloc = sizeof(DN_OPstruct) * (*arraySizeInElements);
DN_OPstruct * a = static_cast<DN_OPstruct*>(CoTaskMemAlloc(bytesToAlloc));
*array = a;
for (UA_Ref_and_TargetNode &i: uaList){
DN_OPstruct iterOp;
iterOp = getOp(...);
opList.push_back(iterOp);
}
return 1;
}
My managed Code looks like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DN_OPstruct
{
private IntPtr TargetNode_Identifier;
private IntPtr NamePtr;
public string Guid
{
get { return Marshal.PtrToStringAnsi(TargetNode_Identifier); }
set { TargetNode_Identifier = Marshal.StringToHGlobalAnsi(value); }
}
public string Name
{
get { return Marshal.PtrToStringAnsi(NamePtr); }
set { NamePtr = Marshal.StringToHGlobalAnsi(value); }
}
public int TargetNode_NamespaceIndex;
...
};
[DllImport(#"...", CallingConvention = CallingConvention.Cdecl,
EntryPoint = "getOpToArr",
ExactSpelling = true, CharSet = CharSet.Ansi)]
public static extern int getOpToArr([MarshalAs(UnmanagedType.LPStr)]string myNodeGuid,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out DN_OPstruct[] array, out int arraySizeInElements);
If i'm trying to call the method, i will jump in the unmanaged code and can debug it through sucessfully and i get an array with my DN_OPstructs back. However, if i read out its fields like .Name or .Guid, i get this error:
First-chance exception at 0x000007fefd921757 in (...).exe: 0xC0000005:
Access violation reading location 0xffffffffffffffff.
If there is a handler for this exception, the program may be safely
continued.
I tried to add "ArraySubType = UnmanagedType.LPStruct" to my method declaration; it did not help.
public static extern int getOpToArr(
[MarshalAs(UnmanagedType.LPStr)]
string myNodeGuid,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
out DN_OPstruct[] array,
out int arraySizeInElements
);
The problem is the second parameter. The unmanaged code cannot synthesise a managed .net array. You need to declare the p/invoke like this:
public static extern int getOpToArr(
string myNodeGuid,
out IntPtr arrayPtr,
out int arrayLen
);
Then you will need to use Marshal.PtrToStructure to marshal the elements of the array to a managed array.
IntPtr arrayPtr;
int arrayLen;
int retval = getOpToArr(nodeGuid, out arrayPtr, out arrayLen);
// check retval
IntPtr ptr = arrayPtr;
DN_OPstruct[] arr = new DN_OPstruct[arrayLen];
for (int i = 0; i < arrayLen; i++)
{
arr[i] = (DN_OPstruct)Marshal.PtrToStructure(ptr, typeof(DN_OPstruct));
ptr += Marshal.SizeOf(typeof(DN_OPstruct));
}
I'm also a little sceptical of the properties in your struct. Why do you have setters as well as getters? It doesn't look like the data flows in that direction. And the unmanaged code that you use shows allocation with CoTaskMemAlloc which doesn't match StringToHGlobalAnsi. So even though I doubt that you should be writing settings and so perhaps should remove the calls to StringToHGlobalAnsi, I also suspect there is confusion over the allocator that you are using.
Do note that the code in your question gives no evidence of how you allocated the array which is returned to the caller. So, for all we know, there could be a problem in that part of the code.
EDIT: I oversimplified my example... In the real code I was assigning values to strMyStringx without correctly using wcscpy_s, so instead of assigning the values I was just passing the pointer, which was out of scope by the time the values were being marshaled into managed code...
I'm trying to marshal a struct with three string properties from C to C#, but I can't get the definition of the struct right in C#. All of the properties print as garbage. Am I marshaling wrong or do my properties have the wrong type?
My custom structure:
typedef struct _MY_STRUCT_STRING {
LPWSTR strMyString1;
LPWSTR strMyString2;
LPWSTR strMyString3;
}MY_STRUCT_STRING, *PMY_STRUCT_STRING;
My C function returns an array of pointers to this struct:
bool bEnumerateString(OUT LONG &i_arr_size, OUT PMY_STRUCT_STRING* &pArrStringStruct)
{
// [...] function simplified to demonstrate building a pointer to an array of struct*
long i_arr_size = 3
PMY_STRUCT_STRING *ptr_arr_string = (PMY_STRUCT_STRING *)malloc(sizeof(PMY_STRUCT_STRING)* i_arr_size);
for (int i = 0; i < i_arr_size; i++) {
ptr_arr_string[i] = (PMY_STRUCT_STRING)malloc(sizeof(MY_STRUCT_STRING));
ptr_arr_string[i]->strMyString1 = L"String 1"; // This would work. In the real code I was assigning values from another array and mistakenly passed the pointer rather than doing wcscpy_s
ptr_arr_string[i]->strMyString2 = L"String 2";
ptr_arr_string[i]->strMyString3 = L"String 3";
}
pArrStringStruct = ptr_arr_string;
return true;
}
C#:
//Import the DLL with my function
[DllImport("My.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "bEnumerateString")]
internal static extern bool bEnumerateString(out long count, out IntPtr pArrStringStruct);
// Define the C# equivalent of the C struct
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct MY_STRUCT_STRING
{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
public string strMyString1;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
public string strMyString1;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
public string strMyString1;
}
[...]
// Code to marshal (try... catch etc removed for succinctness)
IntPtr pArrStruct = IntPtr.Zero;
long lCount = 0;
bool bResult = false;
bResult = bEnumerateString(out lCount, out pArrStruct);
if (!bResult)
{
// Marshal to deref pointer
IntPtr[] pArrStructList = new IntPtr[lCount];
for (ulong i = 0; i < (ulong)lCount; i++)
{
pArrStructList[i] = Marshal.ReadIntPtr(pArrStruct, Marshal.SizeOf(typeof(IntPtr)) * (int)i);
}
// Marshal pointers to struct
var lstMyStringStrct = new List<MY_STRUCT_STRING>(pArrStructList.Length);
foreach (IntPtr ptr in pArrStructList)
{
lstMyStringStrct.Add((MY_STRUCT_STRING)Marshal.PtrToStructure(ptr, typeof(MY_STRUCT_STRING)));
}
// Enumerate struct
foreach (MY_STRUCT_STRING myStr in lstMyStringStrct)
{
// All of these outputs are garbage
Console.WriteLine("strMyString1: " + myStr.strMyString1);
Console.WriteLine("strMyString2: " + myStr.strMyString2);
Console.WriteLine("strMyString3: " + myStr.strMyString3);
}
}
I see one problem. You're C++ structure uses LPWSTR (pointers) whereas you're C# code is expecting fixed size char arrays.
Change your strings from:
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)]
public string strMyString1;
which would be used when the C++ structure was defined like:
char strMyString1[8];
to:
[MarshalAsAttribute(UnmanagedType.LPWStr)]
public string strMyString1;