How to remove directory in Windows synchronous - winapi

RemoveDirectory() is documented as only marking a directory for deletion. I have an application where I have to be sure that the directory is actually deleted (because I create a new one with the same name, or delete directories recursively).
First idea I had was to use GetFileAttributes() to test if the directory still exists, or to use SHFileOperation() for deletion. But when running long test, at some point both solutions fail - CreateDirectory() fails.
Is there a solution for this?

This video by Douglas Niall at the 2015 CppCon covers the solution in detail, starting at about 7:30.
The idea is to first rename (move) the file or directory to another place (on the same volume), which happens synchronously, and then delete it, which happens asynchronously.
Consider this tree:
C:\Users\me\
foo\
bar\
obsolete.txt
If you try to remove bar after deleting obsolete.txt, it may fail because there can be a delay before obsolete.txt is really deleted.
Instead suppose you first move obsolete.txt to C:\Users\me, and give it a temporary name to ensure it doesn't collide with another obsolete.txt in the directory. Maybe you prefix it with a GUID, like 2DCD7863-456C-4B6C-AD84-C4F5E8009D81_obsolete.txt. Now you can delete the file using that temporary name, and, even if there's a delay before it's really deleted, you know bar is truly empty. You can now delete bar or create a new obsolete.txt in bar without worries of a conflict.
To remove bar (a directory) on the way to deleting foo (the root of the tree you're trying to delete), you play the same game. Move it to the parent of the root, call RemoveDirectory, and then proceed along your merry way knowing that it will eventually be deleted.

Possible options:
Delete Directory and check for its existence afterwards
if no handle was open, it is deleted. if a handle is still open there is another problem. Optionally you can wait a few ms after each existence check until it disappears.
Delete all files inside the directory
you mentioned you want to recreate it, so just delete its content. Doing this allows you to see which files/folders are still open inside the directory.

Related

Using ReadDirectoryChangesW to read changes to the folder itself (WINDOWS)

From the doc (ReadDirectoryChangesW):
"Retrieves information that describes the changes within the specified directory. The function does not report changes to the specified directory itself."
My question is: What do I use to report changes to the specified directory itself?
I want to be able to capture changes not only to things and sub-things in the folder itself but also to detect for example, when the folder itself has been deleted.
One strategy would be to actually monitor for changes on the parent of the folder I'm really interested in and then use that to generate an event when the folder I'm interested in is deleted. This works but has the potential to generate thousands of 'uninteresting' events.
A second strategy is to have a recursive monitor for stuff under the folder I'm actually interested in and then a non-recursive monitor on it'a parent. The non-recursive monitor would then be able to tell me when the real folder of interest is deleted.
The latter, second strategy, generates fewer events and is the strategy I would like to use. BUT: It doesn't work 'in process'. That is, if I start to monitor the folder of interest recursively (HANDLE A), and it's parent non-recursively (HANDLE B) and then in the same process, I try and delete the folder of interest, no removal event is generated for it (even though I verify from a console that the thing no longer exists). My suspicion is that this is due to HANDLE A on the folder still being open, and even though I have included the "FILE_SHARE_DELETE" flag in the call to CreateFileW that gave me HANDLE A, it simply can't work.
Note that 'Out of process', i.e. when I delete the folder from within a completely separate process, the above strategy does work.
So, what are my options?
Many thanks,
Ben.

How should I mark a folder as processed in a script?

A script shall process files in a folder on a Windows machine and mark it as done once it is finished in order to not pick it up in the next round of processing.
My tendency is to let the script rename the folder to a different name, like adding "_done".
But on Windows, renaming a folder is not possible if some process has the folder or a file within it open. In this setup, there is a minor chance that some user may have the folder open.
Alternatively I could just write a stamp-file into that folder.
Are there better alternatives?
Is there a way to force the renaming anyway, in particular when it is on a shared drive or some NAS drive?
You have several options:
Put a token file of some sort in each processed folder and skip the folders that contain said file
Keep track of the last folder processed and only process ones newer (Either by time stamp or (since they're numbered sequentially), by sequence number)
Rename the folder
Since you've already stated that other users may already have the folder/files open, we can rule out #3.
In this situation, I'm in favor of option #1 even though you'll end up with extra files, if someone needs to try and figure out which folders have already been processed, they have a quick, easy method of discerning that with the naked eye, rather than trying to find a counter somewhere in a different file. It's also a bit less code to write, so less pieces to break.
Option #2 is good in this situation as well (I've used both depending on the circumstances), but I tend to favor it for things that a human wouldn't really need to care about or need to look for very often.

Windows remembering lower case filename, how to force it to forget?

Here's my problem:
I've got source files I'm publishing (.dita files, publishing using Oxygen) and I need to change capitalization on a lot of them, along with folders and subfolders that they're in. Everything is in source control using SVN.
When I change only an initial cap, say, and leave everything about the filename the same otherwise, Windows "remembers" the lower case name, and that's what gets published, even though the source name is now upper case.
I can even search for the filename, for example Foobar.dita, and the search results will show me "foobar.dita". When I go to that location directly in the file explorer, the file is named Foobar.dita. It's not a duplicate, it's the same file.
What I understand from reading up on this is that Windows isn't case-sensitive, but it "remembers" the filename as one case or the other. So my question is, if I can't force Windows to be case-sensitive, can I somehow force Windows to forget the filename? I've tried deleting it from both Windows and SVN, and recreating it, but it still gets read as lower case when it's initial cap.
If I rename the file, even slightly, it solves the problem, but many of the filenames are just what they need to be, and it's a lot more work to rename them (to think of another good filename) than just to change to initial cap.
UPDATE:
Here's where I read about about the "remembering" idea, in response two, the one with 7 recommendations.
To be explicit: I'm not updating from SVN and thus turning it back to lower case, it's upper case in SVN. It appears upper case in the Windows folder.
UPDATE II: This seems to be what I'm up against:
http://support.microsoft.com/kb/100625
In NTFS, you can create unique file names, stored in the same directory, that differ only in case. For example, the following filenames can coexist in one directory on an NTFS volume:
CASE.TXT
case.txt
case.TXT
However, if you attempt to open one of these files in a Win32 application, such as Notepad, you would only have access to one of the files, regardless of the case of the filename you type in the Open File dialog box.
So it sounds like the only answer is rename the files, not just change case.

Atomically delete a list of paths

I need to be able to delete an arbitrary list of paths (both files and directories) and, if any of the deletions fail, I need to be able to roll back. Is there a Unix command that can accommodate this? If not, a bash script works as well.
There's unlikely to be a command that does this in its full generality. The O/S does not support atomically deleting multiple paths, so it is hard to impossible for a command to do so. Consider a SIGKILL; the command cannot recover, and the kernel won't know it has to undo what was done, so the atomicity is broken.
You can approximate atomicity by moving the deleted files or directories to a trash folder, and then only deleting the contents of the trash folder when everything else has succeeded (and recover the data from the trash folder if anything goes wrong). But it isn't guaranteed atomic. And you have to worry about where to place the trash when the files are on different file systems, so you need a per-file-system trash folder. You also need to worry about atomically deleting 30 files all called 'makefile'; that means you're going to need directory hierarchy information in the trash directory (probably actual directories under the trash directory since anything else is, ultimately, ambiguous).

How to cancel deferred MoveFileEx operation?

I use the below command to delete some files after reboot the machine:
MoveFileEx(PChar(File_Address), Nil, MOVEFILE_DELAY_UNTIL_REBOOT);
How can i cancel execution of this command and prevent files from deleting after reboot?
Files you enqueue for deletion this way are placed in the registry under HKLM\System\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations. Perhaps you can delete entries from there, to prevent the files from being deleted. I couldn't find an API function for this.
I guess you could copy the file (since it hasn't been deleted yet) and then use
MoveFileEx(copy_of_file, original_file, MOVEFILE_DELAY_UNTIL_REBOOT)
to put it back in place during the reboot.
As Ken White has pointed out, though, it would be much much better to avoid this situation in the first place.

Resources