[英]Passing an array of structs to a function as a parameter
我知道這個問題已經在stackoverflow中出現了很多時間,當我開始研究這個問題時,我在檢查了stackoverflow提出的所有問題之后發布了這個問題。
基本上,我想實現這一目標。 大圖:將數組結構從c ++ dll傳遞到ac#應用程序。 這里要注意的是,數組的大小只能由c ++ dll確定。 現在我只知道,可以使用out參數作為函數參數來獲取從c ++到c#的任何數組。 是的,我可以將ref作為out參數傳遞給c ++函數,該參數的大小未知,但已初始化為NULL。 但是我不確定這是對的還是行得通! 另一種方法是,將結構體數組作為返回值,對此我在c#中沒有找到任何示例。
所以我做了:將這個問題簡化為一個c ++問題 :
我嘗試了一些示例示例,以將結構數組從函數返回到main(),從c ++ dll返回到測試c ++應用程序。 ->工作正常。 因此,作為返回值,結構數組可以正常工作。 顯然,由於我不必用確切的大小初始化結構數組,因此從一開始就調用函數來填充數組!
但是要注意的是,我需要以一種可以被c#端接收的可行方式來實現它! 因此,作為函數的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;
}
注釋之后,我想我必須對代碼進行一些注釋。 我不確定我是否可以在要向C#應用程序公開的代碼中使用STL數組或向量或std :: string。 這就是為什么我在這里使用new運算符的原因。 下一個可能出現的問題是刪除在哪里? 好吧,我必須有另一個調用delete的API才能刪除為該結構分配的內存。 當我的C#應用程序成功接收到我返回的所有數據並對其進行處理后,它將被調用。 那不是問題。 但是將數據發送到C#是一個問題,導致我編寫了此示例C ++測試應用程序。
無需編寫任何大語法或任何特殊代碼即可將數據發送到C#。 如果它在C ++中工作,則必須正確導出參數和API,才能在C#中工作。 這就是為什么我試圖使我的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 ++代碼。 這些是每種語言都知道如何跨DLL邊界正確分配/取消分配的Unicode字符串。 唯一的缺點是它們相對較慢。
另一種更復雜的方法是在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 ++會更改所有這些字符串,則應用程序將崩潰。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.