Using webclient to download file in a WPF application - c#

I'm making a WPF application.
I'm using WebClient to download files. I have a list of the name of the files that should be downloaded from a current path. I use an foreach to iterate through each name and then download each file sequency. The name of the file i get from a torrent file which i decode.
public class DownloadGameFile
{
private DownloadTorrentFile DLTorrent;
//List of file that already exist
private List<string> ExistFile = new List<string>();
DirectoryInfo fileInfo;
private volatile bool _completed;
private string savePath = #"C:\Program Files (x86)\program\Client\package\downloads\";
public DownloadGameFile()
{
DLTorrent = new DownloadTorrentFile();
fileInfo = new DirectoryInfo(savePath);
}
public bool StartDownload(int torrentId)
{
try
{
DLTorrent.DecodeTorrent(torrentId);
//File info from a Directory
FileInfo[] files = fileInfo.GetFiles();
foreach (FileInfo i in files)
{
Console.WriteLine("Files exit ");
if (DLTorrent.GameInfomation[i.Name] != i.Length)
{
i.Delete();
}
else
{
Console.WriteLine("add files ");
ExistFile.Add(i.Name);
}
}
//Make a list which file not downloaded yet
var res = DLTorrent.GameInfomation.Keys.Except(ExistFile);
foreach (var x in res)
{
Console.WriteLine(x);
}
foreach (var x in res)
{
DownloadProtocol("http://cdn.path.com/rental/" + torrentId + "/" + x, savePath + x);
}
return true;
}
catch
{
return false;
}
}
public void DownloadProtocol(string address, string location)
{
WebClient client = new WebClient();
Uri Uri = new Uri(address);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgress);
client.DownloadFileAsync(Uri, location);
}
private void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
// Displays the operation identifier, and the transfer progress.
Console.WriteLine("{0} downloaded {1} of {2} bytes. {3} % complete...",
(string)e.UserState,
e.BytesReceived,
e.TotalBytesToReceive,
e.ProgressPercentage);
}
private void Completed(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled == true)
{
Console.WriteLine("Download has been canceled.");
}
else
{
Console.WriteLine("Download completed!");
}
}
This code work fine in an Console app with a current thread blocker. But when I use the same code in an WPF app It doesn't. I'm using a button to execute the StartDownload() function, but when I do that it start downloading all the files at the same time. Example the first file get 3% done and then it switch to another file and so on. I really don't know why this isn't working.

Have you considered not using .DownloadFileAsync? You can try .DownloadFile and start DownloadProtocol() with a single background thread. Although I think you'll have to rethink your DownloadProgress output.
I do something very similar within a winform.

Related

C# is crashing on an event

I am trying to create a C# service as a console app.
The main code:
static void Main(string[] args)
{
var exitCode = HostFactory.Run(
x =>
{
x.Service<HeartBeat>(s =>
{
s.ConstructUsing(heartbeat => new HeartBeat());
s.WhenStarted(heartbeat => heartbeat.Start());
s.WhenStopped(heartbeat => heartbeat.Stop());
});
x.RunAsLocalSystem();
x.SetServiceName("UpgradeServices");
x.SetDisplayName("Service Upgrade");
x.SetDescription("Service is monitoring new version.");
});
int exitCodeValue = (int)Convert.ChangeType(exitCode, exitCode.GetTypeCode());
Environment.ExitCode = exitCodeValue;
}
Then I have code for deleting and copying files as per below:
public class MovingFiles
{
public string fileName;
public string destPath;
private DirectoryInfo directory;
private DirectoryInfo myFile;
public string sourcePath;
public string targetPath;
public MovingFiles(string sourceFolder, string targetFolder)
{
sourcePath = sourceFolder;
targetPath = targetFolder;
}
public void deleteFilesMethod()
{
System.Threading.Thread.Sleep(10000);
string deleteString;
//First we want to delete all files except for the JSON file as this has all of the important settings
if (System.IO.Directory.Exists(targetPath))
{
string[] files = System.IO.Directory.GetFiles(targetPath);
// Loop through each files and then delete these if they are not the JSON file
foreach (string s in files)
{
deleteString = targetPath;
// The file name which is returned will be deleted
fileName = System.IO.Path.GetFileName(s);
if (fileName != "appsettings.json")
{
deleteString = System.IO.Path.Combine(targetPath, fileName);
try
{
System.IO.File.Delete(deleteString);
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
return;
}
}
}
}
else
{
Console.WriteLine("The loop didn't run, source path doesn't exist");
}
}
public void copyFilesMethod()
{
System.Threading.Thread.Sleep(10000);
if (System.IO.Directory.Exists(sourcePath))
{
// Searching for the latest directory created in the sourcePath folder
directory = new DirectoryInfo(sourcePath);
myFile = (from f in directory.GetDirectories()
orderby f.LastWriteTime descending
select f).First();
sourcePath = System.IO.Path.Combine(sourcePath, myFile.Name);
string[] files = System.IO.Directory.GetFiles(sourcePath);
// Copy the files and overwrite destination files if they already exist.
foreach (string s in files)
{
// Use static Path methods to extract only the file name from the path.
fileName = System.IO.Path.GetFileName(s);
if (fileName != "appsettings.json")
{
destPath = System.IO.Path.Combine(targetPath, fileName);
try
{
System.IO.File.Copy(s, destPath, true);
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
return;
}
}
}
}
else
{
Console.WriteLine("The loop didn't run, source path doesn't exist");
}
// Keep console window open in debug mode.
Console.WriteLine("Procedure has been completed.");
}
This should be triggered once there is a new file, which I have written as this:
class FileMonitor
{
public FileSystemWatcher watcher = new FileSystemWatcher();
public string sourcePath;
public string targetPath;
public FileMonitor(string sourceFolder, string targetFolder)
{
sourcePath = sourceFolder;
targetPath = targetFolder;
}
public void watch()
{
watcher.Path = sourcePath;
watcher.NotifyFilter = NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName
| NotifyFilters.CreationTime;
//var one = NotifyFilters.FileName;
watcher.Filter = "*.*";
watcher.Created += new FileSystemEventHandler (OnChanged);
watcher.EnableRaisingEvents = true;
//System.Threading.Thread.Sleep(25000);
}
public void OnChanged(object source, FileSystemEventArgs e)
{
//Copies file to another directory.
MovingFiles FileMoveOne = new MovingFiles(sourcePath, targetPath);
FileMoveOne.deleteFilesMethod();
FileMoveOne.copyFilesMethod();
}
}
What I understand once I run the below it would look every 10 seconds if there is a new file and then trigger the OnChange method, am I right?
public class HeartBeat
{
private readonly Timer _timer;
public HeartBeat()
{
_timer = new Timer(10000)
{
AutoReset = true
};
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
//StringBuilder loggingLine = new StringBuilder();
/* Every 30 seconds it will write to the file */
string[] lines = new string[] {DateTime.Now.ToString() + ": Heartbeat is active. Service is monitoring SS and DS"};
//lines[1] = DateTime.Now.ToString() + " About to check if new files are placed on server";
//loggingLine.Append(lines[i]);
File.AppendAllLines(#"C:\Users\RLEBEDEVS\Desktop\Monitor\Monitor1\HeartBeat.log", lines);
//File.AppendAllLines(#"C:\Users\RLEBEDEVS\Desktop\Monitor\Monitor1\HeartBeat.log", lines);
FileMonitor versioOne = new FileMonitor(#"C:\Users\RLEBEDEVS\Desktop\Monitor\Monitor1", #"C:\Users\RLEBEDEVS\Desktop\Monitor\Monitor2");
versioOne.watch();
}
public void Start ()
{
_timer.Start();
}
public void Stop ()
{
_timer.Stop();
}
}
The issue I am having is inconsistency.
It should copy the files to the folder Monitor2 once a new folder is created, but it is not doing that on the first creation. It does delete and copy the files on the second time once create a folder in monitor1 folder.
On every second time it is trying to copy the files it crashes with the below error which I am not familiar with:
Topshelf.Hosts.ConsoleRunHost Critical: 0 : The service threw an unhandled exception, System.UnauthorizedAccessException: Access to the path 'C:\Users\RLEBEDEVS\Desktop\Monitor\Monitor2\System.Net.Sockets.dll' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalDelete(String path, Boolean checkHost)
at System.IO.File.Delete(String path)
at UpgradeServices.MovingFiles.deleteFilesMethod() in C:\Users\RLEBEDEVS\Desktop\C#\Service\UpgradeServices\MovingFIles.cs:line 48
at UpgradeServices.FileMonitor.OnChanged(Object source, FileSystemEventArgs e) in C:\Users\RLEBEDEVS\Desktop\C#\Service\UpgradeServices\FileMonitor.cs:line 43
at System.IO.FileSystemWatcher.OnCreated(FileSystemEventArgs e)
at System.IO.FileSystemWatcher.NotifyFileSystemEventArgs(Int32 action, String name)
at System.IO.FileSystemWatcher.CompletionStatusChanged(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* overlappedPointer)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
Topshelf.Hosts.ConsoleRunHost Information: 0 : Stopping the UpgradeServices service
Topshelf.Hosts.ConsoleRunHost Information: 0 : The UpgradeServices service has stopped.
The program '[497452] UpgradeServices.exe' has exited with code 1067 (0x42b).
Line 48 is this one, though it performed the tasks previously fine (on the first go).
System.IO.File.Delete(deleteString);
I see that the issue is with the way I am raising the event. Does anybody know what should I change in order to achieve the desired result which is when the service is started on every new folder created in the destiny it would perform the two methods moving and deleting files? The folder will always have only new folders created.
Regards,
It seems that in your heartbeat you starting new FileMonitor every 10 seconds, so after 20 seconds you will have 2 FileMonitor's watching and moving(deleting) the same files at the time. Just start FileMonitor once using hosted service for example. Or remove the timer handler part in your HeartBeat class and just create FileMonitor in constructor:
public HeartBeat()
{
FileMonitor versioOne = new
FileMonitor(#"C:\Users\RLEBEDEVS\Desktop\Monitor\Monitor1", #"C:\Users\RLEBEDEVS\Desktop\Monitor\Monitor2");
versioOne.watch();
// may be save it to instance field so it does not get garbage collected.
// Not sure how FileSystemWatcher behaves with subscription,
// it should prevent the "versionOne" from being collected via subscription.
}

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}");
}
}
}

How can i use if needed the FileInfo[] or if needed the string[]?

In form1 i have two buttons one to select files from directory single file or multiple files.
The second button is to select files from a directory to get all the files in a selected directory.
Now i have a class i'm using to upload the files/directories to my ftp:
At the top of the class i did:
public static DirectoryInfo d;
public static string[] files;
private FileInfo[] dirflist;
Then i'm using it in the event:
private void FtpProgress_DoWork(object sender, DoWorkEventArgs e)
{
try
{
dirflist = d.GetFiles();
//if (dirflist.Length > 0)
//{
foreach (string txf in files)
{
string fn = txf;//txf.Name;
BackgroundWorker bw = sender as BackgroundWorker;
f = e.Argument as FtpSettings;
string UploadPath = String.Format("{0}/{1}{2}", f.Host, f.TargetFolder == "" ? "" : f.TargetFolder + "/", Path.GetFileName(fn));//f.SourceFile));
if (!UploadPath.ToLower().StartsWith("ftp://"))
UploadPath = "ftp://" + UploadPath;
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(UploadPath);
request.UseBinary = true;
request.UsePassive = f.Passive;
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Timeout = 300000;
request.Credentials = new NetworkCredential(f.Username, f.Password);
long FileSize = new FileInfo(f.SourceFile).Length;
string FileSizeDescription = GetFileSize(FileSize);
int ChunkSize = 4096, NumRetries = 0, MaxRetries = 50;
long SentBytes = 0;
byte[] Buffer = new byte[ChunkSize];
using (Stream requestStream = request.GetRequestStream())
{
using (FileStream fs = File.Open(f.SourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
int BytesRead = fs.Read(Buffer, 0, ChunkSize);
while (BytesRead > 0)
{
try
{
if (bw.CancellationPending)
return;
requestStream.Write(Buffer, 0, BytesRead);
SentBytes += BytesRead;
string SummaryText = String.Format("Transferred {0} / {1}", GetFileSize(SentBytes), FileSizeDescription);
bw.ReportProgress((int)(((decimal)SentBytes / (decimal)FileSize) * 100), SummaryText);
}
catch (Exception ex)
{
Debug.WriteLine("Exception: " + ex.ToString());
if (NumRetries++ < MaxRetries)
{
fs.Position -= BytesRead;
}
else
{
throw new Exception(String.Format("Error occurred during upload, too many retries. \n{0}", ex.ToString()));
}
}
BytesRead = fs.Read(Buffer, 0, ChunkSize);
}
}
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
System.Diagnostics.Debug.WriteLine(String.Format("Upload File Complete, status {0}", response.StatusDescription));
}
//}
}
catch (WebException ex)
{
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
ConnectionError = "Error: Please check the ftp address";
break;
case WebExceptionStatus.Timeout:
ConnectionError = "Error: Timout Request";
break;
}
}
}
Now i'm doing loop over the string[] array.
Since i'm selecting multiple files only.
But there might be a case i will select a directory. And then i will need to use the DirectoryInfo(d variable) and the FileInfo[]
If i'm using the FileInfo[] then it's like that:
dirflist = d.GetFiles();
if (dirflist.Length > 0)
{
foreach (FileInfo txf in dirfilist)
{
string fn = txf.Name;
But i don't want to copy over all the code again just for string[] or just for FileInfo[]
I want to make something that i will be able to use FileInfo[] with the foreach or the string[] in the foreach.
And maybe sometimes i will use both upload multiple files and then also to upload a directory with all the files inside.
So maybe it's better to duplicate the whole code and making using once string[] and once FileInfo[] ?
I mean to make two methods one will use FileInfo[] one string[]
How can i use if needed the FileInfo[] or if needed the string[] ?
private void SomeMethod(args)
{
// ...
/* Here I need a specific String Value, or Array of String Values
but sometimes I got it from an array of File,
and sometimes from an array of FileInfo... */
// Call a Function that always returns an array of String
files = GetMyFiles(args);
// resume the job using only files...
/* or replace the above that always manipulates an arrays of FileInfo-s
if you must use FileInfo-s */
}
Then you can overload your function GetMyFiles by passing any argument you want.
string[] GetMyFiles(String DirectoryPath)
// Returns an Array of String that contains all the Files in the Directory.
string[] GetMyFiles(FileInfo MyFileInfo)
// Returns an Array of String with just one File Path.
string[] GetMyFiles()
// Opens a MultiSelect OpenFileDialog,
// then returns the selected Files Path in an Array (or empty Array)
// ...
The other way : Slice your code in multiple parts, then decide which part you're going to use with a conditional check...
private void FtpProgress_DoWork(object sender, DoWorkEventArgs e)
{
// Do the maximum you can do here...
// ...
if ImGoingToUseStringArray
{
string[] files = ....
ResumeWithStringArray(files, sender, e);
}
else
{
FileInfo[] dirflist = ....
ResumeWithFileInfo(dirfList, sender, e);
}
}
private void ResumeWithStringArray(string[] files, object sender, DoWorkEventArgs e)
{
// ...
// you can also call another core Function from here
sendMyFile(args)
}
private void ResumeWithFileInfo(FileInfo[] dirflist, object sender, DoWorkEventArgs e)
{
// ...
// you can also call another core Function from here
sendMyFile(args)
}
Anyway, you'll have to use FileInfo to get the FileSize (required in File Transfer I assume) right ? However, you decide the moment you create that FileInfo per File (or are you using several FileInfo-s at the same time ?) If you think your code get too complicated with a list/array of FileInfo from the start, just creates each instance of FileInfo dynamically when it's required (slice your code in parts)
It seems to me the answer to your question only depends on your taste, or only require some changes in the way you're running the logic.
Put all code that handles a single file into a separate method like this:
private void CopyFile(string fn)
{
BackgroundWorker bw = sender as BackgroundWorker;
f = e.Argument as FtpSettings;
...
}
now decide wheter you want to do the file list stuff or the dir list stuff, and call your new method like this:
File-List:
foreach (string txf in files)
{
this.CopyFile(txt);
}
Dir-List:
dirflist = d.GetFiles();
if (dirflist.Length > 0)
{
foreach (FileInfo txf in dirfilist)
{
this.CopyFile(txt.Name);
}
}

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);
}

How to Transfer Files in different Threads with FileWatcher

I have a form with a File Watcher to which he transfers to multiple addresses all video files placed in a folder. What is the best option so that when multiple files are added to even be able to perform each transfer in a thread. Here's an example of my code:
DockingBarTransferEntities context = new DockingBarTransferEntities();
private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
{
IEnumerable<Diretorios> directories = context.Diretorios.ToList();
foreach (var destino in directories)
{
try
{
Transfere(e.FullPath,Path.GetFileName(e.FullPath),destino);
}
catch (Exception ex)
{
textBox1.Text += "Error: " + ex.Message;
}
}
}
public void Transfere(string fullPath, string name, Diretorios diretorio)
{
try
{
if (Directory.Exists(diretorio.Caminho))
{
string fileName = Path.GetFileName(fullPath);
fileName = String.Format("{0}\\{1}", diretorio.Caminho, fileName);
FileInfo arquivo = new FileInfo(fullPath);
arquivo.CopyTo(fileName, true);
}
}
catch (Exception ex)
{
}
}
It should be as simple as this:
Task.Factory.StartNew(() => Transfere(e.FullPath, Path.GetFileName(e.FullPath), destino));
instead of calling Transfere directly.

Categories