检测到严重错误 c0000374 - C++ dll 将指针断开分配给 C# 的内存返回

本文关键字:分配 断开 返回 内存 指针 严重错误 c0000374 dll C++ 检测 | 更新日期: 2023-09-27 18:01:38

我有一个 c++ dll,它为我的主要 c# 应用程序提供一些功能。在这里,我尝试读取一个文件,将其加载到内存中,然后返回一些信息,例如指向加载数据的指针和到 c# 的内存块计数。Dll 成功地将文件读取到内存,但在返回到主应用程序时,程序由于堆损坏而崩溃(检测到严重错误 c0000374(。

代码非常简单明了,我以前做过一些类似的事情,没有问题,但是我无法弄清楚是什么导致了这里的问题,我尝试使用"new,malloc和GlobalAlloc"分配内存,但都没有帮助。代码如下:

C++ MyDll:

typedef unsigned long         U32;
extern "C" __declspec(dllexport) int ReadFile(LPSTR Path, U32** DataPtr, U32* Count)
{
   FILE *fp;
   U32 *Data;
   CString tempStr(Path);
   long fSize;
   if(!(fp = fopen(tempStr, "rb"))) {
    return 0;
   }
   // Obtain File Size;
   fseek(fp, 0, SEEK_END);
   fSize =  ftell(fp);
   rewind(fp);
   Data = (U32 *)GlobalAlloc(0, fSize);
   if(Data == NULL) {
            fclose(fp);
            return -1;
    }
    // Copy file into the buffer.
        if(!(*Count = fread(Data, sizeof(U32), fSize / sizeof(U32), fp))) {
           fclose(fp);
           free(Data);
           return -2;
        }
   *DataPtr = (U32 *)Data;
       return 1;
}

C# 应用程序:

        [DllImport(@"MyDll.dll", CallingConvention= CallingConvention.Cdecl)]
    private static extern int ReadFile([MarshalAs(UnmanagedType.LPStr)]string Path, out IntPtr dataPtr, out uint Count);
private void readDump(string Path)
{
    uint count = 0;
    IntPtr Data = new IntPtr();
   try{
       if(ReadFile(Path, out Data, out count) == 1) //The Program crashes just right after this statement
       {
           //Do Something ...
       }
    }
    catch() {}
}

程序在调试和发布模式下都崩溃。除非我在加载文件后将程序暂停在调试模式下并在"Visual Studio 的即时窗口"中调用一些内存块。要加载的文件的大小约为64MB,PC上有超过2GB未使用的内存。

更新:我注意到,他们之前使用的一些第三方程序因"异常代码:c0000005"而崩溃,并且在Windows 7(主机(中发生了其他一些奇怪的事情。 所以我在另一个 Windows 安装中测试了代码,一切似乎都正常工作。所以它可能与Windows 7有关。现在我该如何解决问题?"SFC/scannow"未能找到任何问题。

检测到严重错误 c0000374 - C++ dll 将指针断开分配给 C# 的内存返回

如果您的所有代码确实是上面显示的,那么我看不到问题。但是,当我遇到此问题时,有时是因为malloc/new/what检测到堆损坏,通常这种损坏以前已经在程序中发生过,但是崩溃已延迟到下一次调用new/malloc。

如果您在执行上述操作并崩溃之前读取其他文件,或者分配或释放其他缓冲区,我会在那里寻找问题。也许在你写到缓冲区的任何地方抛出一堆断言,并检查边界和你为溢出编写的内容。抱歉,这不是一个具体的答案,我没有足够的代表将此建议作为评论。

我参加聚会迟到了,但我来了。

从我自己的程序中收到了此错误代码,这使我来到了这篇文章。我在 Windows 7 中设置了一个超出范围的数组位置,导致下一个分配使程序崩溃。我通过使用 MinGW 的 gcc 使用 -g 标志进行编译,然后使用 gdb 运行程序来发现错误。在这里的某个地方,您读取或写入了一个无效的位置,下一个分配会拾取堆损坏。我通过边界检查我的迭代器解决了我的问题,但这似乎不是这里的问题。

C程序的主要问题:

  • 用于查找文件大小的方法没问题,但是当您为 Data 数组分配内存时,您将强制转换为 32 位整数数组,这有问题。强制转换为此要求文件的大小(以字节为单位(是 4 的倍数。如果你有一个包含FILE_SIZE % 4 == 1的文件,那么有 3 个字节不是你分配的数据的一部分,但在你查看 u32 数组的最后一个元素时被访问。
  • 如何实现 fread,您应该防止写入这个超出范围的位置,但是当文件大小不是 4 的倍数时,您将错过最后 1 到 3 个字符,因为整数除法会截断余数。
  • 应在退出范围之前关闭文件,包括在读取文件中的所有内容之后。

解决 方案:

  • 对此的解决方案可能是将数组大小四舍五入到最接近的 4 的倍数。然后,您可以保证不会访问从 [0] 到 [fSize - 1] 的范围之外的位置。
  • 如果尝试复制超过文件的最后一个字节导致致命错误,除了使用四舍五入到最接近的 4 的倍数的字符数组,然后逐字节读取之外,我没有像样的解决方案。阅读完所有内容后,您可以转换为 u32,因为 C 允许强制转换。

该版本的软件在强制转换时可能做了一些额外的工作,以便写入超出范围的位置,或者 C# 进行了一些额外的分配调用来执行相同的操作(我不熟悉 C# 如何编译以及它可能施加哪些指令更改(。

用于查找 4 的下一个倍数的一些代码:

size_t diff, rfSize = fSize; /* size_t is preferable for array sizes and indexes, 
                              * but matching to fSize's data type will work and
                              * ensures no truncation occurs. */
/* Only if this is not already a multiple of 4 */
if (diff = fSize % 4)
  /* Mod gives the remainder by division by 4, which is also the difference between 
   * fSize and the next multiple of 4. */
  rfSize= fSize + diff;

您正在分配输出数据 2 次。一次在 C# 中作为新IntPtr,然后在C++中作为GlobalAlloc,然后返回 GlobalAlloc 返回的指针。 因此,新intPtr返回的指针已丢失。

我也晚了,这不是一个非常有用的答案,但我有一个类似的问题,通过清理/重建我的代码来解决。 确实有一些陈旧的.obj文件导致了这个问题。