我该如何在C#中编组wstring *?

3

我有一个在C++中的函数_GetFileList,我正在尝试让它返回一个字符串列表/数组给C#代码。我不必非要使用wstring *或其他任何类型,这只是我找到的例子所采用的方法,但我正在尝试让它返回一个列表/数组而不仅仅是单个字符串。如何进行编组处理,以在C#端具有多个字符串?

C++代码:

extern "C" __declspec(dllexport) wstring * __cdecl _GetFileList(int &size){
    std::list<wstring> myList;
    //Code that populates the list correctly
    size = myList.size();
    wstring * arr = new wstring[size];
    for( int i=0;i<size;i++){
        arr[i] = myList.front();
        myList.pop_front();
    }
    return arr;
}

I call that from C# like this:

[DllImport(@"LinuxDirectory.Interface.dll")]
private static extern IntPtr _GetFileList(ref int size);

public bool GetFileList() {
    int size = 0;
    IntPtr ptr = _GetFileList( ref size );
    //WHAT DO I DO HERE TO GET THE LIST OF STRINGS INTO SOMETHING I CAN READ?
}

我尝试使用Marshal.PtrToStringUni, Marshal.PtrToStringAnsi以及结构体来实现,但似乎无法正确使用语法。


1
https://dev59.com/9VrUa4cB1Zd3GeqPhzsz - pm100
1
@pm100:很好的相关问题,但它不是重复的,因为这个问题实际上在函数签名中有(不幸的是)std::wstring,而不仅仅是在内部使用。 - Ben Voigt
1个回答

3
你无法使用P/Invoke处理完全兼容C的函数签名,这意味着指针必须指向普通数据,而不是完整的C++类。如果你可以重写C++代码,将其更改为调用SysAllocString并返回BSTR,该类型是用于在不同语言编写的组件之间进行数据交换的操作系统提供的字符串类型。P/Invoke已经具备接收BSTR的所有正确魔力,只需在P/Invoke声明中使用C# string类型即可。如果你无法更改C++签名,则必须进行包装。可以使用C++/CLI来处理C++类以及.NET,或者使用标准C++并创建并返回BSTR,从std::wstring复制数据。对于多个字符串,可以使用OS提供的数组类型(SAFEARRAY),它能够在内部携带BSTR,并且再次是语言无关的。或者,使用适当的(在数据中不自然出现的)分隔符字符进行连接和拆分可能更容易。这里有一些非常好的信息:https://msdn.microsoft.com/en-us/magazine/mt795188.aspx运行.NET Framework(Microsoft实现,在Windows上),可以使用辅助类:
#include <windows.h>
#include <atlbase.h>
#include <atlsafe.h>
#include <string>

extern "C" __declspec(dllexport) LPSAFEARRAY GetFileList()
{
    std::vector<std::wstring> myList;
    //Code that populates the list correctly
    size = myList.size();

    CComSafeArray<BSTR> sa(size);
    int i=0;
    for( auto& item : myList ) {
        CComBSTR bstr(item.size(), &item[0]);
        sa.SetAt(i++, bstr.Detach()); // transfer ownership to array
    }
    return sa.Detach(); // transfer ownership to caller
}

在C#方面,p/invoke可以处理所有这些:
[DllImport("NativeDll.dll"),
 return:MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
public static extern string[] GetFileList();

您的问题表明您可能在使用Linux。不幸的是,我找到的文档似乎表明,Mono的p/invoke完全不知道如何处理可变大小的数组。因此,您必须手动处理所有内容,包括释放内存(您的问题中的代码泄漏了new wstring[size] - C#根本不知道如何调用C++的delete[]操作符)。请查看Mono.Unix.UnixMarshal.PtrToStringArray

我也有过单字符串的想法,但明确告诉我不应该/不能这样做。 - WWZee
我不明白如何使用BSTR,我以前从未见过。你能提供一个例子吗? - WWZee
1
我和Wayne一起工作,我可以告诉你他已经在使用C++/CLI了。 - DCShannon
1
@DCShannon:这就使得它变得容易得多了。在C++/CLI类中,您可以返回一个cli::array <System::String^> ^,它只是C#中string []的C ++名称(SAFEARRAY将自动转换,这已经是C#类型),而System :: String 有一个构造函数,它需要大小(wstr.size())和指针(&wstr[0])。 - Ben Voigt
@BenVoigt非常感谢您的帮助。可能是打字错误,但我无法按照您的方式使其工作。我需要将DllImportreturn部分放在两个单独的[ ]块中。 - WWZee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接