删除某人打开文件的目录

我试图以编程方式删除和替换应用程序的内容,“应用程序A”,使用“安装程序”程序,这只是一个自定义WPF .exe应用程序,我们称之为“应用程序B”。 (我的问题涉及“App B”中的代码。)

GUI设置(不是特别重要)
应用程序B有一个GUI,用户可以在其中选择计算机名称以复制App A。 管理员使用文件选择器通过单击“App A.exe”来填充本地计算机上的源目录路径。 还有用于用户名和密码的文本框,因此管理员可以为将要提供App A的目标文件服务器输入其凭据 – 代码模拟用户以防止权限问题。 “复制”按钮启动例程。

杀死应用程序A,文件进程和执行文件删除
复制例程首先杀死域中所有计算机上的“App A.exe”进程,以及explorer.exe,以防它们打开App A的资源管理器文件夹。 显然这将在下class后完成,但有些人可能仍然打开机器并在回家之前锁上他们的机器。 这真的是我想要解决的问题的基础。

在复制更新的文件之前,我们要删除整个旧目录。 为了删除目录(及其子目录),必须删除其中的每个文件。 但是说他们从App A的文件夹中打开了一个文件。 代码在删除任何文件之前找到任何锁定进程(使用Eric J.的答案, 如何找出哪个进程使用.NET锁定文件? ),它会在任何计算机上杀死该进程。继续前进。 如果是本地的,它只使用:

public static void localProcessKill(string processName) { foreach (Process p in Process.GetProcessesByName(processName)) { p.Kill(); } } 

如果是远程的,它使用WMI:

 public static void remoteProcessKill(string computerName, string fullUserName, string pword, string processName) { var connectoptions = new ConnectionOptions(); connectoptions.Username = fullUserName; // @"YourDomainName\UserName"; connectoptions.Password = pword; ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions); // WMI query var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'"); using (var searcher = new ManagementObjectSearcher(scope, query)) { foreach (ManagementObject process in searcher.Get()) { process.InvokeMethod("Terminate", null); process.Dispose(); } } } 

然后它可以删除该文件。 一切都很好。

目录删除失败
在我的下面的代码中,它正在对文件进行递归删除,并且做得很好,直到Directory.Delete() ,它将说明The process cannot access the file '\\\\SERVER\\C$\\APP_A_DIR' because it is being used by another process ,因为我正在尝试删除目录,而我仍然从中打开一个文件(即使代码实际上能够删除物理文件 – 实例仍处于打开状态)。

  public void DeleteDirectory(string target_dir) { string[] files = Directory.GetFiles(target_dir); string[] dirs = Directory.GetDirectories(target_dir); List lstProcs = new List(); foreach (string file in files) { File.SetAttributes(file, FileAttributes.Normal); lstProcs = ProcessHandler.WhoIsLocking(file); if (lstProcs.Count == 0) File.Delete(file); else // deal with the file lock { foreach (Process p in lstProcs) { if (p.MachineName == ".") ProcessHandler.localProcessKill(p.ProcessName); else ProcessHandler.remoteProcessKill(p.MachineName, txtUserName.Text, txtPassword.Password, p.ProcessName); } File.Delete(file); } } foreach (string dir in dirs) { DeleteDirectory(dir); } //ProcessStartInfo psi = new ProcessStartInfo(); //psi.Arguments = "/C choice /CY /N /DY /T 1 & Del " + target_dir; //psi.WindowStyle = ProcessWindowStyle.Hidden; //psi.CreateNoWindow = true; //psi.FileName = "cmd.exe"; //Process.Start(psi); //ProcessStartInfo psi = new ProcessStartInfo(); //psi.Arguments = "/C RMDIR /S /Q " + target_dir; //psi.WindowStyle = ProcessWindowStyle.Hidden; //psi.CreateNoWindow = true; //psi.FileName = "cmd.exe"; //Process.Start(psi); // This is where the failure occurs //FileSystem.DeleteDirectory(target_dir, DeleteDirectoryOption.DeleteAllContents); Directory.Delete(target_dir, false); } 

我在上面的代码中留下了我试过评论过的内容。 虽然我可以杀死附加到文件的进程并删除它们, 但有没有办法杀死附加到文件夹的进程,以便删除它们?

我看到的所有在线尝试使用延迟的循环检查来解决这个问题。 这不适用于此。 我需要杀死打开的文件 – 我这样做 – 但也确保从文件夹中释放句柄,以便最后也可以删除它。 有没有办法做到这一点?

我认为的另一个选项不起作用:我想我可能只是通过在注册表中标记要删除的网络文件夹来冻结“安装”(复制)过程,并安排程序重启文件服务器,然后重新运行。 如何删除Thumbs.db(它被另一个进程使用)给出了这样的代码:

 [DllImport("kernel32.dll")] public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags); public const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4; //Usage: MoveFileEx(fileName, null, MOVEFILE_DELAY_UNTIL_REBOOT); 

但是在文档中,如果使用了MOVEFILE_DELAY_UNTIL_REBOOT ,“该文件不能存在于远程共享上,因为在网络可用之前会执行延迟操作。” 这是假设它可能允许文件夹路径,而不是文件名。 (参考: https : //msdn.microsoft.com/en-us/library/windows/desktop/aa365240 ( v = vs.85 ) .aspx )。

所以我想要处理两种情况 – 两者都是防止文件夹被删除的地方:

1)用户在文件服务器上的应用程序文件夹中打开了本地计算机上的文件。

2)管理员有一个从应用程序文件夹打开的文件,他们将在远程(RDP)进入服务器时看到它。

我已经找到了前进的道路。 如果我遇到这个问题,我想我能做的就是:

1)通过简单地安排IOException块中的文件服务器的程序化重启来冻结“安装”(复制)过程如果我真的想要吹走文件夹(不理想并且可能是过度杀伤,但是遇到同样问题的其他人可能是灵感来自这个选项)。 在服务器重新启动后,需要再次运行安装程序以复制文件。

 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken); LogonUser(userName, domainName, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle); try { using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle())) { using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) { foreach (Computer pc in selectedList) // selectedList is an ObservableCollection { string newDir = "//" + pc.Name + txtExtension.Text; // the textbox has /C$/APP_A_DIR in it if (Directory.Exists(newDir)) { DeleteDirectory(newDir); // <-- this is where the exception happens } } } } } catch (IOException ex) { string msg = "There was a file left open, thereby preventing a full deletion of the previous folder, though all contents have been removed. Do you wish to proceed with installation, or reboot the server and begin again, in order to remove and replace the installation directory?"; MessageBoxResult result = MessageBox.Show(msg, "Reboot File Server?", MessageBoxButton.OKCancel); if (result == MessageBoxResult.OK) { var psi = new ProcessStartInfo("shutdown","/s /t 0"); psi.CreateNoWindow = true; psi.UseShellExecute = false; Process.Start(psi); } else { MessageBox.Show("Copying files..."); FileSystem.CopyDirectory(sourcePath, newDir); MessageBox.Show("Completed!"); } } 

参考: 如何从C#关闭计算机

要么

2)无论如何,完全忽略它并执行我的副本。 文件实际上是删除,我发现有一个我无法删除的文件夹真的没问题,只要我可以写它,我可以。 所以这是我最终选择的那个。

再次,在IOException catch块中:

 catch (IOException ex) { if (ex.Message.Contains("The process cannot access the file") && ex.Message.Contains("because it is being used by another process") ) { MessageBox.Show("Copying files..."); FileSystem.CopyDirectory(sourcePath, newDir); MessageBox.Show("Completed!"); } else { string err = "Issue when performing file copy: " + ex.Message; MessageBox.Show(err); } } 

上面的代码省略了我的Computer模型,其中只有一个Name节点,以及我的Impersonation类的其余部分,它基于我自己对几个不同(但相似)的代码块的演绎,告诉他们如何做。 如果有人需要,这里有一些链接到一些好的答案:

访问共享网络驱动器时需要模拟

使用c#中的身份validation复制文件

相关: 无法使用Directory.Delete删除目录(路径,true)