【问题标题】:std::ifstream issue when running outside of IDE在 IDE 之外运行时出现 std::ifstream 问题
【发布时间】:2015-10-03 09:27:20
【问题描述】:

我有一个在 Visual Studio 调试环境中运行时可以正常工作的函数(同时使用 Debug 和 Release 配置),但在 IDE 的外部运行应用程序时,只是作为结束-用户会这样做,程序崩溃。 Debug 和 Release 版本都会发生这种情况。

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

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

另外有趣的是,在 Windows 8.1 和 100% 的情况下,该问题仅在大约 20% 的情况下发生在 Windows 7 上,尽管这可能与这些操作系统运行的硬件完全不同有关。

我不确定这有什么区别,但项目类型是 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 的破坏肯定是有问题的。

【问题讨论】:

  • 删除古老的函数签名。您可以改用 std::vector&lt;unit8_t&gt; LoadFile(const char* filename) 来消除新/删除和 nullptr 输出参数问题(顺便说一句:我猜 Out 是无效的)
  • 感谢@DieterLücking。尚未完成的唯一原因是,尽管有函数名称,但它实际上仅用于将编译后的着色器加载到具有两个 Out 类型成员的结构中参数,所以它只是让它现在更方便一点。尽管如此,该功能本质上是通用的,它可能应该按照您的建议进行更改或重命名为 LoadShader。 :)

标签: c++ debugging visual-studio-2012 visual-c++ iostream


【解决方案1】:
  • 您没有检查 GetCurrentDirectoryA 的返回值 - 也许您当前的目录名称太长或什么?

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

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

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

  • 启用 ios 异常

【讨论】:

  • 我应该检查 GetCurrentDirectoryA 的返回(我会进行更改),但这仍然不能解释为什么该函数在 VS 调试环境中运行而不是在外部运行时起作用.在使用 GetFileSize() 时,我也会听取您的建议,因为这是一个仅限 Windows 的应用程序。至于 Boost,我实际上正在编写一个基于 DirectX 的框架(库),目前唯一的依赖是 DirectXTK ......我想保持这种状态。 :)
  • 顺便说一句,实现了ios异常处理,没有抛出异常。它仍然像最初描述的那样失败。不过,感谢您的建议。
  • 很公平。假设切换到 GetFileSize 没有帮助,尝试在所有地方添加日志记录,特别是检查当前目录、文件名等的值。另外,参数的来源,比如文件名?也许那里有什么可疑的东西?
  • GetFileSize 没有帮助;它产生与寻找相同的价值。最初,我确实在各处散布 MessageBox() 调用,以尝试在调试环境之外运行时获得洞察力,并且没有什么不合适的。即使在调试器崩溃后附加调试器,我也没有发现任何问题。正如我之前提到的,在 Windows 7 上,断点位于函数的 return 语句上,我遇到了访问冲突。就好像在函数返回时破坏本地堆栈对象时出现问题,但正如您所见,它们都是标准库类。
  • 我的代码肯定有其他问题,因为同样的功能,当导出到一个单独的测试项目而没有其他事情发生时,即使在 IDE 之外也能完美运行。我能够加载我的主项目尝试加载的相同文件并且没有问题。所以调用这个函数的代码肯定有问题,而不是函数本身,对吧?该函数由类的另一个静态成员函数调用。我正在调查...
【解决方案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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-19
    • 2010-11-08
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多