__declspec(DLLEXPORT)::矢量(__declspec(dllexport) ::

2019-08-19 12:07发布

我一直在试图找出如何从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;
}

谢谢

马特

Answer 1:

您可以使用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();


Answer 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功能破坏过程的分配对象。



Answer 3:

你有两个“标准”的方式从C ++去C#。

第一个是C ++ / CLI。 在这种情况下,将构建一个C ++ / CLI库,开出std::vector<std::string>并转换其成System::vector<System::string> 。 然后,你可以自由地使用它作为一个System.String[]在C#。

另一种是COM。 还有你创建一个返回一个COM接口SAFEARRAY包含BSTR字符串。 然后,这个COM接口被实例化,虽然在C#中的System.Runtime.InteropServices。 该SAFEARRAY那么一个对象[],它被套管到单个字符串对象。

到C接口加载到C#设施基本上限制为C.任何C ++将失败和Pete规定“非标准”方法。 (它的工作原理非常好,只是没有什么MS希望你做的。)



文章来源: __declspec(dllexport) ::vector