Pass array from C++ to C# - c#

I need to pass an array from C++ to C#
The C++ header is the following
extern "C" GMSH_API void GMSH_Model_OCC_Fragments(int* arrayPtr);
The C++ cpp is the following
void GMSH_Model_OCC_Fragments(int* arrayPtr)
{
int array[] = {1,2};
arrayPtr = array;
}
The C# code is the following
[DllImport("GMSHCSHARP.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GMSH_Model_OCC_Fragments(out IntPtr arrayPtr);
public void Create()
{
GMSH_Model_OCC_Fragments(out IntPtr arrayPtr);
int[] ReturnArray = new int[2];
int size = Marshal.SizeOf(ReturnArray[0]) * ReturnArray.Length;
Marshal.Copy(arrayPtr, ReturnArray, 0, size);
}
Seems that arrayPtr is transmitted as null and this causes the Marshal.Copy to return an error.
I'd appreciate any help.

When you call C++ from C# you have to play by the rules of C++, so you cannot assign an array by
arrayPtr = array;
You can however fill an array given by C#
C++ Function
void someFunction(char* dest, size_t length){
char* someData = "helloWorld";
size_t copyLen = std::min(length, strlen(someData));
memcpy(dest, someData, copyLen);
//If it was a string you'd also want to make sure it's null terminated
}
C# Function call (Something like this sorry if it's not 100%)
[DllImport("MyDll.dll", CallingConvention = CallingConvention.CDecl)]
private static extern void someFunction(Byte[] dest, uint Length);
Byte[] array = new byte[10];
someFunction(array, 10);
I'm not sure if you can pass heap memory out of C++, but if you can you would do this:
void GMSH_Model_OCC_Fragments(int** dest){
*dest = new int[2];
*dest[0] = 1;
*dest[1] = 2;
}
Note you need to use a double pointer to pass back memory in this fashion. As you need the first pointer to reference the object or array, and the second pointer to keep track of that.

Related

Marshalling C-style array of LPWSTR to managed string[] using pInvoke

I'd like to call an unmanaged method that allocates memory, creates an array of LPWSTRs, and returns it to managed code. I'd like to avoid in/out parameters and writing code to manage memory and variable scopes as much as possible so I decided I would rely on using CoTaskMemAlloc and let the marshaller automagically clean up after me.
Here's what I have (a modified version of a p/invoke tutorial method on MSDN):
extern "C" DLL1_API LPWSTR *TestArrayOfStrings(_In_ int count)
{
STRSAFE_LPWSTR temp = NULL;
wchar_t * ppStrArray[10] = { NULL };
const size_t alloc_size = sizeof(wchar_t *) * 10;
for (int i = 0; i < 10; i++)
{
temp = (STRSAFE_LPWSTR)CoTaskMemAlloc(alloc_size);
if (i % 2 == 0)
StringCchCopy(temp, alloc_size, L"0123456789");
else
StringCchCopy(temp, alloc_size, L"9876543210");
CoTaskMemFree(ppStrArray[i]);
ppStrArray[i] = temp;
}
count = 10;
return ppStrArray;
}
and on the managed side:
[DllImport("Dll1.Windows.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.LPWStr)]
public static extern string[] TestArrayOfStrings(out int count);
As you can see I've tried to use additional attributes but the marshaller just doesn't seem to like it--I keep getting "Cannot marshal 'return value': Invalid managed/unmanaged type combination." I am trying to maintain typing as an array of LPWSTRs and would like to avoid SAFEARRAY, for which marshalling is marked obsolete.
Slightly modified code, but the signature was what was important. This method assigns a string value (the third parameter) to the first element of the uninitialized out array passed in.
[DllImport("Dll1.Windows.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void TestArrayOfStrings(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1)] [Out] out string[] test,
out int size, string someString);
extern "C" DLL1_API void TestArrayOfStrings(wchar_t ***strings, int *size, wchar_t *someString){
const size_t alloc_size = 64;
STRSAFE_LPWSTR temp = (STRSAFE_LPWSTR)CoTaskMemAlloc(alloc_size);
StringCchCopy(temp, alloc_size, someString);
*strings = (wchar_t **)CoTaskMemAlloc(sizeof(wchar_t));
*strings[0] = temp;
*size = 1;
}

P/Invoke Returning Array of Structs with string fields

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.

Marshal a C struct containing a variable length array

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.

Marshal float* to C#

I have a DLL which exports a function that returns a float*, that I would like to use it in my C# code. I am not sure how to Marshal my float* so that I can safely use it in C#.
So, in my C++ DLL, I have declared:
static float* GetSamples(int identifier, int dataSize);
In my C# script, I have:
[DllImport ("__Internal")]
public static extern float[] GetSamples (int identifier, int dataSize);
The C++ GetSamples(int,int) allocates memory and return a pointer t the float array. How do I declare the C# GetSamples to Marshal my float array, and how do I access the data (either by iteration or Marshal.Copy)?
Also, can I delete the float* from C# or do I have to call another C++ function to delete the allocated memory?
EDIT:
So this is what I have tried up to now.
First, on the C# side:
Declaration:
[DllImport ("__Internal")]
public static extern int GetSamples ([In, Out]IntPtr buffer,int length, [Out] out IntPtr written);
Trying to call it:
IntPtr dataPointer = new IntPtr();
IntPtr outPtr;
GetSamples(dataPointer, data.Length, out outPtr);
for (var i = 0; i < data.Length; i++){
copiedData[i] = Marshal.ReadByte(dataPointer, i);
}
Then in my C++ lib:
int AudioReader::RetrieveSamples(float * sampleBuffer, size_t dataLength, size_t * /* out */ written)
{
float* mydata = new float[dataLength];
//This is where I copy the actual data into mydata
memcpy(sampleBuffer, mydata, dataLength*sizeof(float));
delete data;
return dataLength;
}
I don't really know what outPtr is for... And I know I have some additional copying steps that I can removes, I just want to get it working for now.
So this is a bit of a complicated answer...
.NET doesn't know how to handle C++ memory allocation, so regardless returning a float * is dangerous at best for this. Furthermore the .NET memory model is based on COM so it is CoTaskMemAlloc based, not that it really helps you here. So here is what I would suggest:
int AudioReader::RetrieveSamples(
float * sampleBuffer,
int dataLength,
int * /* out */ written)
{
// assuming mydata is already defined
if(sampleBuffer == NULL || dataLength == 0)
{
*written = sizeof(mydata);
return -1;
}
ZeroMemory(sampleBuffer, dataLength);
int toCopy = min(dataLength, sizeof(myData));
//This is where I copy the actual data into mydata
memcpy(sampleBuffer, mydata, toCopy);
*written = toCopy;
return 0;
}
[DLLImport("__internal")]
private static extern int GetSamples(
[In, Out]IntPtr buffer,
[In] int length,
[Out] out int written);
float[] RetrieveFloats()
{
int bytesToAllocate = 0;
GetSamples(IntPtr.Zero, 0, out bytesToAllocate);
if(bytesToAllocate == 0)
return null;
int floatCount = bytesToAllocate/ sizeof(float);
float[] toReturn = new float[floatCount];
IntPtr allocatedMemory = Marshal.AllocHGlobal(bytesToAllocate);
int written = 0;
if(GetSamples(allocatedMemory, bytesToAllocate, out written) != -1)
{
floatCount = written/sizeof(float);
Marshal.Copy(allocatedMemory, toReturn, 0, floatCount);
}
Marshal.FreeHGlobal(allocatedMemory);
return toReturn;
}
Passing a bufferLength of zero would return the space required for the buffer, which can then be allocated and passed in.
You will need to allocate the memory for the buffer in C#, you cannot allocate it in C++

Return array of integers from cross-platform DLL

I created a cross-platform DLL in C++ that compiles on both Windows and Mac OSX. On Windows, I have a C# app that calls the DLL using P/Invoke and on Mac OSX, an objective C app calls the DLL. I have simple functions working just fine but I need a new function that returns an array of integers.
The best example I can find is at Marshal C++ int array to C# and I was able to make it work. However, I would like to modify this example to pass the integer array back as a reference argument instead. The size of the array has to be set at runtime.
Here's what I've tried. The pSize is coming back correctly but the list is empty.
In unmanaged c++:
bool GetList(__int32* list, __int32* pSize)
{
// Some dummy data
vector<int> ret;
ret.push_back(5);
ret.push_back(6);
list = (__int32*)malloc(ret.size());
for (unsigned int i = 0; i < ret.size(); i++)
{
list[i] = ret.at(i);
}
*pSize = ret.size();
return true;
}
In C#:
[DllImport(#"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetList(out IntPtr arrayPtr, out int size);
public static int[] GetList() {
IntPtr arrayValue = IntPtr.Zero;
int size = 0;
bool b = GetFrames(out arrayValue, out size);
// arrayValue is 0 here
int[] result = new int[size];
Marshal.Copy(arrayValue, result, 0, size);
return result;
}
"Caller-allocates" is the only way to make code portable while keeping it maintainable. Not only does your code not change the caller's pointer, but the C# code has no way to free the memory you allocated (malloc-ed memory won't be cleaned up by garbage collection).
If finding the size is quick (doesn't require generating all the output data), just add a second function to return the size.
If you can't get the size until you generate the data, then make one function return the size and a pointer to the content (int**, on the C# side it will be ref IntPtr). And a second function that copies that data to the C# array and frees the native buffer.
Your problem is the definition of list, it really needs to be an __int32** in order to pass back the address of the allocated array. To breeze through the interop difficulties of pointers-to-pointers, how about you instead return the address of list or null if it fails:
__int32* GetList(__int32* pSize)
{
// Some dummy data
vector<int> ret;
ret.push_back(5);
ret.push_back(6);
// per #David's catch, you'll need to allocate the right amount
__int32* list = (__int32*)malloc(ret.size() * sizeof(__int32));
for (unsigned int i = 0; i < ret.size(); i++)
{
list[i] = ret.at(i);
}
*pSize = ret.size();
return list;
}
void RemoveList(__int32* list)
{
free(list);
}
With the appropriate modifications to your C#:
[DllImport(#"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetList(out int size);
[DllImport(#"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern void RemoveList(IntPtr array);
public static int[] GetList()
{
int[] result = null;
int size;
IntPtr arrayValue = IntPtr.Zero;
try
{
arrayValue = GetList(out size);
if (arrayValue != IntPtr.Zero)
{
result = new int[size];
Marshal.Copy(arrayValue, result, 0, size);
}
}
finally
{
// don't forget to free the list
RemoveList(arrayValue);
}
return result;
}

Categories

Resources