When renaming a folder in C#, System.IO.Directory.Move
throws System.IO.IOException
(message "access denied") if that folder or any subfolder is currently opened by a (Windows 7) explorer window.
Using the commandline RENAME
fails, too.
Using a second explorer windows succeeds.
The error persists even after collapsing the parent folder (or its parents). In fact the particular explorer window needs to be closed. So the explorer seems to create some locks just to show the folder structure and does not release them even if the actual folder isnt displayed anymore (which is pure nonsens IMO).
Is there a way to rename a folder (in program e.g. using C#), that is presently displayed (or was visible, see above) by an explorer window?
Update
Found a way as described by my own answer to this question (see below) using SHFileOperation()
. However, this solution is not very feasible (see also below).
I've used API Monitor v2 by Rohitab to monitor Windows API calls.
When changing the directory name from D:\test
to D:\abc
, this call was logged:
explorerframe.dll ITransferSource::RenameItem ( 0x0000000015165738, "abc", TSF_COPY_CREATION_TIME | TSF_COPY_LOCALIZED_NAME | TSF_COPY_WRITE_TIME | TSF_DELETE_RECYCLE_IF_POSSIBLE, 0x00000000150f77d0 )
Digging further into the output of the monitor shows some native calls:
As you can see, they're not using MoveFile
, instead, they use NtOpenFile
with FILE_OPEN_FOR_BACKUP_INTENT
and others to open the original directory, then call NtSetInformationFile
with the new directory name and the flag FileRenameInformation
which is documented here.
Unfortunately, these are all kernel calls.
You can get a handle to a directory in C/C++ from user-mode like this:
HANDLE h = ::CreateFileA("D:\\test",
DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
But then, you still need a user-mode alternative for the NtSetInformationFile
-call.
Some options to proceed (ordered by complexity):
- See if you can use the shell interface
ITransferSource::RenameItem
or find a ready-to-use shell function - Dig further into the user-mode solution and try to find an alternative to
NtSetInformationFile
- Write a driver that contains a IOCTL that does these kernel-mode stuff and call
DeviceIoControl
from C#.
Update
Seems like the SHFileOperation
function does all of the above as found by the OP.
Will leave this answer online, because it might show others how to debug similar problems and get valuable pointers.
So I'm answering my own question after some further reasearch..
The folder can be renamed using SHFileOperation()
as shown here:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776887%28v=vs.85%29.aspx
(whether this uses the 'magic' mentioned by Wouter or not. ;-)
But if there is a windows/.Net API such as System.IO.Directory.Move
, WTF do I need to use the Shell? Not talking about performance ...
Anyway, using SHFileOperation()
is a pain in the a.. using C# since you need to declare all this p-invoke stuff. And in this particular case you need to use different structs for 32 and 64-bit windows, since the packing is different (see http://www.pinvoke.net/default.aspx/shell32.shfileoperation). This is very cumbersome, as usually I'd specify AnyCPU as target. At this point you either need to branch at runtime (very bad indeed), depending whether you are a 64 or 32 bit process, or you build differently for two different targets, which is quite a big impact just to work around the silly explorer.
Regards
I had the same problem. As soon as I had an explorer window open and once navigated into the folder to rename, the Directory.Move
failed with a "access denied" (Windows 7 Professional 64bit, application compiled as x86).
Interestingly, the command Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(...)
succeeds to move the contents to a new directory, it only fails to delete the old directory if you are staying inside a subfolder of the directory to move. This can be fixed by catching the exception that is thrown on the first error and try again a second time. Now the source folder is also removed.
来源:https://stackoverflow.com/questions/32347166/how-to-rename-a-folder-in-c-sharp-which-is-currently-opened-by-windows-explorer