Cách lấy nội dung của mảng từ C ++ dll trong C #

Jan 07 2021

Tôi muốn sử dụng các hàm từ DLL trong C ++ với C #.

Tôi lưu trữ dữ liệu chuỗi trong một vectơ.

Tệp C ++ của tôi chứa:

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

Trong 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();
        }
    }
}

Tôi không biết làm thế nào để tiếp tục duyệt mảng trong c #. Tôi không biết liệu việc sử dụng vector có tối ưu hay không. nếu bạn có giải pháp khác, tôi đã sẵn sàng.

Hãy giúp tôi.

Trả lời

6 xanatos Jan 07 2021 at 00:45

Cách yêu thích của tôi để truyền một mảng chuỗi C ++ -> C # là sử dụng một ủy nhiệm.

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

và sau đó

var lst = new List<string>();

TestReturnArrayStrings(lst.Add);

foreach (string str in lst)
{
    Console.WriteLine(str);
}

Và 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());
        }
    }
}

Ở đây "mẹo" là C # chuyển cho C ++ một đại diện cho List<string>.Add()phương thức và C ++ "điền" trực tiếp vào C # List<>. Bộ nhớ do C ++ quản lý vẫn ở phía C ++, bộ nhớ do C # quản lý vẫn ở phía C #. Không có vấn đề về sở hữu bộ nhớ chéo. Như bạn có thể tưởng tượng, khá dễ dàng để mở rộng "thủ thuật" sang bất kỳ .Add()phương pháp nào khác , như HashSet<string>hoặc Dictionary<string, string>.

Như một phụ chú, tôi đã tạo một github với nhiều ví dụ về việc sắp xếp giữa C / C ++ và C # (cả .NET Framework và .NET Core / 5.0).

2 SimonMourier Jan 07 2021 at 05:47

Một cách để làm điều đó là sử dụng cấu trúc SAFEARRAY của COM vì nó được hỗ trợ bởi .NET (Bộ phân bổ .NET được P / Invoke sử dụng là bộ cấp phát COM), bao gồm hầu hết các loại phụ liên quan, như BSTR .

Vì vậy, trong C / C ++, bạn có thể xác định điều này:

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

Và trong C #, bạn có thể xác định điều này:

[DllImport("ProductLibrary.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.SafeArray)]
public static extern string[] GetProduct();