Delete a file opened for exclusive access in the same process - windows

My windows program receives information from another program via directory/file interface.
That is the other program drops files into a special directory. My program periodically scans the directory, finds the files, processes and then deletes them.
I use CreateFile() function to open such files. To ensure that the other program has finished writing to the file and closed it, I set the dwShareMode parameter to 0. If CreateFile fails with a sharing error I just skip the file until the next itteration.
The problem is that DeleteFile() fails with the ERROR_SHARING_VIOLATION error while the file is opened by my program.
I could close the file before deleteing it, but I would like to avoid the possibility of some other program opening the file just before I delete the file.
I use this code to open files
CreateFile(filePath,DELETE|FILE_READ_DATA,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)
Is it possible to achieve what I want: open the file exclusively an then delete it, so that no other program can interfere between openning and deleteting the file.

FILE_FLAG_DELETE_ON_CLOSE is probably what are you looking for. From MSDN
*FILE_FLAG_DELETE_ON_CLOSE
0x04000000 -
The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles.
If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE share mode.
Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified.*
EDIT: added an example...
So, in you case you should:
HANDLE hFile = ::CreateFile(filePath,
DELETE|FILE_READ_DATA,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_DELETE_ON_CLOSE,
NULL);
//use the file...
::CloseHandle(hFile); //the file gets deleted when the handle is closed

Pass in FILE_SHARE_DELETE for dwShareMode. Note that this will still allow other applications (as well as your own) to call DeleteFile() while you are reading the file, but according to the documentation of DeleteFile() it won't be deleted until you call CloseHandle() to close your read handle.
The DeleteFile function marks a file for deletion on close. Therefore,
the file deletion does not occur until the last handle to the file is
closed. Subsequent calls to CreateFile to open the file fail with
ERROR_ACCESS_DENIED.
Other applications will not be able to read or write the file as long as you don't specify FILE_SHARE_READ or FILE_SHARE_WRITE respectively. Although with FILE_SHARE_DELETE then can move the file, but that would be it.

Related

Windows - Opening a file from code and wait for it to be closed

I need some help and/or advice please.
I'm opening a file from code in either the default associated windows program or with a selected windows program using either ShellExecute or CreateProcess. I then wait for the process to complete. But this does not really work for me because:
Let's say the default associated program for text files (*.txt) is Notepad ++ (NPP). For the process to complete, NPP must not be open to start with and must be closed to complete the process.
But if NPP is already open, the file will be opened in the already opened NPP. But I do not necessarily want to close NPP to complete the process, I just want to close the opened text file and need to detect that the file has been closed and not NPP.
So I realised that waiting for the process to complete is not going to work. I've tried several things like trying to detect if the if the file is opened in another process but have not had any success.
So my question is, what would be the best method to detect when a file is open or in use and vice versa?
It sounds like you want to know when the file handle is "closed", not necessiarly when the program that operated on that file has exited.
Your question is closely related to this question. You could reference that to periodically poll the process handle to see what files are open. There will be timing issues - it might take a few seconds for the process to open the file in the first place.
There are also tools such as Handle.exe that may be useful.
However - none of these solutions are perfect. Some apps, including Notepad.exe, just open the file, read the contents, and immediately close the handle. When the user clicks "save", the file is re-opened for writing, contents saved back to disk, then the file handle is closed again.
A simpler approach would be to periodically poll the last-modified timestamp on the file via GetFileTime. When it changes, you could assume the file has been "Saved". Or apply this technique with some combination of the above and/or waiting for the application that was launched to exit.

Error 223 when opening a file in a OneDrive

For my backup tool I tried to load a file in an OneDrive folder (mapped as a Windows drive), but trying to open a file from this drive results in error 223:
auto hX = CreateFile(L"O:\\BACKUP\\1.dat", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
results in INVALID_HANDLE_VALUE and GetLastError() returns 223:
The file size exceeds the limit allowed and cannot be saved.
This is an > 1GB file.
Is there a way to get a handle without actually downloading the entire file, but only download on demand?
Is there a way to get a handle without actually downloading the entire file, but only download on demand?
If it means for one file, the answer is no as Jonathan mentioned. However it should work if you want to implement the tools to work with OneDrive Files on-demand.
When you use CreateFile to open a file which is not sync to the computer, it will download the scream from OneDrive automatically. The error about The file size exceeds the limit allowed and cannot be saved occurred when there is a setting on your client computer limited the file size when the files downloaded from online. You can refer this settings from link below or test this feature with a small size file which is online view only.
https://answers.microsoft.com/en-us/ie/forum/all/error-0x800700df-the-file-size-exceeds-the-limit/d208bba6-920c-4639-bd45-f345f462934f?auth=1

Can a file copy be detected on windows?

I want to trigger a piece of code to run as soon as a 'copy' command is executed on Windows. Let's say I want to perform some operations as soon as the user tries to copy a file. Is there any way to detect the 'copy' operation on Windows?
You may monitor the clipboard (see Creating a Clipboard Format Listener) and check if there is an object with format CF_HDROP in it. But it will not tell you when the clipboard paste operation actually started.
This also will not cover copying which does not involve clipboard, like direct file_read/file_write loop in a File Manager application. For this you may monitor changes in the file system (see How can I monitor a Windows directory for changes?) but it will not tell you where was the file copied from, just where was it pasted to.

Windows api for locking a file

When we open a word or an excel file. It gets exclusive lock, so if somebody tries to edit and save the changes, he gets an error stating "file is already used by some other user", he only has the read-only copy of that document which is created in temp folder. So can somebody tell me which api is used by windows for locking a file on open.
See dwShareMode param from CreateFile API. Can give you the file or device cannot shared or not opened until the handle to the file or device is closed. Also see LockFile.

How can CreateFile fail with FILE_SHARE_READ and succeed with FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE?

Try it yourself:
Create an XLS file, open it in Excel.
Open sysinternals Process Monitor, and watch what happens while you make a copy of your XLS file in the explorer (just hit ctrl-c ctrl-v).
Two calls to ::CreateProcess in a row. First call asks for read permissions, and gets Access denied. Second call asks for read plus write plus delete and passes.
Is that normal?
If you open a file with FILE_SHARE_READ you're saying you're willing to share access to this file, but only for reads.
If you open with all the flags, you're willing to share access also for writes/delete.
FILE_SHARE_READ is more restrictive than FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE
If some other process (excel) has this file opened for e.g. write (and it has the sharing flags set), the only way you can access it is to accept sharing it for write.
You have to use compatible sharing modes. If Excel opens the file with FILE_SHARE_READ | FILE_SHARE_WRITE then subsequent attempts to open the file must use at least those same flags. Specifically from the MSDN documentation on CreateFile:
You cannot request a sharing mode that conflicts with the access mode that is specified in an existing request that has an open handle. CreateFile would fail and the GetLastError function would return ERROR_SHARING_VIOLATION.

Resources