C ++ API返回一个struct数组

时间:2013-06-03 11:13:45

标签: c# c++ api dll struct

我想在我的C ++ API(dll)中返回一个struct数组。我已经可以返回一个结构并在我的C#测试应用程序中使用它。

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;

}

如何在此函数中传递数组。我必须做一些内存管理,但我不知道我该做什么。

4 个答案:

答案 0 :(得分:4)

您无法以您可能想到的方式从函数返回数组,但您可以返回指向数组的指针。您还需要返回数组的长度,以便可以将数据正确地封送到适当的C#类型。这需要对函数签名稍作更改,而不是通过传递给函数的参数返回信息。

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

在这种情况下,您需要向CharacterInformation添加默认构造函数并使用两阶段初始化(即Init()函数)。

请注意,不应该将指针引用到非静态局部变量,因为变量的内容在超出范围时将被销毁(即函数返回时)

要整理数据,您可以尝试以下内容。这是未经测试的,但应该让你朝着正确的方向发展。

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

您需要添加一个额外的调用才能正确删除C ++中的数据,否则最终会导致内存泄漏。

答案 1 :(得分:3)

我使用这个简单的解决方案:

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

答案 2 :(得分:1)

您还可以在调用函数之前分配数组,并传递缓冲区及其大小。在这种情况下,你的C函数将填充数组。

extern "C" EXPORT_API void GetCharacterPositions(CharacterInformation *pArr, int size)
{
    for ( int i = 0; i < size; ++ i )
    {
       //fill pArr[i]   
    }

}

答案 3 :(得分:-1)

你可以考虑我的代码。我使用参考

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