我知道这个问题在 stackoverflow 中出现了很多次,我在检查了我开始这个问题时 stackoverflow 建议的所有问题后发布了这个问题。
基本上,我想实现这个。 大图:将结构数组从 c++ dll 传递到 c# 应用程序。这里的问题是,数组的大小只能由 c++ dll 确定。现在我只知道可以使用 out 参数作为函数参数来获取从 c++ 到 c# 的任何内容的数组。是的,我可以将 ref 作为大小未知但初始化为 NULL 的输出参数传递给 C++ 函数。但我不确定这是否正确或是否有效!另一种方法是,将结构数组作为返回值,我在 c# 中找不到任何示例。
所以我做了什么:将这个问题简化为一个 c++ 问题:
我尝试了一些示例示例,将结构数组从函数返回到我的 main(),从 c++ dll 返回到测试 c++ 应用程序。 --> 工作正常。因此,作为返回值,结构数组可以正常工作。显然,因为我不必在开始时调用我的函数来填充数组之前用确切的大小初始化我的结构数组!
但要注意的是,我需要以一种 C# 端可以接收的方式来实现它!因此,作为函数的输出参数,而不是作为返回值(如果有人知道如何从 c++ dll 接收结构数组到 c#,请帮忙!)。
stackoverflow 或 google 中关于将结构数组传递给函数的所有问题的大小都是预先确定的。但就我而言,函数只能决定大小。在调用函数之前我不知道数组的大小。而且,我真的试图在单个函数调用中完成任务,因此,我不能有 2 个 API,一个用于计算大小,一个用于获取数组。这将是一项重复性任务,因为必须执行两次相同的功能,一次只获取大小,另一次获取实际数组,这是不可行的!
我有一个示例代码。如果有人可以提供帮助,我们将不胜感激。
// structsEx.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <vector>
#include <string>
using namespace std;
using std::vector;
using std::string;
struct example
{
char* name; // char* or const char*. But not sure if I can have an
// std::string.
int age;
char* company;
};
void passStructsRef(struct example** ex) // Again, not sure if I can have
// std::array or std::vector since
// this will be the API I expose
// to C# side.
{
vector<string> names = {"AAA", "BBB", "CCC", "DDD", "EEE"};
vector<int> ages = {25, 26, 27, 28, 29, 30};
vector<string> companies = { "AAA1", "BBB2", "CCC3", "DDD4", "EEE5" };
*ex = new example[5]; // I think this is a problem. It only initializes
// the first element and hence, I get only the
// first item.
for (int i = 0; i < 5; i++)
{
ex[i] = new example(); // Is this right? Not sure..
ex[i]->age = ages[i];
ex[i]->name = _strdup(names[i].c_str());
ex[i]->company = _strdup(companies[i].c_str());
}
cout << "2 in function" << endl;
for (int i = 0; i < 5; i++)
{
cout << ex[i]->name << "\t" << ex[i]->age << "\t" << ex[i]->company << endl;
}
}
int main()
{
example ex;
ex.age = 30;
ex.name = "AEIOU";
ex.company = "ABCDE";
cout << "1" << endl;
cout << ex.name << "\t" << ex.age << "\t" << ex.company << endl;
cout << "2" << endl;
example *ex2 = nullptr;
passStructsRef(&ex2);
for (int i = 0; i < 5; i++)
{
cout << ex2[i].name << "\t" << ex2[i].age << "\t" << ex2[i].company << endl;
}
_getch();
return 0;
}
评论之后,我想我必须对我的代码进行一些评论。我不确定我是否可以在我要公开给 C# 应用程序的代码中使用 STL 数组或 vector 或 std::string。这就是我在这里使用 new 运算符的原因。下一个可能出现的问题是删除在哪里?好吧,我必须有另一个调用 delete 的 API 来删除为该结构分配的内存。当我的 C# 应用程序成功接收到我返回并处理它们的所有数据时,它将被调用。那不是问题。但是向 C# 发送数据是一个问题,它促使我编写了这个示例 C++ 测试应用程序。
无需大语法或任何特殊代码即可将数据发送到 C#。如果它在 C++ 中工作,则它必须在 C# 中工作,前提是参数和 API 已正确导出。这就是为什么我试图让我的 C++ 方面变得完美。
希望这对您有所帮助。非常感谢。
最佳答案
这里实际上有两个问题。
一个是如何返回结构数组。
经典的方式是这样的:
C++:
int getStructsLength();
int getStructs( int bufferLength, struct example* buffer ); // Returns the count of structures actually written
C#
static extern int getStructsLength();
static extern int getStructs( int bufferLength, [Out, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 0 )] example[] buffer );
另一个是如何返回这些结构中的字符串。
如果你没有太多数据或太多调用并在 Windows 上运行,一种简单的方法是将 const char*
更改为 BSTR
,并调整 C++ 代码因此。这些是 Unicode 字符串,每种语言都知道如何跨 DLL 边界正确分配/解除分配;唯一的缺点是它们相对较慢。
另一种方法,更复杂,在您的 C# 结构中声明以下字段:
[MarshalAs(UnmanagedType.LPStr)] public string name;
在 C++ 中,将 strdup 替换为 CoTaskMemAlloc 和 strcpy 调用(分配时不要忘记终止 '\0')。这样,在从指针生成 .NET 字符串后,互操作将释放这些字符串使用的内存。
另一种最高效的方法是手动编码,即在 C# 结构中声明以下字段:
public IntPtr name;
在 C++ 中,将代码更改为只在其中写入 c_str() 而无需进行复制。
在 C# 中,使用 Marshal.PtrToStringAnsi
将它们转换为 .NET 字符串。但请注意,如果您的 C++ 在您完成自定义编码之前更改这些字符串中的任何一个,您的应用程序将会崩溃。
关于c++ - 将结构数组作为参数传递给函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47623114/