Tracking upload progress of WebClient - c#

So I am currently uploading files via a php script on my webserver using the following code:
string file = "dp.jpg";
System.Net.WebClient Client = new System.Net.WebClient();
Client.Headers.Add("Content-Type", "binary/octet-stream");
byte[] result = Client.UploadFile("http://localhost/upload.php", "POST", file);
String response = System.Text.Encoding.UTF8.GetString(result, 0, result.Length);
What I want to know is how would I use this, or a different method to track how much it has uploaded and display it on a progress bar?
Thanks.

Use UploadFileAsync.
Suscribe to wcUploader.UploadFileCompleted and
wcUploader.UploadProgressChangedevents so you can get the upload progress
and the upload completion.
In the following code you can check how we suscribe to UploadProgressChanged and
we can get e.ProgressPercentage value.
Check the following snippet:
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private readonly WebClient wcUploader = new WebClient();
public Form1()
{
InitializeComponent();
wcUploader.UploadFileCompleted += UploadFileCompletedCallback;
wcUploader.UploadProgressChanged += UploadProgressCallback;
}
private void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = Encoding.UTF8.GetString(e.Result);
button1.Enabled = true;
}));
}
private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = (string)e.UserState + "\n\n"
+ "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend
+ "b (" + e.ProgressPercentage + "%)";
}));
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
button1.Enabled = false;
string toUpload = openFileDialog1.FileName;
textBox1.Text = "Initiating connection";
new Thread(() =>
wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload)).Start();
}
}
}
}
Code snippet extracted from here:
UploadFileAsync not asynchronous?

Related

Converting 2 txt files in 2 different texboxes from lowercase to uppercase

I am trying to convert two different .txt files from lower case to upper case and the main objective is to measure and display the execution time.
Everything goes well if the files are saved with upper cases in my predefined path and the program displays the execution time. In my GUI however, texts do not convert because of the following exception in text-boxes:
System.InvalidOperationException: Cross-thread operation not valid: Control "textBox2" accessed from a thread other than the thread it was created on.
namespace Threads
{
public partial class Form1 : Form
{ String prim= #"C:\Users\Wheelz\Desktop\Laborator09\fis1.txt";
String secund= #"C:\Users\Wheelz\Desktop\Laborator09\fis2.txt";
public Form1()'
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(prim);
textBox1.Text = read;
}
private void button2_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(secund);
textBox2.Text = read;
}
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
textBox1.Text = textBox1.Text.ToUpper();
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
textBox2.Text = textBox2.Text.ToUpper() ;
}
private void timp_Click(object sender, EventArgs e)
{
Thread firstThread = new Thread(new ThreadStart(modifica1));
Thread secondThread = new Thread(new ThreadStart(modifica2));
var ceas= new Stopwatch();
ceas.Start();
firstThread.Start();
secondThread.Start();
ceas.Stop();
if (ceas.ElapsedMilliseconds == 1)
{
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecundă";
}
else
{
if ((ceas.ElapsedMilliseconds < 20))
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecunde";
else
cron.Text = ceas.ElapsedMilliseconds.ToString() + " de milisecunde";
}
}
}
}
Yes, you can't share the form called in mainthread into a subthread.
You must use an Delegate to mainthread to update the textboxes.
READ:
Invoke(Delegate)
Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control.
You can also work with backgroundworker or async
use "BeginInvoke" for update control value in Thread. like ...
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox1.Text = textBox1.Text.ToUpper();
}));
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox2.Text = textBox2.Text.ToUpper();
}));
}

Delegate loads the Alert Form but I can't use any of the components.. its stuck

The problem is below. Here's my code...
// Contents of Form1.cs
// Usual includes
namespace ProcessMonitor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Boolean getStatus()
{
// Returns true if the system is active
if (label1.Text.Equals("Active"))
return true;
return false;
}
private void button1_Click(object sender, EventArgs e)
{
if(getStatus())
{
label1.Text = "Not Active";
button1.Text = "Activate";
}
else
{
label1.Text = "Active";
button1.Text = "Deactivate";
}
}
private void Form1_Load(object sender, EventArgs e)
{
Monitor mon = new Monitor(this);
mon.Run();
}
}
}
// Contents of Monitor.cs
// Usual includes
using System.Management;
using System.Diagnostics;
using System.Threading;
namespace ProcessMonitor
{
class Monitor
{
Form1 parent;
private void ShowAlert(Alert al)
{
al.Show();
}
public Monitor(Form1 parent)
{
this.parent = parent;
}
public void InvokeMethod()
{
//This function will be on main thread if called by Control.Invoke/Control.BeginInvoke
Alert frm = new Alert(this.parent);
frm.Show();
}
// This method that will be called when the thread is started
public void Run()
{
var query = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 0, 0, 1),
"TargetInstance isa \"Win32_Process\");
while (true)
{
using (var watcher = new ManagementEventWatcher(query))
{
ManagementBaseObject mo = watcher.WaitForNextEvent();a
//MessageBox.Show("Created process: " + ((ManagementBaseObject)mo["TargetInstance"])["Name"] + ",Path: " + ((ManagementBaseObject)mo["TargetInstance"])["ExecutablePath"]);
ManagementBaseObject o = (ManagementBaseObject)mo["TargetInstance"];
String str = "";
foreach (PropertyData s in o.Properties)
{
str += s.Name + ":" + s.Value + "\n";
}
this.parent.Invoke(new MethodInvoker(InvokeMethod), null);
}
}
}
}
}
Alert.cs is just a blank form with a label that says “new process has started”. I intend to display the name of the process and location, pid, etc. by passing it to this alert form via the Thread (i.e. class Monitor). I have deliberately made the thread load in form_load so that I can resolve this error first. Adding it as a thread properly after the main form loads fully is a later task. I need to fix this first..
The delegate creates the Alert form but I can’t click on it, its just stuck. Need help to solve this.
Your while loop in Run is blocking the UI thread.
by passing it to this alert form via the Thread
You never actually create a new thread or task here - you just run code which executes in the UI thread, and causes an infinite loop. This will prevent the main form, as well as your Alert form, from ever displaying messages.
You need to push this into a background thread in order for it to work, ie:
private void Form1_Load(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(_ =>
{
Monitor mon = new Monitor(this);
mon.Run();
});
}

using the invoke to access a textbox on the form

i'm writing a simple filestream program in C# using asynchronous reading which uses a thread other than the main thread for its callback.but i'm getting cross-thread exception when i try to write my filecontent in a text box.
here is my program:
using System;
namespace Filestream
{
public partial class Form1 : Form
{
FileStream fs;
byte[] fileContents;
AsyncCallback callback;
public Form1()
{
InitializeComponent();
}
private void synbtn_Click(object sender, EventArgs e)
{
openFileDialog1.ShowDialog();
callback = new AsyncCallback(fs_StateChanged);
fs = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
fileContents = new Byte[fs.Length];
fs.BeginRead(fileContents, 0, (int)fs.Length, callback, null);
}
public void fs_StateChanged(IAsyncResult ar)
{
if (ar.IsCompleted)
{
*textBox1.Text = Encoding.UTF8.GetString(fileContents);*
fs.Close();
}
}
}
}
the part with the star is the part that i'm getting the exception.i tried to use the invoke but i had no luck.can someone correct this part of the code with invoke so i don't get the error.
thanks.
try this.
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new MethodInvoker(() => textBox1.Text = Encoding.UTF8.GetString(fileContents)));
}
else
{
textBox1.Text = Encoding.UTF8.GetString(fileContents);
}
To Expand on Ram's answer
//Can this thread make updates to textbox1?
if(textbox1.InvokeRequired)
{
//No then use the invoke method to update textbox1
textbox1.Invoke(new MethodInvokernew MethodInvoker(() => textBox1.Text = Encoding.UTF8.GetString(fileContents)));
}else{
//Yes then update textbox1
textBox1.Text = Encoding.UTF8.GetString(fileContents);
}
Explanation:
Updates to a UI control must be done on the thread that created the UI control. To test if the current thread is allowed to update a particular UI control call the InvokeRequired method on the control. Invoke can then be used to call a method on using the thread that can update the control

How to update a view without waiting for another operation to finish

lets say I have a GroupBox with several Labels. In these Labels, various IP-related information are displayed. One info is the external IP address of the machine.
string externalIP = "";
try
{
WebRequest request = WebRequest.Create("http://checkip.dyndns.org/");
request.Timeout = 3000;
System.Threading.Tasks.Task<System.Net.WebResponse> response = request.GetResponseAsync();
using (StreamReader stream = new StreamReader(response.Result.GetResponseStream()))
{
if (response.Result.ContentLength != -1)
{
externalIP = stream.ReadToEnd();
}
}
}
catch (Exception e)
{
externalIP = "Error.";
}
if (externalIP == "")
{
return "No service.";
}
else
{
return externalIP = (new Regex(#"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")).Matches(externalIP)[0].ToString();
}
This method is called from following code:
private void updateNetworkIP()
{
string ip4e = "External IPv4: " + getExternalIPv4();
lblIP4external.Text = ip4e;
//Get some more info here.
}
How do I execute the code after getExternalIPv4() even when it's not finished yet? It works when setting a TimeOut like I did above but sometimes the request just takes a little longer but still completes successfully. So I want to still be able to display the external IP but continue to execute the other methods for refreshing the GroupBox.
The BackgroundWorker will deliver what you are after. Sample code:
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler(getExternalIPv4Back);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(writeLabel);
bg.RunWorkerAsync();
//The code below this point will be executed while the BackgroundWorker does its work
You have to define getExternalIPv4Back as a DoWork Event Method and include inside it the code to be executed in parallel; also writeLabel as a RunWorkerCompleted Event(required to edit the label without provoking muti-threading-related errors). That is:
private void getExternalIPv4Back(object sender, DoWorkEventArgs e)
{
IP = "External IPv4: " + getExternalIPv4(); //IP -> Globally defined variable
}
private void writeLabel(object sender, RunWorkerCompletedEventArgs e)
{
  lblIP4external.Text = IP;
}

How can I redirect process output (console) to richtextbox?

what is wrong why is that the richtextbox doesnt get the stream of Process output? theres no text display in richtextbox..
private void button1_Click(object sender, EventArgs e)
{
Process sortProcess;
sortProcess = new Process();
sortProcess.StartInfo.FileName = "sort.exe";
sortProcess.StartInfo.Arguments = this.comboBox1.SelectedItem.ToString();
// Set UseShellExecute to false for redirection.
sortProcess.StartInfo.CreateNoWindow = true;
sortProcess.StartInfo.UseShellExecute = false;
// Redirect the standard output of the sort command.
// This stream is read asynchronously using an event handler.
sortProcess.StartInfo.RedirectStandardOutput = true;
sortOutput = new StringBuilder("");
// Set our event handler to asynchronously read the sort output.
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
// Redirect standard input as well. This stream
// is used synchronously.
sortProcess.StartInfo.RedirectStandardInput = true;
// Start the process.
sortProcess.Start();
// Start the asynchronous read of the sort output stream.
sortProcess.BeginOutputReadLine();
sortProcess.WaitForExit();
richTextBox1.AppendText(sortOutput.ToString());
}
private static void SortOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
sortOutput.Append(Environment.NewLine +
"[" + numOutputLines.ToString() + "] - " + outLine.Data);
}
}
so when sort.exe launches, it displays text, i want all those text be displayed also in richtextbox in RealTime (i dont want to wait for the process to exit, and then read all output)
how can i do it? any wrong part of my code? thanks
UPDATE #botz
i added this in my code
private void SortOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
sortOutput.Append(Environment.NewLine +
"[" + numOutputLines.ToString() + "] - " + outLine.Data);
richTextBox1.AppendText(sortOutput.ToString());
}
but it throws this exception
Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.
WaitForExit() blocks your UI Thread, so you don't see the new output.
Either wait for the process in a separate thread or replace WaitForExit() with something like this:
while (!sortProcess.HasExited) {
Application.DoEvents(); // This keeps your form responsive by processing events
}
In your SortOutputHandler, you can now directly append output to your textbox. But you should remember to check if you need to invoke it on the UI Thread.
You can check if it's on the UI thread this way in your handler:
if (richTextBox1.InvokeRequired) { richTextBox1.BeginInvoke(new DataReceivedEventHandler(SortOutputHandler), new[] { sendingProcess, outLine }); }
else {
sortOutput.Append(Environment.NewLine + "[" + numOutputLines.ToString() + "] - " + outLine.Data);
richTextBox1.AppendText(sortOutput.ToString());
}
This is working for me:
private void button1_Click(object sender, EventArgs e)
{
using (Process sortProcess = new Process())
{
sortProcess.StartInfo.FileName = #"F:\echo_hello.bat";
sortProcess.StartInfo.CreateNoWindow = true;
sortProcess.StartInfo.UseShellExecute = false;
sortProcess.StartInfo.RedirectStandardOutput = true;
// Set event handler
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
// Start the process.
sortProcess.Start();
// Start the asynchronous read
sortProcess.BeginOutputReadLine();
sortProcess.WaitForExit();
}
}
void SortOutputHandler(object sender, DataReceivedEventArgs e)
{
Trace.WriteLine(e.Data);
this.BeginInvoke(new MethodInvoker(() =>
{
richTextBox1.AppendText(e.Data ?? string.Empty);
}));
}
The example you started with was a console application, which doesn't care much about multithreaded access. For Windows Forms when you update a control this has to be done from the main UI thread, which is why BeginInvoke is needed. If you want to check rapidly if a handler like SortOutputHandler is working properly you can use System.Diagnostics.Trace.Write*, which doesn't need BeginInvoke.
EDIT: echo_hello.bat simply echoes the "hello" string:
#echo off
echo hello
If you are going to update the ui from another thread, you need to make sure you are on the main ui thread. In the method check for InvokeRequired. See InvokeRequired
Complete application and source code available from this external link of codeproject :
http://www.codeproject.com/Articles/335909/Embedding-a-Console-in-a-C-Application
This is tutorial of implementation of https://github.com/dwmkerr/consolecontrol.
As I said in the comment I posted to the question, by definition of what a sort does, it is impossible for there to be any output until all the input has been read. So the sort program is a bad example of getting output in realtime. So the following is for anyone in the future that wants to do something like this for console programs in general. The following uses a BackgroundWorker to get the output asynchronously and put it into a TextBox. A RichTextBox could easily be used instead.
public partial class MainWindow : Window
{
const string Path = #"C:\Windows\system32\sort.exe";
BackgroundWorker Processer = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
Processer.WorkerReportsProgress = true;
Processer.WorkerSupportsCancellation = true;
Processer.ProgressChanged += Processer_ProgressChanged;
Processer.DoWork += Processer_DoWork;
}
private void Processer_DoWork(object sender, DoWorkEventArgs e)
{
StreamReader StandardOutput = e.Argument as StreamReader;
string data = StandardOutput.ReadLine();
while (data != null)
{
Processer.ReportProgress(0, data);
data = StandardOutput.ReadLine();
}
}
private void Processer_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string data = e.UserState as string;
if (data != null)
DataBox.Text += data + "\r\n";
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DataBox.Text = string.Empty;
ProcessStartInfo StartInfo = new ProcessStartInfo(Path);
StartInfo.RedirectStandardOutput = true;
StartInfo.RedirectStandardInput = true;
StartInfo.UseShellExecute = false;
Process p = null;
try { p = Process.Start(StartInfo); }
catch (Exception ex)
{
MessageBox.Show($"Error starting {Path}: {ex.Message}");
return;
}
// Get the output
Processer.RunWorkerAsync(p.StandardOutput);
// Put the input
p.StandardInput.WriteLine("John");
p.StandardInput.WriteLine("Alice");
p.StandardInput.WriteLine("Zoe");
p.StandardInput.WriteLine("Bob");
p.StandardInput.WriteLine("Mary");
// Tell the program that is the last of the data
p.StandardInput.Close();
}
}
For the sort program it is not necessary to call ReportProgress until after all the data has been read but this is a more generalized sample.

Categories

Resources