【问题标题】:Passing an array of structs to a function as a parameter将结构数组作为参数传递给函数
【发布时间】:2017-12-03 20:45:26
【问题描述】:

我知道这个问题在 stackoverflow 中已经出现了很多时间,并且在我开始这个问题时检查了 stackoverflow 提出的所有问题后,我发布了这个问题。

基本上,我想实现这一点。 大图:将结构数组从 c++ dll 传递到 c# 应用程序。这里的问题是,数组的大小只能由 c++ dll 确定。现在我只知道使用 out 参数作为函数参数可以获得从 c++ 到 c# 的任何内容的数组。是的,我可以将 ref 传递给 c++ 函数作为 out 参数,其大小未知但初始化为 NULL。但我不确定这是否正确或是否有效!另一种方法是,将结构数组作为返回值,我在 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;
}

在评论之后,我想我必须在我的代码上给出一些 cmets。我不确定我是否可以在我将公开给 C# 应用程序的代码中使用 STL 数组或向量或 std::string。这就是我在这里使用 new 运算符的原因。下一个可能出现的问题是删除在哪里?好吧,我必须有另一个 API 调用 delete 来删除为结构分配的内存。当我的 C# 应用程序成功接收到我返回并处理它们的所有数据时,它将被调用。那不是问题。但是将数据发送到 C# 是一个问题,这导致我编写了这个示例 C++ 测试应用程序。

没有大的语法或任何特殊的代码来向 C# 发送数据。如果它在 C++ 中工作,它必须在 C# 中工作,如果参数和 API 正确导出。这就是为什么我试图让我的 C++ 方面完美。

希望这会有所帮助。提前非常感谢。

【问题讨论】:

  • 强烈推荐read!为什么你的struct 使用char* 指针?
  • 我真的很想对我在我提供的示例代码中所做的一切使用字符串、智能指针和 STL 数组或向量。但是当我必须将它们暴露给 c# 应用程序时使用它们是否正确?我拥有的结构有一个示例结构,我必须将其公开给 c# 应用程序。实际上,在我的开发代码中,我必须有一个包含 2 个字符串的结构,非常大的字符串。我可以将它们作为 std::strings 而不是 char*,并且仍然能够导出它们吗?我会喜欢的,但我真的不确定.. 请指教。

标签: c++ arrays struct


【解决方案1】:

这里实际上有两个问题。

一个是如何返回结构数组。

经典的方式是这样的:

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++ 在您完成自定义编组之前更改任何这些字符串,您的应用将会崩溃。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    • 2020-05-04
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    相关资源
    最近更新 更多