How to correctly marshal this C struct with pointer to array and pointer to pointer members in C# for use with 3rd party dll?
C:
typedef struct SomeStruct {
uint8_t *data[8];
int size[8];
uint8_t **extended_data;
};
Is it all just IntPtr and then you need to allocate the un-mamaged memory, copy data into it, and pin it? How would you do this if it's the case? The struct gets initialized through a function inside dll.
In python this is how I'd wrap and use this struct:
Python:
class SomeStruct(Structure):
_fields_ = [
('data', POINTER(c_uint8) * 8),
('size', c_int * 8),
('extended_data', POINTER(POINTER(c_uint8)))
]
# example use 1
dll = ctypes.CDLL("lib.dll")
some_struct = SomeStruct()
dll.init_struct(ctypes.byref(some_struct))
# or example 2
alloc_struct_fce = dll.alloc_struct
alloc_struct_fce.restype = ctypes.POINTER(SomeStruct) # specify return type
some_struct_ptr = alloc_struct_fce() # this gets passed to other dll functions
dll.some_processing(some_struct_ptr)
some_struct = some_struct_ptr.contents # dereference the pointer
Trying to find a C# equivalent of this code.
extended_data is a bonus if you know how to deal with it, it's dynamic size and I am not sure how I'd get its size.
A real example would be
struct AVFrame together with AVFrame *av_frame_alloc(void)
struct AVPacket with void av_init_packet(AVPacket *pkt) and AVPacket *av_packet_alloc(void)
The .dll provides methods to allocate and free these structures.
Given your struct, we could:
struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public IntPtr[] data;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public int[] size;
public IntPtr extended_data;
}
// I don't know the calling convention of your C method... You didn't show
// us its signature. It could be cdecl or stdcall
[DllImport(#"somedll.dll", CallingConvention = CallingConvention.Cdecl /* or StdCall */)]
public static extern void SomeStructMethod(out SomeStruct someStruct);
Then:
SomeStruct someStruct;
SomeStructMethod(out someStruct);
for (int i = 0; i < someStruct.data.Length; i++)
{
var array = new byte[someStruct.size[i]];
Marshal.Copy(someStruct.data[i], array, 0, array.Length);
Console.Write("> ");
for (int j = 0; j < array.Length; j++)
{
Console.Write($"{array[j]} ");
}
Console.WriteLine();
}
Note that at the end you should call some C method to free the memory allocated in someStruct otherwise you'll have a memory leak!
I can't help you about extended_data, because you haven't told us what it should be,
I woul use following code which is based on xanatos code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport(#"somedll.dll", CallingConvention = CallingConvention.Cdecl /* or StdCall */)]
public static extern void SomeStructMethod(out SomeStruct someStruct);
static void Main(string[] args)
{
SomeStruct someStruct;
SomeStructMethod(out someStruct);
byte[] data = new byte[8];
Marshal.Copy(someStruct.data, data, 0, 8);
byte[][] extendedData = new byte[8][];
for(int i = 0; i < 8; i++)
{
extendedData[i] = new byte[someStruct.size[i]];
Marshal.Copy(someStruct.extended_data[i], extendedData[i], 0, someStruct.size[i]);
}
}
}
struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public IntPtr data;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public int[] size;
public IntPtr[] extended_data;
}
}
Related
I have a C++ DLL with an exported function:
__declspec (dllexport) int AllocateBuf(DataBufferIn *in);
This function allocate buffer array and assign value for test
__declspec (dllexport) int AllocateBuf(DataBufferIn *in){
for (int i = 0 ; i < 4; i++){
in->data_r[i] = (double*)VirtualAlloc(
NULL, // System selects address
1024 * sizeof(double), // Size of allocation
MEM_RESERVE | MEM_COMMIT, // Allocate reserved pages
PAGE_READWRITE); // Protection = no access
for (int j = 0; j < 128; i++){
(in->data_r[i])[j] = j;//Assign value for test
}
}
}
The struct DataBufferIn is like this
struct DataBufferIn
{
double* data_r[4];
double* data_g[4];
};
The following is my code, i wanna marshal this struct in c#, but I get a exception (An unhandled exception of type 'System.Runtime.InteropServices.SafeArrayTypeMismatchException' occurred in C_Sharp_Sample.exe). I get stuck in this for much times, does anyone know how to solve this issue? Thanks :)
public struct DataBufferIn
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_r;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_g;
};
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AllocateBuf(ref DataBufferIn _data_buf_in);
private void button_allocate_buf_Click(object sender, EventArgs e)
{
DataBufferIn data_buf_in = new DataBufferIn();
AllocateBuf(ref data_buf_in);//<---Function call fail...
double[] doubleArrayRed = new double[1024];
Marshal.Copy(data_buf_in.lum_data_r[0], doubleArrayRed, 0, 1024);
.....
}
According to Charlieface suggestion,
Add [StructLayout(LayoutKind.Sequential)] could solve this issue.
[StructLayout(LayoutKind.Sequential)]
public struct DataBufferIn
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_r;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_g;
};
I want to use a c++ dll in c#. I'm using [DllImport] to call the method. I'm having trouble passing struct to a method.
I have a C struct:
typedef struct
{
DWORD TopPoint;
DWORD EndPoint;
WORD dwCount;
MYFUNC_NUMERIC11 *pGetData;
} MYFUNC_BUFFERNORMAL;
MYFUNC_NYMERIC11 is another struct.
typedef struct
{
BYTE Sign; // Sign ("±")
BYTE Integer[3]; // 3-digit integer (no zero suppression)
BYTE Period; // Decimal point (".")
BYTE Decimal[6]; // 6-digit decimal number
} MYFUNC_NUMERIC11;
I have written a C# struct to mimic this.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public unsafe struct MYFUNC_BUFFERNORMAL
{
public uint TopPoint;
public uint EndPoint;
public ushort Count;
public IntPtr pGetData;
}
A pointer to the struct is an argument in a method. C# function is:
[DllImport("MYFUNC_DLL.dll", EntryPoint = "MYFUNC_GetData", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, ThrowOnUnmappableChar = true)]
public static extern int MYFUNC_GetData(IntPtr myfuncHandle, UInt32 dwIO, ref IntPtr pBufferNormal, Byte bccFlg);
This is the method in C:
MYFUNC_STATUS MYFUNC_GetData(MYFUNC_HANDLE myfuncHandle, DWORD dwOut, MYFUNC_BUFFERNORMAL *pBufferNormal , BYTE bccFlg)
The return type is cast to an enum, which has an interpretation. The struct parameter is invalid. I've tried to allocate memory using Marshal.AllocHGlobal(...), but the parameter is still invalid, i.e. there is no error during compilation but the value returned is incorrect.
I've spent quite a few hours on this, still unable to figure out what to do. A lot of similar questions exist already, like here: How do I convert c struct from dll to C# or here: How to pass C# array to C++ and return it back to C# with additional items?, but I, somehow, still haven't figured out a way.
Something like this should work, at least with one element in the array (is it an array?). For an array, you will have to allocate sizeof * count of elements and marshal (StructureToPtr) each element at its offset.
var num = new MYFUNC_NUMERIC11();
num.Integer = new byte[] { 1, 2, 3 };
num.Decimal = new byte[] { 4, 5, 6, 7, 8, 9 };
num.Sign = 10;
num.Period = 11;
var buffer = new MYFUNC_BUFFERNORMAL();
buffer.Count = 1234;
buffer.EndPoint = 5678;
buffer.TopPoint = 9;
buffer.pGetData = Marshal.AllocCoTaskMem(Marshal.SizeOf(num));
try
{
Marshal.StructureToPtr(num, buffer.pGetData, false);
MYFUNC_GetData(Whatever, 0, ref buffer, 0);
}
finally
{
Marshal.FreeCoTaskMem(buffer.pGetData);
}
With these definitions.
[StructLayout(LayoutKind.Sequential)]
public struct MYFUNC_BUFFERNORMAL
{
public uint TopPoint;
public uint EndPoint;
public ushort Count;
public IntPtr pGetData;
}
[StructLayout(LayoutKind.Sequential)]
public struct MYFUNC_NUMERIC11
{
public byte Sign;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Integer;
public byte Period;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Decimal;
}
// check calling convention
[DllImport(#"MYFUNC_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern MYFUNC_STATUS MYFUNC_GetData(IntPtr myfuncHandle, uint dwIO, ref MYFUNC_BUFFERNORMAL pBufferNormal, byte bccFlg);
I want to fill a structure with data so I can pass it to my DLL functions. The structures are similar in both C# and C implementations. For instance something like (in C):
typedef struct{
apple[] apples;
leaf[] leaves;
} tree;
typedef struct{
int taste;
} apple;
typedef struct{
int color;
} leaf;
I want to create a tree structure on C#, fill it with apples and leaves and then send it to a function I have on a DLL.
How?
First of all, I think you should change the C struct a little bit:
typedef struct{
UINT cApples;
const apple *pApples;
UINT cLeaves;
const leaf *pLeaves;
} tree;
On the C# side:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct Tree
{
internal uint cApples;
internal IntPtr pApples;
internal uint cLeaves;
internal IntPtr pLeaves;
}
You can define Leaf and Apple similarly. Then you can populate them on C# side as follows:
private Tree CreateTree(Leaf[] leaves, Apple[] apples)
{
var result = new Tree();
result.cLeaves = (uint)leaves.Length;
result.cApples = (uint)apples.Length;
result.pLeaves = AllocateNativeMemory(leaves);
result.pApples = AllocateNativeMemory(apples);
return result;
}
private IntPtr AllocateNativeMemory<T>(T[] elements) where T : struct
{
int elementSize = Marshal.SizeOf(typeof(T));
IntPtr result = Marshal.AllocHGlobal(elements.Length * elementSize);
for (int i = 0; i < elements.Length; i++)
{
Marshal.StructureToPtr(
elements[i], new IntPtr((long)result + i * elementSize), false);
}
return result;
}
Now you can pass the result of the CreateTree method to the extern method that calls the C side.
Remark: The allocated memory should be freed; otherwise, your app will leak. If you decide that the C# side is responsible to free the allocated memory, you should do it at the end as follows:
private static void FreeTree(Tree tree)
{
FreeNativeMemory<Leaf>(tree.pLeaves, tree.cLeaves);
FreeNativeMemory<Apple>(tree.pApples, tree.cApples);
}
private static void FreeNativeMemory<T>(IntPtr arrayPtr, uint arrayLen) where T : struct
{
int elementSize = Marshal.SizeOf(typeof(T));
for (uint i = 0; i < arrayLen; i++)
{
Marshal.DestroyStructure(new IntPtr((long)arrayPtr + i * elementSize), typeof(T));
}
Marshal.FreeHGlobal(arrayPtr);
}
I am trying to use a DLL written in C in a C# application. I've created a simplified example that replicates the issue I am having.
The C code below creates an array of struct data's and assigns the array pointer to the array parameter passed to the get_data() function. The C# code is supposed to be the boilerplate code needed for marshalling the struct to be used in C#, but is presenting issues for me.
C code
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
struct data {
int32_t value;
union {
struct person {
uint8_t first[10];
uint8_t last[10];
} person;
struct number {
int32_t imaginary;
int32_t real;
} number;
} type;
};
int get_data(int count, struct data ***array)
{
int i;
/* allocate pointers */
*array = calloc(count, sizeof(struct data*));
if (*array == NULL)
return 1;
for (i = 0; i < count; i++) {
/* allocate data struct */
struct data *data = calloc(1, sizeof(struct data));
if (data == NULL)
return 2;
if ((i % 2) == 0) {
/* if even, its human */
data->value = i;
memcpy(data->type.person.first, "john", 4);
memcpy(data->type.person.last, "doe", 3);
} else {
/* if odd its a number */
data->value = i;
data->type.number.imaginary = -1;
data->type.number.real = i + 1;
}
(*array)[i] = data;
}
return 0;
}
C# code
[DllImport("libdata.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 get_data(Int32 count, ref IntPtr array);
[StructLayout(LayoutKind.Sequential)]
public struct Person
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public String first;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public String last;
}
[StructLayout(LayoutKind.Sequential)]
public struct Number
{
public Int32 imaginary;
public Int32 real;
}
[StructLayout(LayoutKind.Explicit)]
public struct TypeUnion
{
[FieldOffset(0)]
public Person person;
[FieldOffset(0)]
public Number number;
}
[StructLayout(LayoutKind.Sequential)]
public struct Data
{
public Int32 value;
public TypeUnion type;
}
Right now when I run my test program I get an exception:
System.TypeLoadException was unhandled
Message=Could not load type 'WpfRibbonApplication1.TypeUnion' from assembly 'WpfRibbonApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
I have tried several different ways of marshalling the Person strings, but get the exception whichever way I try (using this as reference). Am I missing something obvious? Could I get some help to properly read the array created in the C function within my C# application?
Edit (per David Heffernan's comment)
IntPtr arrayPtr = new IntPtr();
int count = 4;
int ret = LibData.get_data(count, ref arrayPtr);
Console.WriteLine("ret=" + ret);
for (int i = 0; i < count; i++)
{
IntPtr dataPtr = (IntPtr)Marshal.ReadIntPtr(arrayPtr) + (i * Marshal.SizeOf(typeof(IntPtr)));
Data data = (Data)Marshal.PtrToStructure(dataPtr, typeof(Data));
Console.WriteLine("value=" + data.value);
if ((i % 2) == 0)
{
// even is human
Console.WriteLine("first=" + data.type.first);
Console.WriteLine("last=" + data.type.last);
}
else
{
// odd is number
Console.WriteLine("imaginary=" + data.type.imaginary);
Console.WriteLine("real=" + data.type.real);
}
Console.WriteLine("");
}
The error message is telling you that you cannot overlay an object field with a non-object field. You are overlaying a string with an int.
There's no way you are going to get around that using FieldOffset to replicate the native union. As I see it you have two main options:
Stop using a union and include both Person and Number structs in the Data struct.
Continue using a union, but marshal it yourself. Use the Marshal class to read the data in the struct. For example, you'd use Marshal.ReadInt32 to read the integers, Marshal.Copy to read the character arrays and so on.
I have a simple C# data structure with a string, an int and a vector of ints:
class MyManagedClass
{
public string m_Str;
int m_Int;
int[] m_IntArray;
}
The equivalent in C++ is:
struct myUnmanagedStruct
{
char* m_Str;
UINT m_Int;
UINT* m_IntArray;
}
I have an unmanaged function that creates an array of myUnmanagedStruct structs. What is the best way to write a managed wrapper that correctly marshals the data such that what is created on the unmanaged side is correctly passed back to the managed side? (i.e. I want to make an array of MyManagedClass objects from an array of MyUnmanagedStructs)
Note:
a) A string is created in the unmanaged struct
b) A vector of ints is created in the unmanaged struct
My best attempt so far is:
On the managed side:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack=1)]
public class MyManagedClass
{
public MyManagedClass()
{
m_IntArray = new int[4];
}
public String m_Str;
public int m_Int;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public int[] m_IntArray;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnsafeLCEArray
{
public int m_Int;
public IntPtr m_CharBuf;
public IntPtr m_IntBuf;
}
public unsafe class LibWrap
{
// Declares managed prototypes for the unmanaged function.
[DllImport("mydll.dll", EntryPoint = "GetUnmanagedStructs")]
public static extern voidGetUnmanagedStructs(out int size, out IntPtr outArray);
}
On the unmanaged side:
typedef struct _MyUnmanagedStruct
{
char* m_Str;
UINT m_Int;
UINT* m_IntArray;
} MyUnmanagedStruct;
typedef struct _UNSAFELCEARRAY
{
char* strBuf;
UINT intBuf;
UINT* intArrayBuf;
} UNSAFELCEARRAY;
extern "C" __declspec(dllexport) void GetUnmanagedStructs( int* pSize, UNSAFELCEARRAY** ppStruct )
{
const int cArraySize = 5;
*pSize = cArraySize;
int numBytes = cArraySize * sizeof( MyUnmanagedStruct);
*ppStruct = (UNSAFELCEARRAY*)CoTaskMemAlloc(numBytes);
UNSAFELCEARRAY* pCurStruct = *ppStruct;
char* typenamebuffer;
char* numBuffer;
int var = 999;
for( int i = 0; i < cArraySize; i++, pCurStruct++ )
{
pCurStruct->intBuf = i+1;
typenamebuffer = (char*)CoTaskMemAlloc( 8 );
memcpy_s(typenamebuffer, 8, "bufABCD", 8);
pCurStruct->strBuf = typenamebuffer;
numBuffer = (char*)CoTaskMemAlloc( 16 );
++var;
memcpy_s(numBuffer, 4, &var, 4);
++var;
memcpy_s(numBuffer+4, 4, &var, 4);
++var;
memcpy_s(numBuffer+8, 4, &var, 4);
++var;
memcpy_s(numBuffer+12, 4, &var, 4);
pCurStruct->intArrayBuf = (UINT*)numBuffer;
}
}
Everything works if I remove the vector of ints from the managed and unmanaged objects, but with the code above the array of ints is uninitialized on return. I use the function below to generate MyManagedClasses from MyUnmanagedStructs
int size;
IntPtr outArray;
LibWrap.GetUnmanagedStructs(out size, out outArray);
manArray = new MyManagedClass[size];
IntPtr current = outArray;
for (int i = 0; i < size; i++)
{
manArray[i] = new MyManagedClass();
Marshal.PtrToStructure(current, manArray[i]);
Marshal.DestroyStructure(current, typeof(sb_LCE));
int numBytes = Marshal.SizeOf(manArray[i]);
current = (IntPtr)((long)current + numBytes);
}
Marshal.FreeCoTaskMem(outArray);
Forgive the lengthy explanation and the fact the unmanaged strut is being filled with dummy values. This is just for illustration.