So erhalten Sie den Inhalt des Arrays aus der C ++ - DLL in C #
Ich möchte Funktionen aus der DLL in C ++ mit C # verwenden.
Ich speichere String-Daten in einem Vektor.
Meine C ++ - Datei enthält:
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern "C" __declspec(dllexport) std::vector<std::string> GetProduct();
std::vector<std::string> GetProduct()
{
std::vector<std::string> vectProduct;
vectProduct.push_back("Citroen");
vectProduct.push_back("C5");
vectProduct.push_back("MOP-C5");
return vectProduct;
}
In C #
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
namespace ConsoleApplication
{
class Program
{
[DllImport("ProductLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern StringBuilder GetProduct();
static void Main(string[] args)
{
StringBuilder vectProduct_impl = GetProduct();
}
}
}
Ich weiß nicht, wie ich das Array in c # weiter durchsuchen soll. Ich weiß nicht, ob die Verwendung von Vektor optimal ist. Wenn Sie eine andere Lösung haben, bin ich bereit.
Bitte helfen Sie.
Antworten
Meine bevorzugte Methode zum Übergeben eines Arrays von Zeichenfolgen C ++ -> C # ist die Verwendung eines Delegaten.
C #:
// If possible use UnmanagedType.LPUTF8Str
// or under Windows rewrite everything to use
// wchar_t, std::wstring and UnmanagedType.LPWStr
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void AddAnsi([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestReturnArrayStrings(AddAnsi add);
und dann
var lst = new List<string>();
TestReturnArrayStrings(lst.Add);
foreach (string str in lst)
{
Console.WriteLine(str);
}
Und C ++:
#include <string>
#include <vector>
extern "C"
{
__declspec(dllexport) void TestReturnArrayStrings(void (add)(const char* pstr))
{
std::string str1 = "Hello";
std::string str2 = "World";
add(str1.data());
add(str2.data());
// Example with std::vector
add("--separator--"); // You can even use C strings
std::vector<std::string> v = { "Foo", "Bar" };
// for (std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it)
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it)
{
add(it->data());
}
add("--separator--"); // You can even use C strings
// With C++ 11
// for (auto& it: v)
for (const auto& it: v)
{
add(it.data());
}
}
}
Hier ist der "Trick", dass C # einen Delegaten an die List<string>.Add()
Methode an C ++ übergibt und C ++ das C # direkt "füllt" List<>
. Der von C ++ verwaltete Speicher verbleibt auf der C ++ - Seite, der von C # verwaltete Speicher auf der C # -Seite. Keine Probleme mit speicherübergreifendem Besitz. Wie Sie sich vorstellen können, ist es ziemlich einfach, den "Trick" auf eine andere .Add()
Methode wie HashSet<string>
oder zu erweitern Dictionary<string, string>
.
Als Nebenbemerkung habe ich einen Github mit vielen Beispielen zum Marshalling zwischen C / C ++ und C # (sowohl .NET Framework als auch .NET Core / 5.0) erstellt.
Eine Möglichkeit besteht darin, die SAFEARRAY-Struktur von COM zu verwenden , die von .NET unterstützt wird (der von P / Invoke verwendete .NET-Allokator ist der COM-Allokator), einschließlich der meisten zugeordneten Untertypen wie BSTR .
In C / C ++ können Sie Folgendes definieren:
extern "C" __declspec(dllexport) LPSAFEARRAY GetProduct();
LPSAFEARRAY GetProduct()
{
LPSAFEARRAY psa = SafeArrayCreateVector(VT_BSTR, 0, 3);
LONG index = 0;
// _bstr_t is a smart class that frees allocated memory automatically
// it needs #include <comdef.h>
// but you can also use raw methods like SysAllocString / SysFreeString
_bstr_t s0(L"Citroen"); // could use "Citroen" if you really want ANSI strings
// note SafeArrayPutElement does a copy internally
SafeArrayPutElement(psa, &index, s0.GetBSTR());
index++;
_bstr_t s1(L"C5");
SafeArrayPutElement(psa, &index, s1.GetBSTR());
index++;
_bstr_t s2(L"MOP - C5");
SafeArrayPutElement(psa, &index, s2.GetBSTR());
index++;
return psa;
}
Und in C # können Sie Folgendes definieren:
[DllImport("ProductLibrary.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.SafeArray)]
public static extern string[] GetProduct();