本文介绍了返回一个std :: wstring从C ++到C#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我有一个非托管C ++ DLL,我已经包装与一个简单的C接口,所以我可以从C#调用PInvoke它。下面是C包装器中的示例方法: const wchar_t * getMyString() { / /假设someWideString是一个std :: wstring,将保持 //在内存中的来电呼叫的生命。 return someWideString.c_str(); } 这是我的C#DLLImport设置。 [DllImport(my.dll,CharSet = CharSet.Unicode,CallingConvention = CallingConvention.Cdecl)] private static extern string GetMyString(); 但是字符串没有正确编组,经常拧紧第一个字符,的汉字。我已经记录了从C侧执行的输出,以确认std :: wstring是否正确形成。 我也试过更改DLLImport返回一个IntPtr和 [DllImport(my.dll,CallingConvention(my.dll));使用Marshal.PtrToStringUni = CallingConvention.Cdecl)] private static extern IntPtr GetMyString(); public string GetMyStringMarshal() { return Marshal.PtrToStringUni(GetMyString()); } 有任何想法吗? 用答案更新 如下所述,这不是我绑定的问题,而是我的wchar_t *的生命周期。我的书面假设是错误的,someWideString事实上是在我的调用期间复制到其余的应用程序。因此,它只存在于堆栈上,并在我的C#代码可以完成编组之前被释放。 正确的解决方法是传递一个指针到我的方法由shf301描述,或者确保我的wchar_t *引用在我的C#接口有时间复制之前不会被移动/重新分配/销毁。 返回std :: wstring下来到我的C层作为一个const& std :: wstring意味着我调用c_str()将返回一个引用,不会立即dealloc'd超出我的C方法的范围。 然后,调用C#代码需要使用Marshal.PtrToStringUni()将数据从引用复制到托管字符串中。解决方案你将不得不重写你的getMyString函数,因为 Hans Passant的回答。 您需要让C#代码将缓冲区传递给您的C ++代码。这样你的代码(确定,CLR Marshaller)控制缓冲区的生命周期,你不会陷入任何未定义的行为。 下面是一个实现: / p> C ++ void getMyString(wchar_t * str,int len) { wcscpy_s(str,len,someWideString.c_str()); } C# [DllImport(my.dll,CallingConvention = CallingConvention.Cdecl,CharSet = CharSet.Unicode)] private static extern void GetMyString(StringBuffer str,int len); public string GetMyStringMarshal() { StringBuffer buffer = new StringBuffer(255); GetMyString(buffer,buffer.Capacity); return buffer.ToString(); } I have an unmanaged C++ DLL that I have wrapped with a simple C interface so I can call PInvoke on it from C#. Here is an example method in the C wrapper:const wchar_t* getMyString(){ // Assume that someWideString is a std::wstring that will remain // in memory for the life of the incoming calls. return someWideString.c_str();}Here is my C# DLLImport setup.[DllImport( "my.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl )]private static extern string GetMyString();However the string is not correctly marshalled, often screwing up the first character or sometimes way off showing a bunch of chinese characters instead. I have logged output from the implementation on the C side to confirm that the std::wstring is correctly formed.I have also tried changing the DLLImport to return an IntPtr and convert with a wrapped method using Marshal.PtrToStringUni and it has the same result.[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl )]private static extern IntPtr GetMyString();public string GetMyStringMarshal(){ return Marshal.PtrToStringUni( GetMyString() );}Any ideas?Update with AnswerSo as mentioned below, this is not really an issue with my bindings but the lifetime of my wchar_t*. My written assumption was wrong, someWideString was in fact being copied during my calls to the rest of the application. Therefore it existed only on the stack and was being let go before my C# code could finish marshalling it.The correct solution is to either pass a pointer in to my method as described by shf301, or make sure my wchar_t* reference does not get moved / reallocated / destroyed before my C# interface has time to copy it.Returning the std::wstring down to my C layer as a "const &std::wstring" means my call to c_str() will return a reference that won't be immediately dealloc'd outside the scope of my C method.The calling C# code then needs to use Marshal.PtrToStringUni() to copy data from the reference into a managed string. 解决方案 You are going to have to rewrite your getMyString function for the reasons mentioned in Hans Passant's answer.You need to have the C# code pass a buffer in to your C++ code. That way the your code (ok, the CLR Marshaller) controls the lifetime of the buffer and you don't get into any undefined behavior.Below is an implementation:C++void getMyString(wchar_t *str, int len){ wcscpy_s(str, len, someWideString.c_str());}C#[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode )]private static extern void GetMyString(StringBuffer str, int len);public string GetMyStringMarshal(){ StringBuffer buffer = new StringBuffer(255); GetMyString(buffer, buffer.Capacity); return buffer.ToString();} 这篇关于返回一个std :: wstring从C ++到C#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-23 00:17