3

Does anyone know why sometimes a Directory.Move() operation in C# hangs/waits instead of throwing an exception immediately?

For example:
If I use the Directory.Move() method inside a try block, then navigate to that folder in File Explorer, Windows creates some handles locking it.

Then, I expect the catch block to be executed immediately, but instead it's like the application just hangs for 10-15 seconds before it throws an exception.

The funny thing is, that if I go outside of the folder in File Explorer during these 10-15 seconds, then the application actually completes the Move() operation.

It's like: instead of throwing an exception immediately, Windows hangs for 10-15 seconds to see if the people who are responsible for the handles (locks) closes these handles by themselves.

Is there a way to make the application throw the exception immediately?

Geoff James
  • 3,122
  • 1
  • 17
  • 36
Tom K
  • 33
  • 2
  • can you provide some code? – Usman May 13 '16 at 12:21
  • This sounds like windows is trying, failing, waiting and retrying before throwing the exception – Shon May 13 '16 at 12:24
  • 1
    it seems it simply waits for the fileobject handles to be unlocked. When file/folder is "in use" then it's not deletable, but you're never guaranteed that the platform will abort immediatelly. it may wait for a moment and abort only after some timeout. I'm pretty sure that depends on the windows version, filesystem type, registry driver settings, and - also - any intrusive software like antivirus/watchers/monitors/etc can affect that too – quetzalcoatl May 13 '16 at 12:24
  • That's because as you said the directory is locked but the file system doesn't rise an error immediately, it waits a time interval to allow the resource to be released. Without this, accesing archives which can have a lot of load from different processes would be a nightmare... – Gusman May 13 '16 at 12:25
  • 1
    See [this](http://stackoverflow.com/q/882686/1997232). – Sinatr May 13 '16 at 12:32
  • @Usman A very simple example would just be try { Directory.Move(folder, destination); } catch (Exception) { // Do something } – Tom K May 13 '16 at 12:37
  • @quetzalcoatl I think you're right about the fact that it depends on the windows version, etc. It seems like the hanging/waiting is a bit different when I try it on another version of Windows compared to the server where I'm normally using it – Tom K May 13 '16 at 12:41
  • 1
    Could you use threading to timeout the method manually? I've used it to timeout checking whether a directory exists, I haven't tested whether the same would work for moving a directory hence only a comment. Something like this: `try { Thread t = new Thread( new ThreadStart(delegate() { Directory.Move(source, dest); })); t.Start(); bool completed = t.Join(1000); if (!completed) { t.Abort(); } } catch { // Whatever }` – Equalsk May 13 '16 at 12:46
  • 2
    From the [reference source](http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs,bd0a671e5d8c491c) you can see that the directory move function is calling the WinAPI `MoveFile` function, which will almost certainly mean that there will be differences between OS versions. If you want to throw immediately, you could check whether the directory is locked before you attempt, but there's a lot of potential for a race condition doing that. (IE- Between checking the lock and calling for the move, the directory becomes locked, and you revert to the old/slow behavior.) – theB May 13 '16 at 12:46
  • @Equalsk The size of the directory to be moved varies in my system, but I guess using threading to specify a maximum could be a possibility. Thanks for your comment – Tom K May 13 '16 at 12:57
  • @theB I'm already using some checks to make sure that none of the files in the directory are locked and exactly the race conditions were difficult to deal with. However, actually I didn't think of checking whether the directory is locked before attempting to move it. I'll have a look at it. Thank you! – Tom K May 13 '16 at 13:07
  • @theB Do you have any idea of how to check whether a directory is locked? I have been searching for a while now, and it seems like the only method I can find is to use a try-catch-block and a move operation, but then I'm just back again where I started – Tom K May 13 '16 at 14:26
  • The only thing I can think off the top of my head is to try locking one/all files with `LockFileEx` using both the `LOCKFILE_EXCLUSIVE_LOCK` and `LOCKFILE_FAIL_IMMEDIATELY`, but I have no clue whether that would actually work in this case. – theB May 13 '16 at 16:23

1 Answers1

0

The answer to your question "Does anyone know why sometimes a move operation in C# hangs/waits instead of throwing an exception immediately?" is probably that the .net framework is issued with a Pending state from its request for an NTFS lock, and eventually gives up.

The System.IO.Directory.Move maps directly to a Kernel32 function; I guess that eventually this finds itself calling LockFileEx (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx) which allows the caller to specify whether to immediately fail if the lock cannot be obtained, or wait for a specified time. I guess that Kernel32 uses the variant of this which allows for the setting of a timeout. It doesn't seem that the .net framework has any influence on what timeout it used.

PhillipH
  • 6,182
  • 1
  • 15
  • 25