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

C++ dll returned string is corrupted in the C# caller, why?

本文关键字:损坏 是什么 程序 调用 返回 字符串 C++dll      更新时间:2023-10-16

我有一个C#应用程序,它调用C++DLL。

在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的二进制文件是

,而在C#中,retstring的二进制文件是

我认为C#二进制

"28 67 a3 f7 fe 07 00 00 0a 00 00 09 00 00 00"

是System.String类型的标头。

更重要的是,如果我在CPP代码中返回之前添加以下行,我也可以在C#代码中获得正确的retstring:

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 0 a 00 00 09 00 00 00 dd dd dd dd dddd dd dd…"。

我只能注意到ret2比ret1长——ret2有数百个WCHAR。

有什么想法吗?提前Thx。

我总是使用BSTR,因为它使内存分配/释放的责任透明。

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();

您没有说明如何分配字符串,但一旦使用了动态分配的字符串,就需要解决这个问题。BSTR的伟大之处在于它使用了共享的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:UsersRonDocumentsVisual 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/en-us/library/sak564ww(v=vs.100).aspx