问题
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