如何重命名当前由Windows资源管理器打开的c#中的文件夹

时间:2015-09-02 07:36:22

标签: c# directory rename windows-explorer

在C#中重命名文件夹时,如果(Windows 7)资源管理器窗口当前打开了该文件夹或任何子文件夹,则System.IO.IOException会抛出RENAME(消息"访问被拒绝") 。 使用命令行SHFileOperation()也会失败。 使用第二个浏览器窗口成功。

即使在折叠父文件夹(或其父文件夹)后,错误仍然存​​在。实际上,需要关闭特定的资源管理器窗口。 因此,探索器似乎只是为了显示文件夹结构而创建一些锁,即使实际文件夹不再显示也不会释放它们(这是纯粹的非文本IMO)。

有没有办法重命名文件夹(在程序中,例如使用C#),目前在资源管理器窗口中显示(或可见,见上文)?

更新

使用from python_utilities import tools 找到我自己对此问题的回答(见下文)所描述的方法。但是,这种解决方案不太可行(另见下文)。

3 个答案:

答案 0 :(得分:5)

我使用API Monitor v2 by Rohitab来监控Windows API调用。

将目录名从D:\test更改为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,而是使用NtOpenFileFILE_OPEN_FOR_BACKUP_INTENT和其他人一起打开原始目录,然后使用NtSetInformationFile调用FileRenameInformation新目录名称和标记为here的标记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);

不幸的是,这些都是内核调用。

您可以从用户模式获取C / C ++目录的句柄,如下所示:

NtSetInformationFile

但是,您仍需要NtSetInformationFile - 呼叫的用户模式替代方案。

要继续的一些选项(按复杂程度排序):

  • 查看您是否可以使用shell接口ITransferSource::RenameItem或查找即用型shell函数
  • 进一步深入了解用户模式解决方案,并尝试找到DeviceIoControl
  • 的替代方案
  • 编写一个包含执行这些内核模式内容的IOCTL的驱动程序,并从C#调用 (SetAssemblyFileVersion target) -> C:\WORKSPACE\Backend\Back-End\msbuild-by-convention\Scripts\targets.msbuild(207,3): error MSB4018: The "Attrib" task failed unexpectedly. [C:\WORKSPACE\Backend\Back-End\Build\Scripts\main.msbuild] C:\WORKSPACE\Backend\Back-End\msbuild-by-convention\Scripts\targets.msbuild(207,3): error MSB4018: System.ArgumentException: Illegal characters in path. [C:\WORKSPACE\Backend\Back-End\Build\Scripts\main.msbuild]

<强>更新

似乎SHFileOperation函数执行上述所有操作。

将在线留下这个答案,因为它可能会向其他人展示如何调试类似问题并获得有价值的指示。

答案 1 :(得分:3)

所以我在进一步研究之后回答了我自己的问题。

可以使用SHFileOperation()重命名该文件夹,如下所示: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776887%28v=vs.85%29.aspx (这是否使用了Wouter提到的'魔术'。 - - )

但是如果有{/ 1}}等Windows / .Net API,WTF我是否需要使用Shell?不是谈论表现......

无论如何,使用System.IO.Directory.Move是一个痛苦的...使用C#,因为你需要声明所有这些p调用的东西。在这种特殊情况下,您需要为32位和64位窗口使用不同的结构,因为打包是不同的(请参阅http://www.pinvoke.net/default.aspx/shell32.shfileoperation)。这非常麻烦,因为通常我会将 AnyCPU 指定为目标。此时您需要在运行时进行分支(确实非常糟糕),这取决于您是64位还是32位进程,还是针对两个不同的目标进行不同的构建,这对于解决这个愚蠢的资源管理器来说是一个非常大的影响。

此致

答案 2 :(得分:0)

我遇到了同样的问题。只要我打开一个资源管理器窗口并导航到要重命名的文件夹,Directory.Move就会失败并且拒绝访问&#34;访问被拒绝&#34; (Windows 7 Professional 64位,应用程序编译为x86)。

有趣的是,命令Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(...)成功将内容移动到新目录,如果您要停留在要移动的目录的子文件夹中,它只能删除旧目录。这可以通过捕获第一个错误引发的异常并再次尝试再次尝试来解决。现在也删除了源文件夹。