2

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.

4 Answers 4

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Oke thanks, but how can I convert this pointer to an array in C#. I tried it with Marshal.PtrToStructure but this won't work for an array. And I think I need to know the size of the array before calling GetCharacterPositions(), am I right?
3

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);
}

Comments

1

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]   
    }

}

2 Comments

I also tried your solution but when I call this in C# all the element values are 0. This is the function declaration in C#: [DllImport("API.dll", CallingConvention = CallingConvention.Cdecl)] private static extern void GetCharacterPositions(CharacterInformation[] characterInformationArray, int size);
Your C function accepts an array which will be actually copied on the stack and original content will not be changed. There is nice article on MSDN talking about passing arrays to unmanaged code - msdn.microsoft.com/en-us/library/z6cfh6e6(v=vs.80).aspx
-1

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.