In my following code, the Timer_Elapsed event is not hitting.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Reflection;
using System.Diagnostics;
namespace Logger
{
internal class Country
{
public string CountryName { get; set; }
public string CountryCode { get; set; }
}
internal class CountryLogger
{
List<Country> countries = new List<Country>()
{
new Country{CountryName = "India", CountryCode="IND"},
new Country{CountryName = "United States of America", CountryCode="USA"},
new Country{CountryName = "United Kingdom", CountryCode="UK"},
new Country{CountryName = "Australia", CountryCode="AUS"}
};
public void WriteToLog()
{
string fileName = #"C:\ParallelLog.txt";
using (StreamWriter writer = new StreamWriter(fileName, true))
{
foreach (Country Country in countries.AsParallel().AsOrdered())
{
writer.WriteLine("{0} - {1}", Country.CountryName, Country.Count ryCode);
writer.WriteLine();
}
}
}
}
internal class Program
{
static void Main(string[] args)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Enabled = true;
timer.Interval = 3 * 60 * 1000;
timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
}
static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//for (int i = 0; i < 10; i++)
//{
Task task = Task.Factory.StartNew(() =>
{
CountryLogger countriesLogger = new CountryLogger();
countriesLogger.WriteToLog();
});
//}
}
}
}
Also the Task task = Task.Factory.StartNew(() => object is not looping through the for loop (Commented because it doesn't work).
Can someone suggest the better way of writing this code to work?!
What should the first program thread do whilst letting the timer run?
Here, I'm just waiting for the user to hit return:
static void Main(string[] args)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
timer.Interval = 3 * 60 * 1000;
timer.Enabled = true;
Console.WriteLine("Press return to exit");
Console.ReadLine();
GC.KeepAlive(timer); //Can't decide if this is needed or not
}
What is important is that if you return from Main (or just hit that final }), your program is going to exit. I can't remember whether timers keep themselves alive in terms of Garbage Collection, so I've added a GC.KeepAlive just in case.
I've also switched around the order of assignments on the timer - so that it's not enabled until I know that the handler is attached and that the interval is set correctly.
Related
So basically, I m making a program where 25 tasks write the same random task to one file until it reaches a certain size, and I would like to dynamically display the file size of the selected file in 0.5 seconds interval, so I made a timer hooked with Timer_Elapsed which should be executed every 0.5 seconds and display on UI which I specify on mainWindow.xaml on the textblock x:Name="fileSize", so I placed the createTimer function in to btnGo_Click function in mainwindow.xaml.cs so the event would extract the right fileInfo of the selectedFile. Any advice for my wrong would be appreciated. I'm also sharing the FileIO class in case it is needed, so they are full solutions. Even aside from the questions I asked, any general advice to better my code would be appreciated because I need to get a grasp of the good code example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.Threading;
namespace WriteFile
{
internal class FileIO
{
private object _lock = new object();
public volatile bool sizeReached = false;
private StreamWriter sw = null;
Mutex mut = null;
public FileIO()
{
if (!Mutex.TryOpenExisting("MyMutex", out mut))
{
mut = new Mutex(true, "MyMutex");
mut.ReleaseMutex();
}
}
internal void WriteFile(string FilePath)
{
while (!sizeReached)
{
mut.WaitOne();
try
{
using (sw = new StreamWriter(FilePath, true))
{
sw.WriteLine(Guid.NewGuid());
}
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message);
}
finally
{
if (sw != null)
{
sw.Close();
}
}
mut.ReleaseMutex();
}
}
internal void SizeMonitor(string FPath, int MaxSize, Task[] tasks)
{
FileInfo fi = null;
while (!sizeReached)
{
if (File.Exists(FPath))
{
fi = new FileInfo(FPath);
if (fi.Length >= MaxSize)
{
sizeReached = true;
}
}
if (sizeReached)
{
foreach (Task task in tasks)
{
task.Wait();
}
}
Thread.Sleep(1);
}
MessageBox.Show(fi.Length.ToString());
MessageBox.Show("Done");
}
}
}
mainWindow.xaml
<Window x:Class="WriteFile.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WriteFile"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock x:Name="fileSize"/>
<TextBox Name ="TargetSize" VerticalAlignment="Center" FontSize="20">
</TextBox>
<Label Content="Target Size" HorizontalAlignment="Left" Margin="92,150,0,0" VerticalAlignment="Top"/>
<Button Name ="btnGo" Content="Write to file" HorizontalAlignment="Left" Margin="92,267,0,0" VerticalAlignment="Top" Width="100" Click="btnGo_Click"/>
</Grid>
</Window>
mainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using Microsoft.Win32;
using System.ComponentModel;
using System.Timers;
using System.Threading;
using System.Runtime.CompilerServices;
namespace WriteFile
{
public partial class MainWindow : Window
{
System.Timers.Timer timer = new System.Timers.Timer();
Task[] tasks;
Task MonitorTask;
static FileIO fio = new FileIO();
static string fPath;
static FileInfo fileInfo;
public MainWindow()
{
InitializeComponent();
CreateTimer();
}
public void CreateTimer()
{
var timer = new System.Timers.Timer(500); // fire every 0.5 second
timer.Enabled = true;
timer.Elapsed += Timer_Elapsed;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
fileSize.Text = fileInfo.Length.ToString();
}
private void btnGo_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.ShowDialog();
Stream myStream;
saveFileDialog.FilterIndex = 2;
saveFileDialog.RestoreDirectory = true;
if (File.Exists(saveFileDialog.FileName))
{
File.Delete(saveFileDialog.FileName);
}
if ((myStream = saveFileDialog.OpenFile()) != null)
{
StreamWriter sw = new StreamWriter(myStream);
sw.Write(" your text");
myStream.Close();
}
int NoOfTasks = 25;
int Target = Convert.ToInt32(TargetSize.Text);
fPath = saveFileDialog.FileName;
tasks = new Task[NoOfTasks];
fio.sizeReached = false;
fileInfo = new FileInfo(fPath);
for (int i = 0; i < NoOfTasks; i++)
{
tasks[i] = new Task(() => fio.WriteFile(fPath));
tasks[i].Start();
}
MonitorTask = new Task(() => fio.SizeMonitor(fPath, Target, tasks));
MonitorTask.Start();
}
}
}
As mentioned by others, FileInfo.Length value is pre-cached. If the associated file has changed, the FileInfo requires to refresh those cached values. To accomplish this, you must explicitly call the FileInfo.Refresh method. FileInfo does not monitor the associated file.
Furthermore, System.Threading.Timer executes the callback on a background thread. In WPF you can only reference a DispatcherObject (for example TextBlock) from a dispatcher thread (UI thread). For this reason you should use the DispatcherTimer. DispatcherTimer executes the callback on the dispatcher thread.
Additionally, you would want to stop the timer once the writing to the file is completed.
You should not use Task.Start. Instead of Task.Start and a Mutex you should simply await the Task. In your context, you can await a collection of Task objects using Task.WhenALl.
And instead of creating Task explicitly, you should use the async API of the StreamWriter.
You actually don't have concurrent code (neither would you benefit from it): the Mutex, declaration of a volatile field and the lock object are redundant. Also closing the StreamWriter explicitly in a finally block is not needed as you already use a using-block to handle the lifetime of the instance.
There are some more flaws in your code (some logical issues for instance). I have refactored your version to eliminate some of the issues for example by using the asynchronous StreamWriter API and async/await.
Because it is difficult to make any sense of your code (due to the lack of context) it is not possible to further improve it. For example, creating a single concatenated string (consisting of e.g. 25 values) would result in a single file write (and resource allocation), which would significantly improve the overall performance.
FileIO.cs
namespace WriteFile
{
internal class FileIO
{
public bool IsSizeReached { get; private set; }
public FileIO()
{
}
internal async Task WriteToFileAsync(string filePath)
{
if (this.IsSizeReached)
{
return;
}
using (var sw = new StreamWriter(filePath, true))
{
await sw.WriteLineAsync(Guid.NewGuid());
}
}
internal async Task SizeMonitorAsync(string fPath, int maxSize, IEnumerable<Task> tasks)
{
FileInfo fi = new FileInfo(fPath);
this.IsSizeReached = fi.Length >= maxSize;
if (this.IsSizeReached)
{
return;
}
await Task.WhenAll(tasks);
MessageBox.Show(fi.Length.ToString());
MessageBox.Show("Done");
}
}
}
MainWindow.xaml.cs
namespace WriteFile
{
public partial class MainWindow : Window
{
private DispatcherTimer Timer { get; }
private FileIO Fio { get; }
private FileInfo DestinationFileInfo { get; }
public MainWindow()
{
InitializeComponent();
this.Fio = new FileIO();
}
public void StartTimer()
{
this.Timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(500),
DispatcherPriority.Background,
OnTimerElapsed,
this.Dispatcher);
this.Timer.Start();
}
public void StopTimer() => this.Timer.Stop();
private void OnTimerElapsed(object sender, EventArgs e)
{
this.fileSize.Text = this.DestinationFileInfo.Length.ToString();
}
private async void btnGo_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.ShowDialog();
saveFileDialog.FilterIndex = 2;
saveFileDialog.RestoreDirectory = true;
if (File.Exists(saveFileDialog.FileName))
{
File.Delete(saveFileDialog.FileName);
}
Stream myStream;
if ((myStream = saveFileDialog.OpenFile()) != null)
{
// Dispose StreamWriter and underlying Stream
using (var sw = new StreamWriter(myStream))
{
await sw.WriteAsync(" your text");
}
}
int noOfTasks = 25;
// TODO::You must validate the input to prevent invalid or unreasonable values.
// Currently, this line will throw and crash the application if the input is invalid (not numeric).
int target = Convert.ToInt32(TargetSize.Text);
string fPath = saveFileDialog.FileName;
var tasks = new List<Task>();
this.DestinationFileInfo = new FileInfo(fPath);
StartTimer();
for (int i = 0; i < noOfTasks; i++)
{
Task writeToFileTask = this.Fio.WriteToFileAsync(fPath);
tasks.Add(writeToFileTask);
}
await this.Fio.SizeMonitorAsync(fPath, target, tasks));
StopTimer();
}
}
}
Timers.Timer works asynchronously and it raises the Elapsed event on the pool thread if SynchronizingObject is not set.
And work with WPF UI elements can only be done on the thread of their Dispatcher (almost always this is the main UI thread of the application).
Two solutions out of many possible:
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
// Asynchronously getting the right data
string text = fileInfo.Length.ToString();
// Using the Window's Dispatcher
// to setting ready-made data to UI elements.
Dispatcher.BeginInvoke(() => fileSize.Text = text);
}
The second option is without using a timer:
public MainWindow()
{
Initialized += RefreshAsync;
InitializeComponent();
}
public async void RefreshAsync(object? sender, EventArgs e)
{
while(true)
{
string text = await Task.Run(() => fileInfo.Length.ToString());
fileSize.Text = text;
await Task.Delay(500);
}
}
I am trying to create an application that shows the system statistics but it also executes a timer each second to update the current CPU clock speed. I have tried numerous solutions but none seem to work for me. I will attach my code below. I am using the System.Management DLL to call the stats I am wanting. Thank you!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management;
namespace ezCPU
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = this.Text + " - v" + Application.ProductVersion;
getCPUInfo();
}
public string convertClockSpeed(string s)
{
double clockSpeed = Convert.ToDouble(s);
double newClockSpeed = (clockSpeed / 1000);
return Math.Round(newClockSpeed, 2).ToString() + " GHz";
}
public void getCPUInfo()
{
ManagementObjectSearcher myProcessorObject = new ManagementObjectSearcher("select * from Win32_Processor");
foreach (ManagementObject obj in myProcessorObject.Get())
{
string cpuName = obj["Name"].ToString();
txtCPUName.Text = cpuName;
string cpuManufacturer = obj["Manufacturer"].ToString();
if (cpuManufacturer == "GenuineIntel")
{
txtCPUManufacturer.Text = "Genuine Intel";
}
else
{
txtCPUManufacturer.Text = cpuManufacturer;
}
string cpuCores = obj["NumberOfCores"].ToString();
txtCores.Text = cpuCores;
string cpuThreads = obj["ThreadCount"].ToString();
txtThreads.Text = cpuThreads;
string cpuMaxSpeed = obj["MaxClockSpeed"].ToString();
txtClockSpeed.Text = convertClockSpeed(cpuMaxSpeed);
}
}
private void cpuTimer_Tick(object sender, EventArgs e)
{
ManagementObjectSearcher myProcessorObject = new ManagementObjectSearcher("select * from Win32_Processor");
foreach(ManagementObject obj in myProcessorObject.Get())
{
string currentClockSpeed = obj["CurrentClockSpeed"].ToString();
txtCPUSpeed.Text = convertClockSpeed(currentClockSpeed);
}
}
}
}
This is the result of the code. However, I am unable to move the form around while it is still updating.
Have you tried the windows forms timer? It has the elapsed event that even spins up a background thread to do the work, keeping your ui responsive:
https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.elapsed?redirectedfrom=MSDN&view=netframework-4.8
public class Form1: Form
{
public System.Windows.Forms.Timer aTimer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
// Create a timer and set a two second interval.
aTimer.Interval = 2000;
// Hook up the tick event for the timer.
aTimer.Tick += OnTimedEvent;
// Have the timer fire repeated events (true is the default)
aTimer.AutoReset = true;
// Start the timer
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program at any time... ");
Console.ReadLine();
}
private void OnTimedEvent(object sender, EventArgs e)
{
// update your statistics here
}
}
If you want to update the ui there, then the tick event is the way to go: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer.tick?view=netframework-4.8
So I created a service to run on a server and look at different folder shares and put them in a database table for a customized search but it pulls a datatable of search locations in and iterates through them one at a time which worked great with small test folders. Now that I tried to use it for real it does not make it through the first folder path before my timer restarts it. I could make it time longer but I basically want this to run constantly and start over as soon as the first one is done or not sure if I can run all paths at the same time. I had it running every 30 Minutes but definitely not long enough.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace MFTSearchService
{
public partial class MFTSearchService : ServiceBase
{
Timer timer = new Timer(); // name space(using System.Timers;)
public MFTSearchService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
WriteToFile("Service is started at " + DateTime.Now);
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = TimeSpan.FromMinutes(10).TotalMilliseconds; //number in milisecinds
timer.Enabled = true;
//global::MFTSearchService.Search.SearchStart();
}
protected override void OnStop()
{
WriteToFile("Service is stopped at " + DateTime.Now);
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
WriteToFile("Service is recall at " + DateTime.Now);
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = TimeSpan.FromMinutes(30).TotalMilliseconds; //number in milisecinds
timer.Enabled = true;
global::MFTSearchService.Search.SearchStart();
}
public void WriteToFile(string Message)
{
string path = AppDomain.CurrentDomain.BaseDirectory + "\\Logs";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\Logs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
if (!File.Exists(filepath))
{
// Create a file to write to.
using (StreamWriter sw = File.CreateText(filepath))
{
sw.WriteLine(Message);
}
}
else
{
using (StreamWriter sw = File.AppendText(filepath))
{
sw.WriteLine(Message);
}
}
}
}
}
Instead of calling
global::MFTSearchService.Search.SearchStart();
in your timer, you can add a BackgroundWorker and start it in your timer. For every tick on your timer, you can check if Backgroundworker is busy or not. BackgroundWorker will not run until it's done if you check it before running it.
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
WriteToFile("Service is recall at " + DateTime.Now);
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = TimeSpan.FromMinutes(30).TotalMilliseconds; //number in milisecinds
timer.Enabled = true;
if (!backgroundWorker1.IsBusy())
{
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
global::MFTSearchService.Search.SearchStart();
}
Background worker will not lock your main thread so your timer will continue looping. So you're free to put any interval to it.
I've created a Gist with my NetMQ implementation as I feel it a bit much to paste here: https://gist.github.com/gthvidsten/e626d7e6c51012b1ba152d22e034d93d
If I do the following in a .Net Core console app, everything works fine and I receive the MessageReceived event:
static void Main(string[] args)
{
_transportWithHost = new NetMqTransport(
"tcp://localhost:9990",
"tcp://localhost:9991",
true);
_transportWithHost.Start();
Console.WriteLine("Press [Enter] to publish");
Console.ReadLine();
_transportWithHost.MessageReceived += (sender, e) =>
{
; // Breakpoints here are hit
};
_transportWithHost.Publish(new byte[] { 1, 2, 3, 4 });
Console.WriteLine("Press [Enter] to exit");
Console.ReadLine();
}
However, if I try to do the same in an NUnit test environment, the MessageReceived event is never fired!
class NetMqTransportTests
{
private NetMqTransport _transportWithHost;
[OneTimeSetUp]
public void Setup()
{
_transportWithHost = new NetMqTransport(
"tcp://localhost:9990",
"tcp://localhost:9991",
true);
_transportWithHost.Start();
}
[Test]
public void PublishTest()
{
ManualResetEvent mre = new ManualResetEvent(false);
_transportWithHost.MessageReceived += (sender, e) =>
{
mre.Set();
// Breakpoints here are never hit as MessageReceived is never called
};
_transportWithHost.Publish(new byte[] { 1, 2, 3, 4 });
bool eventFired = mre.WaitOne(new TimeSpan(0, 0, 5));
Assert.True(eventFired);
}
}
Why does the virtually identical code work in a console app, but not in an NUnit environment?
I was able to reproduce it and found this thread https://github.com/zeromq/netmq/issues/482 which indicates its a timing issue between starting the Publisher and time for the Subscriber to receive the message.
using NetMQ;
using NetMQ.Sockets;
using NUnit.Framework;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Tests
{
class NetMqTransportTests
{
[Test]
public void TestMulticastNetMQ()
{
bool wasCalled = false;
var coreEventbus = "tcp://localhost:12345";
Task.Run(() =>
{
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect(coreEventbus);
subSocket.Subscribe("account");
while (true)
{
string messageTopicReceived = subSocket.ReceiveFrameString();
string messageReceived = subSocket.ReceiveFrameString();
Assert.IsTrue(messageReceived == "testing");
wasCalled = true;
break;
}
}
});
Thread.Sleep(TimeSpan.FromSeconds(1));
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind(coreEventbus);
Thread.Sleep(500);
pubSocket.SendMoreFrame("account").SendFrame("testing");
}
Thread.Sleep(TimeSpan.FromSeconds(5));
Assert.IsTrue(wasCalled);
}
}
}
Update:
Here are the Unit Tests that come with the NetMQ library: https://github.com/zeromq/netmq/blob/master/src/NetMQ.Tests/XPubSubTests.cs
See how they break up instantiating NetMqTransport into using both XPublisherSocket and XPublisherSocket...
Also notice as per the issue 482 they do a 500ms delay to let the subscriber connect before receiving the message, just like they were talking about in the issue:
[Fact]
public void TopicPubSub()
{
using (var pub = new XPublisherSocket())
using (var sub = new XPublisherSocket())
{
var port = pub.BindRandomPort("tcp://127.0.0.1");
sub.Connect("tcp://127.0.0.1:" + port);
sub.SendFrame(new byte[] { 1, (byte)'A' });
// let the subscriber connect to the publisher before sending a message
Thread.Sleep(500);
var msg = pub.ReceiveFrameBytes();
Even tho I setup a background worker, I can't seem to make it not freeze the UI.
I looked around online but could not find a fix. I have tried creating basic threads and that worked but I need to also update UI stuff as it runs so thats why I switched to backgroundworkers, but if you could help me figure out how to use a basic thread and get that working that would be ok.
using MediaToolkit;
using MediaToolkit.Model;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using VideoLibrary;
namespace ConsoleApp1
{
public partial class Form1 : Form
{
private List<VideoInfo> vids = new List<VideoInfo>();
private List<VideoInfo> failedVids = new List<VideoInfo>();
private int threads = 0;
BackgroundWorker worker;
private delegate void DELEGATE();
static void Main(string[] args)
{
Form1 form = new Form1();
form.ShowDialog();
}
public Form1()
{
InitializeComponent();
worker = new BackgroundWorker();
}
private void button1_Click(object sender, EventArgs e)
{
if(threads == 0)
{
progressBar1.Maximum = vids.Count;
progressBar1.Value = 0;
failedVids.Clear();
worker.DoWork += doWork;
worker.RunWorkerAsync();
//downloadVid();
}else
{
Console.WriteLine("Waiting for threads" + threads);
}
/*Thread thread1 = new Thread(new ThreadStart(downloadVid));
thread1.Start();*/
}
private void button1_Click_1(object sender, EventArgs e)
{
VideoInfo vid = new VideoInfo(tbLink.Text, tbTitle.Text, tbArtist.Text);
vids.Add(vid);
tbLink.Clear();
tbTitle.Clear();
listView1.Items.Add(vid.getLink());
}
private int downloadThread(int i)
{
Console.WriteLine(i);
var source = #"C:\Users\derri\Downloads\DownloadTest\";
var youtube = YouTube.Default;
VideoInfo vidInfo = vids[(int) i];
var vid = youtube.GetVideo(vidInfo.getLink());
Console.WriteLine(vidInfo.getLink());
try
{
File.WriteAllBytes(source + vid.FullName, vid.GetBytes());
}
catch (Exception e)
{
failedVids.Add(vids[(int)i]);
Console.WriteLine(e);
goto End;
}
var inputFile = new MediaFile { Filename = source + vid.FullName };
var outputFile = new MediaFile { Filename = $"{source + vidInfo.getArtist() + " - " + vidInfo.getTitle()}.mp3" };
using (var engine = new Engine())
{
engine.GetMetadata(inputFile);
engine.Convert(inputFile, outputFile);
}
File.Exists(outputFile.Filename);
File.Delete(inputFile.Filename);
setTags(vidInfo.getArtist(), vidInfo.getTitle());
End:
threads--;
return 1;
}
private void doWork(object sender, DoWorkEventArgs e)
{
Delegate del = new DELEGATE(downloadVid);
this.Invoke(del);
}
private void downloadVid()
{
int prog = 0;
for (int i = 0; i < vids.Count; i++)
{
Console.WriteLine(i);
while ((threads > 5)) { }
Thread thread = new Thread(() => { prog += downloadThread(i); });
thread.Start();
threads++;
System.Threading.Thread.Sleep(1000);
//thread.Join();
/*ParameterizedThreadStart start = new ParameterizedThreadStart(downloadThread);
Thread t = new Thread(start);
t.Start(i);
progressBar1.Value++;
threads++;*/
}
while (threads > 0){}
foreach (VideoInfo failedVid in failedVids)
{
listView2.Items.Add(failedVid.getLink());
}
listView1.Clear();
vids.Clear();
}
private void setTags(string artist, string title)
{
TagLib.File file = TagLib.File.Create("C:\\Users\\derri\\Downloads\\DownloadTest\\" + artist + " - " + title + ".mp3");
file.Tag.Artists = (new String[] { artist });
file.Tag.Title = (title);
file.Save();
}
}
}
this.Invoke will run on the UI thread.
Look into how to use either ReportProgress or RunWorkerCompleted events of the BackgroundWorker class. The both allow you to pass data from the background thread to the UI thread.