.net 4.0 -如何防止在64位Windows上从c++到c#返回字符串时出现AccessViolationExce

.net 4.0 - How do I prevent AccessViolationException when returning a string from C++ to C# on 64-bit Windows?

本文关键字:返回 字符串 AccessViolationExce c++ 上从 何防止 Windows 64位 net      更新时间:2023-10-16

我正在使用第三方专有DLL,我无法获得其源代码。不过,我可以使用SWIG 1.3.39自动生成的包装器代码。包装器代码由编译成DLL的c++文件(使用一些描述DLL的头文件)和对c++包装器DLL进行PInvoke调用的c#项目组成。

根据我对供应商文档的解释,我已经根据目标平台将解决方案中的所有内容编译为x86或x64。供应商提供了32位和64位版本的专有DLL,我已经确保在给定的构建中使用了正确的DLL。我的机器是32位的。在我的机器上测试我的应用程序的x86版本,无论是发布版本还是调试版本,似乎都工作得很好。然而,在64位上,应用程序在Debug模式下工作,但在System模式下失败。

我读了一篇很好的博客文章,它似乎很好地描述了调试与发布的问题,以及引起这篇博客文章的这个问题和答案。但是,我不确定如何排除这种情况下的问题。

AccessViolationException似乎在第一次从c++包装器返回(或试图返回)任何实际长度的字符串时发生。下面是有问题的c#代码:

// In one file of the C# wrapper:
public string GetKey()
{
    // swigCPtr is a HandleRef to an object already created
    string ret = csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr);
    return ret;
}
// In the csWrapperPINVOKE class in another file in the C# wrapper:
[DllImport("csWrapper.dll", EntryPoint="CSharp_mdMUHybrid_GetKey")]
public static extern StringBuilder mdMUHybrid_GetKey(HandleRef jarg1);

和c++包装器中麻烦的c++代码:

SWIGEXPORT char * SWIGSTDCALL CSharp_mdMUHybrid_GetKey(void * jarg1) {
  char * jresult ;
  mdMUHybrid *arg1 = (mdMUHybrid *) 0 ;
  char *result = 0 ;
  arg1 = (mdMUHybrid *)jarg1; 
  result = (char *)(arg1)->GetKey();
  jresult = SWIG_csharp_string_callback((const char *)result); 
  return jresult;
}

SWIGEXPORT已经定义为__declspec(dllexport)。在调试中,我发现SWIG_csharp_string_callback,定义为:

/* Callback for returning strings to C# without leaking memory */
typedef char * (SWIGSTDCALL* SWIG_CSharpStringHelperCallback)(const char *);
static SWIG_CSharpStringHelperCallback SWIG_csharp_string_callback = NULL;

被设置为委托(在c#包装器中):

static string CreateString(string cString) {
  return cString;
}

我试过用这段代码来使用Marshal.PtrToStringAut这样的结构,但无济于事。如何排除故障和/或解决此问题?

正确的方法是让托管代码分配非托管代码将写入的缓冲区(字符串数据)。假设由于某种原因这是不切实际的,您需要做的是以一种可以从托管代码中释放的方式分配字符串数据。

通常的方法是使用LocalAlloc分配内存,然后可以使用Marshal.FreeHGlobal从托管代码中释放内存。这样你就不再需要SWIG_csharp_string_callbackCreateString了。

c++代码:

SWIGEXPORT HLOCAL SWIGSTDCALL CSharp_mdMUHybrid_GetKey(mdMUHybrid* jarg1)
{
    char const* const str = jarg1->GetKey();
    std::size_t const len = std::strlen(str);
    HLOCAL const result = ::LocalAlloc(LPTR, len + 1u);
    if (result)
        std::strncpy(static_cast<char*>(result), str, len);
    return result;
}

c#代码:

// In one file of the C# wrapper:
public string GetKey()
{
    return csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr);
}
// ...
public static class csWrapperPINVOKE
{
    // ...
    [DllImport("csWrapper.dll")]
    private static extern IntPtr CSharp_mdMUHybrid_GetKey(HandleRef jarg1);
    public static string mdMUHybrid_GetKey(HandleRef jarg1)
    {
        var ptr = CSharp_mdMUHybrid_GetKey(jarg1);
        try
        {
            return Marshal.PtrToStringAnsi(ptr);
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }
    }
}
作为题外话,你展示的那一小段c++代码是一个丑陋的带有类的C语言遗迹;如果这是其余代码库的代表,那么,哇……: -/