将字符串从 c# 编组到 c++

Posted

技术标签:

【中文标题】将字符串从 c# 编组到 c++【英文标题】:Marshalling string from c# to c++ 【发布时间】:2011-01-19 15:11:31 【问题描述】:

我是微软世界的新手。 我在尝试将一个简单的字符串从 c# 传递给 dll/c++ 时遇到很多问题 我已经阅读了很多帖子和文档,但问题是一样的。

C++ 代码

extern "C" __declspec(dllexport) int Init( long l , char* url );

C#代码

[DllImport("MCRenderer.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
    public static extern int Init(long a, StringBuilder url);


Init(hndl.ToInt64(), str );

问题是当字符串参数是长值时正确传递了

0x00000000 <Bad Ptr>

你能帮帮我吗...我真的很困惑 谢谢!! AG

【问题讨论】:

【参考方案1】:

你应该传递一个字符串,url应该是字符串类型而不是StringBuilder。

【讨论】:

我做了一个快速查找 - StringBuilder 对 unicode 很有用,所以这可能是它存在的原因。 不,显然它没有,因为它仍然存在int/long 问题... :) (另外:为什么StringBuilder 不起作用?你不能将其传递为[In, Out]?) @Lambert - StringBuilder 默认作为 [InAttribute, OutAttribute] 传递【参考方案2】:

这是因为您编组不正确:C++ 中的 longint 在 C# 中(通常)都是 int

【讨论】:

之所以不好是因为传递一个long会导致高32位(即0x00000000)被用作url参数的值,解释了正在观察的问题. 脚注:IIRC,那^仅在 32 位系统上是正确的。 64位系统在寄存器中传递参数,所以你可能会错误地得到正确的结果,因为无论参数是否为64位,寄存器都是64位的。 好的,我找到了解决方案。 C++ extern "C" __declspec(dllexport) int Init( int l , LPCTSTR url ); // 注意我将 char* 更改为 LPCSTR 数据类型 C# [DllImport("MCRenderer.dll" , CharSet=CharSet.Unicode) ] // 强制 Charset,没有 Unicode 我在 c++ 端的变量 url 中捕获了奇怪的字符 public static extern int初始化(int a,字符串 url); IntPtr hndl = renderPanel.Handle;字符串 url = textBox1.Text;初始化(hndl.ToInt32(),网址);希望这对你有用......谢谢大家。【参考方案3】:

来自MSDN,您需要:

唯一需要注意的是StringBuilder必须为返回值分配足够的空间,否则文本会溢出,导致P/Invoke抛出异常

同样来自Marshaling between Managed and Unmanaged Code

不要通过引用传递 StringBuilder(使用 out 或 ref)。否则,CLR 将期望这个参数的签名是 wchar_t ** 而不是 wchar_t *,并且它不能固定 StringBuilder 的内部缓冲区。性能将显着下降。 当非托管代码使用 Unicode 时使用 StringBuilder。否则,CLR 将不得不复制字符串并在 Unicode 和 ANSI 之间进行转换,从而降低性能。通常您应该将 StringBuilder 编组为 Unicode 字符的 LPARRAY 或 LPWSTR。 始终提前指定 StringBuilder 的容量,并确保容量足够大以容纳缓冲区。非托管代码方面的最佳实践是接受字符串缓冲区的大小作为参数以避免缓冲区溢出。在 COM 中,您也可以使用 IDL 中的 size_is 来指定大小。

所以在您的示例中,您需要将本机大小指定为StringBuilder 的参数,例如StringBuilder str = new StringBuilder(SIZE_OF_NATIVE_STRING);

【讨论】:

【参考方案4】:

尝试在dll函数参数中使用LPCSTR

extern "C" __declspec(dllexport) int Init( long l , **LPCTSTR** url );

这是我找到的一个很好的例子,说明如何做到这一点。

C++:

extern "C" __declspec(dllexport) void doSomething(LPCTSTR asString)

    std::cout << "here" << (wchar_t)asString << endl;
    system ("pause");

C#:

class Program

    static void Main(string[] args)
    
        String myString = "String in C#";
        doSomething(myString);
    
    private const String path = @"C:\testdll2.dll"; //Make sure that the DLL is in this location

    [DllImport(path)]
    private static extern void doSomething(string asString);

【讨论】:

以上是关于将字符串从 c# 编组到 c++的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ dll 编组导出的字符串向量

C# 将 char* 编组到 StringBuilder 总是得到空字符串

C#编组来自C++ DLL的无符号字符返回值[重复]

将引用类型从 C++ 编组到 C#

C# 和 C++ dll - 编组字符串

将包含 int 和 int[] 的结构从 C# 编组到 C++