2

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.

Scleractinian
  • 77
  • 1
  • 7
  • If you can change your callback to use a `BSTR` you can use the solution in this question: http://stackoverflow.com/questions/4455234/passing-string-from-c-to-c-sharp – shf301 Dec 08 '11 at 03:03

2 Answers2

0

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.

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
0

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;
}
dtb
  • 213,145
  • 36
  • 401
  • 431
  • I get chinese characters in the unmanaged side, but the managed side looks like it should. Am I missing some in or out or ref here? – Scleractinian Dec 08 '11 at 04:20
  • That's better than nothing :-) Unfortunately I can't really do anything but take a stab in the dark. Answer updated. – dtb Dec 08 '11 at 04:41
  • This is the closest answer that comes to doing what I need and thanks for that. I'll keep trying different ways to convert these symbols to what they should be. I thought that you could get a return string value from managed but it seems most examples and questions I've found pass strings to unmanaged but the call came from managed whereas I'm calling from unmanaged to get a string return value from managed, but I still get nothing but symbols back, no big deal. I'll change my program to do it differently. – Scleractinian Dec 08 '11 at 15:52