在 IDE 外部运行时的 std::ifstream 问题

std::ifstream issue when running outside of IDE

提问人:urbanite 提问时间:7/15/2015 最后编辑:urbanite 更新时间:7/16/2015 访问量:156

问:

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

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

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

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

我不确定这有什么区别,但项目类型是 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 文件并且不删除它,问题就会消失。在我的情况下,ifstream 的破坏一定导致了问题。

C 调试 visual-studio-2012 visual-c ++ iostream

评论

0赞 7/15/2015
删除古老的函数签名。您可以改用来摆脱新的/删除和 nullptr 输出参数问题(顺便说一句:我猜 Out 是无效的)std::vector<unit8_t> LoadFile(const char* filename)
0赞 urbanite 7/15/2015
谢谢@DieterLücking。尚未完成此操作的唯一原因是,尽管函数的名称如此,但它实际上仅用于将编译的着色器加载到具有两个 Out 参数类型成员的结构中,因此它只是使它目前更方便一些。尽管如此,该函数本质上是通用的,可能应该按照您的建议进行更改或重命名为 LoadShader。:)

答:

0赞 Andre 7/15/2015 #1
  • 您没有检查 GetCurrentDirectoryA 的返回值 - 也许您当前的目录名称太长了?

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

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

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

  • 启用 ios 例外

评论

0赞 urbanite 7/15/2015
我应该检查 GetCurrentDirectoryA 的返回(我将进行更改),但这仍然不能解释为什么该函数在 VS 调试环境中运行时工作,而不是在外部运行时工作。我也会接受您的建议,使用 GetFileSize(),因为这是一个仅限 Windows 的应用程序。至于 Boost,我实际上正在编写一个基于 DirectX 的框架(库),目前唯一的依赖项是 DirectXTK......我想保持这种状态。:)
0赞 urbanite 7/15/2015
顺便说一句,实现了ios异常处理,没有抛出异常。它仍然像最初描述的那样失败。不过,感谢您的建议。
0赞 Andre 7/15/2015
很公平。假设切换到 GetFileSize 没有帮助,请尝试在任何地方添加日志记录,特别是检查当前目录、文件名等的值。另外,参数的来源(例如文件名)呢?也许那里有鱼腥味?
0赞 urbanite 7/16/2015
GetFileSize 没有帮助;它产生了与 Seek'ing 相同的值。最初,我确实在各处散布了 MessageBox() 调用,以试图在调试环境之外运行时获得洞察力,并且没有什么不合适的。即使在调试器崩溃后附加调试器,我也看不出任何问题。正如我之前提到的,在 Windows 7 上,断点位于函数的 return 语句上,并且我收到访问冲突。就好像当本地堆栈对象在函数返回时被破坏时出了点问题,但正如你所看到的,它们都是 std 库类。
0赞 urbanite 7/16/2015
我的代码一定还有其他问题,因为当将相同的函数导出到一个单独的测试项目中时,没有其他事情发生,即使在 IDE 之外也能完美运行。我能够加载我的主项目尝试加载的相同文件,并且没有问题。所以调用这个函数的代码一定有问题,而不是函数本身,对吧?此函数由类的另一个静态成员函数调用。 我正在调查...
0赞 urbanite 7/16/2015 #2

好吧,我放弃了尝试使用 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;
}