我一直在尝试研究如何将c ++ dll中的字符串数组返回到c#应用程序,但我仍然坚持如何执行此操作或在最基本的级别上查找文章。
假设我有以下代码。如何修复粗体线:
extern "C" {
__declspec(dllexport) int GetANumber();
//unsure on this line:
**__declspec(dllexport) ::vector<std::string> ListDevices();**
}
extern::vector<std::string> GetStrings()
{
vector<string> seqs;
return seqs;
}
extern int GetANumber()
{
return 27;
}
感谢
马特
答案 0 :(得分:6)
你可以使用COM自动化SAFEARRAY类型,即使没有完整的COM(没有对象,没有类,没有接口,没有TLB,没有注册表等),只是使用DLL导出,因为.NET支持它原生于P / Invoke,如下所示:
C ++:
extern "C" __declspec(dllexport) LPSAFEARRAY ListDevices();
LPSAFEARRAY ListDevices()
{
std::vector<std::string> v;
v.push_back("hello world 1");
v.push_back("hello world 2");
v.push_back("hello world 3");
CComSafeArray<BSTR> a(v.size()); // cool ATL helper that requires atlsafe.h
std::vector<std::string>::const_iterator it;
int i = 0;
for (it = v.begin(); it != v.end(); ++it, ++i)
{
// note: you could also use std::wstring instead and avoid A2W conversion
a.SetAt(i, A2BSTR_EX((*it).c_str()), FALSE);
}
return a.Detach();
}
C#:
static void Main(string[] args)
{
foreach(string s in ListDevices())
{
Console.WriteLine(s);
}
}
[DllImport("MyUnmanaged.dll")]
[return: MarshalAs(UnmanagedType.SafeArray)]
private extern static string[] ListDevices();
答案 1 :(得分:2)
您无法直接执行此操作 - 您需要额外的间接级别。对于C风格的兼容接口,您需要返回基本类型。 忘记使用来自任何其他编译器的C ++ DLL - 没有严格的C ++ ABI。
因此,您需要返回一个指向已分配字符串向量的不透明指针,例如
#define MYAPI __declspec(dllexport)
extern "C" {
struct StringList;
MYAPI StringList* CreateStringList();
MYAPI void DestroyStringList(StringList* sl);
MYAPI void GetDeviceList(StringList* sl);
MYAPI size_t StringList_Size(StringList* sl);
MYAPI char const* StringList_Get(StringList* v, size_t index);
}
实施明智:
std::vector<std::string>* CastStringList(StringList* sl) {
return reinterpret_cast<std::vector<std::string> *>(sl);
}
StringList* CreateStringList() {
return reinterpret_cast<StringList*>(new std::vector<std::string>);
}
void DestroyStringList(StringList* sl) {
delete CastStringList(sl);
}
void GetDeviceList(StringList* sl) {
*CastStringList(sl) = GetStrings(); // or whatever
}
size_t StringList_Size(StringList* sl) {
return CastStringList(sl)->size();
}
char const* StringList_Get(StringList* v, size_t index) {
return (*CastStringList(sl))[index].c_str();
}
完成所有这些后,您可以在C#端提供更清晰的包装。当然,不要忘记通过DestroyStringList函数销毁已分配的对象。
答案 2 :(得分:2)
您有两种“标准”方法可以从C ++升级到C#。
第一个是C ++ / CLI。在这种情况下,您将构建一个C ++ / CLI库,它接受std::vector<std::string>
并将其转换为System::vector<System::string>
。然后你可以在C#中自由地使用它作为System.String[]
。
另一个是COM。在那里,您创建一个COM接口,返回包含SAFEARRAY
字符串的BSTR
。然后通过C#中的System.Runtime.InteropServices实例化此COM接口。然后SAFEARRAY
是一个Object [],它可以被装入单个字符串对象。
将C接口加载到C#的工具基本上限于C.任何C ++都将失败,Pete提供“非标准”方法。 (效果很好,不是MS要你做的。)