c # — __declspec (dllexport) :: vector & lt; std :: string & gt;

Я пытался выяснить, как вернуть массив строк из dll c ++ в приложение 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;
}

Спасибо

Matt

4

Решение

Вы можете использовать COM Automation 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();
6

Другие решения

Вы не можете сделать это напрямую — вам нужен дополнительный уровень косвенности. Для интерфейса, совместимого со стилем 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

У вас есть два «стандартных» способа перехода с C ++ на C #.

Первый — это C ++ / CLI. В этом случае вы создадите библиотеку C ++ / CLI, которая принимает std::vector<std::string> и преобразовать это в System::vector<System::string>, Тогда вы можете свободно использовать его как System.String[] в C #.

Другой COM. Там вы создаете интерфейс COM, который возвращает SAFEARRAY содержащий BSTR строка. Этот COM-интерфейс затем создается с помощью System.Runtime.InteropServices в C #. SAFEARRAY затем Object [], который может быть помещен в однострочный объект.

Возможность загрузки интерфейсов C в C # в основном ограничена C. Любой C ++ потерпит неудачу, и Пит обеспечивает этот «нестандартный» подход. (Это работает очень хорошо, только не то, что MS хочет, чтобы вы сделали.)

2
По вопросам рекламы [email protected]