use*_*015 11 c# directory rename windows-explorer
在C#中重命名文件夹时,如果(Windows 7)资源管理器窗口当前打开了该文件夹或任何子文件夹,则System.IO.Directory.Move抛出System.IO.IOException("拒绝访问"消息).使用命令行也RENAME失败了.使用第二个浏览器窗口成功.
即使在折叠父文件夹(或其父文件夹)后,该错误仍然存在.实际上,需要关闭特定的资源管理器窗口.因此,探险家似乎只是为了显示文件夹结构而创建一些锁,即使实际文件夹不再显示也不会释放它们(这是纯粹的非文本IMO).
有没有办法重命名文件夹(在程序中,例如使用C#),目前由资源管理器窗口显示(或可见,见上文)?
更新
找到了我自己对这个问题的回答(见下文)所描述的方法SHFileOperation().但是,这种解决方案不太可行(另见下文).
我使用了Rohitab的API Monitor v2来监控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 )
Run Code Online (Sandbox Code Playgroud)
进一步深入监视器的输出显示一些本机调用:
正如你所看到的,他们没有使用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);
Run Code Online (Sandbox Code Playgroud)
但是,您仍然需要NtSetInformationFile-call 的用户模式替代方案.
一些选项(按复杂程度排序):
ITransferSource::RenameItem或找到一个现成的shell函数NtSetInformationFileDeviceIoControl从C#调用.更新
似乎该SHFileOperation功能完成了OP发现的所有上述内容.
将在线留下这个答案,因为它可能会向其他人展示如何调试类似问题并获得有价值的指示.
所以我在进一步研究后回答我自己的问题..
可以使用SHFileOperation()如下所示重命名该文件夹:
https: //learn.microsoft.com/en-us/windows/win32/shell/manage
(无论这是否使用 Wouter 提到的“魔法”。;-)
但是如果有 windows/.Net API,比如System.IO.Directory.Move,我还需要使用 Shell 吗?不谈性能...
不管怎样,使用SHFileOperation()C# 是一种痛苦,因为你需要声明所有这些 p-invoke 东西。在这种特殊情况下,您需要对 32 位和 64 位 Windows 使用不同的结构,因为打包不同(请参阅https://www.pinvoke.net/default.aspx/shell32.shfileoperation)。这非常麻烦,因为通常我会指定AnyCPU作为目标。此时,您要么需要在运行时进行分支(确实非常糟糕),具体取决于您是 64 位还是 32 位进程,要么您为两个不同的目标进行不同的构建,这对于解决愚蠢的资源管理器来说是一个相当大的影响。
问候
| 归档时间: |
|
| 查看次数: |
1249 次 |
| 最近记录: |