__declspec(dllexport):: vector <std :: string> </std :: string>

时间:2013-04-04 11:36:26

标签: c# c++ vector pinvoke declspec

我一直在尝试研究如何将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;
}

感谢

马特

3 个答案:

答案 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要你做的。)