如何传递一个可以从本机写入到c#的空字符串缓冲区?
本文关键字:缓冲区 字符串 本机 何传递 一个 | 更新日期: 2023-09-27 18:17:13
我试图从我的托管代码到我的非托管代码的字符串:
非托管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);
}
在我的托管程序中,我的字符串看起来很好,但它在非托管端变成了垃圾。我没有复制所有的代码,但我认为这说明了我想要实现的目标。如果我做错了,或者我犯了错误,请告诉我。谢谢。
试试这个:
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;
}
我遇到了这个确切的问题,试图从c#返回一个字符串到C。我遇到了很多死胡同,但Olivier Levrey在codeproject上建议使用IntPtr
来完成这一点。这个解决方案对我来说非常有效。
当前你有这个委托声明:
private delegate int GetNameFromDictionaryCallback(UInt64 key, string value);
马上想到的是,value
应该是一个out
参数,就像IDictionary<,>.TryGetValue(key, out value)
一样。因为它被赤裸裸地声明为string value
并没有提供任何语义上的机会来返回实际值!正如@dtb建议的那样,通常提供的一种选择是使用StringBuilder
。对我来说,我得到的结果和你得到的完全一样——返回到C端的字符串是一堆乱七八糟的东西。但奥利维尔的解决方案奏效了。
修改您的委托为value
使用IntPtr
:
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
命名的内存缓冲区中。