How can I pass an empty string buffer that can be written to from native to c#?

柔情痞子 提交于 2019-12-14 02:18:52

问题


I'm trying to get a string from my managed code into my unmanaged code:

unmanaged 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;
}

managed 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);
}

Within my managed program my string looks fine but it turns into garbage on the unmanaged side. I didn't copy all the code but I think this makes the point of what I'm trying to accomplish. If I'm going about this the wrong way or I made mistakes, please let me know. Thanks.


回答1:


Try this:

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;
}



回答2:


I ran into this exact issue, trying to return a string from C# to C. I ran into a lot of dead-ends, but Olivier Levrey on codeproject suggested the use of IntPtr to accomplish this. The solution worked great for me.

Currently you have this delegate declaration:

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

Immediately what comes to mind is that really, value should be an out parameter, like for IDictionary<,>.TryGetValue(key, out value). For it to be declared nakedly as string value does not provide any semantic opportunity to return the actual value! As @dtb suggests, one option often afforded is to use a StringBuilder. For me, I got exactly the result you got -- the string returned to the C side was gobbledygook. But Olivier's solution worked.

Modify your delegate to use an IntPtr for value:

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

value represents an unmanaged string to which we can assign a value. The actual steps to achieve this is a bit of a mess, but it works in the end.

Assuming you have some string s that you want to return to the C side, this is the code you need to implement in order to transport its contents into the 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);

Once this is done, your return value has been copied into the memory buffer named by value.



来源:https://stackoverflow.com/questions/8425501/how-can-i-pass-an-empty-string-buffer-that-can-be-written-to-from-native-to-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!