C# 删除某人打开文件的目录

C# 删除某人打开文件的目录,c#,wpf,process,directory,handle,C#,Wpf,Process,Directory,Handle,我正在尝试使用一个“安装程序”程序以编程方式删除和替换应用程序“App A”的内容,这只是一个自定义的WPF.exe应用程序,我们称之为“App B”。(我的问题涉及“应用程序B”中的代码。) GUI设置(不是特别重要) 应用程序B有一个GUI,用户可以在其中选择要将应用程序a复制到的计算机名。管理员通过单击“App A.exe”在本地计算机上填写源目录路径时,会使用一个文件选择器。还有用于用户名和密码的文本框,因此管理员可以输入其将提供应用程序a的目标文件服务器的凭据-代码用这些来模拟用户,以

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

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

终止应用程序A、文件处理和删除文件
复制例程首先终止域中所有计算机上的“App A.exe”进程以及explorer.exe,以防它们打开App A的explorer文件夹。显然,这将在下班后完成,但可能有人在回家之前仍然把东西打开并锁上了机器。这就是我想要解决的问题的基础

在复制更新的文件之前,我们希望删除整个旧目录。要删除目录(及其子目录),必须删除其中的每个文件但是说他们打开了应用程序a文件夹中的一个文件。该代码在删除任何文件之前(使用Eric J.的答案中的代码),会在运行该文件的任何计算机上终止该进程。如果是本地的,它只使用:

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()
,其中会显示
进程无法访问文件“\\\\SERVER\\C$\\APP\u A\u DIR”,因为它正被另一个进程使用,因为我正在试图删除目录,而我的一个文件仍处于打开状态(即使代码实际上能够删除实例仍处于打开状态的物理文件)


但是在文档中,如果使用
MOVEFILE\u DELAY\u直到重新启动
,“文件不能存在于远程共享上,因为延迟操作是在网络可用之前执行的。”这是假设它可能允许文件夹路径,而不是文件名。(参考:)。

因此,我想处理两种情况-这两种情况都是防止删除文件夹的情况:

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

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

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

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<Computer>
            {
                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!");
    }
}
上面的代码省略了我的计算机模型,它只包含一个名称节点,以及我的模拟类的其余部分,它基于我自己对几个不同(但类似)代码块的翻译,这些代码块是如何执行的。如果有人需要,这里有几个链接可以找到一些好的答案:


相关:

您可以注销已登录的用户。通过杀死explorer.exe,您已经有点“粗鲁”:),因此注销用户可以解决所有问题。这比重新启动要好,因为无论出于何种原因,服务器都有可能无法恢复。是的,不幸的是,域中有成千上万的用户,只有一小部分是此应用程序的用户。我想我可以安全地杀死explorer.exe(因为它会重新启动,人们可以轻松地重新打开文件夹)。。。尽管我们的政策是每个人都应该在离开时注销,而且我们在安装更新时实际上会重新启动计算机,但我不确定我们希望采用的方法是在整个域范围内注销,但可能可以转到我们数据库中的用户表来查找它们。我们可以采取这种选择性的方法吗?如何将用户映射到已登录的计算机?等等。。应用程序在MachineX上运行,用户将RDP导入MachineX?或者他们通过UNC远程运行应用程序?或者该应用程序位于客户端计算机上,一些组件指向MachineX?用户通过UNC路径连接到它。该应用程序将存储在文件服务器上。我们会在他们的桌面上有一个指向它的快捷方式。因此,这可能不是一个大问题,他们会发现它所在的文件夹,并从中打开一个文件,因为他们必须查看快捷方式的属性才能访问它。但it团队成员可能正在查看日志,打开一个配置文件,等等。
[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);
[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<Computer>
            {
                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!");
    }
}
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);
    }
}