วิธีรับเนื้อหาของอาร์เรย์จาก C ++ dll ใน C #
ฉันต้องการใช้ฟังก์ชันจาก DLL ใน C ++ กับ C #
ฉันเก็บข้อมูลสตริงเป็นเวกเตอร์
ไฟล์ 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 # ส่งผ่านไปยัง C ++ เป็นตัวแทนของList<string>.Add()
เมธอดและ C ++ "เติม" C # List<>
โดยตรง หน่วยความจำที่จัดการโดย C ++ ยังคงอยู่ในฝั่ง C ++ หน่วยความจำที่จัดการโดย C # จะยังคงอยู่ในฝั่ง C # ไม่มีปัญหาในการเป็นเจ้าของหน่วยความจำข้าม ในขณะที่คุณสามารถจินตนาการว่ามันเป็นเรื่องง่ายมากที่จะขยาย "เคล็ดลับ" เพื่ออื่นใด.Add()
วิธีการเหมือนหรือHashSet<string>
Dictionary<string, string>
ในฐานะ sidenote ฉันได้สร้างgithub ที่มีตัวอย่างมากมายเกี่ยวกับการจัดระเบียบระหว่าง C / C ++ และ C # (ทั้ง. NET Framework และ. NET Core / 5.0)
วิธีการหนึ่งที่จะทำก็คือการใช้ COM ของโครงสร้าง SAFEARRAYขณะที่มันได้รับการสนับสนุนโดย .NET (.NET ที่จัดสรรโดยใช้ P / วิงวอนเป็น COM จัดสรร) รวมทั้งประเภทย่อยที่เกี่ยวข้องเช่นBSTR
ดังนั้นใน 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();