2017-12-03 6 views
0

Ich weiß, diese Frage ist viel in Stackoverflow gekommen und ich posten diese Frage nach Überprüfung aller Fragen von Stackoverflow vorgeschlagen, wenn ich diese Frage begann.Übergabe eines Arrays von Strukturen an eine Funktion als Parameter

Grundsätzlich möchte ich dies erreichen. Großes Bild: Übergeben Sie ein Array von Strukturen von C++ DLL zu einer C# -Anwendung. Der Catch hier ist, dass die Größe des Arrays nur durch die C++ DLL bestimmt werden kann. Jetzt weiß ich nur, dass es möglich ist, ein Array von allem von C++ nach C# zu bekommen, mit dem out-Parameter als Funktionsargument. Und ja, ich kann eine Referenz an eine C++ - Funktion als Out-Parameter übergeben, dessen Größe nicht bekannt ist, aber auf NULL initialisiert wird. Aber ich bin mir nicht sicher, ob das richtig ist oder ob es funktionieren wird! Und eine andere Möglichkeit besteht darin, das Array von Strukturen als Rückgabewert zu erhalten, für das ich kein Beispiel in C# gefunden habe.

Also, was ich tat: Vereinfachtes dieses Problems in eine C++ Problem:

habe ich versuchte, einige Beispiele Beispiele eine Reihe von Strukturen aus einer Funktion meines main() zurück, von einem C++ dll zu einem Test C++ Anwendung. -> Funktioniert gut. Als ein Rückgabewert funktioniert ein Array von Strukturen gut. Offensichtlich, weil ich mein Array von Strukturen nicht mit der genauen Größe initialisieren muss, am Anfang, bevor ich meine Funktion anrufe, um das Array zu füllen!

Aber der Haken ist, muss ich es in einer Weise implementieren, die möglich sein wird, von der C# Ende erhalten werden! Also, als out-Parameter für die Funktion, und nicht als Rückgabewert (wenn jemand weiß, wie man ein Array von Strukturen von C++ dll bis C# erhält, bitte helfen!).

Alle Fragen in stackoverflow oder in google zum Übergeben eines Array von Strukturen an eine Funktion haben ihre Größe zuvor bestimmt. Aber in meinem Fall wird die Funktion nur in der Lage sein, die Größe zu bestimmen. Ich kenne die Größe des Arrays vor dem Aufruf der Funktion nicht. Und ich versuche wirklich, die Aufgaben in einem einzigen Funktionsaufruf zu erledigen, und daher kann ich nicht 2 APIs haben, eine für die Berechnung der Größe und eine für das Erhalten des Arrays. Es wäre eine sich wiederholende Aufgabe, da die gleiche Funktionalität zweimal ausgeführt werden muss, einmal um nur die Größe zu erhalten und eine andere Zeit, um das tatsächliche Array zu erhalten, was nicht machbar ist!

Ich habe einen Beispielcode. Wenn jemand helfen kann, wäre es sehr geschätzt.

// 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; 
} 

Nach dem Kommentar, ich denke, ich muss ein paar Kommentare zu meinem Code geben. Ich bin mir nicht sicher, ob ich STL-Arrays oder Vektoren oder std :: string in meinem Code verwenden kann, die ich für C# -Anwendungen bereitstellen werde. Deshalb benutze ich hier einen neuen Operator. Die nächste Frage, die sich stellen könnte, ist wo ist Löschen? Nun, ich muss einen anderen API-Aufruf löschen, um den für die Struktur zugewiesenen Speicher zu löschen. Welche wird von meiner C# -Anwendung aufgerufen, wenn alle von mir zurückgegebenen Daten erfolgreich verarbeitet und verarbeitet wurden. Das ist kein Problem. Aber das Senden von Daten an C# ist ein Problem, das mich veranlasst hat, diese Beispiel-C++ - Testanwendung zu schreiben.

Es gibt keine große Syntax oder einen speziellen Code zum Schreiben von Daten an C#. Wenn es in C++ funktioniert, muss es in C# funktionieren, wenn die Parameter und APIs ordnungsgemäß exportiert werden. Deshalb versuche ich, meine C++ Seite perfekt zu machen.

Hoffe, das hilft. Vielen Dank im Voraus.

+0

Gründlich empfohlen [lesen] (https://stackoverflow.com/questions/46991224/are-there-any-valid-use-cases-to-use-new-and-delete-raw-pointers-or-c -style-arr)! Warum verwendet Ihre 'struct' 'char *' Zeiger? – user0042

+0

Ich würde wirklich lieben, String, Smart Pointer und STL-Arrays oder Vektoren für alles zu verwenden, was ich in dem von mir bereitgestellten Beispielcode getan habe. Aber wird es richtig sein, sie zu verwenden, wenn ich sie C# -Anwendung aussetzen muss? Die Struktur, die ich dort habe, ist eine Beispielstruktur, die ich C# -Anwendung aussetzen muss. In Wirklichkeit muss ich in meinem Entwicklungscode eine Struktur mit 2 Strings haben, wirklich sehr großen Strings. Kann ich sie als std :: strings anstelle von char * haben und trotzdem exportieren? Ich würde das lieben, aber ich bin mir wirklich nicht sicher .. Bitte beraten. – Esash

Antwort

1

Hier sind eigentlich zwei Fragen.

Man ist, wie Array von Strukturen zurückgegeben wird.

Der klassische Weg ist dies:

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); 

Ein weiterer ist, wie Zeichenfolgen in diesen Strukturen zurückzukehren.

Wenn Sie nicht zu viele Daten oder zu viele Anrufe haben und unter Windows laufen, ist eine einfache Möglichkeit,in BSTR zu ändern und C++ - Code entsprechend anzupassen. Dies sind Unicode-Zeichenfolgen, die jede Sprache weiß, wie sie über DLL-Grenzen hinweg richtig zugeordnet/freigegeben wird; Der einzige Nachteil ist, dass sie relativ langsam sind.

Ein andere Methode, komplexe, erklären Sie das folgende Feld in Ihrer C# Struktur:

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

In C++ ersetzen strdup mit CoTaskMemAlloc und strcpy Anrufen (nicht vergessen '\ 0' beendet wird, wenn die Zuteilung) . Auf diese Weise wird das Interop den Speicher freigeben, der von diesen Strings verwendet wird, nachdem aus den Zeigern .NET-Strings erstellt wurden.

Ein anderer Weg, die meisten performant ist die manuelle Rangierung tun, das heißt erklären die folgenden Felder in Ihrem # Struktur C:

public IntPtr name; 

In C++, den Code ändern c_str() gibt Kopien, ohne nur zu schreiben.

Verwenden Sie in C# Marshal.PtrToStringAnsi, um sie in .NET-Zeichenfolgen zu konvertieren. Aber Vorsicht, wenn C++ eine dieser Zeichenfolgen ändert, bevor Sie mit dem benutzerdefinierten Marshalling fertig sind, stürzt Ihre App ab.

Verwandte Themen