如何将C++ DLL返回的std::vector<std::string>数据显示在C#窗体的ListBox中
解决方案:在C#窗体中调用C++ DLL的
std::vector返回值并填充ListBox 因为C++的std::vector<std::string>和C#的托管类型无法直接通过P/Invoke交互,我们需要通过兼容接口包装或者中间层来实现,下面是两种实用的方案:
方案一:修改C++ DLL,导出C风格兼容接口
这是最直接的方式,无需额外中间项目,只需调整DLL导出函数,用C语言兼容的类型传递数据:
步骤1:修改C++ DLL代码
把原函数包装成C风格导出函数,避免名字修饰,同时用char**和长度来传递字符串集合:
#include <vector> #include <string> #include <cstring> // 原函数保持不变 std::vector<std::string> foo(std::vector<std::string> strVec) { return strVec; } // 导出C风格的包装函数 extern "C" __declspec(dllexport) char** FooWrapper(const char** inputArray, int inputCount, int* outCount) { // 将C风格数组转为std::vector<std::string> std::vector<std::string> inputVec; for (int i = 0; i < inputCount; ++i) { inputVec.push_back(inputArray[i]); } // 调用原函数 std::vector<std::string> resultVec = foo(inputVec); *outCount = resultVec.size(); // 分配返回的char**数组 char** resultArray = new char*[resultVec.size()]; for (int i = 0; i < resultVec.size(); ++i) { const char* str = resultVec[i].c_str(); resultArray[i] = new char[strlen(str) + 1]; strcpy_s(resultArray[i], strlen(str) + 1, str); } return resultArray; } // 导出释放内存的函数,避免内存泄漏 extern "C" __declspec(dllexport) void FreeStringArray(char** array, int count) { for (int i = 0; i < count; ++i) { delete[] array[i]; } delete[] array; }
步骤2:在C#窗体项目中调用
在窗体类里添加P/Invoke声明,然后调用函数并填充ListBox:
using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace YourWinFormApp { public partial class YourForm : Form { // P/Invoke声明C++ DLL的导出函数 [DllImport("YourCppDll.dll", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr FooWrapper(IntPtr inputArray, int inputCount, out int outCount); [DllImport("YourCppDll.dll", CallingConvention = CallingConvention.Cdecl)] private static extern void FreeStringArray(IntPtr array, int count); public YourForm() { InitializeComponent(); } // 示例调用方法,比如按钮点击事件 private void btnCallFoo_Click(object sender, EventArgs e) { // 准备输入参数(示例输入) string[] inputStrings = { "Hello", "World", "C++", "C#" }; // 将C#字符串数组转为C风格的char** IntPtr[] inputPtrs = new IntPtr[inputStrings.Length]; for (int i = 0; i < inputStrings.Length; ++i) { inputPtrs[i] = Marshal.StringToHGlobalAnsi(inputStrings[i]); } IntPtr inputArray = Marshal.AllocHGlobal(IntPtr.Size * inputPtrs.Length); Marshal.Copy(inputPtrs, 0, inputArray, inputPtrs.Length); try { // 调用DLL函数 int outCount; IntPtr resultArray = FooWrapper(inputArray, inputStrings.Length, out outCount); // 将返回的char**转为C#字符串数组 string[] resultStrings = new string[outCount]; IntPtr[] resultPtrs = new IntPtr[outCount]; Marshal.Copy(resultArray, resultPtrs, 0, outCount); for (int i = 0; i < outCount; ++i) { resultStrings[i] = Marshal.PtrToStringAnsi(resultPtrs[i]); } // 填充ListBox lstData.Items.Clear(); lstData.Items.AddRange(resultStrings); // 释放返回的内存 FreeStringArray(resultArray, outCount); } finally { // 释放输入参数的内存 Marshal.FreeHGlobal(inputArray); foreach (IntPtr ptr in inputPtrs) { Marshal.FreeHGlobal(ptr); } } } } }
方案二:使用C++/CLI作为中间包装层
如果不想修改原C++ DLL代码,或者需要更优雅的类型转换,可以用C++/CLI创建包装类库,直接桥接原生C++和.NET类型:
步骤1:创建C++/CLI类库项目
在Visual Studio中创建CLR类库项目,添加对原C++ DLL的引用(或直接包含原函数头文件和链接库)。
步骤2:编写包装类
#include <vector> #include <string> #include "YourOriginalCppHeader.h" // 包含原foo函数的头文件 using namespace System; using namespace System::Collections::Generic; namespace CppCliWrapper { public ref class FooWrapper { public: static List<String^>^ CallFoo(List<String^>^ inputList) { // 将.NET List<String>转为std::vector<std::string> std::vector<std::string> inputVec; for each (String^ str in inputList) { pin_ptr<const wchar_t> wstr = PtrToStringChars(str); std::string nativeStr(wstr); inputVec.push_back(nativeStr); } // 调用原C++函数 std::vector<std::string> resultVec = foo(inputVec); // 将std::vector<std::string>转为.NET List<String> List<String^>^ resultList = gcnew List<String^>(); for (const auto& str : resultVec) { resultList->Add(gcnew String(str.c_str())); } return resultList; } }; }
步骤3:在C#窗体项目中使用
引用C++/CLI类库,直接调用并填充ListBox:
using System; using System.Collections.Generic; using System.Windows.Forms; using CppCliWrapper; // 引用包装类库 namespace YourWinFormApp { public partial class YourForm : Form { public YourForm() { InitializeComponent(); } private void btnCallFoo_Click(object sender, EventArgs e) { // 准备输入 List<string> inputList = new List<string> { "Test1", "Test2", "Test3" }; // 调用包装函数 List<string> resultList = FooWrapper.CallFoo(inputList); // 填充ListBox lstData.Items.Clear(); lstData.Items.AddRange(resultList.ToArray()); } } }
注意事项
- 确保C++ DLL和C#项目的平台一致(比如都是x86或x64),否则会出现调用失败。
- 方案一中必须调用
FreeStringArray释放内存,避免内存泄漏。 - 如果需要处理Unicode字符串,可把
char*换成wchar_t*,对应C#中用Marshal.StringToHGlobalUni和Marshal.PtrToStringUni。
内容的提问来源于stack exchange,提问作者east1000
相关产品推荐
相关产品推荐

