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

发布于 2024-12-20 06:38:21 字数 1394 浏览 2 评论 0原文

我试图将托管代码中的字符串获取到非托管代码中:

非托管 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);
}

在我的托管程序中,我的字符串看起来很好,但在非托管方面它变成了垃圾。我没有复制所有代码,但我认为这说明了我想要完成的任务。如果我以错误的方式处理这件事或者犯了错误,请告诉我。谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

贪了杯 2024-12-27 06:38:21

试试这个:

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

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;
}
手心的海 2024-12-27 06:38:21

遇到了这个问题,试图将字符串从 C# 返回到 C。我遇到了很多死胡同,但是 Olivier Levrey 在 codeproject 建议使用 IntPtr 来完成此操作。该解决方案对我来说非常有效。

目前您有这个委托声明:

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

立即想到的是,value 实际上应该是一个 out 参数,就像 IDictionary<,>.TryGetValue(键,输出值)。将其赤裸裸地声明为字符串值不会提供任何语义机会返回实际值!正如 @dtb 所建议的,通常提供的一种选择是使用 StringBuilder。对我来说,我得到的结果正是你得到的——返回到 C 端的字符串是官方的。但奥利维尔的解决方案奏效了。

修改您的委托以使用 IntPtr 作为 value

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

value 表示我们可以为其分配值的非托管字符串。实现这一目标的实际步骤有点混乱,但最终还是有效的。

假设您有一些想要返回到 C 端的字符串 s,这是您需要实现的代码,以便将其内容传输到 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 命名的内存缓冲区中。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文