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.
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);
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;
}
}
Below I have a code snippet from c++.
I need to return array of pointers (to TempStruct).
The problem is that on c# side I get only one element. On the other I get AV.
**C++**
extern "C" __declspec(dllexport) void GetResult(TempStruct** outPtr, long *size)
{
*outPtr = (TempStruct*)new TempStruct*[2];
outPtr[0] = new TempStruct("sdf", 123);
outPtr[1] = new TempStruct("abc", 456);
*size = 2;
}
**C#**
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void GetResult(out IntPtr outPtr, out int size);
IntPtr ptr = IntPtr.Zero;
int length;
GetResult(out ptr, out length);
int size = Marshal.SizeOf(typeof(TempStruct));
TempStruct[] someData2 = new TempStruct[length];
for (int i = 0; i < length; i++)
{
IntPtr wskptr = (IntPtr)(ptr.ToInt64() + (size * i));
someData2[i] = (TempStruct)Marshal.PtrToStructure(wskptr, typeof(TempStruct));
}
You are doing it wrong.
You are mixing pointer types.
By using the new TempStruct() you are creating an array of pointers to TempStruct. The example I gave you created an array of TempStruct. See the difference?
Now... TempStruct** outPtr should be TempStruct*** outPtr (because you want to return (*) an array (*) of pointers (*)... Or TempStruct**& if you prefer :-)
Change this line
someData2[i] = (TempStruct)Marshal.PtrToStructure(Marshal.ReadIntPtr(wskptr), typeof(TempStruct));
Because you must read the single pointers.
I do hope you are deleting the various TempStruct with delete and using the
delete[] ptr;
operator to delete the array of structures.
Full example:
C++:
struct TempStruct
{
char* str;
int num;
// Note the strdup. You don't know the source of str.
// For example if the source is "Foo", then you can't free it.
// Using strdup solves this problem.
TempStruct(const char *str, int num)
: str(strdup(str)), num(num)
{
}
~TempStruct()
{
free(str);
}
};
extern "C"
{
__declspec(dllexport) void GetResult(TempStruct ***outPtr, int *size)
{
*outPtr = new TempStruct*[2];
(*outPtr)[0] = new TempStruct("sdf", 123);
(*outPtr)[1] = new TempStruct("abc", 456);
*size = 2;
}
__declspec(dllexport) void FreeSomeData(TempStruct **ptr, int size)
{
for (int i = 0; i < size; i++)
{
delete ptr[i];
}
delete[] ptr;
}
}
C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1), Serializable]
internal struct TempStruct
{
public string str;
public int num;
}
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void GetResult(out IntPtr outPtr, out int numPtr);
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void FreeSomeData(IntPtr ptr, int num);
// C++ will return its TempStruct array in ptr
IntPtr ptr;
int size;
GetResult(out ptr, out size);
TempStruct[] someData2 = new TempStruct[size];
for (int i = 0; i < size; i++)
{
IntPtr ptr2 = Marshal.ReadIntPtr(ptr, i * IntPtr.Size);
someData2[i] = (TempStruct)Marshal.PtrToStructure(ptr2, typeof(TempStruct));
}
// Important! We free the TempStruct allocated by C++. We let the
// C++ do it, because it knows how to do it.
FreeSomeData(ptr, size);
Note that you don't need [Serializable] and Pack=1 on the C# struct
More correct for the C++:
__declspec(dllexport) void GetResult(TempStruct **&outPtr, int &size)
{
outPtr = new TempStruct*[2];
outPtr[0] = new TempStruct("sdf", 123);
outPtr[1] = new TempStruct("abc", 456);
size = 2;
}
It is more correct because both outPtr and size can't be NULL. See https://stackoverflow.com/a/620634/613130 . The C# signature is the same.
The C++ code is wrong. It's returning an array of pointers to struct. The fact that you cast the value returned by new should have alerted you to the fact that you made a mistake. You want to return an array of struct.
It should be:
*outPtr = new TempStruct[2];
(*outPtr)[0].str = "sdf";
(*outPtr)[0].i = 123;
(*outPtr)[1].str = "abc";
(*outPtr)[1].i = 456;
*size = 2;
I have make a dll from dev c++.and I want to call the function.
the dev c++ code like this :
//the head file
struct sAdd
{
int* aarr;
int* barr;
int length;
};
DLLIMPORT int AddStruct (struct sAdd*);
//the c file
DLLIMPORT int AddStruct (struct sAdd* sadd)
{//sum the aarr and the barr and return the result.
int sum=0;
int* aarr=sadd->aarr;
int* barr=sadd->barr;
int numArr=sadd->length;
int i;
for(i=0;i<numArr;i++)
sum+=*(aarr+i);
i=0;
for(i=0;i<numArr;i++)
sum+=*(barr+i);
return sum;
}
In order to call the AddStruct function,I need to define a struct first.
public struct sAdd
{
public int[] a;
public int[] b;
public int length;
}
[DllImport("DllMain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AddStruct(ref sAdd sadd);
the code to call the AddStruct function is like this:
sAdd sadd = new sAdd();
sadd.a = new int[4] { 1, 2, 3 ,4};
sadd.b = new int[4] { 1, 2, 3 ,4};
sadd.length=4;
Console.WriteLine("sAdd:" + AddStruct(ref sadd).ToString());
the result should be 20,but I get a 37109984 or some other big number.
So ,I am not sure how to change the code to get a right reusult.Maybe I need to use the IntPtr or other ways??thanks .
At last,I deal with the problem.Just modify the code in c#.
[StructLayout(LayoutKind.Sequential)]
public struct sAdd
{
public IntPtr a;
public IntPtr b;
public int length;
};
sAdd sadd = new sAdd();
int[] a = new int[4] { 1, 2, 3 ,4};
int[] b = new int[4] { 1, 2, 3 ,4};
sadd.length = 4;
sadd.a = Marshal.UnsafeAddrOfPinnedArrayElement(a, 0);
sadd.b = Marshal.UnsafeAddrOfPinnedArrayElement(b, 0);
Console.WriteLine("sAdd:" + DllMainWrapper.AddStruct(ref sadd).ToString());
Your C code is broken.
You cannot compute the length of an array using sizeof when all you have is a pointer to the first element. You can only do so when you have a "real" array declaration in scope.
So this:
int* aarr=sadd->aarr;
int* barr=sadd->barr;
int numAarr=sizeof(aarr)/sizeof(int);
int numBarr=sizeof(barr)/sizeof(int);
is broken, numArr will be a constant value (1 if your pointer size is the same as your integer size, otherwise probably 2 on a 64-bit system with 32-bit int).