C #의 C ++ DLL에서 배열 내용을 가져 오는 방법
C #과 함께 C ++에서 DLL의 함수를 사용하고 싶습니다.
벡터에 문자열 데이터를 저장합니다.
내 C ++ 파일에는 다음이 포함됩니다.
#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;
}
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();
}
}
}
C #에서 배열을 계속 탐색하는 방법을 모르겠습니다. 벡터 사용이 최적인지 모르겠습니다. 다른 솔루션이 있으면 준비되었습니다.
도와주세요.
답변
문자열 배열 C ++-> 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);
그리고
var lst = new List<string>();
TestReturnArrayStrings(lst.Add);
foreach (string str in lst)
{
Console.WriteLine(str);
}
그리고 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());
}
}
}
여기서 "트릭"은 C #이 List<string>.Add()
메서드에 대한 델리게이트를 C ++에 전달하고 C ++가 C #을 직접 "채운다"는 것 List<>
입니다. C ++에서 관리하는 메모리는 C ++ 측에 남아 있고 C #에서 관리하는 메모리는 C # 측에 남아 있습니다. 교차 메모리 소유권 문제가 없습니다. 상상할 수 있듯이 "트릭"을 , 또는 .Add()
같은 다른 방법 으로 확장하는 것은 매우 쉽습니다 .HashSet<string>
Dictionary<string, string>
참고로 C / C ++와 C # (. NET Framework 및 .NET Core / 5.0 모두) 간의 마샬링에 대한 많은 예제 가 포함 된 github 를 만들었습니다 .
이를 수행하는 한 가지 방법 은 BSTR 과 같은 대부분의 관련 하위 유형을 포함하여 .NET에서 지원 하는 COM의 SAFEARRAY 구조 를 사용하는 것입니다 (P / Invoke에서 사용하는 .NET 할당자는 COM 할당 자입니다) .
따라서 C / C ++에서 다음을 정의 할 수 있습니다.
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;
}
그리고 C #에서는 다음과 같이 정의 할 수 있습니다.
[DllImport("ProductLibrary.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.SafeArray)]
public static extern string[] GetProduct();