Multiple opening of temporary file only in same process - c++

I have a question about FILE_ATTRIBUTE_TEMPORARY marked files.
First of all, here is what I want to do:
I have a DLL, that takes a Filename, and opens that file internally and reads from it. I do not know how this file is handled inside.
The file I want to give to that DLL will be created by my process. It must be a temporary file and its data must be held only in RAM and must not be accessed by other processes. So I use the Win32 function CreateFile() with the FILE_ATTRIBUTE_TEMPORARY and the FILE_FLAG_DELETE_ON_CLOSE. This so far works, fine.
I have a tes code where I test if the file can be accessed a second time, while still opened. Here it is:
HANDLE WINHandle = CreateFile("TempFileWIN.txt", (GENERIC_WRITE | GENERIC_READ) ,(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 0, CREATE_ALWAYS, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE), 0);
ifstream ifs("TempFileWIN.txt", (ios::in | ios::trunc));
if(ifs.is_open())
{
cout << "Success!" << endl;
}
else if(ifs.fail())
{
cout << "Failed!" << endl;
}
I am using the fstream to test if the file could be opened with a stream. That code up there doesn't work. The output is "Failed!".
I know, that the file could be opened with the CreateFile a second time. I checked that out. But want to know if it is possible to open the file by an external DLL that works with (e.g.) a fstream.
I hope you can help me with this matter.
Best regards.
Edit:
Maybe a better question is how I can lock a file to my process and ensure, that it can never be accessed by an other process (even if my process is killed). The file must be openable with C++ fstream object.

If I were you, I would keep the handle of the open file, and pass it to the DLL code, and not use the filename, since you're likely to run into access restrictions at some point if you try to access a temporary, delete-on-close file using 'normal' file access.
It is possible to use a Windows handle in a fstream object as described in this answer: https://stackoverflow.com/a/476014/393701

Related

c++ filestream problems when opening file in read write mode

Consider the following code snippet:
const char * filePath = "C:/blah.mtt";
fstream fs(filePath, ios::in | ios::out | ios::binary);
if (fs.fail())
std::cout << "Failed to open the file!\n";
the fs.fail() check succeeds always. Does it mean that I can't open a file in both read write mode at the same time?
Creating an empty file first and then running the above code, fs.fail() is false always. What is the rational for such a behavior by the fstream class?
Note: I do have requisite permissions for creating the file. I am trying this on windows 10 using VS2015
Does it mean that I can't open a file in both read write mode at the same time?
No, you can do this, but the question is whether you can create a file by doing so.
Generally you'll need to add the trunc flag (ironically one of the options for how to handle an existing file), or remove the in flag (see here).
Yes, this is a bit of a pain, but it comes from how the original POSIX APIs work. Blame them!
Creating an empty file first and then running the above code, fs.fail() is false always. What is the rational for such a behavior by the fstream class?
You can always open a file that exists (well, subject to permissions). That behaviour makes sense.
the fs.fail() check succeeds always. Does it mean that I can't open a file in both read write mode at the same time?
Refer to #Lightness Races in Orbit's answer for a better explanation.
Creating an empty file first and then running the above code, fs.fail() is false always. What is the rational for such a behavior by the fstream class?
If you look at the constructor definition of fstream you can see that mode defines the way you open it. It has other options like app to append to an existing file. If you open up a file using the following code:
fstream fs(filePath, ios::in | ios::out | ios::binary);
You are saying create a new file if it doesn't exist. Which fails if you pre-created it. You should add the app, ate or truncflag if you want it to open successfully. This depends on what exactly you want to do. However, do note that in between the steps of creating and then opening it doesn't guarantee that the file is still there. You should try to do it in one swoop and let exception handling do its work, since you can never go around the errors anyway.

Portable temporary files with C++ (C++ standard libraries or Boost)?

Is there a portable way of creating temporary files with C++ that get automatically deleted when the program terminates (regardless of whether it crashes, gets killed, or just reaches return 0; in main().).
On Unix systems, I can open a file, delete it and then keep the still existing handle. This works with FILE *, std::fstream etc.
On Windows, this appears not to work. The only way I found is using CreateFile with the FILE_FLAG_DELETE_ON_CLOSE flag.
Is there something smarter that (1) works both on Linux and Windows, (2) has the "the file is removed on when the program terminates" behaviour as on Linux. I would be fine with #ifdef code as long as the file type that I work with is the same on both systems (e.g. std::fstream or FILE *).
I know about this solution, but this appears only to work on graceful exits and would require me to either set up central handlers and manage all temporarily opened files.
Edit: Rephrased the question to "how can I get files on Windows that are automatically removed as removed-but-still-open files in Linux.
http://www.cplusplus.com/reference/cstdio/tmpfile/
Creates a temporary binary file, open for update ("wb+" mode, see
fopen for details) with a filename guaranteed to be different from any
other existing file.
The temporary file created is automatically deleted when the stream is
closed (fclose) or when the program terminates normally. If the
program terminates abnormally, whether the file is deleted depends on
the specific system and library implementation.
For abnormal termination, handle std::terminate by using: http://en.cppreference.com/w/cpp/error/set_terminate
Clean up the file with std::remove and then re-throw. I haven't tested this abornmal termination stuff yet but it should work. The temporary file creation works for sure. I've used it before.
It can be wrapped in a streambuf for usage with streams.
Also as a partial solution you can write a class, that deletes the file on destruction. On windows you can use MoveFileExW(filepathFull, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) if that fails to have the file deleted on reboot. All of this can be hidden in the class. See for example: tmpfile.h tmpFile.cpp
Alternatively you can use something like:
struct TmpFile{
FILE* file;
TmpFile(std::string path){
#ifdef _WIN32
HANDLE handle = CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 0);
if(handle == INVALID_HANDLE_VALUE) throw "Error";
int fd = _open_osfhandle((intptr_t)h, _O_APPEND | _O_RDONLY);
if(fd == -1){ CloseHandle(handle); throw "Error"; }
file = _fdopen(fd, "a+");
if(f == NULL){ _close(fd); throw "Error"; }
#else
file = fopen(path.c_str(), "w+");
unlink(path.c_str());
#endif
}
~TmpFile(){
fclose(file);
}
};
Check the access modifiers and adjust to your needs (especially the error handling) This has the nice properties of RAII and will delete the file even in exception cases. However some failures where the dtor is not called will leak the handle in which case the file might not be deleted on windows.
The Win32 handling is taken from here: https://stackoverflow.com/a/7369662/1930508 Check the explanations there.

Deleting a file with an open Handle

I shouldnt be able to delete a file with an open handle, correct? So i create a file, then i straight away try to delete it, expecting this to fail. Or am i wrong and the handle doesnt have to be closed before deleting the file?
HANDLE hFile = CreateFile (TEXT(file),
GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
//FAIL
}
if(DeleteFile(file))
{
//Should it ever get here cos i dont close the handle?
}
It depends on how the file has been opened. If the share mode has FILE_SHARE_DELETE specified, then it may be deleted by others.
Even if you memory map the file, and it has been opened with this flag (and read/write sharing), then it can still be deleted by the shell (at least I've tried this and it happens, but perhaps the file has simply been renamed and moved to the recycle bin). In such cases, subsequently accessing the memory will result in an 'InPageError' C-style exception.
Yes, it would fail.
The DeleteFile function fails if an application attempts to delete a
file that is open for normal I/O or as a memory-mapped file.
Have you tried this? MS documentation states that:
The DeleteFile function fails if an application attempts to delete a file that is open for normal I/O or as a memory-mapped file.
So if you're not getting that behaviour I'd suggest it's down to the way you've opened the file. Are you sure that your check on whether the file is open is completely comprehensive?Have you tried writing to the file first? Can you see the file outside of your own code? (i.e. from Explorer) Look here for more details.

Receiving a Sharing Violation Opening a File Code 32

I have been trying the following piece of code that does not work. What I am trying to do is to start executing my exe (one that I created a simple dialog based application using VC6.0) then from inside this application modify its own contents stored on the hard drive.
So there is a running copy of the exe and from this running copy it will open the disk copy into a buffer. Once loaded into a buffer then begin a search for a string. Once the string is found it will be replaced with another string which may not be the same size as the original.
Right now I am having an issue of not being able to open the file on disk for reading/writing. GetLastError returns the following error "ERROR_SHARING_VIOLATION The process cannot access the file because it is being used by another process.".
So what I did I renamed the file on disk to another name (essential same name except for the extension). Same error again about sharing violation. I am not sure why I am getting this sharing violation error code of 32. Any suggestions would be appreciated. I'll ask my second part of the question in another thread.
FILE * pFile;
pFile = fopen ("Test.exe","rb");
if (pFile != NULL)
{
// do something like search for a string
}
else
{
// fopen failed.
int value = GetLastError(); // returns 32
exit(1);
}
Read the Windows part of the File Locking wikipedia entry: you can't modify files that are currently executing.
You can rename and copy them, but you can't change them. So what you are trying to do is simply not possible. (Renaming the file doesn't unlock it at all, it's still the same file after the rename, so still not modifiable.)
You could copy your executable, modify that copy, then run that though.

Copying contents of one file to another in C++

I am using the following program to try to copy the contents of a file, src, to another, dest, in C++. The simplified code is given below:
#include <fstream>
using namespace std;
int main()
{
fstream src("c:\\tplat\test\\secClassMf19.txt", fstream::binary);
ofstream dest("c:\\tplat\\test\\mf19b.txt", fstream::trunc|fstream::binary);
dest << src.rdbuf();
return 0;
}
When I built and executed the program using CODEBLOCKS ide with GCC Compiler in windows, a new file named "....mf19.txt" was created, but no data was copied into it, and filesize = 0kb. I am positive I have some data in "...secClassMf19.txt".
I experience the same problem when I compiled the same progeam in windows Visual C++ 2008.
Can anyone please help explain why I am getting this unexpected behaviour, and more importantly, how to solve the problem?
You need to check whether opening the files actually succeeds before using those streams. Also, it never hurts to check if everything went right afterwards. Change your code to this and report back:
int main()
{
std::fstream src("c:\\tplat\test\\secClassMf19.txt", std::ios::binary);
if(!src.good())
{
std::cerr << "error opening input file\n";
std::exit(1);
}
std::ofstream dest("c:\\tplat\\test\\mf19b.txt", std::ios::trunc|std::ios::binary);
if(!dest.good())
{
std::cerr << "error opening output file\n";
std::exit(2);
}
dest << src.rdbuf();
if(!src.eof())
std::cerr << "reading from file failed\n";
if(!dst.good())
std::cerr << "writing to file failed\n";
return 0;
}
I bet you will report that one of the first two checks hits.
If opening the input file fails, try opening it using std::ios::in|std::ios::binary instead of just std::ios::binary.
Do you have any reason to not use CopyFile function?
Best
As it is written, your src instance is a regular fstream, and you are not specifying an open mode for input. The simple solution is to make src an instance of ifstream, and your code works. (Just by adding one byte!)
If you had tested the input stream (as sbi suggests), you would have found that it was not opened correctly, which is why your destination file was of zero size. It was opened in write mode (since it was an ofstream) with the truncation option to make it zero, but writing the result of rdbuf() simply failed, with nothing written.
Another thing to note is that while this works fine for small files, it would be very inefficient for large files. As is, you are reading the entire contents of the source file into memory, then writing it out again in one big block. This wastes a lot of memory. You are better off reading in chunks (say 1MB for example, a reasonable size for a disk cache) and writing a chunk at a time, with the last one being the remainder of the size. To determine the source's size, you can seek to the end and query the file offset, then you know how many bytes you are processing.
And you will probably find your OS is even more efficient at copying files if you use the native APIs, but then it becomes less portable. You may want to look at the Boost filesystem module for a portable solution.