如何传递一个可以从本机写入到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);
}

在我的托管程序中,我的字符串看起来很好,但它在非托管端变成了垃圾。我没有复制所有的代码,但我认为这说明了我想要实现的目标。如果我做错了,或者我犯了错误,请告诉我。谢谢。

如何传递一个可以从本机写入到c#的空字符串缓冲区?

试试这个:

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命名的内存缓冲区中。