Directory.Delete runs into IOException - c#

I have created a shell script in C# that deletes Windows home folders on the server. The home folders are given by a text-file. The script loops thru the folders and deletes them. I have two log files a "success"-log and a "error"-log.
The deleting part looks like:
if (Directory.Exists(serverShare + "\\" + user))
{
try
{
Directory.Delete(serverShare + "\\" + user, true);
successLog.WriteLine(serverShare + "\\" + user + " --- deleted");
}
catch (Exception ex)
{
errorLog.WriteLine(serverShare + "\\" + user + " --- Error: {0}", ex.ToString());
}
}
else
{
errorLog.WriteLine(serverShare + "\\" + user + " -- Errror: Directory not exists!");
}
Now I run into an exception:
"System.IO.IOException: The process cannot access the file because it
is being used by another process. directory.delete pictures
documents".
The folder was not in use (the users who access it, works no longer in the company), so no another process can access it.
The exception occurs with folders my documents\my music, my documents\my pictures. So it could be do something with special Windows folders?
The other folders are deleted. So the script "works". Also no problems in local environment.

I have solved the problem. Some (system) folders have Archive or ReadOnly flags. It seems the Directory.Delete (with recursive subfolder deletion) can't delete these folders. So you have to remove these attributes first.
Removing mehtod:
public static void ClearAttributes(string currentDir)
{
if (Directory.Exists(currentDir))
{
File.SetAttributes(currentDir, FileAttributes.Normal);
string[] subDirs = Directory.GetDirectories(currentDir);
foreach (string dir in subDirs)
{
ClearAttributes(dir);
}
string[] files = files = Directory.GetFiles(currentDir);
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
}
}
}
Then, before Directory.Delete run this method.
ClearAttributes(serverShare + "\\" + user);
Directory.Delete(serverShare + "\\" + user, true);
So it works fine and all folders are deleted.

Can you manually delete the folder ?
You can use this Process explorer to get the locking file name.
https://technet.microsoft.com/en-sg/sysinternals/bb896653.aspx
Or you can put
GC.Collect();
Directory.Delete(serverShare + "\\" + user, true);

Related

C# File.copy and Directory.CreateDirectory working on Win10 but in Win7 it appends folder to parent folder

The same code, one on windows 10, the other on windows 7.
The idea is to have a directory from a network drive replicate over to a local drive.
On windows 10, the machine I am writing it on, it works perfectly fine as intended.
On windows 7, the target machine, it 'works' but the sub folder structure is messed up.
Example,
C:\target -> the target location
C:\targetNewFolderName1 -> What its being copied to
C:\targetNewFolderName2
C:\targetNewFolderNameN
When it should be doing this below,(which it is, on windows 10, not on windows 7)
C:\target -> the target location
C:\target\NewFolderName1 -> What its being copied to
C:\target\NewFolderName2
C:\target\NewFolderNameN
Master is a network directory, #"\\server\fu\bar\target"
Slave is a local directory, #"C:\target"
These are passed to the function.
Function header, private void CheckMasterToSlave(string MasterPath, string SlavePath, string BackupPath, string[] MasterFilesList, string[] SlaveFilesList)
The below code snipit is within a foreach; foreach (string master in MasterFilesList).
log.Info(master + " doesnt exist, copying");
string directoryCheck = (SlavePath + master.Substring(MasterPath.Length)).Substring(0,
(SlavePath + master.Substring(MasterPath.Length)).LastIndexOf("\\"));
if (!Directory.Exists(directoryCheck))
{
log.Debug(directoryCheck + " Directory not present, touching.");
try
{
Directory.CreateDirectory((SlavePath +
master.Substring(MasterPath.Length)).Substring(0, (SlavePath +
master.Substring(MasterPath.Length)).LastIndexOf("\\")));
}
catch
{
log.Error(master + " directory failed to be created in slave environment.");
}
}
try
{
File.Copy(master, SlavePath + master.Substring(MasterPath.Length));
log.Info(SlavePath + master.Substring(MasterPath.Length) + " Successfully created.");
BackupFile(master.Replace(MasterPath, SlavePath), BackupPath, SlavePath);
}
catch
{
log.Error(master + " failed to copy, backup has been halted for this file.");
}
I do not understand why this works as intended on windows 10 but moving it to windows 7 causes this issue.
What would be causing this and how can I stop the new folder from appending to the parent folder in windows 7?
Use Path.Combine to build a path name from different path components instead of just using string concatenation.
Alright, I am stupid and forgot to change to release. When changes that NineBerry mentioned were made. It did work.
I still do not understand why the original did work on windows 10 but not on windows 7. Especially since the BackupFile portion does the same thing as the old 'wrong' way. But both work now.
Regardless, here is the updated bit.
log.Info(master + " doesnt exist, copying");
string[] EndDirectoryFile = master.Substring(MasterPath.Length).Split('\\');
string[] EndDirectory = new string[EndDirectoryFile.Length-1];
for (int i = 0; i < EndDirectoryFile.Length - 1; i++)
{
EndDirectory[i] = EndDirectoryFile[i];
}
string directoryCheck = Path.Combine(SlavePath, Path.Combine(EndDirectory));
if (!Directory.Exists(directoryCheck))
{
log.Debug(directoryCheck + " Directory not present, touching.");
try
{
Directory.CreateDirectory(directoryCheck);
}
catch
{
log.Error(master + " directory failed to be created in slave environment.");
}
}
try
{
File.Copy(master, SlavePath + master.Substring(MasterPath.Length));
log.Info(SlavePath + master.Substring(MasterPath.Length) + " Successfully created.");
BackupFile(master.Replace(MasterPath, SlavePath), BackupPath, SlavePath);
}
catch
{
log.Error(master + " failed to copy, backup has been halted for this file.");
}

How can I fix my code to move files that already exist to another directory?

This is my first C# script and first non-SQL based script in general.I'm really proud of it (and I couldn't have done it this quickly without help from this community, thanks!), but I know it's going to be all kinds of messy.
The script loops through all files in a single directory, removes special characters from the file names, and renames the files to a standard, user-friendly, name. The script is looking for a specific set of files in the directory. If it finds a file that isn't supposed to be in the directory, it moves the file to a safe folder and renames it. If the folder
I'm working with 4 files that have dynamic names that will include numbers and special characters. The renaming process happens in two steps:
Remove special characters and numbers from the name. Ex: From "EOY 12.21.2018 - 12.28.2018 PRF.xls" to "EOYPRF.xls"
Rename the file to clearly label what the file is. Ex: From "EOYPRF.xls" to "EOY_CompanyName.xls"
There may be files added to this directory by accident, and since they are payroll files, they are highly confidential and cannot be moved unless they need to be moved (only if they are one of the 4 files), so I move them to a subdirectory in the same directory the files are stored in and rename them.
I am also trying to account for if my script or process messes up midway. This script is part of a larger automation process run in SSIS, so there are many failure points. It may be possible that the script fails and leaves one or all of the 4 files in the directory. If this is the case, I need to move the file out of the main directory before the user adds new, unaltered master files to be processed. If the directory contains files of the same final name ("EOY_CompanyName.xls") then it will not work properly.
I'm testing the script by placing the three scenario in the directory.
2 files that are not in any way associated with the 4 master files.
4 unaltered master files formatted with numbers and special characters: "EOY 12.21.2018 - 12.28.2018 PRF.xls"
4 master files already in their final state (simulating a failure before the files are moved to their final directory). Ex: "EOY_CompanyName.xls"
The problem I'm facing is in the rare scenario where there are both unaltered master files and final master files in the directory, the script runs up until the first unaltered file, removes the special characters, then fails at the final renaming step because a file already exists with the same name (Scenario 3 from the 3 points above). It'll then continue to run the script and will move one of the master files into the unexpected file directory and stop processing any other files for some reason. I really need some help from someone with experience.
I've tried so many things, but I think it's a matter of the order in which the files are processed. I have two files named "a.xls" and "b.xls" which are placeholders for unexpected files. They are the first two files in the directory and always get processed first. The 3rd file in the directory is the file named above in its unaltered form ("EOY 12.21.2018 - 12.28.2018 PRF.xls"). It gets renamed and moved into the unexpected files folder, but really it should be passed over to move the master files containing the final name ("EOY_CompanyName.xls") into the unexpected folder. I want to make sure that the script only processes new files whenever it's run, so I want to move any already processed files that failed to get moved via the script into another directory.
public void Main()
{
///Define paths and vars
string fileDirectory_Source = Dts.Variables["User::PayrollSourceFilePath"].Value.ToString();
string fileDirectory_Dest = Dts.Variables["User::PayrollDestFilePath"].Value.ToString();
string errorText = Dts.Variables["User::errorText"].Value.ToString();
DirectoryInfo dirInfo_Source = new DirectoryInfo(fileDirectory_Source);
DirectoryInfo dirInfo_Dest = new DirectoryInfo(fileDirectory_Dest);
string finalFileName = "";
List<string> files = new List<string>(new string[]
{
fileDirectory_Source + "EOY_PRF.xls",
fileDirectory_Source + "EOY_SU.xls",
fileDirectory_Source + "FS_PRF.xls",
fileDirectory_Source + "FS_SU.xls"
});
Dictionary<string, string> fileNameChanges = new Dictionary<string, string>();
fileNameChanges.Add("EOYReportPRF.xls", "EOY_PRF.xls");
fileNameChanges.Add("PayrollEOY.xls", "EOY_SU.xls");
fileNameChanges.Add("PRFFundingStatement.xls", "FS_PRF.xls");
fileNameChanges.Add("SUFundingStatement.xls", "FS_SU.xls");
///Determine if all files present
int count = dirInfo_Source.GetFiles().Length;
int i = 0;
///Loop through directory to standardize file names
try
{
foreach (FileInfo fi in dirInfo_Source.EnumerateFiles())
{
string cleanFileName = Regex.Replace(Path.GetFileNameWithoutExtension(fi.Name), "[0-9]|[.,/ -]", "").TrimEnd() + fi.Extension;
File.Move(fileDirectory_Source + Path.GetFileName(fi.Name), fileDirectory_Source + cleanFileName);
///Move unexpectd files in source directory
if (!fileNameChanges.ContainsKey(cleanFileName))
{
errorText = errorText + "Unexpected File: " + cleanFileName.ToString() + " moved into the Unexpected File folder.\r\n";
File.Move(dirInfo_Source + cleanFileName, dirInfo_Source + "Unexpected Files\\" + Path.GetFileNameWithoutExtension(cleanFileName) + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + fi.Extension);
}
if (fileNameChanges.ContainsKey(cleanFileName))
{
///Final Friendly File Name from Dict
var friendlyName = fileNameChanges[cleanFileName];
///Handle errors produced by files that already exist
if (files.Contains(fileDirectory_Source + friendlyName))//File.Exists(fileDirectory_Source + friendlyName))
{
MessageBox.Show("File.Exists(dirInfo_Source + friendlyName)" + File.Exists(dirInfo_Source + friendlyName).ToString() + " cleanFileName " + cleanFileName);
errorText = errorText + "File already exists: " + friendlyName.ToString() + " moved into the Unexpected File folder.\r\n";
File.Move(dirInfo_Source + friendlyName, dirInfo_Source + "Unexpected Files\\" + Path.GetFileNameWithoutExtension(friendlyName) + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + Path.GetExtension(friendlyName));
return;
}
///Rename files to friendly name
File.Move(dirInfo_Source + cleanFileName, dirInfo_Source + friendlyName);
finalFileName = friendlyName.ToString();
}
///Count valid PR files
if (files.Contains(dirInfo_Source + finalFileName))
{
i++;
}
}
///Pass number of files in source folder to SSIS
Dts.Variables["User::FilesInSourceDir"].Value = i;
}
catch (Exception ex)
{
errorText = errorText + ("\r\nError at Name Standardization step: " + ex.Message.ToString()) + $"Filename: {finalFileName}\r\n";
}
///Search for missing files and store paths
try
{
if (i != 4)
{
var errors = files.Where(x => !File.Exists(x)).Select(x => x);
if (errors.Any())
errorText = (errorText + $" Missing neccessary files in PR Shared drive. Currently {i} valid files in directory.\r\n\n" + "Files missing\r\n" + string.Join(Environment.NewLine, errors) + "\r\n");
}
}
catch (Exception ex)
{
errorText = errorText + ("Error at Finding Missing PR Files step: " + ex.Message.ToString()) + "\r\n\n";
throw;
}
///Loop through directory to move files to encrypted location
try
{
if (i == 4)
foreach (FileInfo fi in dirInfo_Source.EnumerateFiles())
{
fi.MoveTo(fileDirectory_Dest + Path.GetFileName(fi.FullName));
}
}
catch (Exception ex)
{
errorText = errorText + ("Error at Move Files to Encrypted Directory step: " + ex.Message.ToString()) + "\r\n";
}
Dts.TaskResult = (int)ScriptResults.Success;
Dts.Variables["User::errorText"].Value = errorText;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}
I would ideally like to move all files that are in the folder before the files need to be cleaned and renamed so I dont receive errors or commit records to the database that already exist.
If you made it this far, thank you for your time and I appreciate you taking the hour it probably took to read this. You are a hero.
As I understand you want to move out any of the "4 short names" if they already exist before doing anything else. I would go with below, please note, I did not run the code..
I hope I understood you correct
///Loop through directory to standardize file names
try
{
//Cleanup source folder
foreach (string fileShortName in files)
{
if (File.Exists(fileDirectory_Source + fileShortName))
{
//Time to move the file, its old
errorText = errorText + "Old File: " + fileShortName + " moved into the Old File folder.\r\n";
File.Move(dirInfo_Source + fileShortName, dirInfo_Source + "Old Files\\" + Path.GetFileNameWithoutExtension(fileShortName) + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + Path.GetExtension(fileShortName));
}
}
foreach (FileInfo fi in dirInfo_Source.GetFiles())

C# File Permissions

I am currently writing a program in C# that will copy all user profile files to an external device (in this case, my home server).
When my code iterates through my files and folders, it throws an UnauthorizedAccessException.
I have Googled this and searched StackOverflow, but I am unable to find a clear solution that doesn't involve terminating my process. The idea is that it should copy the files and folders that have read permissions.
I had this when I first started, but I easily fixed it by limiting what directories I would backup (though I would prefer a full backup).
Here is my code:
FileInfo f = new FileInfo(_configuration.Destination);
if (!f.Directory.Exists)
{
f.Directory.Create();
}
string[] backupDirectories = new string[]
{
"Desktop",
"Documents",
"Downloads",
"Favorites",
"Links",
"Pictures",
"Saved Games",
"Searches",
"Videos",
".git",
".android",
".IdealC15",
".nuget",
".oracle_jre_usage",
".vs",
"Contacts"
};
foreach (string dirPath in backupDirectories)
{
DirectoryInfo dirInfo = new DirectoryInfo(_path + "\\" + dirPath);
if (dirInfo.Exists)
{
foreach (string dirP in Directory.GetDirectories(dirInfo.FullName, "*", SearchOption.AllDirectories))
{
DirectoryInfo dirI = new DirectoryInfo(dirP);
if (dirI.Exists)
{
string dir = dirP.Replace(_path, _configuration.Destination);
try
{
Directory.CreateDirectory(dir);
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Create Directory: " + dir + Environment.NewLine);
});
} catch (Exception e)
{
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Could NOT Create Directory: " + dir + Environment.NewLine);
});
continue;
}
foreach (FileInfo theFile in dirInfo.GetFiles("*", SearchOption.AllDirectories))
{
string newPath = theFile.FullName;
string file = newPath.Replace(_path, _configuration.Destination);
try
{
File.Copy(newPath, file, true);
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Create File: " + file + Environment.NewLine);
});
} catch (Exception ex)
{
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Could NOT Create File: " + file + Environment.NewLine);
});
}
}
}
}
}
}
I apologise if the code is messy, but I will describe sort of what it is doing. The first bit checks if the backup folder exists on the external drive.
The second part says what folders I need to backup (if you're able to fix this and make it backup all directories with permissions, please help me in doing so).
The first loop starts the iteration for each of the backupDirectories. The second loop starts the iteration for each of the directories in the backup directory. The third loop starts the iteration for each of the folders in the backup directory.
The exception is thrown at Directory.GetDirectories(dirInfo.FullName, "*", SearchOption.AllDirectories), and it is trying to access C:\Users\MyName\Documents\My Music. Attempting to access it in explorer does give me a permissions error, though it isn't listed in explorer when I try going to "Documents" (I am in Windows 10 Pro).
As I recommended, since the Operating System authority is higher than the application, it is likely that you cannot do more than what the Operating System would allow you to do (that is to access or not to access certain folder).
Thus, folders' accessibility is best solved in the Operating System level.
But you could still do two things in the program level to minimize the damage when you search for the items.
To use Directory.AccessControl to know the access level of a directory before you do any query on it. This is not so easy, and there are elaborated answers about this here and also here.
To minimize the damage made by unauthorized access issues by using SearchOption.TopDirectoryOnly instead of SearchOption.AllDirectories, combined with recursive search for all the accessible directories. This is how you can code it
public static List<string> GetAllAccessibleDirectories(string path, string searchPattern) {
List<string> dirPathList = new List<string>();
try {
List<string> childDirPathList = Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly).ToList(); //use TopDirectoryOnly
if (childDirPathList == null || childDirPathList.Count <= 0) //this directory has no child
return null;
foreach (string childDirPath in childDirPathList) { //foreach child directory, do recursive search
dirPathList.Add(childDirPath); //add the path
List<string> grandChildDirPath = GetAllAccessibleDirectories(childDirPath, searchPattern);
if (grandChildDirPath != null && grandChildDirPath.Count > 0) //this child directory has children and nothing has gone wrong
dirPathList.AddRange(grandChildDirPath.ToArray()); //add the grandchildren to the list
}
return dirPathList; //return the whole list found at this level
} catch {
return null; //something has gone wrong, return null
}
}
The function above minimize the damage caused by the unauthorized access only to the sub-directories which have the issue. All other accessible directories can be returned.

Exception while recursively deleting files and folders in WP8

I am trying to delete a folder from Isolated Storage which has files and folders inside recursively. I am using a piece of code suggested by others on Stackoverflow and other blogs. The code is as follows:
private void deleteSubApp(string pappname)
{
try
{
string directory = "apps/" + pappname;
IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication();
if (iso.DirectoryExists(directory))
{
string[] files = iso.GetFileNames(directory + #"/*");
foreach (string file in files)
{
try
{
iso.DeleteFile(directory + #"/" + file);
}
}
string[] subDirectories = iso.GetDirectoryNames(directory + #"/*");
foreach (string subDirectory in subDirectories)
{
try
{
deleteSubApp(directory + #"/" + subDirectory);
}
}
iso.DeleteDirectory(directory);
}
}
}
Since Windows Phone 8 does not allow a built in function to delete a folder unless its empty, deleting it recursively as the above code remains the only option. But when i run the code, i get an exception which is:
System.IO.IsolatedStorage.IsolatedStorageException: Unable to delete,
directory not empty or does not exist.
Please help to find any errors in the code, due to which it is failing? Or am I missing something?
Change
deleteSubApp(directory + #"/" + subDirectory);
To
deleteSubApp(pappname + #"/" + subDirectory);
Otherwise you pass in something like "apps/pappname/subdirectory" on the recursive call and it will set directory to "apps/apps/pappname/subdirectory" which does not exist.

C# delete files in folder with filter

I am using SharpDevelop to write a C# program (not console).
I want to delete files within a specified directory, but be able to EXCLUDE files beginning, ending, or containing certain words.
TO completely delete ALL files in a folder I am using this :
private void clearFolder(string FolderName)
{
DirectoryInfo dir = new DirectoryInfo(FolderName);
foreach(FileInfo fi in dir.GetFiles())
{
fi.Delete();
}
foreach (DirectoryInfo di in dir.GetDirectories())
{
clearFolder(di.FullName);
di.Delete();
}
}
I use it like
ClearFolder("NameOfFolderIWantToEmpty");
Is there a way to modify this so that I can delete all files and direcotries EXCEPT those files and directories containing specific words?
Something like :
CleanFolder(FolderToEmpty,ExcludeAllFileAndDirectoriesContaingThisPhrase);
so that if I did
CleanFolder("MyTestFolder","_blink");
It would NOT delete files and directories with names like
_blinkOne (file)
Test_blineGreen (file)
Test_blink5 (directory)
_blinkTwo (file within the Text_blink5 directory)
Folder_blink (empty directory)
but WOULD delete files and directories like
test (file)
test2 (directory)
test3_file (file within test2 directory)
test4 (empty directory)
I suspect I might have to iterate through each file and directory, checking them one at a time for the matching filter and deleting it if it does not match, but I am not sure how to do that.
Something with FileInfo() and DirectoryInfo() perhaps?
Can somebody help by providing a working example?
(modified version of the above is preferred, but if a new method is required, as long as it doesn't require an outside dll, is OK.
Just test to see if the FileInfo.Name property (string) StartsWith or EndsWith a specified string.
foreach (FileInfo fInfo in di.GetFiles())
{
if (!fInfo.Name.StartsWith("AAA") ||
!fInfo.Name.EndsWith("BBB"))
{
fInfo.Delete();
}
}
Or if you are looking for a word anywhere in the filename, use the Contains method:
foreach (FileInfo fInfo in di.GetFiles())
{
if (!fInfo.Name.Contains("CCC"))
{
fInfo.Delete();
}
}
Use the Directory.GetFiles(string, string) method to get a list of files that match your pattern, and use Enumerable.Except(IEnumerable<T>) to get the files you actually want to delete.
string pattern = "*.*";
var matches = Directory.GetFiles(folderName, pattern);
foreach(string file in Directory.GetFiles(folderName).Except(matches))
File.Delete(file);
There's no need to use DirectoryInfo here, since you appear to be concerned only with manipulating the files in the directory.
if(!fi.Name.Contains("_blink"))
fi.Delete();
I think I have a solution that will work for me.
Posting the full code here so that others may use it, tweak it, and/or examine it for possible flaws.
This is my first time using StackOverFlow, and knowing that I have this resource available to search and the ability to ask questions and people can actually help, is a great comfort to me as a person who is new to all of this stuff.
Thanks a ton everybody!
// Search directory recursively and delete ALL sub-directories and files
// with names that do NOT contain the given search pattern
private void clearFolderWithFilter(string folderName, string filesToExclude)
{
DirectoryInfo dir = new DirectoryInfo(folderName);
foreach(FileInfo fi in dir.GetFiles())
{
if(!fi.Name.Contains(filesToExclude))
{
// System.Diagnostics.Debug.WriteLine("DELETING file " + fi + " because it does NOT contain '" + filesToExclude + "' ");
fi.Delete();
} else {
// System.Diagnostics.Debug.WriteLine("SAVING file " + fi + " because it contains '" + filesToExclude + "' ");
}
}
foreach (DirectoryInfo di in dir.GetDirectories())
{
if(!di.Name.Contains(filesToExclude))
{
// System.Diagnostics.Debug.WriteLine("DELETING directory " + di + " because it does NOT contain '" + filesToExclude + "' ");
clearFolderWithFilter(di.FullName, filesToExclude);
di.Delete();
} else {
// System.Diagnostics.Debug.WriteLine("SAVING directory " + di + " because it contains '" + filesToExclude + "' ");
}
}
}
Usage :
clearFolderWithFilter(#"C:\Path\MyFolder","DoNotDelete_");

Categories

Resources