将结构数组传递到函数作为参数

Passing an array of structs to a function as a parameter

本文关键字:函数 参数 结构 数组      更新时间:2023-10-16

我知道这个问题已经在Stackoverflow中出现了很多时间,并且在我启动此问题时检查了Stackoverflow建议的所有问题后,我将发布此问题。

基本上,我想实现这一目标。大图:将一系列结构从C DLL传递到C#应用程序。这里的捕获是,阵列的大小只能由C DLL确定。现在,我只知道,将OUT参数作为函数参数可以获得从C 到C#的任何数组。是的,我可以将REF传递给C 函数,作为OUT参数,其大小尚不清楚但被初始化为null。但是我不确定这是正确的还是是否有效!另一种方法是,将结构数组作为返回值,我在c#。

中没有找到任何示例。

所以我做了什么:将此问题简化为C 问题

我尝试了一些示例示例将构造数组从函数返回到我的主(),从C DLL到测试C 应用程序。 ->正常工作。因此,作为返回值,一系列结构效果很好。显然,因为在调用我的功能填充数组之前,我不必初始化我的结构数组,以填充数组!

但是,我需要以C#End可行的方式实施它!因此,作为函数的OUT参数,而不是作为返回值(如果有人知道如何接收从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;
}

评论后,我认为我必须对我的代码发表一些评论。我不确定是否可以在我的代码中使用STL数组,向量或STD :: String,我将将其暴露于C#应用程序。这就是为什么我在这里使用新操作员的原因。下一个可能出现的问题在哪里删除?好吧,我必须有另一个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 中,用CotaskMemalloc和strcpy调用替换strdup(分配时不要忘记终止" 0")。这样,这些字符串从指示器制作.NET字符串后将互动的内存免费。

另一种方法,大多数表现是进行手动编组,即声明您的C#结构中的以下字段:

 public IntPtr name;

在C 中,将代码更改为仅在不制作副本的情况下编写C_STR()。

在C#中,使用Marshal.PtrToStringAnsi将它们转换为.NET字符串。但是请注意,如果您的C 会在完成自定义编组之前更改这些字符串,那么您的应用程序将崩溃。