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

时间:2014-05-05 11:18:10

标签: c# c++ dll dynamic-memory-allocation heap-corruption

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

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

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未使用的RAM。

更新:我注意到,他们以前工作的一些第三方程序崩溃了#34;异常代码:c0000005"以及其他一些奇怪的事情发生在Windows 7中(主办)。所以我在另一个Windows安装中测试了代码,一切看起来都像他们应该的那样工作。所以它可能与Windows 7相关。现在我该如何解决这个问题呢? " sfc / scannow"未能找到任何问题。

3 个答案:

答案 0 :(得分:21)

如果您的所有代码确实如上所示,那么我没有看到问题。但是,当我遇到这个问题时,有时它因为malloc / new /的任何东西检测到堆损坏,通常这个损坏已经发生在程序中,但是崩溃一直延迟到下一次调用new / malloc。

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

我参加聚会很晚,但是我去了。

我从自己的程序中收到了此错误代码,这使我进入了这篇文章。我在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;