Возврат содержимого std :: wstring из C ++ в C # - PullRequest
3 голосов
/ 13 августа 2011

У меня есть неуправляемая C ++ DLL, которую я обернул простым интерфейсом C, чтобы я мог вызывать PInvoke из C #.Вот пример метода в оболочке C:

const wchar_t* getMyString()
{
    // Assume that someWideString is a std::wstring that will remain
    // in memory for the life of the incoming calls.
    return someWideString.c_str();
}

Вот моя настройка C # DLLImport.

[DllImport( "my.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl )]
private static extern string GetMyString();

Однако строка неправильно распределяется, часто портя первый символ илииногда вместо этого показывается куча китайских иероглифов.Я зарегистрировал вывод от реализации на стороне C, чтобы подтвердить, что std :: wstring сформирован правильно.

Я также попытался изменить DLLImport, чтобы он возвращал IntPtr и конвертировал с помощью упакованного метода, используя Marshal.PtrToStringUniи он имеет тот же результат.

[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern IntPtr GetMyString();
public string GetMyStringMarshal()
{
    return Marshal.PtrToStringUni( GetMyString() );
}

Любые идеи?

Обновить с ответом

Так что, как уже упоминалось ниже, это на самом деле не проблема с моими привязками, новремя жизни моего wchar_t *.Мое письменное предположение было неверным, someWideString фактически копировалось во время моих звонков в остальную часть приложения.Поэтому он существовал только в стеке и был освобожден до того, как мой код C # смог завершить его маршалинг.

Правильное решение - либо передать указатель на мой метод, как описано в shf301, либо убедиться, что мой wchar_t* ссылка не перемещается / не перераспределяется / не уничтожается, пока мой интерфейс C # не успел ее скопировать.

Возвращение std :: wstring вниз на мой уровень C в виде "const & std :: wstring" означает мой вызовc_str () вернет ссылку, которая не будет немедленно удалена из области действия моего метода C.

Затем вызывающему коду C # необходимо использовать Marshal.PtrToStringUni () для копирования данных из ссылки вуправляемая строка.

Ответы [ 2 ]

6 голосов
/ 13 августа 2011

Вам придется переписать вашу функцию getMyString по причинам, указанным в ответ Ханса Пассанта .

Вам необходимо, чтобы код C # передавал буфер в код C ++.Таким образом, ваш код (хорошо, CLR Marshaller) контролирует время жизни буфера, и вы не вступаете в неопределенное поведение.

Ниже приведена реализация:

C ++

void getMyString(wchar_t *str, int len)
{
    wcscpy_s(str, len, someWideString.c_str());
}

C #

[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode )]
private static extern void GetMyString(StringBuffer str, int len);
public string GetMyStringMarshal()
{
    StringBuffer buffer = new StringBuffer(255);
    GetMyString(buffer, buffer.Capacity);
    return buffer.ToString();
}
4 голосов
/ 13 августа 2011

Вам необходимо указать атрибут MarshalAs для возвращаемого значения:

    [DllImport( "my.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    [return : MarshalAs(UnmanagedType.LPWStr)]
    private static extern string GetMyString();

Убедитесь, что функция действительно cdecl и что объект wstring не уничтожается, когда функция возвращается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...