Goal: Retrieve the array of structs from c++, from the c++ side it's finalized to return pointer to struct pointer (Double pointer)(Not in my control).
Sample c++ code :
struct Output
{
char* Name;
};
extern "C"
{
__declspec(dllexport) Output** getoutput()
{
Output* items = (Output*)malloc(sizeof(Output) * 4);
items->Name = "Hello World";
return &items;
}
}
c# Side code :
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Output
{
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string Name;
};
[DllImport(#"CPPInvokeExposed.dll",
CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getoutput();
static void Main(string[] args)
{
var output = Program.getoutput();
Output[] outputs = new Output[1];
MarshalUnmananagedArray2Struct<Output>(output, 1, out outputs);
**outputs[0]// this has junk chars**
}
public static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray)
{
var size = Marshal.SizeOf(typeof(T));
mangagedArray = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr ins = new IntPtr(unmanagedArray.ToInt64() + i * size);
mangagedArray[i] = Marshal.PtrToStructure<T>(ins);
}
}
Is not clear whether the problem is in c++ or c# code. What should be the correct way get the Char* from c++ which exists in struct.
One strange thing is, for the same code if c++ code return single pointer(Output*) instead double pointer(Output**), there is no junk character, getting the correct assigned value. looks like something wrong while returning double pointer from c++.
You C++ code causes undefined behaviour:
return &items;
This returns the address of an automatic variable.
That variable will not exist anymore when the function returns. Accessing that memory location is illegal and causes undefined behaviour.
A clean solution would be to return the pointer itself, not the address. But in your question you state that the return type is not under your control.
In that case you must create the second level of indirection by yourself:
__declspec(dllexport) Output** getoutput()
{
Output* items = (Output*)malloc(sizeof(Output) * 4);
items->Name = "Hello World";
Output **retval = (Output**)malloc(sizeof(Output*))
*retval = items;
return retval;
}
Of course you also need to take care about freeing both levels of memory allocation afterwards.
BTW:
You allocate memory for 4 structs but only assign a value to member of the first element.
Related
In the below example, the dataArray defined in C++ works if defined as an array, but not as a pointer (Just turns out garbage data). Is there another way to marshal the C# array so that it reads the pointer in as an array?
C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CSharpFoo{
int alpha;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5]
public int[] dataArray;
int beta;
}
C++
struct CPPFoo{
int alpha;
//int* dataArray; //Doesn't work, even though initialized to an array elsewhere
int dataArray[5];
int beta;
}
Being passed through a function like this
C#
[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public extern static bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)] ResultCallback callbackPointer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ResultCallback(CSharpFoo value);
C++
//Callback
typedef void(__stdcall * ResultCallback)(CPPFoo);
__declspec(dllexport) bool InitializeDLL(ResultCallback callback);
Thanks in advance!
Edit::
Because "initialized to an array elsewhere" wasn't clear:
CPPFoo(int dummy){ //Constructor
alpha = 32;
dataArray = new int[5];
for (int i = 0; i < 5; i++){
dataArray[i] = i;
}
beta = 13;
}
//dataArray C++ {0,1,2,3,4}
//alpha C# 32
//dataArray C# {Total random garbage} (dataArray[3] is 13!)
//beta C# 0
PS, the CPPFoo struct is a complex struct that comes from a DLL, so I cannot change it. For now, to get things working, I copy it to a more appropriate array like in NonCreature0714's answer, but this results in all of the data being copied--twice. It's this double copy that I'm trying to avoid.
Another edit:
While it seems that for a struct containing a single array the values get passed properly, for a complex struct, garbage gets thrown in.
I have updated the code to reflect a more complex struct!
I did a couple of tests on my local machine, as far as I understood your code. I had no problems with the following code
c# side
struct Foo
{
public int alpha;
public IntPtr Data;
public int beta;
public void GetData(ref int[] buffer,int length)
{
Marshal.Copy(Data,buffer,0,length);
}
}
class Program
{
[DllImport("MyPtr.dll",EntryPoint = "InitializeDLL", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)]ResultCallback callbackPointer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ResultCallback(ref Foo value);
static void CallBackMe(ref Foo value)
{
var buffer = new int[5];
value.GetData(ref buffer,buffer.Length);
}
static void Main(string[] args)
{
InitializeDLL(CallBackMe);
}
}
C++ side
struct CPPFoo {
int* dataArrayPtr;
CPPFoo()
{
dataArrayPtr = new int[5];
for (int i = 0; i < 5; i++) {
dataArrayPtr[i] = i;
}
}
};
typedef void(__stdcall * ResultCallback)(CPPFoo);
extern "C" __declspec(dllexport) bool InitializeDLL(ResultCallback callback)
{
CPPFoo f;
callback(f);
return true;
}
Code in action
My experience in C# is very limited but it seems that it works with int dataArray[5]; because the code agrees to the type of CSharpFoo and CPPFoo. The size of both is five ints. When you change the type to a pointer then one is the size of five ints and the other is a single pointer which would be less then five ints.
Most likely passing a dynamically sized array will need a size and to get a pointer you may need to do something with unsafe code but I don't write in C# so that is just a guess.
struct CPPFoo {
int* dataArray = nullptr;
int size = 0;
}
Use a vector.
#include <vector>
struct CPPFoo {
std::vector<int> dataArray;
};
Vector is more readable and more safe. It only slighty increases the size of the code, and is only slightly slower than using a primitive array. You also don’t need to worry about keeping around a variable for size, and updating it every time it’s changes.
The following code snippet is from Unities Bonjour client example, which demonstrates how to interact with native code from C#. It's a simple C function that returns a string back to C# (Unity):
char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
const char* _GetLookupStatus ()
{
// By default mono string marshaler creates .Net string for returned UTF-8 C string
// and calls free for returned value, thus returned strings should be allocated on heap
return MakeStringCopy([[delegateObject getStatus] UTF8String]);
}
The C# declaration of the function looks like:
[DllImport ("__Internal")]
private static extern string _GetLookupStatus ();
There are a few things that puzzle me here:
Is this the right way to return a string from iOS native code to C#?
How does the returned string ever get freed?
Is there a better way to do it?
Any insights in this matter are appreciated.
Thank you.
1.No.
2.You have to do that yourself.
3.Yes
If you allocate memory inside a function on the C or C++ side, you must free it. I don't see any code allocating memory on the side but I assume you left that part. Also, do not return a variable declared on the stack to C#. You will end up with undefined behavior including crashes.
Here is a C++ solution for this.
For the C solution:
char* getByteArray()
{
//Create your array(Allocate memory)
char * arrayTest = malloc( 2 * sizeof(char) );
//Do something to the Array
arrayTest[0]=3;
arrayTest[1]=5;
//Return it
return arrayTest;
}
int freeMem(char* arrayPtr){
free(arrayPtr);
return 0;
}
The only difference is that the C version uses malloc and free function to allocate and de-allocate memory.
C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);
//Test
void Start()
{
//Call and return the pointer
IntPtr returnedPtr = getIntArray();
//Create new Variable to Store the result
byte[] returnedResult = new byte[2];
//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, 2);
//Free native memory
freeMem(returnedPtr);
//The returned value is saved in the returnedResult variable
byte val1 = returnedResult[0];
byte val2 = returnedResult[1];
}
Note that this is only a test that uses char with 2 characters only. You can make the size of the string dynamic by adding a out int outValue parameter to the C# function then adding int* outValue parameter to the C function. You can then write to this parameter on the C side the size of the character is and access that size from the C# side.
This size can then be passed to the last argument of the Marshal.Copy function and remove the current hard-coded 2 value limit. I will leave this for you to do but if confused, see this post for example of that.
The better solution is to pass StringBuilder to the native side then write to it. The bad side is that you have to declare the size of the StringBuilder on time.
C++:
void __cdecl _GetLookupStatus (char* data, size_t size)
{
strcpy_s(data, size, "Test");
}
C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int _GetLookupStatus(StringBuilder data, int size);
//Test
void Start()
{
StringBuilder buffer = new StringBuilder(500);
_GetLookupStatus (buffer, buffer.Capacity);
string result = buffer.ToString();
}
If you are looking for the fastest way then you should use char array on the C# side, pin it on C# side then send it to C as IntPtr. On the C side, you can use strcpy_s to modify the char array. That way, no memory is allocated on the C side. You are just re-using the memory of the char array from C#. You can see the float[] example at the end of the answer here.
I'm trying to do some basic C#\C interop which returns strange results. These are definitions on the C side :
typedef struct _RESULT {
BOOL success;
char *err;
} RESULT;
typedef struct _INPUT_DATA {
char *message;
} INPUT_DATA;
API int execute_out(IN INPUT_DATA *input, OUT RESULT *result);
With simple implementation:
API int execute_out(INPUT_DATA *input, RESULT *result){
result = (RESULT*)malloc(sizeof RESULT);
result->err = (char*)malloc(sizeof 128);
strcpy(result->err, "Result");
result->success = TRUE;
return 0;
}
Now, my C# definitions are as follows :
[StructLayout(LayoutKind.Sequential)]
public struct INPUT_DATA
{
[MarshalAs(UnmanagedType.LPStr)]
public string message;
}
[StructLayout(LayoutKind.Sequential)]
public struct RESULT
{
public bool success;
public IntPtr err;
}
[DllImport("unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static int execute_out(INPUT_DATA input, out RESULT result);
When I setup code like this on the managed side :
Unmanaged.INPUT_DATA input = new Unmanaged.INPUT_DATA();
input.message = "Test";
Unmanaged.RESULT result;
Unmanaged.execute_out(input, out result);
I receive empty data for err variable defined in RESULT struct and also my success flag is not set correctly (yields false). Can anyone tell me what I'm missing here?
Also, what would be the best practice similar to this case :
Should caller (managed code) allocate memory for the RESULT struct and later free it, or should be there another call to free the allocated memory on the unmanaged side?
Based on the suggestions from #HansPassant and #Olaf, this is the actual code that works for me :
API int execute_out(INPUT_DATA *input, RESULT *result){
//result = (RESULT*)malloc(sizeof RESULT); //this malloc is redundat which effectively replaces original pointer
result->err = (char*)malloc(sizeof 128);
strcpy(result->err, "Result");
result->success = TRUE;
return 0;
}
Malloc is commented, as it replaces original pointer passed from the managed code.
On the managed side, functions need to be declared like this :
[DllImport("unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static int execute_out(ref INPUT_DATA input, [Out] out RESULT result);
Notice the ref keyword to properly pass INPUT_DATA pointer and [Out] attribute before RESULT pointer to tell the marshaller we need something back.
In the end, complete call from managed side looks like this :
Unmanaged.INPUT_DATA input = new Unmanaged.INPUT_DATA();
input.message = "Test";
Unmanaged.RESULT result;
Unmanaged.execute_out(ref input, out result);
string error = Marshal.PtrToStringAnsi(result.err);
Hope someone will find this useful.
I asked a similar question yesterday, but this is slightly different. I am having problems passing arrays of struct from c# to c++, and getting this back again.
Here is the c++ code. Firstly, the struct:
struct Tri
{
public:
int v1, v2, v3;
}
and now the c++ dll part:
extern "C" __declspec(dllexport) void Dll_TriArray(Tri *tri)
{
int num = 10;
tri = new Tri[num];
for (int i = 0; i < num; i++)
{
tri[i].v1 = i + 5;
tri[i].v2 = i + 10;
tri[i].v3 = i + 25;
}
}
and here's the c# code, again starting with the struct:
[StructLayout(LayoutKind.Sequential)]
public struct Tri
{
public int v1, v2, v3;
}
public class Testing
{
[DllImport("testing.dll")]
static extern void Dll_TriArray(out Tri[] tryArray);
public GetTriArray()
{
Tri[] triArray;
Dll_TriArray(out triArray);
}
}
So the triArray i get when calling the GetTriArray method will come back as null. I have thought about passing an IntPtr in as the argument, but then how does one marshal an intptr into/from an array of struct?
BTW - at this stage, i'm not interested in memory leaks.
I'm not an expert (by any means) in C# but the C++ part gets passed a pointer to Tri-struct, which in C++ can be used like an dynamic array you allocate and fill that correctly but you don't have a way to get it back, because from C-perspective you'd need to modify the caller's (C#) pointer but you only get a copy and not a reference to the original.
In C++ the closest thing to what you are tying to do, would be to change the prototype to void Dll_TriArray(Tri *&tri) (call by ref, not call by copy) but I'm not sure how to interface that with C# (probably Dll_TriArray(ref triArray); ).
I'm using C# with P/Invoke to access to a DLL method. The definition of the method is the following:
[DllImport("userManager.dll")]
static extern int GetUsers(out IntPtr userList);
Original structs:
typedef struct user_list {
unsigned short NumUsers;
USER_LIST_ITEM List[VARLEN];
} USER_LIST
typedef struct user_list_item {
char name[260];
unsigned char address[256];
} USER_LIST_ITEM
And the struct layout I've done is the following:
[StructLayout(LayoutKind.Sequential)]
public class USER_LIST
{
public uint NumUsers;
[MarshalAs(UnmanagedType.ByValArray)]
public USER_LIST_ITEM [] List;
}
[StructLayout(LayoutKind.Sequential)]
public class USER_LIST_ITEM
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string address;
};
But I get an error when I try to unmarshall it:
USER_LIST userList = new USER_LIST();
// Prepare pointer
IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList));
Marshal.StructureToPtr(userList, uList, false);
result = GetUsers(out uList);
Marshal.PtrToStructure(uList, userList); <--
The runtime has encountered a fatal error. The address of the error was at 0x79f82af6, on thread 0x464. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
I get the NumUsers property right, but it seems the error occurs when unmarshalling the array. Any thoughts?
If you specify an array in a structure used as an out parameter, you need to tell the marshaler what length is the array going to be. With your code, the marshaler is probably allocating a zero-length array or just using null, which produces the crash. Unfortunately there seems to be no way to specify a variable-length out array as a member of a structure, because MarshalAs.SizeParamIndex only works for methods. You might get away with specifying a large, constant-size array using MarshalAs.SizeConst, but generally you'd have to parse the (presumably callee-allocated) return buffer like this:
var count = Marshal.ReadInt32 (uList) ;
var users = new List<USER_LIST_ITEM> () ;
var ptr = (long)uList + 4 ;
for (int i = 0 ; i < count ; ++i)
{
users.Add (Marshal.PtrToStructure (typeof (USER_LIST_ITEM),
new IntPtr (ptr))) ;
ptr += Marshal.SizeOf (typeof (USER_LIST_ITEM)) ;
}
You'll have to pay extra attention to alignment&padding and 32/64 bit issues.
That is because List has not been allocated yet.
You will need initialize all the fields.
Another problem I see is with the following:
IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList));
...
result = GetUsers(out uList);
Are you sure that out should not be ref? Else there is no point (not sure if ref is correct either).
Update: Looking at your code again, you should be doing this (and avoid that memory leak poking your eye).
IntPtr uList;
var result = GetUsers(out uList);
var userlist = (USER_LIST) Marshal.PtrToStructure(ulist, typeof(USER_LIST));
Marshal.FreeHGlobal(ulist); // pray here or shoot the author of the C function
Update again:
Your p/invoke signature is likely wrong or you are interpreting it wrong.
I can guess it probably something like:
int GetUsers(USER_LIST* ulist);
And that what you have is not the same thing.
If this is case, the solution is easy.
Change USER_LIST to a class (but keep sequential layout) and use
// pinvoke sig
int GetUsers(USER_LIST ulist);
var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ulist);
-- or --
Call it by ref.
// pinvoke sig
int GetUsers(ref USER_LIST ulist);
var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ref ulist);
This way, you dont have to mess with manual marshalling, and I cant see anymore potential for memory leaks.
Final update:
Given the signature you posted, it looks like GetUsers returns a pointer to a list of USER_LIST with the return value being the count. Nice memory leak there.
Anyways, I would probably experiment with an unsafe approach here, and just walk thru the result , and make sure everything gets freed. (I still think you should shoot the author).
I think your original code isn't probably so wrong.
You've probably just used the wrong overload of Marshal.PtrToStructure.
Have you tried this?
[DllImport("userManager.dll")]
static extern int GetUsers(out IntPtr userList);
[DllImport("userManager.dll")]
static extern void UMFree(IntPtr userList);
static void Main()
{
IntPtr userList; // no need to allocate memory in managed code;
GetUsers(out userList); // memory is allocated by native function
USER_LIST u = (USER_LIST)Marshal.PtrToStructure(userList, typeof(USER_LIST));
UMFree(userList);
}
Using unsafe code:
public unsafe struct USER_LIST
{
public uint numUsers;
public USER_LIST_ITEM* list;
}
public unsafe struct USER_LIST_ITEM
{
public fixed byte name[260];
public fixed byte address[256];
}
class Program
{
[DllImport("userManager.dll")]
static unsafe extern int GetUsers(USER_LIST** userList);
[DllImport("userManager.dll")]
static unsafe extern int UMFree(USER_LIST* userList);
private static unsafe void Main()
{
USER_LIST* list;
GetUsers(&list);
UMFree(list);
}
}