I am trying to programmatically delete and replace the contents of an application, \"App A\", using an \"installer\" program, which is just a custom WPF .exe app, we\'ll cal
So there are 2 scenarios I wanted to handle - both are where the folder is prevented from being deleted:
1) A user has a file open on their local machine from the application's folder on the file server.
2) An admin has a file open from the application's folder, which they will see while remoted (RDP'ed) into the server.
I've settled on a way forward. If I run into this issue, I figure about all I can do is to either:
1) Freeze the "installation" (copying) process by simply scheduling a programmatic reboot of the file server in the IOException
block if I really want to blow away the folder (not ideal and probably overkill, but others running across this same issue may be inspired by this option). The installer will need to be run again to copy the files after the server reboots.
[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!");
}
}
Reference: How to shut down the computer from C#
OR
2) Ignore it altogether and perform my copy, anyway. The files actually do delete, and I found there's really no problem with having a folder I can't delete, as long as I can write to it, which I can. So this is the one I ultimately picked.
So again, in the IOException
catch block:
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);
}
}
Code above leaves out my model for Computer
, which just has a Name
node in it, and the rest of my Impersonation class, which is based on my own rendition of several different (but similar) code blocks of how they say to do it. If anyone needs that, here are a couple of links to some good answers:
Need Impersonation when accessing shared network drive
copy files with authentication in c#
Related: Cannot delete directory with Directory.Delete(path, true)