首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >__declspec(dllexport) ::vector<std::string>

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

Stack Overflow用户
提问于 2013-04-04 19:36:26
回答 3查看 2.9K关注 0票数 4

我一直在尝试解决如何从c++ dll向c#应用程序返回字符串数组的问题,但我被困在如何做到这一点或在非常基本的层次上查找文章上。

假设我有下面的代码。如何修复粗体线条:

代码语言:javascript
复制
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;
}

谢谢

哑光

EN

回答 3

Stack Overflow用户

发布于 2013-04-05 00:40:11

您可以使用COM自动化调用类型,即使不执行完整的COM (无对象、无类、无接口、无TLB、无注册表等),只需使用DLL导出即可,因为.NET通过P/ SAFEARRAY本机支持它,如下所示:

C++:

代码语言:javascript
复制
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#:

代码语言:javascript
复制
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();
票数 6
EN

Stack Overflow用户

发布于 2013-04-04 19:53:55

你不能直接这么做--你需要一个额外的间接层。对于C风格兼容的接口,您需要返回一个原始类型。忘记从任何其他编译器中使用C++ DLLs -没有严格的C++ ABI。

因此,您需要返回一个不透明的指针,指向一个已分配的字符串向量,例如

代码语言:javascript
复制
#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);
}

和实现方面:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2013-04-04 20:07:04

从C++到C#有两种“标准”方法。

第一种是C++/CLI。在本例中,您将构建一个C++/CLI库,该库接受std::vector<std::string>并将其转换为System::vector<System::string>。然后,您就可以在C#中将其作为System.String[]自由使用。

另一种是COM。在那里,您将创建一个COM接口,该接口返回一个包含BSTR字符串的SAFEARRAY。然后通过C#中的System.Runtime.InteropServices实例化此COM接口。这样,SAFEARRAY就是一个Object[],可以将其大小写转换为单个string对象。

将C接口加载到C#中的工具基本上仅限于C。任何C++都会失败,而Pete提供了这种“非标准”方法。(它工作得很好,只是不是微软想让你做的事情。)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15810016

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档