在析构函数中调用CloseHandle会导致运行时内存错误,即如何正确关闭结构/类中的文件句柄
calling CloseHandle in destructor causes runtime memory error, how to properly close a file handle in a struct/class?
错误消息为:"0x7c810eac"的指令引用了"0x00000000"的内存。无法"写入"内存。
如果我移除析构函数,一切都会好起来的。但我不明白这里发生了什么。无论我读到哪里,我都应该关闭句柄,但这里的代码不允许我。(是的,我知道我可以手动完成……但这是客户端代码中不必要的一行,我认为应该由对象处理。)
#include <windows.h>
#include <iostream>
#include <string>
struct fileHandle {
HANDLE hFile;
fileHandle(std::string path) {
hFile = CreateFile(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_ARCHIVE, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("error: INVALID_HANDLE_VALUE");
}
}
~fileHandle() {
CloseHandle(hFile);
}
void save(std::string data) {
if (!WriteFile(hFile, data.c_str(), data.size(), NULL, NULL)) {
printf("WriteFile failed: ", GetLastError());
}
}
};
int main() {
fileHandle my_handle("test_file.txt");
my_handle.save("some text");
}
更新:当文件不存在时会发生这种情况。当文件确实存在程序打印错误时,但这是有意的。我在这里只要求共同覆盖这个文件创建时的情况(我知道如何重写处理创建以覆盖现有文件。)
编译器:http://sourceforge.net/projects/mingwbuilds/files/host-windows/releases/4.7.2/32-bit/threads-posix/sjlj/x32-4.7.2-release-posix-sjlj-rev7.7z
更新2:我并没有提到这个代码可以工作并写入文件。内存错误在最后触发。
请使用-Wall编译所有代码。节省了大量时间
在您的代码printf
中,它具有无效的格式字符串。printf
的正确解决方案是(注意%lu):
void save(std::string data) {
if (!WriteFile(hFile, data.c_str(), data.size(), NULL, NULL)) {
printf("WriteFile failed: %lu", GetLastError());
}
}
如果您使用-Wall
进行编译,那么您的代码会发出警告:
filehandle.cpp: In member function 'void fileHandle::save(std::string)':
filehandle.cpp:18:50: warning: too many arguments for format [-Wformat-extra-args]
此外,您应该将错误打印到stderr
,因为它没有缓冲区。printf
使用缓冲区,这就是为什么您没有得到任何输出。在错误后添加n
也是一个好主意。
同时阅读其他答案以改进代码。使用三条规则。
看完评论后,我意识到这确实不是segfault的原因。(也可以看看Ron Burk的解决方案,看看哪里出了问题。)
根据Windows API文档,只有当lpOverlapped parameter
不是NULL
时,lpNumberOfBytesWritten
参数才可以是NULL
。
因此,您必须提供一个指向DWORD的指针,WriteFile可以在其中存储它实际读取的字节数。最后的保存是:
void save(std::string data) {
DWORD writtenBytes;
if (!WriteFile(hFile, data.c_str(), data.size(), &writtenBytes, NULL)) {
printf("WriteFile failed: %lu", GetLastError());
}
}
如果文件存在,则不会弹出错误,因为将INVALID_HANDLE_VALUE传递给WriteFile似乎会使WriteFile的返回时间早于它使用指针的时间。
WriteFile()的参数错误。倒数第二个参数不能为NULL。将其更改为您已设置为0的DWORD地址,一切都将正常工作。当它试图写回写入的字节数时,它在WriteFile()中的内核中正在消亡。
如果应用程序在调试器下运行,则如果函数接收到无效的句柄值或伪句柄值,则函数将引发异常。
因此,当对CreateFile的调用失败时,对CloseHandle的后续调用将引发SEH异常。
解决方案是,如果对CreateFile的调用成功,则代码必须仅调用CloseHandle。
正如其他人所指出的,您对WriteFile的使用是错误的。这里我不再赘述细节。
~fileHandle() {
if(hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
}
您需要检查文件是否正确打开。
if (hFile != INVALID_HANDLE_VALUE) CloseFile(hFile);
您没有遵循三原则,因此hFile
的所有权在fileHandle
的副本之间共享,因为编译器生成的复制构造函数和复制赋值运算符很浅。
例如:
fileHandle my_handle("test_file.txt");
fileHandle my_second_handle = my_handle;
其中哪一个应该真正关闭销毁手柄?第一个?当第二个超出作用域并调用析构函数时会发生什么?
在这种情况下,我认为您应该禁止复制或分配,或者明确所有权。
编辑:在你的片段中,问题可能是罗恩指出的,但这仍然是一个重要的问题。
不过我不会这么对待它我认为如果文件无法打开,fileHandle
就不应该存在。拥有一个无效对象有什么意义?我只需要在构造函数中抛出一个异常来禁止无效对象,而不是在析构函数中进行检查。
fileHandle(std::string path) {
hFile = CreateFile(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_ARCHIVE, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
throw std::exception("file not found");
}
}
为了安全起见,请使用
~fileHandle()
{
if (hfile != INVALID_HANDLE_VALUE)
{
CloseHandle(hfile);
hfile = INVALID_HANDLE_VALUE;
}
}
- 在 c++ 中拥有一组结构的正确方法是什么?
- 将信息输入到下面显示的结构向量中的正确语法/格式是什么
- i2d_X509_REQ_INFO无法正确转换req_info结构
- 如何修复函数中的 fstream 文件输入以将正确的信息存储在结构数组中?
- 我想直接在结构中插入,但没有一种方法可以正确避免填充问题
- C++正确的结构初始化
- 如何正确转发声明结构"using XXX"?
- 如何在自定义 LLVM 传递之间正确传递数据结构
- ctypes - 结构包含垃圾/不正确的数据
- 如何正确使用结构的共享指针成员?
- 我将如何获得正确的公式结构以使我的程序C++中的侧 c?
- 如何通过解析缓冲区并将数据放入正确的结构来处理传入的数据包连接?
- C++中的结构是否按顺序分配内存?每次都以某种方式获得指针比较的正确答案
- 结构C++ – 从文件中读取不正确
- CMake 项目结构:如何正确地将库合并在一起并将它们包含在多个可执行文件中
- 在 C# 中序列化这些值以在C++中作为已知结构正确读取时遇到问题
- 存储将经常更新的大数组的正确结构是什么
- 错误:隐式删除,因为默认定义格式不正确(结构向量)
- c++ std::priority_queue是dijkstra队列的正确结构吗?
- C++项目和库的正确结构