Как я могу передать пустой строковый буфер, который может быть записан из нативного в C #? - PullRequest
2 голосов
/ 08 декабря 2011

Я пытаюсь получить строку из моего управляемого кода в мой неуправляемый код:

неуправляемый dll:

typedef int (__stdcall * GetNameFromDictionaryCallback)(ULONGLONG id, WCHAR * name);

declspec(dllexport) void __stdcall UnmanagedSetNameLookupCallback(GetNameFromDictionaryCallback fn)
{
     GetNameFromDictionaryCallback GetNameFromDictionary = fn;

     WCHAR * value = new WCHAR[256];
     ULONGLONG key = 0x250000000DA44785;
     GetNameFromDictionary(key, value); // reverse P/Invoke call to C# function
     wprintf_s("%ls", value); // just to display the result for simplicity
     delete value;
}

управляемого dll:

public class ProgramInterop
{
    private delegate int GetNameFromDictionaryCallback(UInt64 key, string value);
    private static GetNameFromDictionaryCallback mGetNameInstance;
    private Dictionary<UInt64, string> dict;

    private ProgramInterop()
    {
        mGetNameInstance = new GetNameFromDictionaryCallback(GetNameFromDictionary);
        UnmanagedSetNameLookupCallback(mGetNameInstance);
    }

    public bool GetNameFromDictionary(UInt64 key, string value)
    {
        return dict.TryGetValue(key, out value);
    }

    [DllImport("Unmanaged.dll")]
    private static extern void UnmanagedSetNameLookupCallback(GetNameFromDictionaryCallback fn);
}

В моей управляемой программе моя строка выглядит хорошо, но с неуправляемой стороны она превращается в мусор.Я не копировал весь код, но я думаю, что это то, что я пытаюсь достичь.Если я поступаю неправильно или я допустил ошибки, пожалуйста, дайте мне знать.Спасибо.

Ответы [ 2 ]

0 голосов
/ 23 апреля 2013

I столкнулся с этой проблемой , пытаясь вернуть строку из C # в C. Я столкнулся с множеством тупиков, но Оливье Леврей в проекте кода предложил использовать IntPtr для достижения этой цели. Решение отлично сработало для меня.

В настоящее время у вас есть это объявление делегата:

private delegate int GetNameFromDictionaryCallback(UInt64 key, string value);

Сразу приходит на ум то, что на самом деле value должен быть параметром out, как для IDictionary<,>.TryGetValue(key, out value). Для того, чтобы он был объявлен как «1014», он не предоставляет никакой семантической возможности для возврата действительного значения ! Как предполагает @dtb, один из часто доступных вариантов - использовать StringBuilder. Для меня я получил именно тот результат, который вы получили - вернувшаяся на сторону C строка была пустяком. Но решение Оливье сработало.

Измените свой делегат, чтобы использовать IntPtr для value:

private delegate int GetNameFromDictionaryCallback(UInt64 key, IntPtr value);

value представляет неуправляемую строку, которой мы можем присвоить значение. Фактические шаги для достижения этого немного беспорядок, но в конце концов это работает.

Предполагая, что у вас есть строка s, которую вы хотите вернуть на сторону C, этот код вам нужно реализовать для переноса ее содержимого в IntPtr value:

// Convert from managed to unmanaged string and store in `sPtr`
IntPtr sPtr = Marshal.StringToHGlobalAnsi(s);
// Create a byte array to receive the bytes of the unmanaged string (including null terminator)
var sBytes = new byte[s.Length + 1];
// Copy the the bytes in the unmanaged string into the byte array
Marshal.Copy(sPtr, sBytes, 0, s.Length);
// Copy the bytes from the byte array into the buffer 
Marshal.Copy(sBytes, 0, buffer, sBytes.Length);
// Free the unmanaged string
Marshal.FreeHGlobal(sPtr);

Как только это будет сделано, ваше возвращаемое значение будет скопировано в буфер памяти с именем value.

0 голосов
/ 08 декабря 2011

Попробуйте это:

delegate int GetNameFromDictionaryCallback(
    UInt64 key, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder value);

public bool GetNameFromDictionary(UInt64 key, StringBuilder value)
{
    string s;
    if (dict.TryGetValue(key, out s))
    {
        value.Append(s);
        return true;
    }
    return false;
}
...