微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C ++ dll返回的字符串在C#调用程序中损坏,为什么?

如何解决C ++ dll返回的字符串在C#调用程序中损坏,为什么?

| 我有一个调用C ++ DLL的C#应用​​程序。 在C#中,我的代码如下:
[DllImport(@\"111.dll\",CharSet = CharSet.Unicode)]
public extern static String Func1(String arg);
......
String arg = \"test text\";
String retstring = Func1(arg);
在CPP中,我的功能定义如下:
extern \"C\"
{
__declspec(dllexport) LPWSTR Func1(LPWSTR arg)
{
          ....
          LPWSTR ret1 = L\"1?2?3?4?5\";
          LPWSTR ret2 = SomeActualFunction(arg);
          retturn ret1; // return ret2;
     }
}
如果我在C ++的Func1()中返回ret1,则一切正常。在VS2008的内存窗口中,我可以看到正确的Unicode二进制文件。在C ++中,ret1的二进制文件是 \“ 31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00 \” ,在C#中retstring的二进制文件是 \“ 28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00 \” 。我认为C#二进制文件 \“ 28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 \” 是System.String类型的标头。 而且,如果我在CPP代码返回之前添加以下行,那么我也可以在C#代码中获得正确的重新字符串:
ret2 = L\"1?2?3?4?5\";
但是,当我在C ++ DLL中返回ret2时,C#中返回的字符串ret似乎已损坏。根据我的检查,C ++ DLL中的二进制文件是正确的Unicode。但是C#代码中retstring的二进制文件是 “ 28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 dd dd dd dd dd dd dd dd dd dd dd ....”。 我只能注意到ret2比ret1长-ret2有数百个WCHAR。 有任何想法吗?提前谢谢。     

解决方法

        为此,我将始终使用“ 3”,因为它使内存分配/取消分配的职责透明化。 C ++
#include <comutil.h>
BSTR GetSomeText()
{
    return ::SysAllocString(L\"Greetings from the native world!\");
}
C#
[DllImport(@\"test.dll\",CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
您没有说出如何分配字符串,但是一旦使用了动态分配的字符串,就需要解决该问题。 ѭ3的伟大之处在于它使用共享的COM分配器,该分配器使C#编组可以使用与分配该字符串的C ++代码相同的分配器来分配该字符串。     ,        
[DllImport(@\"111.dll\",CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPStr)]
public extern static String Func1(String arg);
要么
[DllImport(@\"111.dll\",CharSet = CharSet.Unicode)]
public extern static IntPtr Func1(String arg);
// In your calling code
string result = Marshal.PtrToStringUni(Func1(\"somestring\"));
    ,        您的函数是__cdecl还是__stdcall调用约定? IIRC,C#的默认值为
__stdcall
,但C ++的默认值为
__cdecl
。尝试加
CallingConvention=CallingConvention.Cdecl
。 另一种可能性:既然您说返回静态字符串有效,那么
SomeActualFunction
返回的指针是否仍然有效?如果它指向该函数中的本地缓冲区,则从该函数返回后将不再有效。     ,        因此,如果将指针返回到硬编码的字符串L \“ 1?2?3?4?5 \”,则一切正常。但是,如果返回SomeActualFunction,答案是错误的。也许C ++代码不正确? SomeActualFunction如何工作?例如,它可能从此时已销毁的某个堆栈分配的对象返回LPWSTR。尝试首先使用C ++客户端对此进行测试。     ,        我已经在SkyDrive上上传了示例代码(http://cid-48a119f5ed65483e.office.live.com/self.aspx/.Public/MarshalString.zip)。 它演示了如何将托管字符串传递给非托管代码以及如何对其进行操作。     ,        我想我已经开始工作了:对C ++ DLL的C#调用,将字符串返回给C#。我将分享我认为使之起作用的东西: 在C#中
using System.Runtime.InteropServices;

[DllImport(
    @\"C:\\Users\\Ron\\Documents\\Visual Studio 2013\\ etc. ...(The whole path)... my.dll,CallingConvention = CallingConvention.Cdecl
    )]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string cpp_brand_files(string home_dir,string xml_lic);
然后在C#中调用:
      string a_str = cpp_brand_files(home_dir,xml_license);
在C ++ DLL中:
  #using <mscorlib.dll>

   using namespace System;

   extern \"C\" __declspec(dllexport)
   wchar_t* cpp_brand_files(char* home_dir,char* xml_lic);
在C ++中进一步:
  wchar_t* cpp_brand_files(char* home_dir,char* xml_lic)
  {

      BSTR samp = ::SysAllocString( L\"This is the long,long string.\" );
      return samp;
  }
并且,在配置属性中使用/ cli(公共语言运行时支持)编译C ++ DLL。右键单击C ++项目,然后单击属性->配置属性->常规->公共语言运行时支持。 因此,这就是使它起作用(返回字符串)的基本要素。 我正在使用Visual Studio 2013 RC。专家,请评论多余或缺少的内容。     ,        如果需要封送其他数据类型,这是供您参考的链接 NET 4.0的更新的链接 https://msdn.microsoft.com/zh-CN/library/sak564ww(v=vs.100).aspx     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。