2017-12-03 13 views
0

この質問は、stackoverflowで多くの時間が掛かっていることを知り、この質問を開始したときにstackoverflowによって提案されたすべての質問を確認した後、この質問を投稿しています。構造体の配列をパラメータとして関数に渡す

基本的には、これを達成したいと思います。 大きな画像: C++ DLLからC#アプリケーションに構造体の配列を渡します。ここでキャッチするのは、配列のサイズは、C++ dllによってのみ決定できるということです。今私は、関数の引数としてoutパラメータを使って、C++からc#への配列の取得が可能であることを知っています。そして、はい、サイズを知られていないがNULLに初期化されているoutパラメータとして、C++関数への参照を渡すことができます。しかし、これが正しいのか、それともうまくいくのかは分かりません!もう1つの方法は、構造体の配列を戻り値として取得することです。このためには、C#で例が見つかりませんでした。

だから私がやったこと:をcに、この問題を簡体++問題

私はC++ DLLからテストまで、)(私のメインの関数から構造体の配列を返すために、いくつかのサンプル例を試してみましたC++アプリケーション。 - >うまく動作します。したがって、戻り値として、構造体の配列は正常に動作します。明らかに、構造体の配列を正確なサイズで初期化する必要はないので、配列を生成するために関数を呼び出す前に冒頭に!

しかし、キャッチは、私はC#endで受信可能な方法で実装する必要があります!だから、戻り値ではなく、関数のoutパラメータとして(誰かがC++ dllからc#に構造体の配列を受け取る方法を知っていれば、助けてください!)。

構造体の配列を関数に渡すことについては、stackoverflowまたはgoogleのすべての質問は以前に決定されたサイズを持っています。しかし、私の場合、関数はサイズを決定することしかできません。関数を呼び出す前に配列のサイズを知ることはできません。そして、私は本当に単一の関数呼び出しでタスクを完了しようとしているので、サイズを計算するためのAPIと配列を取得するAPIの2つを持つことはできません。同じ機能が2回実行されなければならないので、繰り返し実行する必要があります.1回はサイズを取得し、もう1回は実際の配列を取得することです。これは実現不可能です。

サンプルコードがあります。誰かが助けることができれば、本当に感謝します。

// 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を使用できるかどうかはわかりません。だから私はここで新しい演算子を使用しています。発生する可能性のある次の質問は、削除はどこですか?さて、私は構造体に割り当てられたメモリを削除するdeleteを呼び出す別のAPIを持たなければなりません。返され処理されたすべてのデータを正常に受信したときにC#アプリケーションから呼び出されます。それは問題ではない。しかし、C#にデータを送ることは、私にこのサンプルC++テストアプリケーションを書くことにつながりました。

C#にデータを送信するための大きな構文や特別なコードはありません。 C++で動作する場合は、パラメータとAPIを適切にエクスポートするとC#で動作する必要があります。だから私は自分のC++側を完璧にしようとしているのです。

これが役に立ちます。ありがとうございます。

+0

徹底的にお勧めします(https://stackoverflow.com/questions/46991224/are-there-any-valid-use-cases-to-use-new-and-delete-raw-pointers-or-c)。 -style-arr)!あなたの 'struct'が' char * 'ポインタを使うのはなぜですか? – user0042

+0

私は、私が提供したサンプルコードで行ったすべてのことに対して、文字列、スマートポインタ、STL配列またはベクトルを使用したいと思っています。しかし、それらをC#アプリケーションに公開する必要があるときに、それらを使用するのは正しいでしょうか?私がそこに持っている構造は、私がC#アプリケーションに公開しなければならないサンプル構造です。実際には、私の開発コードでは、2つの文字列、本当に本当に大きな文字列を持つ構造を持たなければなりません。私はそれらをchar *の代わりにstd :: stringsとして持つことができますか?それでもそれらをエクスポートできますか?私はそれを愛するが、私は本当に確実ではない..助言してください。 – Esash

答えて

1

実際には2つの質問があります。

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#の構造で、次のフィールドを宣言:C++で

[MarshalAs(UnmanagedType.LPStr)] public string name; 

を、CoTaskMemAllocおよびstrcpyの呼び出し(割り当てる際に '\ 0' 終端を忘れないでください)とのstrdupを置き換えます。この方法では、interopは、ポインタから.NET文字列を作成した後、これらの文字列によって使用されるメモリを解放します。 C++で

public IntPtr name; 

、単にコピーを作成することなく、そこにc_strを書く()にコードを変更します。

別の方法として、最もパフォーマンスは、つまり、あなたのC#の構造体の次のフィールドを宣言し、手動でマーシャリングを行います。

C#では、Marshal.PtrToStringAnsiを使用して.NET文字列に変換します。しかし、カスタムマーシャリングを完了する前にC++でこれらの文字列が変更されると、アプリケーションがクラッシュすることに注意してください。

関連する問題