Как вернуть текст из нативного (C ++) кода - PullRequest
31 голосов
/ 15 марта 2011

Я использую Pinvoke для взаимодействия между собственным (C ++) кодом и управляемым (C #) кодом.Чего я хочу добиться, так это получить некоторый текст из нативного кода в мой управляемый код.Для этого я пробую много вещей, например, передачу строки / строителя строк по ссылке, использование [IN] и [OUT], маршалинг в LPSTR, возврат строки из функции и т. Д., Но в моем случае ничего не работает.Любая помощь с небольшим кодом будет принята с благодарностью.

Ответы [ 4 ]

45 голосов
/ 15 марта 2011

Я бы сделал это с BSTR, так как это означает, что вам не нужно вызывать в native два раза на строку, один раз, чтобы получить длину, а затем один раз, чтобы получить содержимое.

С помощьюBSTR маршаллер позаботится о том, чтобы освободить BSTR с помощью правильного менеджера памяти, чтобы вы могли безопасно передать его из своего кода C ++.

C ++

#include <comutil.h>
BSTR GetSomeText()
{
    return ::SysAllocString(L"Greetings from the native world!");
}

C #

[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();

Есть один незначительный недостаток BSTR, а именно, что он несет полезную нагрузку UTF-16, но ваши исходные данные вполне могут быть char*.

Чтобы преодолеть это, вы можете свернуть преобразование с char* в BSTR следующим образом:

BSTR ANSItoBSTR(const char* input)
{
    BSTR result = NULL;
    int lenA = lstrlenA(input);
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
    if (lenW > 0)
    {
        result = ::SysAllocStringLen(0, lenW);
        ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
    } 
    return result;
}

Это самый сложный способ, и теперь его легко добавитьдругие оболочки для преобразования в BSTR из LPWSTR, std::string, std::wstring и т. д.

4 голосов
/ 15 марта 2011

Вот пример этого через C #.Я вызываю нативную функцию GetWindowText через C # с помощью pInvoking.GetWindowText возвращает заголовок окна, в которое handle передается.

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern int GetWindowTextLength(IntPtr hWnd);

    public static string GetText(IntPtr hWnd)
    {
        // Allocate correct string length first
        int length = GetWindowTextLength(hWnd);
        StringBuilder sb = new StringBuilder(length + 1);
        GetWindowText(hWnd, sb, sb.Capacity);
        return sb.ToString();
    }        

    private void button1_Click(object sender, EventArgs e)
    {
        string str = GetText(this.Handle);
    }
4 голосов
/ 15 марта 2011

Здесь - тема, в которой обсуждался маршалинг строк.

Необходимо пометить параметр атрибутом

[MarshalAs(UnmanagedType.LPSTR)]
0 голосов
/ 24 марта 2017

Если вы возвращаете char * и не хотите изменять код C / C ++ для выделения памяти для возвращаемого значения (или вы не можете изменить этот код), тогда вы можете изменить свойC # extern функция-прототип, чтобы вернуть IntPtr и выполнить маршалинг самостоятельно.

Например, вот фрагмент взаимодействия, которое я написал для Pocketsphinx :

[DllImport("sphinxbase.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr /* char const * */ jsgf_grammar_name(IntPtr /* jsgf_t * */ jsgf);

А вот get-accessor для класса C # JsgfGrammar.m_pU - это IntPtr, который указывает на необработанный объект jsgf_t.

public string Name
{
    get
    {
        IntPtr pU = jsgf_grammar_name(m_pU);
        if (pU == IntPtr.Zero)
            strName = null;
        else
            strName = Marshal.PtrToStringAnsi(pU);
        return strName;
    }
}

Изменение этого примера для других форматов строк (например, Unicode) должно быть тривиальным.

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