Write all of the processes to a text file - c#

I am trying to write all of the processes to a text file, but it will only write the first process in my system. Would you guys mind seeing if there is anything wrong or that I can adjust to fix this issue?
private void button5_Click(object sender, EventArgs e)
{
try
{
var ap = Process.GetProcesses();
SaveFileDialog sfdv2 = new SaveFileDialog();
if(sfdv2.ShowDialog() == DialogResult.OK)
{
foreach(Process process in ap)
{
string path = sfdv2.FileName;
BinaryWriter bw2 = new BinaryWriter(File.Create(path));
bw2.Write("test" + " " + ap.ToString());
bw2.Dispose();
}
}
}
catch(Exception ex) { MessageBox.Show(ex.Message); }
}

Take a step back and see what you are writing.
For each process in the list, you create a file with the same name (and overwrite the previous one) and write "test {process}" in it.
The code does what you tell it, and that's why you end up with only one process in the file.
You can fix this by opening the file outside the loop and closing it afterwards, or even better, you can write it with a using statement. Also, please don't use BinaryWriter for writing to text file. There are many other methods, and the suggested one is StreamWriter. Take a look at this documentation page to see some examples.
string path = sfdv2.FileName;
// with using, the file will be also closed when it's disposed.
using (var file = new System.IO.StreamWriter(path))
{
foreach(Process process in ap)
{
file.WriteLine("test " + ap.ToString());
}
}

You may wish to consider using the Async version of the button click event handler. This is because writing to files is potentially a long running process. So you might want to start it and not wait to block your UI. Combine this with StreamWriter.WriteAsync methods and use in conjunction with await.
e.g.
private async void button5_Click(object sender, EventArgs e)
{
try
{
var ap = Process.GetProcesses();
SaveFileDialog sfdv2 = new SaveFileDialog();
if(sfdv2.ShowDialog() == DialogResult.OK)
{
string path = sfdv2.FileName;
using(StreamWriter bw2 = new StreamWriter(File.Create(path)))
{
foreach(Process process in ap)
{
await bw2.WriteAsync("test" + " " + process.ProcessName.ToString());
}
}
}
}
catch(Exception ex) { MessageBox.Show(ex.Message); }
}

Try splitting the initial problem into smaller, easier tasks:
Lines we want to write down:
var linesToWrite = Process
.GetProcesses()
.Select(process => $"test {process.ProcessName}");
UI:
using (SaveFileDialog dialog = new SaveFileDialog() {
//TODO: here we put dialog's parameters
}) {
if (dialog.ShowDialog() == DialogResult.OK) {
//TODO: here we put the main routine
}
}
Combining it all together:
private void button5_Click(object sender, EventArgs e) {
using (SaveFileDialog dialog = new SaveFileDialog() {
//TODO: here we put dialog's parameters
}) {
if (dialog.ShowDialog() == DialogResult.OK) {
File.WriteAllLines(dialog.FileName, Process
.GetProcesses()
.Select(process => $"test {process.ProcessName}"));
}
}
}

Related

FileSystemWatcher C# - cannot access file because it is being used by another process

I'm using FileSystemWatcher to detect directory changes, and after that I read file content and insert it to database.
Here's my code:
private FileSystemWatcher _watcher;
public MainWindow()
{
try
{
InitializeComponent();
GetFiles();
//Task.Factory.StartNew(() => GetFiles())
// .ContinueWith(task =>
// {
// }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
catch(Exception ex)
{
//..
}
}
public bool GetFiles()
{
_watcher = new FileSystemWatcher(Globals.iniFilesPath, "*.ini");
_watcher.Created += FileCreated;
_watcher.IncludeSubdirectories = false;
_watcher.EnableRaisingEvents = true;
return true;
}
private void FileCreated(object sender, FileSystemEventArgs e)
{
try
{
string fileName = Path.GetFileNameWithoutExtension(e.FullPath);
if (!String.IsNullOrEmpty(fileName))
{
string[] content = File.ReadAllLines(e.FullPath);
string[] newStringArray = content.Select(s => s.Substring(s.LastIndexOf('=') + 1)).ToArray();
ChargingStationFile csf = new Product
{
Quantity = Convert.ToDecimal(newStringArray[1]),
Amount = Convert.ToDecimal(newStringArray[2]),
Price = Convert.ToDecimal(newStringArray[3]),
FileName = fileName
};
ProductController.Instance.Save(csf);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
If I run this code with CTRL+F5 I received this message:
But If I go with F5 (Debugging mode) than I receive this and not this error about cannot access process and item is sucessfully saved. This is confusing me really..
Should I dispose watcher? or something like that? Maybe I'm missing something here?
This is first time I'm using FileSystemWatcher, obliviously something is really wrong here..
P.S I've found out that this line is causing an exception:
string[] content = File.ReadAllLines(e.FullPath);
how come?
Thanks guys
Cheers
File.ReadAllLines() cannot access the file when it is open for writing in another application but you can use a FileStream and StreamReader instead.
Replace string[] content = File.ReadAllLines(e.FullPath); with the following code and you should be able to read the contents of the file regardless of whether it is open in another application:
List<string> content = new List<string>();
using (FileStream stream = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader sr = new StreamReader(stream))
{
while (!sr.EndOfStream)
content.Add(sr.ReadLine());
}
As mention in this answer:
Most likely what is happening here is that the FileCreated event is
being raised and tries to process the file before is has been
completely written to disk.
So, you need to wait until the file has finished to copy. According to this other answer:
From the documentation for FileSystemWatcher:
The OnCreated event is raised as soon as a file is created. If a file
is being copied or transferred into a watched directory, the OnCreated
event will be raised immediately, followed by one or more OnChanged
events.
So, a workaround for your case will be to create a list of strings containing the paths of the files that could not be read in the Created method handler, and re-process those paths in the Changed event of the FileSystemWatcher (read the comments in the code) :
public partial class MainWindow : Window {
private FileSystemWatcher _watcher;
public MainWindow() {
try {
InitializeComponent();
GetFiles();
} catch (Exception ex) {
MessageBox.Show($"Exception: {ex.Message}");
}
}
private bool GetFiles() {
_watcher = new FileSystemWatcher(#"C:\TestFolder", "*.ini");
_watcher.Created += FileCreated;
_watcher.Changed += FileChanged; // add this.
_watcher.IncludeSubdirectories = false;
_watcher.EnableRaisingEvents = true;
return true;
}
// this field is new, and contains the paths of the files that could not be read in the Created method handler.
private readonly IList<string> _waitingForClose = new List<string>();
private void FileChanged(object sender, FileSystemEventArgs e) {
if (_waitingForClose.Contains(e.FullPath)) {
try {
string[] content = File.ReadAllLines(e.FullPath);
string[] newStringArray = content.Select(s => s.Substring(s.LastIndexOf('=') + 1)).ToArray();
MessageBox.Show($"On FileChanged: {string.Join(" --- ", newStringArray)}");
// Again, process the data from the file to saving in the database.
// removing the path, so as not to reprocess the file..
_waitingForClose.Remove(e.FullPath);
} catch (Exception ex) {
MessageBox.Show($"Exception on FileChanged: {ex.Message} - {e.FullPath}");
}
}
}
private void FileCreated(object sender, FileSystemEventArgs e) {
try {
string fileName = Path.GetFileNameWithoutExtension(e.FullPath);
if (!String.IsNullOrEmpty(fileName)) {
string[] content = File.ReadAllLines(e.FullPath);
string[] newStringArray = content.Select(s => s.Substring(s.LastIndexOf('=') + 1)).ToArray();
MessageBox.Show($"On FileCreated: {string.Join(" --- ", newStringArray)}");
// process the data from the file to saving in the database.
}
} catch (Exception ex) {
// if the method fails, add the path to the _waitingForClose variable
_waitingForClose.Add(e.FullPath);
//MessageBox.Show($"Exception on FIleCreated: {ex.Message} - {e.FullPath}");
}
}
}

Wait for previous function to complete

I'm working on a web installer and one of the things I have currently is
void MoveFiles()
{
lbldlstatus.Text = "Moving Files";
string InstallDirectory = Directory.GetCurrentDirectory() + "/DoxramosRepack-master";
DirectoryInfo d = new DirectoryInfo(InstallDirectory);
foreach(var file in d.GetFiles("*"))
{
try
{
if (File.Exists(file.Name)) {
File.Delete(file.Name);
}
Directory.Move(file.FullName, file.Name);
Cleanup();
}
catch(Exception e)
{
MessageBox.Show(e.ToString());
lbldlstatus.Text = "Repack Installation Failed";
}
}
}
void Cleanup()
{
lbldlstatus.Text = "Cleaning Up Files";
try
{
if (File.Exists("Repack.zip"))
{
File.Delete("Repack.zip");
}
if(Directory.Exists("DoxramosRepack-master"))
{
Directory.Delete("DoxramosRepack-master");
}
lbldlstatus.Text = "Repack Installed Successfully";
}
When I get to Cleanup() I have a System.IO.IOException.
Process cannot access the file Repack.zip because it being used by
another process.
The full code runs
Download->Extract->Move->Cleanup.
I'm not sure what process is being used, but I'm looking to find a way for each process to wait for the previous to finish before starting.
According to extract code below
void Extract()
{
string zipPath = #"Repack.zip";
string extractPath = #".";
try
{
using (ZipFile unzip = ZipFile.Read(zipPath))
{
unzip.ExtractAll(extractPath);
lbldlstatus.Text = "Extracting Files";
MoveFiles();
}
}
catch (ZipException e)
{
MessageBox.Show(e.ToString());
lbldlstatus.Text = "Repack Installation Failed";
}
}
You are calling the move files before you are finish with the zip file. Seeing as the move file method is responsible for calling the clean up function then you should make sure that the zip file is already disposed of before trying to delete it.
void Extract()
{
string zipPath = #"Repack.zip";
string extractPath = #".";
try
{
using (ZipFile unzip = ZipFile.Read(zipPath))
{
unzip.ExtractAll(extractPath);
lbldlstatus.Text = "Extracting Files";
}
MoveFiles();
}
catch (ZipException e)
{
MessageBox.Show(e.ToString());
lbldlstatus.Text = "Repack Installation Failed";
}
}
Clean up should also be called after everything has been moved. Currently the example code is calling it repeatedly in the for loop.
The code which you pasted on pastebin is different from what you have posted here. The code in pastebin never calls cleanup.
Anyways the problem is because you are calling MoveFiles() from within the using block here:
using (ZipFile unzip = ZipFile.Read(zipPath))
{
unzip.ExtractAll(extractPath);
lbldlstatus.Text = "Extracting Files";
MoveFiles();
}
Move it outside the using block.

C# thread hangs on close file

I have a thread that calls a static method to update file properties using WindowsAPICodePack ShellPropertyWriter and BackgroundWorker. The thread calls the method below for each file in a folder of 1000+ files and hangs on the ShellPropertyWriter.close() after the 700th update or so.
Nothing to do with the file itself, tried using different files that successfully updated before.
public static bool ShellPropertyUpdate(VideoEntry mediaEntry)
{
try
{
ShellFile mediafile = ShellFile.FromFilePath(mediaEntry.FilePath);
ShellPropertyWriter pw = mediafile.Properties.GetPropertyWriter();
pw.WriteProperty(SystemProperties.System.Music.Artist, mediaEntry.Actor);
pw.WriteProperty(SystemProperties.System.Music.Genre, mediaEntry.Genre);
pw.WriteProperty(SystemProperties.System.Rating, mediaEntry.Rating);
pw.Close();
}
catch (Exception ex)
{
return false;
}
return true;
}
private void mnuWriteMetadataToFiles_Click(object sender, EventArgs ev)
{
this.WorkerThread = new BackgroundWorker();
this.WorkerThread.DoWork += new DoWorkEventHandler(WorkerThread_WriteMetadataToFiles);
this.WorkerThread.ProgressChanged += new ProgressChangedEventHandler(WorkerThread_ProgressChanged);
this.WorkerThread.RunWorkerCompleted += (s, e) => WorkerThread_Completed("Writing metadata to files", s, e);
this.WorkerThread.WorkerReportsProgress = true;
this.WorkerThread.WorkerSupportsCancellation = true;
this.WorkerThread.RunWorkerAsync(WMPlayer);
}
private void WorkerThread_WriteMetadataToFiles(object sender, DoWorkEventArgs e)
{
int counter = 0;
BackgroundWorker worker = (BackgroundWorker)sender;
MediaPlayer wmp = (MediaPlayer)e.Argument;
// ... Loop with the foreach video in the library and write it to file.
foreach (VideoEntry entry in wmp.Videos)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
worker.ReportProgress(counter, "Updating '" + entry.Filename + "'" + Environment.NewLine + "Processing file");
if (VideoToFile.ShellPropertyUpdate(entry))
{
result &= true;
}
counter++;
}
}
e.Result = result;
}
Never heard of this assembly before, but it smells like handle exhaustion to me. Try this instead:
using (ShellFile mediafile = ShellFile.FromFilePath(mediaEntry.FilePath))
{
ShellPropertyWriter pw = mediafile.Properties.GetPropertyWriter();
pw.WriteProperty(SystemProperties.System.Music.Artist, mediaEntry.Actor);
pw.WriteProperty(SystemProperties.System.Music.Genre, mediaEntry.Genre);
pw.WriteProperty(SystemProperties.System.Rating, mediaEntry.Rating);
pw.Close();
}
Here every file handle is closed immediately, instead of at garbage collector's discretion. ShellFile must implement IDisposable for this to work, otherwise this code will not compile. I'm fairly certain that ShellFile implements it.
Apparently it does have something to do with the files themselves. I took out few problem files and the Thread continued processing until the next problem file. I have no clue what's wrong with the file, however I'm willing to pass on updating problem files. Is there a way to stop/kill the thread? I can't use DoWorkEventArgs.cancel() since the thread is hanging and not coming back.

Background Worker Locking Main Thread - Windows Forms C#

I have a background worker that I use to create files in the background.
I had it working so that the files were created and the UI was still responsive.
I made some changes and now I can't figure out why the background worker is locking my main thread.
Here are my background worker methods. I don't have a progress changed event.
private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
{
if (filecreator_bgw.CancellationPending == true)
{
e.Cancel = true;
}
else
{
myManager.createFiles((SelectedFileTypes) e.Argument);
}
}
private void filecreator_bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
//status_label.Text = "Canceled!";
}
else if (e.Error != null)
{
//status_label.Text = "Error: " + e.Error.Message;
}
else
{
// Check the file manager object to see if the files were created successfully
status_label.Text = "COMPLETE";
file_statusLabel.Text = "Files Created: " + DateTime.Now.ToShortTimeString();
System.Threading.Thread.Sleep(5000);
status_label.Text = "Click Create Files to Begin";
createfiles_button.Enabled = true;
}
}
Here is the method to create the files.
public void createFiles(SelectedFileTypes x)
{
if (string.IsNullOrEmpty(Filename) || (x.isCSV == false && x.isTAB == false && x.isXML == false))
{
filesCreated = false;
return;
}
// Declare the streams and xml objects used to write to the output files
XDocument xmlFile;
StreamWriter swCSV;
StreamWriter swTAB;
CSVFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
Path.GetFileNameWithoutExtension(Filename) + "CSV_TEST.csv";
swCSV = new StreamWriter(CSVFilename);
TABFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
Path.GetFileNameWithoutExtension(Filename) + "TAB_TEST.csv";
swTAB = new StreamWriter(TABFilename);
XMLFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
Path.GetFileNameWithoutExtension(Filename) + "XML_TEST.csv";
xmlFile = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("Crosswalk"));
xmlFile.Add(new XElement("ACCOUNTS"));
// String array for use when creating xml nodes
string[] splits;
// String used to read in a line from the input file
string line = "";
// Use a try and catch block, if any errors are caught, return false
try
{
// Read each line in the file and write to the output files
using (StreamReader sr = new StreamReader(Filename))
{
int i = 0;
while ((line = sr.ReadLine()) != null)
{
if (x.isCSV)
{
swCSV.WriteLine(line.Replace(delim, ","));
}
if (x.isTAB)
{
swTAB.WriteLine(line.Replace(delim, "\t"));
}
if (x.isXML)
{
if (i <= 0)
{
i++;
continue;
}
splits = line.Split(new string[] { delim }, StringSplitOptions.RemoveEmptyEntries);
xmlFile.Root.Add(
new XElement("ACCOUNTS",
from s in header
select new XElement(s, splits[Array.IndexOf(header, header.Where(z => z.Equals(s, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault())])
)
);
}
}
// Dispose of all objects
swCSV.Close();
swCSV.Dispose();
swTAB.Close();
swTAB.Dispose();
if (x.isXML)
{
//xmlFile.Save(Path.GetFullPath(Filename) + Path.GetFileNameWithoutExtension(Filename) + "_TEST.xml");
xmlFile.Save(XMLFilename);
}
}
}
catch (Exception)
{
filesCreated = false;
return;
}
// Return true if file creation was successfull
filesCreated = true;
}
In the do work method, I build a simple struct to determine what output file types should be made and then I pass it to the method. If I comment out that call to create the files, the UI still does not respond.
In the create files method, I build out the files based on the input file that I am transforming. I do use a LINQ statement to help build out XML tags, but the arrays holding the tags values are small, 3-5 elements depending on the file chosen.
Is there a simple solution, or should I re-design the method. If I have to re-design, what are things I should keep in mind to avoid locking the main thread.
Thanks
Here is how I call the runworkerasync method:
private void createfiles_button_Click(object sender, EventArgs e)
{
SelectedFileTypes selVal = new SelectedFileTypes();
foreach (var structVal in outputformats_checkedListBox.CheckedItems)
{
if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
selVal.isCSV = true;
if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
selVal.isTAB = true;
if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
selVal.isXML = true;
}
// Call the FileManager object's create files method
createfiles_button.Enabled = false;
filecreator_bgw.RunWorkerAsync(selVal);
}
UPDATE:
I updated the call to start the worker and then the call to create the files using the argument passed into the worker.
You cannot interact with most UI controls directly from a BackgroundWorker. You need to access outputformats_checkedListBox.CheckedItems from the UI thread and pass the resulting SelectedFileTypes object into the BackgroundWorker as a parameter.
Also, pleas enote that your cancellation logic really didn't do much. In order for it to work well, you need to check CancellationPending throughout the process, not just when starting.
Here is a rough example of how you should start the worker:
private void StartWorker()
{
SelectedFileTypes selVal = new SelectedFileTypes();
foreach (var structVal in outputformats_checkedListBox.CheckedItems)
{
if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
selVal.isCSV = true;
if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
selVal.isTAB = true;
if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
selVal.isXML = true;
}
filecreator_bgw.RunWorkerAsync(selVal);
}
private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
{
SelectedFileTypes selVal = (SelectedFileTypes)e.Argument;
myManager.createFiles(selVal);
}

Update ui thread

I have a ListBox which I put some files, if the file is not AVI I automatically converts it but I want when the files converting message will write on a label that the files are now converted to another format, i know i need use Dispatcher in order to update the UI thread but i use now Winform instead of WPF, and i need help with this.
BTW i cannot use Task because i am using .Net 3.5
private void btnAdd_Click(object sender, EventArgs e)
{
System.IO.Stream myStream;
OpenFileDialog thisDialog = new OpenFileDialog();
thisDialog.InitialDirectory = "c:\\";
thisDialog.Filter = "All files (*.*)|*.*";
thisDialog.FilterIndex = 1;
thisDialog.RestoreDirectory = false;
thisDialog.Multiselect = true; // Allow the user to select multiple files
thisDialog.Title = "Please Select Source File";
thisDialog.FileName = lastPath;
List<string> list = new List<string>();
if (thisDialog.ShowDialog() == DialogResult.OK)
{
foreach (String file in thisDialog.FileNames)
{
try
{
if ((myStream = thisDialog.OpenFile()) != null)
{
using (myStream)
{
listBoxFiles.Items.Add(file);
lastPath = file;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
for (int i = 0; i < listBoxFiles.Items.Count; i++)
{
string path = (string)listBoxFiles.Items[i];
FileInfo fileInfo = new FileInfo(path);
if (fileInfo.Extension != ".AVI")
{
listToRemove.Add(path);
}
}
(new System.Threading.Thread(sendFilesToConvertToPcap)).Start();
foreach (string file in listToRemove) //remove all non .AVI files from listbox
{
listBoxFiles.Items.Remove(file);
}
}
}
this function need to change the Label:
public void sendFilesToConvertToPcap()
{
if (listToRemove.Count == 0) // nothing to do
{
return;
}
lblStatus2.Content = "Convert file to .AVI...";
foreach (String file in listToRemove)
{
FileInfo fileInfo = new FileInfo(file);
myClass = new (class who convert the files)(fileInfo);
String newFileName = myClass.mNewFileName;
listBoxFiles.Items.Add(myClass._newFileName);
}
lblStatus2.Content = "Finished...";
}
From your question, it seems that you'd like to convert several files. You may want to consider using the BackgroundWorker class and overwrite the DoWork and ProgressChanged events as described in this article. You can update the label and other controls in the ProgressChanged event.
public void sendFilesToConvertToPcap()
{
.....
....
this.Invoke((MethodInvoker)delegate {
lblStatus2.Text = "Convert file to .AVI..."; });
....
}
This is a very common requirement for long-running processes. If you don't explicitly call methods on a separate thread, they will automatically run on the main (see: UI) thread and cause your UI to hand (as I suspect you already know).
http://www.dotnetperls.com/backgroundworker
Here is an old, but excellent link with an example on how to use the background worker to handle the threadpool for you.
This way, you can just create a worker to manage running your conversion process on a separate thread, and I believe there is even an example for creating a process bar.

Categories