§ ¶The annoying hidden current directory lock
I'm one of those people who uses the Command Prompt all of the time on a Windows system, and I frequently run into cases where some program has annoyingly locked a directory and I get "in use by another program" errors trying to remove it.
The usual reason is that some program that I've launched has it locked as its process current directory. Most often it's Notepad, since I launch Notepad all the time to view one-off files or to temporarily record snippets of text. Each Win32 program has a current directory as part of process state, and annoyingly, it keeps a lock on this directory -- which means you can't move or delete it. Usually what I do then is dig out Process Explorer and use its search function to hunt down the offender.
A more subtle cause of this problem is a program in which you've used an Open or Save dialog and have browsed into a directory. The common file dialogs change the process current directory as the user browses the filesystem, and this can also lead to accidentally locking folders. For example, you could open a file through File / Open, thus changing the process current directory to that file's containing folder, and then use drag-and-drop to reopen a different file. However, the drag and drop operation doesn't change the current directory, which still points to and is locking down the old location. This nonintuitive behavior also causes some gotchas for GUI programs:
- Any relative paths lacking a drive specifier need to be used or resolved before the GUI comes up. Otherwise, the user may change the current directory, and then those paths can't be properly resolved anymore.
- Worker threads shouldn't ever use relative paths since the current directory is a per-process and not a per-thread state. (Corollary: A module or library should never attempt to use SetCurrentDirectory to try to resolve relative paths.)
On the bright side, this behavior also has a use: if a program is stuck with an unwanted current directory, you can bring up a file dialog to change it. You do need to either accept the operation or keep the dialog up while you fiddle with the directory, because when you cancel the file dialog it will attempt to restore the original current directory. I've seen some people work around this issue by using an unlocker style program to forcibly close the offending file handle instead, without the program knowing. As a programmer, the thought of a user closing random kernel handles in a program terrifies me, but somehow they manage not to lose all of their work constantly.
For these reasons, my current thinking is that for programs which don't have file UI, it might be a good idea to call SetCurrentDirectory() on startup to change the current directory to a decent default, such as the program EXE directory or the user profile root, to avoid mysteriously locking down a random directory. This also has the benefit of avoiding random surprises if someone accidentally sneaks a relative path into a config file. For programs that do have file UI, it's less clear as doing so might undesirably change the initial file dialog location. The rules for that are somewhat obscure and change with each version of Windows; they are documented under the lpstrInitialDir parameter of the OPENFILENAME Structure.
Closing random handles in a program causes serious bad karma. One bug I eventually solved at work (of which the symptoms could be summed up as "$foo fails in weird and impossible ways") turned out to be caused by calling CloseHandle() twice. What usually happened is the first CloseHandle succeeded, then a different thread ran and opened a new handle which got given the same number as the just-closed one, then the first thread ran again and closed thread 2's handle. If the scheduler was feeling particularly evil a third thread then got scheduled and opened a new handle, which again reused the same handle id.
Fixing that one bug solved almost all the stability issues of that component (what's left was caused by a Microsoft DLL calling TerminateThread on a thread that was holding the loader critical section).
Torkell (link) - 29 10 10 - 05:15
Interesting. I had thought that Win32 used version bitfields in the handle to combat this, but maybe that's only in GDI.
The best way I know of to catch this is to use Application Verifier, but unfortunately a number of base Windows XP components trip it by calling CloseHandle() with a null handle. Hopefully that's been cleaned up in Vista and up.
Phaeron - 29 10 10 - 05:31
You may want to try Cedric Collomb's Unlocker, it helped me many times already :
[Deleted. Did you not read the both the article and the comments about what a bad idea these programs are? --Phaeron]
nicolas (link) - 29 10 10 - 09:31
I used to use Unlocker all the time. I hate it when programs that have crashed or been closed still hold on to files.
mikesum32 - 30 10 10 - 06:45
I had a weird issue once where a file would not delete. It showed up in explorer as having 0 byte file size, and whenever i tried deleting it would claim the file didn't exist. Disk error checker didn't do anything. I eventually figured out the filename had some kind of illegal character in it, and the only way I found to delete the file was to open 7zip and delete from within that program. They must have their own file handing system in there that doesn't use the default windows one. :)
matt - 30 10 10 - 09:59
Yup, happens occasionally. Sometimes using the short 8.3 name works, other times you have to resort to escape syntax (\\?\c:\foo) in order to delete it. The main reason it happens is that the filenames allowed by the NT kernel and NTFS is a superset of the filenames allowed by Win32, so you can get into nasty situations like a file named after a device, which causes Explorer to happily open the printer port. Programs that can break the 260 character limit on paths will have a better chance of accessing these files.
Phaeron - 30 10 10 - 10:38
@Phaeron: it doesn't look like there's any kind of versioning or structure in kernel handles (apart from handles being a multiple of 4 - I think the bottom two bits have been used as flags by some APIs). I also noticed that the handles appeared to be sequentiallly or near-sequentially assigned, with the kernel tending to reuse handle numbers.
Amusingly, someone had spotted the double close a few years back with a code analysis tool. They left it in thinking it wasn't all that important a warning, and it languished for years in the bug-tracker as low-priority.
Torkell (link) - 30 10 10 - 22:15
AFAIK Win7 Open/Save dialogs don't change the current directory anymore. Not sure about Vista though...
My biggest "fight" deleting file was when suddenly I've got a file which file name ended with space, e.g. something like "file.avi ".
dwrbudr - 01 11 10 - 06:19
I understand that Unlocker is a big no-no in most situations, but sometimes it does come in handy. That, and Process Explorer. Of course, many a time no matter what you do, you can *never* get Windows to let go of and eject a USB drive successfully. That's something I had honestly hoped Win7 would solve, but unfortunately seems they just can't be bothered to fix such irritating bugs where unknown processes lock the drive indefinitely until you're forced to reboot.
James - 09 11 10 - 02:51
Raymond Chen at Microsoft has been covering some of the history of this lately:
Scott - 12 11 10 - 03:01
@James: Windows (at least XP) by default mounts USB drives with write caching disabled, so if the drive is idle you can usually just unplug it.
Torkell (link) - 12 11 10 - 07:35
@Torkell: Yeah... I'd rather not, thanks. I've lost data despite having that setting turned on and a friend yanking out the USB cable. :(
James - 15 11 10 - 00:35
+1 for unlocker. Once you know the risks.
roger (link) - 16 11 10 - 01:08
Do not unplug USB sticks in Windows without unmounting.
JPT - 17 11 10 - 23:31
Make that +2. I've never had a problem with "unlocking" a forgotten file handle or whatever you call it - always make sure all relevant programs are shut first, etc. Usually it's something idiotic like Explorer having looked at it and not registered that it wasn't any more (not like it matters unless you're copying stuff to/from the drive/folder/etc?), windows indexer (on a removable disk!), antivirus doing a background scan OR having done an on-access one but not let go of the baton etc.
It doesn't however work in all cases - recently I found a certain program's installer (which I was using an external HDD to go round a lot of workplace machines and install... we're not quite into the network distribution groove) just would not let go of the drive.... no matter if every program was shut, unlocker run multiple times, "safe eject" done more times that you could count. Just yanking the cord out didn't seem to hurt it, but it wasn't too much more bother to just shut down the PC first... thankfully.
It's a true and needless annoyance - hopefully it doesn't happen in W7? Or does it? 10 years of dev since XP, you think they'd fix it :)
tahrey - 18 11 10 - 00:28
It is a really annoying problem.
When I download a file from the browser (K-Meleon) to the USB drive,
then the folder where the file is saved is kept locked (I mean after the download is ended) and I can't use "safe eject".
ale5000 - 13 12 10 - 05:12