Basically I have a WPF application Where the user Write A process Name ,
Then a new Thread start where it keeps scanning if the process is Opened yet or not , The Thread will be Alive untill the process is Found .. So i can Get the handle and write The Memory !
private void scanBtn_Click (object sender, RoutedEventArgs e)
{
Thread s = new Thread(( ) => {
this.Dispatcher.Invoke((Action)(( ) =>
{
scanner(pName.Text);
}));
});
try
{
if (pName.Text != string.Empty)
{
InfoTxt.Text = "[ WAITING FOR PROCESS TO OPEN ]";
s.Start();
pName.IsEnabled = false;
if (!s.IsAlive)
{
pName.IsEnabled = true;
InfoTxt.Text = "[ FOUND ]";
Process p = Process.GetProcessesByName(pName.Text)[0];
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private static void scanner ( string procName)
{
while (true)
{
Process s = SeekProcName(procName);
if (s != null) break;
}
}
private static Process SeekProcName(string pName)
{
Process[] procs = Process.GetProcesses().Where(p => p.MainWindowHandle != (IntPtr)0).ToArray();
Process f = null;
foreach (var item in procs)
{
if (item.ProcessName.ToLower() == pName.ToLower())
{
f = item;
break;
}
}
return f;
}
The s thread is trying to run a delegate on the UI thread, which means it's no different than calling SeekProcName(procName) in the event handler directly.
It would be better to use Task.Run and async/await to run the check in a background thread. When await returns, execution resumes in the UI thread which means the UI can be updated without requiring Invoke or BeginInvoke
private async void scanBtn_Click (object sender, RoutedEventArgs e)
{
InfoTxt.Text = "[ WAITING FOR PROCESS TO OPEN ]";
//Read the textbox contets *before* starting the task
var name=pName.Text;
var p=await Task.Run(()=>SeekProcName(name));
if (p!=null)
{
InfoTxt.Text = "[ FOUND ]";
}
}
This can be called in a loop, with a small delay between iterations, without blocking the UI thread :
private async void scanBtn_Click (object sender, RoutedEventArgs e)
{
while(someCondition)
{
InfoTxt.Text = "[ WAITING FOR PROCESS TO OPEN ]";
var name=pName.Text;
var p=await Task.Run(()=>SeekProcName(name));
if (p!=null)
{
InfoTxt.Text = "[ FOUND ]";
}
await Task.Delay(1000);
}
}
Related
I have an app with a start button that calls a long running time function. In order
to add a Stop button I've added a thread for this function to avoid the UI freezes and be able to stop the processing in anytime.
The code without threading in average takes 12 minutes to complete the processing, but with threading in the way I have below
takes 4 times more. Below is shown the code for the start button where is called the function "LongRunningFunction" . The function
needs a string argument to work "LongRunningFunction(Somestring)".
I've tested with Task.Run and Task.Factory.StartNew but it happens the same with both methods.
Is there an alternative way to set a thread for my case that doesn't affect too much the performance?
public partial class Form1 : Form
{
CancellationTokenSource cts = new CancellationTokenSource(); // Create the token source.
public Form1()
{
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
cts = new CancellationTokenSource();
Task.Run(()=> LongRunningFunction(Somestring, cts.Token), cts.Token);
//Task.Factory.StartNew(() => LongRunningFunction(Somestring, cts.Token), cts.Token, TaskCreationOptions.None, TaskScheduler.Default);
}
private void Stop_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
cts = null;
MessageBox.Show("Processing cancelled");
}
}
public void LongRunningFunction(string String, CancellationToken token)
{
//Long running processing
//...
MessageBox.Show("Processing finished");
}
}
Update:
The only what I changed is the way I declare the function and added an if statement inside the while loop
that is inside the function. Is like below:
The CancelationToken was added in order to be able to stop the processing when Stop button is pressed.
Without thread I declare the function like this:
public void LongRunningFunction(string String)
{
while (condition)
{
//My code within While loop
}
MessageBox.Show("Processing finished");
}
and with Thread I define the function like this:
public void LongRunningFunction(string String, CancellationToken token)
{
while (condition)
{
if (token.IsCancellationRequested)
{
break;
}
//My code within While loop
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
Update2:
Inside LongRunningFunction() is called another function that prints the lines. Is like below.
public void LongRunningFunction(string fileName, CancellationToken token)
{
StreamWriter writer = new StreamWriter(#outputfile, true, Encoding.UTF8, 4096);
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
List<byte> buffer = new List<byte>();
List<string> buffer1 = new List<string>();
SoapHexBinary hex = new SoapHexBinary();
while (chunk.Length > 0)
{
if (token.IsCancellationRequested) // ### For Cancel Thread ###
{
break;
} // ### For Cancel Thread ###
chunk = reader.ReadBytes(1024);
foreach (byte data in chunk)
{
if (somecondition)
{
buffer.Add(data);
}
else if (other condition)
{
buffer.Add(data);
PrintFunction(buffer, hex, outputfile, writer); // Print Line
}
else if (some other condition)
{
buffer.Add(data);
}
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
if (writer != null)
{
writer.Dispose();
writer.Close();
}
}
private void PrintFunction(List<byte> buffer, SoapHexBinary hex, string outputfile, StreamWriter writer)
{
if (buffer.Count > 0)
{
if (buffer.Count >= lowlimit)
{
hex.Value = buffer.ToArray();
string Register = hex.ToString();
Regex pattern1 = new Regex(#"some pattern");
if (pattern1.IsMatch(Register))
{
Match l1 = Regex.Match(Register, #"somepattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
writer.Write("{0}|{1}|{2}", Convert.ToInt32(l1.Groups[1].ToString(), 16), l1.Groups[2].Value, l1.Groups[3].Value);
Match l2 = Regex.Match(Register, #"otherpattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
if (l2.Success)
{
foreach (Match m in Regex.Matches(l2.Groups[2].ToString(), pattern2, RegexOptions.IgnoreCase | RegexOptions.Compiled))
{
//Some foreach code
}
foreach (Match x in Regex.Matches(var, #"pattern"))
{
//come code
}
writer.WriteLine("," + String.Join(",", var1));
}
else
{
writer.WriteLine();
}
}
}
}
buffer.Clear();
}
Update3:
Hi bebosh,
I still have doubts how to apply in my function, the way you define the delegate in your example function.
My function looks like this:
public void LongRunningFunction(string fileName)
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
}
It could be something like this or how?:
private void LongRunningFunction(string fileName)
{
MethodInvoker action = delegate
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
};
}
Bebosh's answer was good enough. To increase the performance further you can set the ThreadPriority of the "thread" by setting the ".Priority = ThreadPriority.AboveNormal" right after setting the "thread.IsBackground = true;".
Could you try this code:
bool Stop = false;
Thread thread;
private void StartButton_Click(object sender, EventArgs e)
{
string FileName = #"...\a.bin";
thread = new Thread(new ThreadStart(() => DoLongProcess(FileName)));
thread.IsBackground = true;
thread.Start();
}
private void StopButton_Click(object sender, EventArgs e)
{
Stop = true;
}
private void DoLongProcess(string file)
{
using (BinaryReader reader = new BinaryReader(File.Open(file, FileMode.Open)))
{
int pos = 0;
int length = (int)reader.BaseStream.Length;
while (pos < length)
{
if (Stop)
thread.Abort();
// using Invoke if you want cross UI objects
this.Invoke((MethodInvoker)delegate
{
label1.Text = pos.ToString();
});
pos += sizeof(int);
}
}
}
use interrupting the thread
Thread thread;
public MainWindow()
{
InitializeComponent();
}
private async void StartButtonClick(object sender, RoutedEventArgs e)
{
thread = new Thread(ExecuteLong);
var task = Task.Run(() =>
thread.Start());
await task;
}
private void ExecuteLong()
{
try
{
// long task
}
catch (ThreadInterruptedException e)
{
MessageBox.Show("cancelled!");
return;
}
MessageBox.Show("finished");
}
private void CancelButtonClick(object sender, RoutedEventArgs e)
{
this.thread.Interrupt();
}
I'm a newb in programming and I'm trying to do my first thingy that would be for someone else and not just me (so shouldn't be that crappy ^^ )
It's a Online-Checker for clients in LAN network (so he can just paste a list of clients, and it returns the online or offline).
fyi: I'm using Try/Catch because ping.send to an offline host returns in an Error which crashed the application.
Currently it looks like this:
private void btn_check_Click(object sender, EventArgs e)
{
string[] hosts = txt_hosts.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (String host in hosts)
{
pinger(host);
}
}
public void pinger(string host)
{
var ping = new System.Net.NetworkInformation.Ping();
try
{
var result = ping.Send(host);
txt_result.Text += "true" + Environment.NewLine;
Application.DoEvents();
}
catch
{
txt_result.Text += "false"+Environment.NewLine;
Application.DoEvents();
}
}
Now, the interface is like frozen whenever a ping.send is processing (and that's quiet long cause of the timeout of pings).
Is there any way to do this threaded? Before I tried to start a thread, but that doesn't work either because both write in txt_result and that returns an error.
Thanks for any help!
If use acync/await:
// send request
foreach (string host in hosts)
pinger(host);
// async function
async void pinger(string host)
{
var ping = new System.Net.NetworkInformation.Ping();
bool bResp;
try
{
var result = await ping.SendPingAsync(host, 4000);
bResp = result.Status == System.Net.NetworkInformation.IPStatus.Success;
}
catch { bResp = false; }
txt_result.Text += bResp.ToString() + Environment.NewLine;
}
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
pinger(host);
});
It could throw an exception at the line : txt_result.Text = "...";
Because you are trying to modify a value in a thread from another thread.
So you could write:
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
{
txt_result.Text = "...";
}));
Which will request the UI thread to modify the value.
Run on a background worker.
public void pinger(string host)
{
var bw = new BackgroundWorker();
bw.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var ping = new System.Net.NetworkInformation.Ping();
try
{
var result = ping.Send(host);
e.Result = new object[] { result};
}
catch(Exception ex)
{
// Catch specific exceptions here as needed
}
};
bw.RunWorkerCompleted += (bw_txt_results);
bw.RunWorkerAsync();
}
private void bw_txt_results(object sender, RunWorkerCompletedEventArgs e)
{
txt_result = e.result[0].ToString();
}
I am struggling with threading.
The problem is when I am iterating trough foreach loop.
When setting this.Document, the application performs login, that is triggered with an event and takes few seconds to complete. In the worker_RunWorkerCompleted method I need to perform some actions that depend on current login information.
The problem is that before I can perform this action for the first file, the this.Document already changes making the application perform another login. This way I can never actually perform my actions.
My question is: How can I pause the next thread until previous thread has completed.
Is there any other solution to my problem?
I tried with AutoResetEvent but I got no luck. I set waitOne() just after the RunWorkerAsync call and .Set() in the RunWorkerCompleted. The code never gets to RunWorkerCompleted...
Here is the code:
public void Start(object obj)
{
try
{
foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
{
Worker = new BackgroundWorker();
Worker.DoWork += new DoWorkEventHandler(worker_DoWork);
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
using (Stream stream = pair.Value)
{
primaryDocument = new Document(stream);
DataHolderClass dataHolder = new DataHolderClass();
dataHolder.FileName = pair.Key;
dataHolder.Doc = secondaryDocument;
//background thread call
Worker.RunWorkerAsync(dataHolder);
}
}
}
catch (Exception ex)
{
// exception logic
}
finally
{
// complete logic
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
DataHolderClass dataHolder = ((DataHolderClass)e.Argument);
// setting this attribute triggers execution of login event
this.Document = dataHolder.Doc;
e.Result = (dataHolder);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// here I need to perform some actions that are depending on the current login
DataHolderClass dataHolder = ((DataHolderClass)e.Result);
this.eventAggregator.GetEvent<ActionEvent>().Publish(new Message(EMessageType.Info) { Title = dataHolder.FileName });
}
no9,
Try the following:
System.Threading.ManualResetEvent _busy = new System.Threading.ManualResetEvent(false);
void ResumeWorker()
{
// Start the worker if it isn't running
if (!backgroundWorker1.IsBusy) backgroundWorker1.RunWorkerAsync(dataHolder);
// Unblock the worker
_busy.Set();
}
void PauseWorker()
{
// Block the worker
_busy.Reset();
}
void CancelWorker()
{
if (backgroundWorker1.IsBusy) {
// Set CancellationPending property to true
backgroundWorker1.CancelAsync();
// Unblock worker so it can see that
_busy.Set();
}
}
then in your code run the method.
Let me know if it works :)
class SimpleWaitPulse
{
static readonly object _locker = new object();
static bool _go;
static void Main()
{ // The new thread will block
new Thread (Work).Start(); // because _go==false.
Console.ReadLine(); // Wait for user to hit Enter
lock (_locker) // Let's now wake up the thread by
{ // setting _go=true and pulsing.
_go = true;
Monitor.Pulse (_locker);
}
}
static void Work()
{
lock (_locker)
while (!_go)
Monitor.Wait (_locker); // Lock is released while we’re waiting
Console.WriteLine ("Woken!!!");
}
}
Can you use pulse ?
Taken from : Threading in C# from albahari.com
Well, the design is terrible... but if you need to stick to it, you can set wait handles in a previous worker and wait for it in next. This is the minimal fix, still quite an abomination:
public void Start(object obj)
{
try
{
BackgroundWorker previousWorker = null;
DataHolderClass previousWorkerParams = null;
foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
{
// signal event on previous worker RunWorkerCompleted event
AutoResetEvent waitUntilCompleted = null;
if (previousWorker != null)
{
waitUntilCompleted = new AutoResetEvent(false);
previousWorker.RunWorkerCompleted += (o, e) => waitUntilCompleted.Set();
// start the previous worker
previousWorker.RunWorkerAsync(previousWorkerParams);
}
Worker = new BackgroundWorker();
Worker.DoWork += (o, e) =>
{
// wait for the handle, if there is anything to wait for
if (waitUntilCompleted != null)
{
waitUntilCompleted.WaitOne();
waitUntilCompleted.Dispose();
}
worker_DoWork(o, e);
};
using (Stream stream = pair.Value)
{
primaryDocument = new Document(stream);
DataHolderClass dataHolder = new DataHolderClass();
dataHolder.FileName = pair.Key;
dataHolder.Doc = secondaryDocument;
// defer running this worker; we don't want it to finish
// before adding additional completed handler
previousWorkerParams = dataHolder;
}
previousWorker = Worker;
}
if (previousWorker != null)
{
previousWorker.RunWorkerAsync(previousWorkerParams);
}
}
catch (Exception ex)
{
// exception logic
}
finally
{
// complete logic
}
}
I have a list Uri's that I want "clicked" To achieve this I"m trying to create a new web-browser control per Uri. I create a new thread per Uri. The problem I'm having is the thread end before the document is fully loaded, so I never get to make use of the DocumentComplete event. How can I overcome this?
var item = new ParameterizedThreadStart(ClicIt.Click);
var thread = new Thread(item) {Name = "ClickThread"};
thread.Start(uriItem);
public static void Click(object o)
{
var url = ((UriItem)o);
Console.WriteLine(#"Clicking: " + url.Link);
var clicker = new WebBrowser { ScriptErrorsSuppressed = true };
clicker.DocumentCompleted += BrowseComplete;
if (String.IsNullOrEmpty(url.Link)) return;
if (url.Link.Equals("about:blank")) return;
if (!url.Link.StartsWith("http://") && !url.Link.StartsWith("https://"))
url.Link = "http://" + url.Link;
clicker.Navigate(url.Link);
}
You have to create an STA thread that pumps a message loop. That's the only hospitable environment for an ActiveX component like WebBrowser. You won't get the DocumentCompleted event otherwise. Some sample code:
private void runBrowserThread(Uri url) {
var th = new Thread(() => {
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
br.Navigate(url);
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
var br = sender as WebBrowser;
if (br.Url == e.Url) {
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread(); // Stops the thread
}
}
Here is how to organize a message loop on a non-UI thread, to run asynchronous tasks like WebBrowser automation. It uses async/await to provide the convenient linear code flow and loads a set of web pages in a loop. The code is a ready-to-run console app which is partially based on this excellent post.
Related answers:
https://stackoverflow.com/a/22262976/1768303
https://stackoverflow.com/a/21775343/1768303
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApplicationWebBrowser
{
// by Noseratio - https://stackoverflow.com/users/1768303/noseratio
class Program
{
// Entry Point of the console app
static void Main(string[] args)
{
try
{
// download each page and dump the content
var task = MessageLoopWorker.Run(DoWorkAsync,
"http://www.example.com", "http://www.example.net", "http://www.example.org");
task.Wait();
Console.WriteLine("DoWorkAsync completed.");
}
catch (Exception ex)
{
Console.WriteLine("DoWorkAsync failed: " + ex.Message);
}
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
// navigate WebBrowser to the list of urls in a loop
static async Task<object> DoWorkAsync(object[] args)
{
Console.WriteLine("Start working.");
using (var wb = new WebBrowser())
{
wb.ScriptErrorsSuppressed = true;
TaskCompletionSource<bool> tcs = null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
tcs.TrySetResult(true);
// navigate to each URL in the list
foreach (var url in args)
{
tcs = new TaskCompletionSource<bool>();
wb.DocumentCompleted += documentCompletedHandler;
try
{
wb.Navigate(url.ToString());
// await for DocumentCompleted
await tcs.Task;
}
finally
{
wb.DocumentCompleted -= documentCompletedHandler;
}
// the DOM is ready
Console.WriteLine(url.ToString());
Console.WriteLine(wb.Document.Body.OuterHtml);
}
}
Console.WriteLine("End working.");
return null;
}
}
// a helper class to start the message loop and execute an asynchronous task
public static class MessageLoopWorker
{
public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
EventHandler idleHandler = null;
idleHandler = async (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return to the message loop
await Task.Yield();
// and continue asynchronously
// propogate the result or exception
try
{
var result = await worker(args);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
// signal to exit the message loop
// Application.Run will exit at this point
Application.ExitThread();
};
// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
// set STA model for the new thread
thread.SetApartmentState(ApartmentState.STA);
// start the thread and await for the task
thread.Start();
try
{
return await tcs.Task;
}
finally
{
thread.Join();
}
}
}
}
From my experience in the past the webbrowser does not like operating outside of the main application thread.
Try using httpwebrequests instead, you can set them as asynchronous and create a handler for the response to know when it is succesfull:
how-to-use-httpwebrequest-net-asynchronously
A simple solution at which the simultaneous operation of several WebBrowsers occurs
Create a new Windows Forms application
Place the button named
button1
Place the text box named textBox1
Set properties of text
field: Multiline true and ScrollBars Both
Write the following
button1 click handler:
textBox1.Clear();
textBox1.AppendText(DateTime.Now.ToString() + Environment.NewLine);
int completed_count = 0;
int count = 10;
for (int i = 0; i < count; i++)
{
int tmp = i;
this.BeginInvoke(new Action(() =>
{
var wb = new WebBrowser();
wb.ScriptErrorsSuppressed = true;
wb.DocumentCompleted += (cur_sender, cur_e) =>
{
var cur_wb = cur_sender as WebBrowser;
if (cur_wb.Url == cur_e.Url)
{
textBox1.AppendText("Task " + tmp + ", navigated to " + cur_e.Url + Environment.NewLine);
completed_count++;
}
};
wb.Navigate("https://stackoverflow.com/questions/4269800/webbrowser-control-in-a-new-thread");
}
));
}
while (completed_count != count)
{
Application.DoEvents();
Thread.Sleep(10);
}
textBox1.AppendText("All completed" + Environment.NewLine);
I have a list Uri's that I want "clicked" To achieve this I"m trying to create a new web-browser control per Uri. I create a new thread per Uri. The problem I'm having is the thread end before the document is fully loaded, so I never get to make use of the DocumentComplete event. How can I overcome this?
var item = new ParameterizedThreadStart(ClicIt.Click);
var thread = new Thread(item) {Name = "ClickThread"};
thread.Start(uriItem);
public static void Click(object o)
{
var url = ((UriItem)o);
Console.WriteLine(#"Clicking: " + url.Link);
var clicker = new WebBrowser { ScriptErrorsSuppressed = true };
clicker.DocumentCompleted += BrowseComplete;
if (String.IsNullOrEmpty(url.Link)) return;
if (url.Link.Equals("about:blank")) return;
if (!url.Link.StartsWith("http://") && !url.Link.StartsWith("https://"))
url.Link = "http://" + url.Link;
clicker.Navigate(url.Link);
}
You have to create an STA thread that pumps a message loop. That's the only hospitable environment for an ActiveX component like WebBrowser. You won't get the DocumentCompleted event otherwise. Some sample code:
private void runBrowserThread(Uri url) {
var th = new Thread(() => {
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
br.Navigate(url);
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
var br = sender as WebBrowser;
if (br.Url == e.Url) {
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread(); // Stops the thread
}
}
Here is how to organize a message loop on a non-UI thread, to run asynchronous tasks like WebBrowser automation. It uses async/await to provide the convenient linear code flow and loads a set of web pages in a loop. The code is a ready-to-run console app which is partially based on this excellent post.
Related answers:
https://stackoverflow.com/a/22262976/1768303
https://stackoverflow.com/a/21775343/1768303
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApplicationWebBrowser
{
// by Noseratio - https://stackoverflow.com/users/1768303/noseratio
class Program
{
// Entry Point of the console app
static void Main(string[] args)
{
try
{
// download each page and dump the content
var task = MessageLoopWorker.Run(DoWorkAsync,
"http://www.example.com", "http://www.example.net", "http://www.example.org");
task.Wait();
Console.WriteLine("DoWorkAsync completed.");
}
catch (Exception ex)
{
Console.WriteLine("DoWorkAsync failed: " + ex.Message);
}
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
// navigate WebBrowser to the list of urls in a loop
static async Task<object> DoWorkAsync(object[] args)
{
Console.WriteLine("Start working.");
using (var wb = new WebBrowser())
{
wb.ScriptErrorsSuppressed = true;
TaskCompletionSource<bool> tcs = null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
tcs.TrySetResult(true);
// navigate to each URL in the list
foreach (var url in args)
{
tcs = new TaskCompletionSource<bool>();
wb.DocumentCompleted += documentCompletedHandler;
try
{
wb.Navigate(url.ToString());
// await for DocumentCompleted
await tcs.Task;
}
finally
{
wb.DocumentCompleted -= documentCompletedHandler;
}
// the DOM is ready
Console.WriteLine(url.ToString());
Console.WriteLine(wb.Document.Body.OuterHtml);
}
}
Console.WriteLine("End working.");
return null;
}
}
// a helper class to start the message loop and execute an asynchronous task
public static class MessageLoopWorker
{
public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
EventHandler idleHandler = null;
idleHandler = async (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return to the message loop
await Task.Yield();
// and continue asynchronously
// propogate the result or exception
try
{
var result = await worker(args);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
// signal to exit the message loop
// Application.Run will exit at this point
Application.ExitThread();
};
// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
// set STA model for the new thread
thread.SetApartmentState(ApartmentState.STA);
// start the thread and await for the task
thread.Start();
try
{
return await tcs.Task;
}
finally
{
thread.Join();
}
}
}
}
From my experience in the past the webbrowser does not like operating outside of the main application thread.
Try using httpwebrequests instead, you can set them as asynchronous and create a handler for the response to know when it is succesfull:
how-to-use-httpwebrequest-net-asynchronously
A simple solution at which the simultaneous operation of several WebBrowsers occurs
Create a new Windows Forms application
Place the button named
button1
Place the text box named textBox1
Set properties of text
field: Multiline true and ScrollBars Both
Write the following
button1 click handler:
textBox1.Clear();
textBox1.AppendText(DateTime.Now.ToString() + Environment.NewLine);
int completed_count = 0;
int count = 10;
for (int i = 0; i < count; i++)
{
int tmp = i;
this.BeginInvoke(new Action(() =>
{
var wb = new WebBrowser();
wb.ScriptErrorsSuppressed = true;
wb.DocumentCompleted += (cur_sender, cur_e) =>
{
var cur_wb = cur_sender as WebBrowser;
if (cur_wb.Url == cur_e.Url)
{
textBox1.AppendText("Task " + tmp + ", navigated to " + cur_e.Url + Environment.NewLine);
completed_count++;
}
};
wb.Navigate("https://stackoverflow.com/questions/4269800/webbrowser-control-in-a-new-thread");
}
));
}
while (completed_count != count)
{
Application.DoEvents();
Thread.Sleep(10);
}
textBox1.AppendText("All completed" + Environment.NewLine);