Как переименовать папку в C#, которая в настоящее время открыта проводником windows

при переименовании папки в C#System.IO.Directory.Move закидываем System.IO.IOException (сообщение "доступ запрещен"), если эта папка или любая подпапка в настоящее время открыта окном проводника (Windows 7). Использование командной строки RENAME тоже не удается. Использование второго проводника windows успешно.

ошибка сохраняется даже после сворачивания родительской папки (или ее родителей). На самом деле конкретное окно Проводника должно быть закрыто. Таким образом, проводник, похоже, создает некоторые блокировки, чтобы показать структуру папок и не выпускает их, даже если фактическая папка больше не отображается (что является чистым nonsens IMO).

есть ли способ переименовать папку (в программе, например, с помощью C#), которая в настоящее время отображается (или была видна, см. выше) в окне проводника?

обновление

нашел способ, как описано в моем собственном ответе на этот вопрос (см. ниже), используя SHFileOperation(). Однако это решение не очень осуществимо (см. Также ниже).

3 ответов


я использовал монитор API v2 от Rohitab для мониторинга вызовов Windows API.

при изменении имени каталога с D:\test to D:\abc данный вызов был зарегистрирован:

explorerframe.dll   ITransferSource::RenameItem ( 0x0000000015165738, "abc", TSF_COPY_CREATION_TIME | TSF_COPY_LOCALIZED_NAME | TSF_COPY_WRITE_TIME | TSF_DELETE_RECYCLE_IF_POSSIBLE, 0x00000000150f77d0 )

копание дальше в вывод монитора показывает некоторые собственные вызовы:

enter image description here

как вы можете видеть, они не используют MoveFile, вместо этого, они используют NtOpenFile С FILE_OPEN_FOR_BACKUP_INTENT и другие, чтобы открыть оригинальный каталог, затем звоните NtSetInformationFile С новым именем каталога и флаг FileRenameInformation что документально здесь.

к сожалению, это все вызовы ядра.

вы можете получить дескриптор каталога в C / C++ из пользовательского режима, например:

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);

но тогда вам все равно нужна альтернатива пользовательского режима для NtSetInformationFile-звоните.

некоторые опции для продолжения (упорядоченные по сложности):

  • посмотреть, если вы можете использовать интерфейс оболочки ITransferSource::RenameItem или найти готовую к использованию функцию оболочки
  • углубитесь в решение пользовательского режима и попробуйте найти альтернативу NtSetInformationFile
  • напишите драйвер, содержащий IOCTL, который делает эти вещи в режиме ядра и вызовите DeviceIoControl из C#.

обновление

кажется SHFileOperation функция делает все вышеперечисленное, как найдено OP.

оставим этот ответ онлайн, потому что он может показать другим, как отлаживать подобные проблемы и получать ценные указатели.


поэтому я отвечаю на свой вопрос после некоторого дальнейшего исследования..

папка может быть переименована с помощью SHFileOperation() как показано здесь: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776887%28v=vs.85%29.aspx (использует ли это "магию", упомянутую Ваутером, или нет. ;-)

но если есть API windows/.Net, такой как System.IO.Directory.Move, WTF мне нужно использовать оболочку? Не говоря уже о производительности ...

в любом случае, используя SHFileOperation() бол в.. используя C#, так как вам нужно объявить все это p-invoke. И в этом конкретном случае вам нужно использовать разные структуры для 32 и 64-битных окон, так как упаковка отличается (см.http://www.pinvoke.net/default.aspx/shell32.shfileoperation). Это очень громоздко, как обычно я бы указал любой ЦП в качестве мишени. На этом этапе вам либо нужно ветвиться во время выполнения (очень плохо), в зависимости от того, являетесь ли вы 64 или 32-битным процессом, либо вы строите по-разному для двух разных целей, что довольно большое влияние, просто чтобы обойти глупые проводника.

в отношении


у меня была та же проблема. Как только я открыл окно Проводника и после перехода в папку для переименования,Directory.Move не удалось с "отказано в доступе" (Windows 7 Professional 64bit, приложение скомпилировано как x86).

интересно, команда Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(...) удается переместить содержимое в новый каталог, он не может удалить старый каталог, только если вы остаетесь внутри подпапки каталога для перемещения. Это можно исправить, поймав исключение, которое вызывается первая ошибка и повторите попытку во второй раз. Теперь исходная папка также удаляется.