John Z. Li
John Z. Li

Reputation: 1995

Does the close method of ofstream also close the underlying handle

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,see the screen shot below

Upvotes: 0

Views: 240

Answers (1)

Mike Kinghan
Mike Kinghan

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

Related Questions