简体   繁体   English

如何从 C# 中的 C++ dll 获取数组的内容

[英]How to get content of array from C++ dll in C#

I want to use functions from DLL in C++ with C#.我想在 C++ 和 C# 中使用 DLL 中的函数。

I store string data in a vector.我将字符串数据存储在向量中。

My C++ file contains:我的 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;
}

In C#在 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();
        }
    }
}

I don't know how to continue to browse the array in c#.不知道怎么继续浏览c#中的数组。 I don't know if the use of vector is optimal.我不知道使用矢量是否是最佳的。 if you have other solution I'm ready.如果您有其他解决方案,我准备好了。

Please help.请帮忙。

My favourite way for passing an array of strings C++-->C# is by using a delegate.我最喜欢的传递字符串数组 C++-->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);

and then接着

var lst = new List<string>();

TestReturnArrayStrings(lst.Add);

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

And C++:和 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());
        }
    }
}

Here the "trick" is that C# passes to C++ a delegate to the List<string>.Add() method, and C++ "fills" directly the C# List<> . Here the "trick" is that C# passes to C++ a delegate to the List<string>.Add() method, and C++ "fills" directly the C# List<> . The memory managed by C++ remains in the C++ side, the memory managed by the C# remains in the C# side. The memory managed by C++ remains in the C++ side, the memory managed by the C# remains in the C# side. No problems of cross-memory ownership.没有跨内存所有权的问题。 As you can imagine, it is quite easy to expand the "trick" to any other .Add() method, like HashSet<string> , or Dictionary<string, string> .可以想象,很容易将“技巧”扩展到任何其他.Add()方法,例如HashSet<string>Dictionary<string, string>

As a sidenote, I've created a github with many examples about marshaling between C/C++ and C# (both .NET Framework and .NET Core/5.0). As a sidenote, I've created a github with many examples about marshaling between C/C++ and C# (both .NET Framework and .NET Core/5.0).

One way to do it is to use COM's SAFEARRAY structure as it's supported by .NET (the .NET Allocator used by P/Invoke is the COM allocator), including most of associated sub types, like BSTR .一种方法是使用 COM 的SAFEARRAY 结构,因为它受 .NET 支持(P/Invoke 使用的 .NET 分配器是COM分配器,包括大多数相关的子分配器)。

So, in C/C++, you can define this:因此,在 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;
}

And in C#, you can define this:在 C# 中,您可以这样定义:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM