Reputation: 1995
On Windows platform, a file handle is got from calling CreateFile, and then the handle is used to initialize an ofstream object. A minimal example is as below:
#include"stdafx.h"
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
class CSV_writer {
public:
std::ofstream my_ofstream;
private:
HANDLE my_handle = INVALID_HANDLE_VALUE;
int file_descriptor = -1;
FILE * my_file = nullptr; //FILE type is actually a IO buff.
const static unsigned int fl = 256;
public:
explicit CSV_writer(const TCHAR * file_name_) {
//get current directory
TCHAR current_path[MAX_PATH];
GetCurrentDirectory(MAX_PATH, current_path);
TCHAR filename[fl]{ 0 };
_tcscat_s(filename, file_name_);
_tcscat_s(filename, _T(".csv"));
if (current_path[_tcslen(current_path) - 1] != _T('\\') && _tcslen(current_path) < MAX_PATH - 1) {
_tcscat_s(current_path, _T("\\"));
}
else {
throw std::exception("path length exceeding limit.");
}
if (_tcslen(current_path) + _tcslen(filename) + 1 < MAX_PATH) {
_tcscat_s(current_path, filename);
}
else {
//current path exceeds the max path length defined in MAX_PATH
throw std::exception("path length exceeding limit.");
}
this->my_handle = CreateFile(
current_path,
GENERIC_READ | GENERIC_WRITE, //access permit, both read and write
0, //cannot be shared and cannot be opened again until the handle to the file or device is closed
nullptr, //returned handle can not be inherited by child process
CREATE_ALWAYS, //always create a new file, overwrite old one if it exists
FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (my_handle != INVALID_HANDLE_VALUE) {
int file_descriptor = _open_osfhandle((intptr_t)my_handle, _O_TEXT);
if (file_descriptor != -1) {
this->my_file = _fdopen(file_descriptor, "w");
if (this->my_file != nullptr) {
this->my_ofstream = std::ofstream(this->my_file);
}
}
}
}
~CSV_writer() {
// Closes stream, file, file_descriptor, and file_handle.
this->my_ofstream.flush();
this->my_ofstream.close();
this->my_file = nullptr;
this->file_descriptor = -1;
this->my_handle = INVALID_HANDLE_VALUE;
}
};
int main(int argc, char* argv[])
{
CSV_writer csv_writer(L"memory_layout");
csv_writer.my_ofstream << "Type,\t" << "Size,\t" << "Offset,\t" << "Address\n";
return 0;
}
My question is, after calling "my_ofstream.close()" afterwards, will the underlying file handle also released? Or I have to call Windows API CloseHandle() manually after calling close()?
Update: to those who say that there is no constructor of ofstream taking FILE*, actually there is, kind of,
Upvotes: 0
Views: 240
Reputation: 61575
I hope you're already aware that the constructor you are using:
std::ofstream(FILE * fp)
is a non-standard, undocumented Microsoft extension, unguaranteed even by Microsoft.
In that case, Microsoft does not promise you even that:
int fd = ...;
...
FILE * fp = _fdopen(fd, "w");
...
std::osftream ofs(fp);
...
ofs.close();
will do fclose(fp)
- never mind _close(fd)
.
If however you take it as given that ofs.close()
does fclose(fp)
- and evidently you do - then Microsoft
does promise you that it will also _close(fd)
. From the documentation
Remarks
...
File descriptors passed into _fdopen are owned by the returned FILE * stream. If _fdopen is successful, do not call _close on the file descriptor. Calling fclose on the returned FILE * also closes the file descriptor.
(My emphasis.)
Upvotes: 2