I've written a simple (console)script in C# that recursively deletes files and folders in the given parameter ex: delete_folder.exe "C:\test"
I use this tiny piece of code during an uninstall to delete the remaining files after the uninstall.
The code on itself works fine, but I receive the error: System.IO.IOException: The process cannot access the file 'C:\test\test2' because it is being used by another process.
Just before this error the uninstaller stops and deletes a couple of services (created by the installer)
It seems to me that Windows still uses the folder test2. So my question is: How to check if a folder is in use by another process and how to stop that process from being there, to be able to delete the remaining folders.
The console application looks like this:
class Program
{
public static void log(string t)
{
File.AppendAllText("C:\\testlog.txt", DateTime.Now.ToString() + "----" + t + "\r\n");
}
static void Main(string[] args)
{
try
{
string path = args[0];
if (Directory.Exists(path) == true)
{
Directory.Delete(path, true);
}
else
{
Console.WriteLine("Dir not found!");
}
}
catch (Exception ex)
{
log(ex.ToString());
}
}
}
A try...catch might sound not too elegant (actually, basing the normal flow of an algorithm on this statement is inadvisable in most of the cases), but seems the only way through here. Bear in mind that there is no System.IO (direct) property telling whether an element is in use; and thus relying on this is the usual solution for this kind of problems (post about file in use). Sample code:
foreach (string dir in System.IO.Directory.GetDirectories(rootDirectory))
{
try
{
System.IO.Directory.Delete(dir);
}
catch
{
//There was a problem. Exit the loop?
}
}
Related
My problem is that I cannot code logs (the one who wrote down every action you do in your program). So I am asking how can you create logs, and another thing I have this code here. I think it is for creating logs but I don't know how to edit the things that must be edited in the codes below. Can someone help me?
public class LogWriter
{
private string m_exePath = string.Empty;
public LogWriter(string logMessage)
{
LogWrite(logMessage);
}
public void LogWrite(string logMessage)
{
m_exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
try
{
using (StreamWriter w = File.AppendText(m_exePath + "\\" + "LOGG.txt"))
{
Log(logMessage, w);
}
}
catch (Exception ex)
{
MessageBox.Show("new"+ex);
}
}
public void Log(string logMessage, TextWriter txtWriter)
{
try
{
txtWriter.Write("\r\nLog Entry : ");
txtWriter.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
txtWriter.WriteLine(" :");
txtWriter.WriteLine(" :{0}", logMessage);
txtWriter.WriteLine("-------------------------------");
}
catch (Exception ex)
{
MessageBox.Show("old"+ex);
}
}
}
I would suggest you look into a logging framework if it is practical for you to use something other than this class you have shown. Look at Serilog or even Nlog. They are well designed for thread safety and efficiency.
To use this class, or any class (that is not static) you create an instance with new:
LogWriter logger = new LogWriter("a message here..");
Unfortunately this class was designed so you cannot instantiate it without writing a log entry so you will get "a message here.." log written at this point.
Now that you have an instance you can call methods on it, for instance:
logger.LogWrite("This is a log message...");
You should be able to see your log entries in the LOGG.txt file in the same folder as your program.
Don't think of this in terms of just this logger. If you had more experience programming you would already know how to do this. A little time invested in tutorials will go a long way.
switch (filesToAdd.Count)
{
case 0:
Console.WriteLine("No files to ZIP detected. Returning to menu.");
return;
default:
Console.Clear();
foreach (var file in filesToAdd)
{
try
{
Directory.Move(file, folderWithFolders);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
break;
}
This code constantly catches the exception where folderWithFolders already exists, because I create it earlier on based on user input.
Ideally, it should contain multiple folders that are stored in the list filesToAdd once the switch statement default case is finished.
Is it possible to use this Directory.Create method without creating an entirely new directory (since that means that it will be impossible for me to add every folder from the list) and instead be able to add a folder at a time through the foreach loop to folderWithFolders without creating a new directory; do any such methods exist?
Thank you.
Edit: So just in case someone stumbles upon this a few months in the future, I got it working by doing the following:
I changed:
try
{
Directory.Move(file, folderWithFolders);
}
Instead I changed it to:
try
{
Directory.Move(file, folderWithFolders + "\\"
+ Path.GetFileName(file));
}
If indeed filesToAdd is the list of folders you want to move to the folder folderWithFolders, then I would suggest
foreach (var file in filesToAdd)
{
try
{
Directory.Move(file, Path.Combine(folderWithFolders,file));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
We're hosting the script in out app. On exceptions/crashes, we'd like to see the line number in the stacktrace.
I can't find if there is a setting to include debug info when setting up the CSScript compiler?
I believe you mean CS-Script (if not please correct me). I am not sure how you are calling it but I did find this command line documentation(it seems that the position in their help file is not reflected in their URL, you need to navigate to Overview -> Command-line interface). With .net the line number is included in the stack trace if you have a corresponding .pdb file and the line number will also be correct if there is no optimization done at compile time (this should not be a problem for CS-Script). In the documentation for cscs.exe there is a switch /dbg or /d. When you include this switch the corresponding .pdb file will be included with your .exe (or with the .dll if building a library). Once you have both files line numbers will now be available in the stack trace of any given exception that hits an operation in that assembly.
/dbg or /d
Forces compiler to include debug information.
Assume we have a file called Test.cs with some test code:
cscs.exe /e /dbg Test.cs
This will output 2 files:
Test.exe
Test.pdb
Here is the sample content of Test.cs, when you execute it you will see the line numbers.
using System;
namespace MyProgram
{
class Program
{
static void Main(string[] args)
{
try
{
throw new Exception("OH NO");
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
Console.ReadLine();
}
}
}
For Executing inline
This should also be possible with the DebugBuild flag in the EvaluatorConfig section set to true. In my example below you will get everything expected BUt when using LoadCode it uses a temporary file name so the file name looks funny although the line number is correct. There are also LoadXXX commands for loading one or more files which will give a prettier stack trace as the file name is now known.
class Program
{
static void Main(string[] args)
{
CSScript.EvaluatorConfig.DebugBuild = true;
CSScript.EvaluatorConfig.Engine = EvaluatorEngine.CodeDom;
Console.WriteLine("Debug on = " + CSScript.Evaluator.DebugBuild);
dynamic script = CSScript.Evaluator
.LoadCode(#"using System;
public class Script
{
public int Sum(int a, int b)
{
try{
throw new Exception();}
catch(Exception ex){
Console.WriteLine(ex.StackTrace);
}
return a+b;
}
}");
int result = script.Sum(1, 2);
Console.WriteLine(result);
Console.ReadLine();
}
}
I did test this all to ensure that it does work. If you have problems just let me know.
In a C# program, I have an error check that gets repeated a lot:
try
{
File.Move(searchfolder + question1 +"_"+ filestring +".txt",
searchfolder + question1 +".txt");
}
catch (Exception ex)
{
File.AppendAllText(adminfolder + question1 +"_l.txt", "!");
side.Value = Convert.ToString(ex) + "[Check-In error at "
+ Convert.ToString(MYLINE) +"] "+ side.Value;
}
MYLINE is some number, and MYLINE is the only thing that changes across my program.
So a normal C++ #define macro would make this much simpler to work with (I would just write the full "#define CHECKIN(MYLINE) ..." once at the top of the program).
How would a pro deal with this in C#?
...and MYLINE is the only thing that changes across my program. So a normal C++ #define macro would make this much simpler to work with
Well, perhaps, but since C# doesn't have a concept of macros... just use a method:
static class FileMover
{
public static void MoveMyFile(string myline)
{
// your existing code here
}
}
On a side note, there is glaring problem in your code. In your catch block you call File.AppendAllText()... which, of course, can throw an exception as well. You need to account for that.
Given a folder I want to make sure that ALL the files on that directory are deleted.
I know there maybe IOExceptions or Access Denied errors but how do just leave them aside and continue with my deletion of the files that I actually can delete? Is this possible?
Please shed some light on me on where I can begin.
If you want to delete all files you can delete, you could create a list of files (recursively for subdirections) and then delete them separately, skipping the ones that throw an exception.
IF you loop through the files in the directory and delete each one within a try/catch you can then continue even after an exception. If you try to delete the entire directory then once it fails it fails.
Edit: Code as requested
private void DeleteFiles(DirectoryInfo Directory)
{
bool AllFilesDeleted = true;
foreach(FileInfo oFile in Directory.GetFiles())
{
try
{
oFile.Delete();
}
catch (Exception ex) { AllFilesDeleted = false; }
}
foreach (DirectoryInfo oDirectory in Directory.GetDirectories())
{
DeleteFiles(oDirectory);
}
if (AllFilesDeleted)
{
try
{
Directory.Delete();
}
catch (Exception ex){}
}
}
IOExceptions or Access Denied errors but how do just leave them aside and continue with my deletion
Huh? If you are having IO issues or you don't have access to the files you can't delete them. They are exceptions. They are telling you "hey, this went wrong, and here's why". They aren't polite warning messages that you can just ignore, they are the reason your delete did not work in the first place.
Answering the recursive search question:
void delete(DirectoryInfo di) {
foreach(DirectoryInfo di2 in di.GetDirectories()) {
delete(di2);
}
foreach(FileInfo fi in di.GetFiles()) {
fi.Delete();
}
}
...as suggested above, try...catch around various parts will cope with the inability to delete certain files.
If you change the order a bit in what #Will A suggested and add a line to delete the directory itself - it should do the trick. Something like
static void delete(DirectoryInfo di)
{
foreach (FileInfo fi in di.GetFiles())
{
try
{
fi.Delete();
}
catch (Exception)
{
}
}
foreach (DirectoryInfo di2 in di.GetDirectories())
{
delete(di2);
}
try
{
di.Delete();
}
catch (Exception)
{
}
}
it should clear the empty folders