Reputation: 377
I would like to return an array of a struct in my C++ API (dll). I can already return a struct and use this in my C# test application.
struct CharacterInformation {
int id;
double x;
double y;
CharacterInformation(int ID, double X, double Y) : id(ID), x(X), y(Y) {}
};
extern "C" EXPORT_API CharacterInformation GetCharacterPositions(void) {
//std::vector<Character> characters = simulator->getCharacters();
//CharacterInformation characterInformationArray [] = { CharacterInformation(0, 1, 2) };
//return characterInformationArray;
CharacterInformation characterInformation = CharacterInformation(0,1,2);
return characterInformation;
}
How can I pass the array in this function. I have to do some memory management but I don't know what I have to do.
Upvotes: 2
Views: 3649
Reputation: 377
I got it working with this simple solution:
C++
extern "C" EXPORT_API CharacterInformation* GetCharacterPositions(void) {
std::vector<Character> characters = simulator->getCharacters();
CharacterInformation characterInformation;
CharacterInformation *characterInformationArray = new CharacterInformation[GetCharactersCount()];
for(int i = 0; i < GetCharactersCount(); i++) {
characterInformation.id = characters[i].getID();
characterInformation.x = characters[i].getPosition().x;
characterInformation.y = characters[i].getPosition().y;
characterInformationArray[i] = characterInformation;
}
return characterInformationArray;
}
C#
[DllImport("API.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetCharacterPositions();
IntPtr characterPositions = GetCharacterPositions();
for (int i = 0; i < GetCharactersCount(); ++i)
{
var data = new IntPtr(characterPositions.ToInt64() + structSize * i);
var characterInformation = (CharacterInformation)Marshal.PtrToStructure(data, typeof(CharacterInformation));
Console.WriteLine("Character with ID: " + characterInformation.id + " X: " + characterInformation.x + " Y: " + characterInformation.y);
}
Upvotes: 3
Reputation: 20073
You can't return an array from a function in the way you are probably thinking but you can return a pointer to the array. You will also need to return the length of the array so that you can properly marshal the data to the appropriate C# type. This requires a slight change to the function signature to instead return the information through parameters passed to the function.
extern "C" EXPORT_API bool GetCharacterPositions(CharacterInformation** data, int *length)
{
CharacterInformation *characterInformationArray = new CharacterInformation[arraysize];
// initialize contents of array.
// ... snip ...
*length = arraysize;
*data = characterInformationArray;
return true;
}
In this case you will need to add a default constructor to CharacterInformation
and use two-phase initialization (i.e. an Init()
function).
Be aware that you should not return a pointer or reference to a non-static local variable as the contents of the variable will be destroyed when it goes out of scope (i.e. when the function returns).
To marshal the data you can try something like the following. This is untested but should give you a push in the right direction.
[DllImport("Dllname.dll",
CallingConvention = CallingConvention.Winapi,
CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool GetCharacterPositions(out IntPtr arrayPtr, out int size);
public static List<CharacterInformation> GetCharacterPositions()
{
var arrayValue = IntPtr.Zero;
var size = 0;
var list = new List<CharacterInformation>();
if ( !GetCharacterPositions(out arrayValue, out size))
{
return list;
}
var dataEntrySize = Marshal.SizeOf(typeof(CharacterInformation));
for ( var i = 0; i < size; i++)
{
var cur = (CharacterInformation )Marshal.PtrToStructure(arrayValue, typeof(CharacterInformation ));
list.Add(cur);
arrayValue = new IntPtr(arrayValue.ToInt32() + dataEntrySize);
}
return list;
}
You will need to add an additional call in order to properly delete the data in C++ otherwise you will end up with a memory leak.
Upvotes: 4
Reputation: 569
you can consider my code. I use the reference
#include<iostream>
using namespace std;
struct CharacterInformation {
int id;
double x;
double y;
CharacterInformation(int ID, double X, double Y) : id(ID), x(X), y(Y) {}
};
extern "C" EXPORT_API void GetCharacterPositions(CharacterInformation &ci) {
CharacterInformation ciTmp(0,1,2);
ci.id = ciTmp.id;
ci.x = ciTmp.x;
ci.y = ciTmp.y;
}
int main()
{
CharacterInformation ci(0,0,0);
GetCharacterPositions(ci);
cout<<ci.id<<ci.x<<ci.y<<endl;
return 0;
}
Upvotes: -1
Reputation: 378
You can also allocate an array before the call to function and pass the buffer and its size. Your C function will fill the array in this case.
extern "C" EXPORT_API void GetCharacterPositions(CharacterInformation *pArr, int size)
{
for ( int i = 0; i < size; ++ i )
{
//fill pArr[i]
}
}
Upvotes: 1