std::ifstream 当 运行 在 IDE 之外时出现问题

std::ifstream issue when running outside of IDE

我有一个函数在 运行 在 Visual Studio 调试环境(具有调试和发布配置)中时工作正常,但是当 运行 应用程序 在 IDE 之外 ,就像最终用户会做的那样,程序崩溃了。 Debug 和 Release 版本都会发生这种情况。

我知道调试和发布配置之间可能存在的差异(优化、调试符号等),并且至少在一定程度上了解运行 Visual Studio 中的应用程序之间的差异] 与外部(调试堆、工作目录等)。我已经查看了其中的一些内容,none 似乎解决了这个问题。这实际上是我第一次发布到 SO;通常我可以从现有帖子中找到解决方案,所以我真的很困惑!

我能够附加一个调试器,奇怪的是我收到两条不同的错误消息,这取决于我是 运行 Windows 7 还是 Windows 8.1 上的应用程序。对于 Windows 7,错误只是一个访问冲突,它在 return 语句上中断。对于 Windows 8.1,这是一个堆损坏错误,它在 std::ifstream 的构造中中断。在这两种情况下,所有局部变量都被正确填充,所以我知道这不是函数无法找到文件或无法将其内容读入缓冲区 data 的问题。

同样有趣的是,这个问题在 Windows 8.1 上只有大约 20% 的时间发生,而在 Windows 7 上有 100% 的时间发生,尽管这可能与截然不同的这些OS的硬件是运行。

我不确定它有什么不同,但项目类型是 Win32 桌面应用程序并且它初始化 DirectX 11。您会注意到文件类型被解释为二进制,这是正确的,因为这个函数主要是加载编译的着色器。

这里是静态成员函数LoadFile:

HRESULT MyClass::LoadFile(_In_ const CHAR* filename, _Out_ BYTE** data, _Out_ SIZE_T* length)
{
    CHAR pwd[MAX_PATH];
    GetCurrentDirectoryA(MAX_PATH, pwd);
    std::string fullFilePath = std::string(pwd) + "\" + filename;

    std::ifstream file(fullFilePath, std::ifstream::binary);

    if (file)
    {
        file.seekg(0, file.end);
        *length = (SIZE_T)file.tellg();
        file.seekg(0, file.beg);

        *data = new BYTE[*length];

        file.read(reinterpret_cast<CHAR*>(*data), *length);

        if (file) return S_OK;
    }

    return E_FAIL;
}

更新:

有趣的是,如果我在堆上分配 std::ifstream file 并且不删除它,问题就会消失。在我的案例中,一定有一些关于 ifstream 的破坏导致了问题。

  • 您没有检查 GetCurrentDirectoryA 的 return 值 - 也许您当前的目录名称太长或其他原因?

  • 如果您已经在使用 Win32(不可移植!),请使用 GetFileSize 获取文件大小而不是执行搜索

  • 更好的是,使用 boost 编写可移植代码

  • 在编译器选项中打开所有警告

  • 启用 ios 例外

好吧,我放弃了尝试使用 ifstream。显然我不是唯一遇到此问题的人...只需搜索 "ifstream destructor crash".

由于此应用程序基于 DirectX,并且只会在 Windows 上 运行,因此我选择了 Windows API 路线并且一切正常。

工作代码,以防有人关心:

HRESULT MyClass::LoadFile(_In_ const CHAR* filename, _Out_ BYTE** data, _Out_ SIZE_T* length)
{
    CHAR pwd[MAX_PATH];
    GetCurrentDirectoryA(MAX_PATH, pwd);
    string fullFilePath = string(pwd) + "\" + filename;

    WIN32_FIND_DATAA fileData;
    HANDLE file = FindFirstFileA(fullFilePath.c_str(), &fileData);

    if (file == INVALID_HANDLE_VALUE) return E_FAIL;

    file = CreateFileA(fullFilePath.c_str(),
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (file == INVALID_HANDLE_VALUE) return E_FAIL;

    *length = (SIZE_T)fileData.nFileSizeLow;
    *data = new BYTE[*length];
    DWORD bytesRead;

    if (ReadFile(file, *data, *length, &bytesRead, NULL) == FALSE || bytesRead != *length)
    {
        delete[] *data;
        *length = 0;
        CloseHandle(file);
        return E_FAIL;
    }

    CloseHandle(file);
    return S_OK;
}