I got this error
Method name expected.
for this code:
thSystem[index] = new Thread(new ThreadStart(cls.StartProcess(out string)));
How to do the overload for output value?
I want to take out string from StartProcess.
Below is my code:
//declare function
clsCallProcess cls = new clsCallProcess(index, url, name, timer);
thSystem[index] = new Thread(new ThreadStart(cls.StartProcess(out string)));
thSystem[index].Start();
//function
public class clsCallProcess
{
private int mindex;
private string murl;
private string mname;
private int mtimer;
bool IsRunning = true;
// The constructor obtains the state information.
public clsCallProcess(int index, string url, string name, int timer)
{
mindex = index;
murl = url;
mname = name;
mtimer = timer;
}
public void StartProcess(out string result)
{
//First run checking
result = "Connection Success";
while (IsRunning)
{
Thread.Sleep(mtimer);
IsRunning = false;
try
{
//FileManager.WriteActivityLog("Process Start", mname);
DateTime start = DateTime.Now;
string html = Utility.RequestWebpage(murl, string.Empty, true);
TimeSpan minisecond = DateTime.Now - start;
FileManager.WriteActivityLog("time:" + minisecond.Milliseconds.ToString() + ",html length:" + html.Length.ToString(), mname);
//FileManager.WriteActivityLog("html:" + html, mname);
//FileManager.WriteActivityLog("Process End", mname);
}
catch (Exception ex)
{
ExceptionManager.WriteErrorLog(ex.Message, true, mname);
result = ex.Message;
}
IsRunning = true;
}
}
}
Apparently you run something in a loop forever (right now your thread loop will never end as IsRunning will always be true) to check for some webpage periodically.
One way to get the periodic results back is an event handler which gets raised every time you have made a connection attempt. It could look like this:
public class PageWatcher
{
private int mindex;
private string murl;
private string mname;
private int mtimer;
bool IsRunning = true;
public event EventHandler<ConnectionAttemptEventArgs> ConnectionAttempt;
// The constructor obtains the state information.
public PageWatcher(int index, string url, string name, int timer)
{
mindex = index;
murl = url;
mname = name;
mtimer = timer;
}
public void StartProcess()
{
//First run checking
string lastResult = "Connection Success";
while (IsRunning)
{
Thread.Sleep(mtimer);
IsRunning = false;
try
{
//FileManager.WriteActivityLog("Process Start", mname);
DateTime start = DateTime.Now;
string html = Utility.RequestWebpage(murl, string.Empty, true);
TimeSpan minisecond = DateTime.Now - start;
FileManager.WriteActivityLog("time:" + minisecond.Milliseconds.ToString() + ", html length:" + html.Length.ToString(), mname);
//FileManager.WriteActivityLog("html:" + html, mname);
//FileManager.WriteActivityLog("Process End", mname);
lastResult = "Connection Success";
}
catch (Exception ex)
{
ExceptionManager.WriteErrorLog(ex.Message, true, mname);
lastResult = ex.Message;
}
IsRunning = true;
OnConnectionAttempt(result);
}
}
private void OnConnectionAttempt(string result)
{
var handler = ConnectionAttempt;
if (handler != null)
{
handler(this, new ConnectionAttemptEventArgs(mindex, result));
}
}
}
public class ConnectionAttemptEventArgs : EventArgs
{
public readonly int Index;
public readonly string Result;
public ConnectionAttemptEventArgs(int index, string result)
{
Index = index;
Result = result;
}
}
Then use it like this:
PageWatcher watcher = new PageWatcher(index, url, name, timer);
watcher.ConnectionAttempt += HandleConnectionAttempt;
thSystem[index] = new Thread(new ThreadStart(watcher.StartProcess));
thSystem[index].Start();
...
void HandleConnectionAttempt(object sender, ConnectionAttemptEventArgs e)
{
Console.WriteLine("Connection attempt watcher {0}, result {1}", e.Index, e.Result);
}
I also renamed your class as clsCallProcess is not really a good name. Prefixing it with cls to indicate it's a class is rather pointless and CallProcess is what it's doing but not what its intent is - which is to watch a connection to a webpage.
Have a "continuation" delegate that takes the result. The delegate would be called at the end of the method.
clsCallProcess cls = new clsCallProcess(index, url, name, timer);
thSystem[index] = new Thread(new ThreadStart(
() =>
cls.StartProcess(result => Console.WriteLine (result))));
// ---------------------------^
// If you want to do something other than write it to the console, do that here.
thSystem[index].Start();
Here's the framework of the StartProcess (
public void StartProcess(Action<string> continueWith)
{
string result;
while (IsRunning)
{
// do stuff
result = "success";
}
// all done... call continuation delegate.
continueWith (result);
}
For an alternate, perhaps more friendly way of doing it (if you have .net 4), use: Continuations with Task Parallel Library
Related
I am building a screen recording app in C# using Windows Graphics Capture API. I am using this script. I can select monitor and can record it to mp4 file. I am trying to add Pause/Resume functionality.
Here is code of main Window that initiates Recording
try
{
newFile = GetTempFile();
using (var stream = new FileStream(newFile, FileMode.CreateNew).AsRandomAccessStream())
using (_encoder = new Encoder(_device, item))
{
await _encoder.EncodeAsync(
stream,
width, height, bitrate,
frameRate);
}
}
catch (Exception ex)
{
//
}
And here is the main function from Encoder class, which is used above
private async Task EncodeInternalAsync(IRandomAccessStream stream, uint width, uint height, uint bitrateInBps, uint frameRate)
{
if (!_isRecording)
{
_isRecording = true;
_frameGenerator = new CaptureFrameWait(
_device,
_captureItem,
_captureItem.Size);
using (_frameGenerator)
{
var encodingProfile = new MediaEncodingProfile();
encodingProfile.Container.Subtype = "MPEG4";
encodingProfile.Video.Subtype = "H264";
encodingProfile.Video.Width = width;
encodingProfile.Video.Height = height;
encodingProfile.Video.Bitrate = bitrateInBps;
encodingProfile.Video.FrameRate.Numerator = frameRate;
encodingProfile.Video.FrameRate.Denominator = 1;
encodingProfile.Video.PixelAspectRatio.Numerator = 1;
encodingProfile.Video.PixelAspectRatio.Denominator = 1;
var transcode = await _transcoder.PrepareMediaStreamSourceTranscodeAsync(_mediaStreamSource, stream, encodingProfile);
await transcode.TranscodeAsync();
}
}
}
And finally this is the initializer function in CaptureFrameWait class
private void InitializeCapture(SizeInt32 size)
{
_framePool = Direct3D11CaptureFramePool.CreateFreeThreaded(
_device,
DirectXPixelFormat.B8G8R8A8UIntNormalized,
1,
size);
_framePool.FrameArrived += OnFrameArrived;
_session = _framePool.CreateCaptureSession(_item);
_session.IsBorderRequired = false;
_session.StartCapture();
}
How can we modify this to pause the recording? I have tried to dispose the _framepool and _session objects on Pause and initialize them again on Resume in CaptureFrameWait class, like shown below. It works fine, but sometimes TranscodeAsync function terminates during pause and ends recording. How can we avoid that?
bool _paused = false;
public void PauseSession(bool status)
{
if (status) {
_paused = true;
_framePool?.Dispose();
_session?.Dispose();
}
else {
InitializeCapture(_size);
_paused = false;
}
}
One solution is to get a deferral. Doc says:
The MediaStreamSource will then wait for you to supply the
MediaStreamSample until you mark the deferral as complete.
So for example, add two private members to the Encoder class and two methods:
private MediaStreamSourceSampleRequestedEventArgs _args;
private MediaStreamSourceSampleRequestDeferral _def;
public bool IsPaused { get; private set; }
public void Pause()
{
IsPaused = true;
}
public void Resume()
{
IsPaused = false;
// complete the request we saved earlier
OnMediaStreamSourceSampleRequested(_mediaStreamSource, _args);
}
And modify OnMediaStreamSourceSampleRequested methods like this (where I've put comments):
private void OnMediaStreamSourceSampleRequested(MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
if (_isRecording && !_closed)
{
// if paused get a deferral and save the current arguments.
// OnMediaStreamSourceSampleRequested will not be called again until we complete the deferral
if (IsPaused)
{
_def = args.Request.GetDeferral();
_args = args;
return;
}
try
{
using (var frame = _frameGenerator.WaitForNewFrame())
{
if (frame == null)
{
args.Request.Sample = null;
DisposeInternal();
return;
}
var timeStamp = frame.SystemRelativeTime;
var sample = MediaStreamSample.CreateFromDirect3D11Surface(frame.Surface, timeStamp);
args.Request.Sample = sample;
// when called again (manually by us) complete the work
// and reset members
if (_def != null)
{
_def.Complete();
_def = null;
_args = null;
}
}
}
catch (Exception e)
{
...
}
}
else
{
...
}
}
Another solution is to simply freeze the frames timestamp, so add these members:
private TimeSpan _pausedTimestamp;
public bool IsPaused { get; private set; }
public void Pause()
{
IsPaused = true;
}
public void Resume()
{
IsPaused = false;
}
And modify OnMediaStreamSourceSampleRequested methods like this (where I've put comments):
private void OnMediaStreamSourceSampleRequested(MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
if (_isRecording && !_closed)
{
try
{
using (var frame = _frameGenerator.WaitForNewFrame())
{
if (frame == null)
{
args.Request.Sample = null;
DisposeInternal();
return;
}
// if paused, "freeze" the timestamp
TimeSpan timeStamp;
if (IsPaused)
{
timeStamp = _pausedTimestamp;
}
else
{
timeStamp = frame.SystemRelativeTime;
_pausedTimestamp = timeStamp;
}
var sample = MediaStreamSample.CreateFromDirect3D11Surface(frame.Surface, timeStamp);
args.Request.Sample = sample;
}
}
catch (Exception e)
{
...
}
}
else
{
...
}
}
I have been given the task to create a interface where I receive data through socket from the sender, for this purpose I am using NetMQ PushSocket for the sender side and then I receive the data at client side sung PullSocket and I have to update the UI (WPF app) when data is received so I receive data using poller in ReceiveReady event of the PullSocket when I do this in a seperate service class and call that class in UI ViewModel the UI thread hangs, so I use Poller.Run in a task, now the problem is that when I stop the poller and then restart it again it doesn't call the ReceiveReady event
Here is the ReceiverService for receiving the data.
public class ReceiverService
{
string msg;
string _address;
int _port;
PullSocket receiver;
NetMQPoller poller;
private MapViewModel ViewModel { get; set; }
public ReceiverService(MapViewModel mapViewModel, int port = 5555)
{
_address = GetComputerLanIP();
_port = port;
receiver = new PullSocket($"tcp://{_address}:{_port}");
receiver.Options.Linger = TimeSpan.Zero;
this.ViewModel = mapViewModel;
poller = new NetMQPoller { receiver };
receiver.ReceiveReady += receiver_ReceiveReady;
}
public void Start()
{
receiver.Connect($"tcp://{_address}:{_port}");
poller.Run();
}
public void Stop()
{
receiver.Disconnect($"tcp://{_address}:{_port}");
poller.Stop();
}
private void receiver_ReceiveReady(object sender, NetMQSocketEventArgs e)
{
// receive won't block as a message is ready
msg = e.Socket.ReceiveFrameString();
// send a response
if (!string.IsNullOrEmpty(msg))
{
try
{
//Updaing the ViewModel here
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
private string GetComputerLanIP()
{
string strHostName = Dns.GetHostName();
IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
foreach (var ipAddress in ipEntry.AddressList)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
return ipAddress.ToString();
}
}
return "";
}
private string GetValueFromMessage(string identifier)
{
msg.Replace("{", "");
msg.Replace("}", "");
identifier = /*" " + */identifier + " = ";
try
{
int index = msg.IndexOf(identifier) + identifier.Length;
if (index != -1)
{
int index2 = msg.IndexOf(";", index);
if (index2 == -1)
{
index2 = msg.Length;
}
return msg.Substring(index, index2 - index);
}
}
catch (IndexOutOfRangeException ex)
{
return null;
}
return null;
}
}
and in my ViewModel I have set commands for these
private void StartReceiver()
{
Task.Run(() => ReceiverService.Start());
}
private void StopReceiver()
{
Task.Run(() => ReceiverService.Stop());
}
What am I doing wrong? I am new to NetMQ and WPF. TIA
at first it would be good to make a task inside ReceiverService, kind of an ActorModel, because in the end if You would like to reuse it anywhere You need to remember that You should creat a Task first.
always it would be good to have socket in using statement, because You should always close socket if You are not using it
public async Task StartAsync() {
await Task.Run(() => ThreadBody())
}
public void Stop()
{
_poller.Stop();
}
private void ThreadBody()
{
using (PullSocket receiverSocket = new PullSocket())
using (_poller = new NetMQPoller())
{
receiverSocket.Connect($"tcp://{_address}:{_port}");
receiverSocket.ReceiveReady += receiver_ReceiveReady;
_poller.Add(receiverSocket);
_poller.Run();
}
}
So, I have a win form where I have to search for a string in a text file and display the line number and the entire line if I found the string. The search has to be multithreaded and all the line numbers and the lines must be on a listview. For example if the word "language" is in line number 60 , the listview must display:
60 "the line has the word language"
I have used the background worker in this regard but I am not being able to display the correct line number and the lines. Firstly, one line is being displayed multiple times and secondly, the line number is always coming to be 0. However, when I output the result in Console, the result is correct. I think I am making some error in putting the result into the listview.
Here' s my main form.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// private bool start_cancel;
bool bln = true;
private void StartCancelbtn_Click(object sender, EventArgs e)
{
if (bln)
text2();
else
text1();
bln = !bln;
}
private void text1()
{
StartCancelbtn.Text = "Start";
this.backgroundWorker1.CancelAsync();
}
private void text2()
{
StartCancelbtn.Text = "Cancel";
StartThread();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
// Get the Words object and call the main method.
main_work WC = (main_work)e.Argument;
WC.CountWords(worker, e);
if (worker.CancellationPending)
{
e.Cancel = true;
// break;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
main_work.ReportState state =
(main_work.ReportState)e.UserState;
ListViewItem l1 = new ListViewItem();
l1.Text = state.LinesCounted.ToString();
l1.SubItems.Add(state.line);
listView1.Items.Add(l1);
}
private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
MessageBox.Show("Error: " + e.Error.Message);
else if (e.Cancelled)
MessageBox.Show("Word counting canceled.");
else
MessageBox.Show("Finished counting words.");
}
private void StartThread()
{
main_work WC = new main_work();
WC.CompareString = this.searchtext.Text;
WC.SourceFile = this.filenametextbox.Text;
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync(WC);
}
private void browsebtn_Click(object sender, EventArgs e)
{
using (OpenFileDialog brw = new OpenFileDialog())
{
if (brw.ShowDialog() == DialogResult.OK)
{
using (StreamReader sr = new StreamReader(brw.FileName))
{
filenametextbox.Text = brw.FileName.ToString();
}
}
}
}
}
}
Here is my main_work class where the actual comparison is happening:
class main_work
{
public class ReportState
{
public int LinesCounted;
public string line;
}
Queue q = new Queue();
public string SourceFile;
public string CompareString;
public string line2 ;
public int linenumber=0;
public int linenumber1 = 0;
int LinesCounted1;
// public string line2;
public void CountWords(
System.ComponentModel.BackgroundWorker worker,
System.ComponentModel.DoWorkEventArgs e)
{
// Initialize the variables.
ReportState state = new ReportState();
string line1 = "";
int elapsedTime = 1;
DateTime lastReportDateTime = DateTime.Now;
if (CompareString == null ||
CompareString == System.String.Empty)
{
MessageBox.Show("Please Enter a string to be searched");
}
else
{
// Open a new stream.
using (System.IO.StreamReader myStream = new System.IO.StreamReader(SourceFile))
{
// Process lines while there are lines remaining in the file.
while (!myStream.EndOfStream)
{
if (worker.CancellationPending)
{
e.Cancel = true;
// break;
}
else
{
line1 = myStream.ReadLine();
line2 = (CountInString(line1, CompareString));
LinesCounted1 = (linenumbercount(line1, CompareString, linenumber1));
// Raise an event so the form can monitor progress.
int compare = DateTime.Compare(
DateTime.Now, lastReportDateTime.AddSeconds(elapsedTime));
if (compare > 0)
{
state.LinesCounted = LinesCounted1;
state.line = line2;
worker.ReportProgress(0, state);
lastReportDateTime = DateTime.Now;
}
System.Threading.Thread.Sleep(1);
}
}
// Report the final count values.
state.LinesCounted = LinesCounted1;
state.line = line1;
worker.ReportProgress(0, state);
lastReportDateTime = DateTime.Now;
}
}
}
private string CountInString(
string SourceString,
string CompareString)
{
if (SourceString.Contains(CompareString))
{
line2 = SourceString;
Console.WriteLine(SourceString);
}
return line2;
}
private int linenumbercount(
string SourceString,
string CompareString,
int linenumber)
{
// Lines = 0;
if (SourceString == null)
{
return 0;
}
if (SourceString.Contains(CompareString))
{
Console.WriteLine(linenumber);
}
linenumber++;
return linenumber;
}
}
Any feedback will be helpful. Please let me know what I am doing wrong and where is it that I am making a mistake as I am new to background worker and multithreading. Thanks.
CODE UPDATED TO REFLECT ANSWER: SAME PROBLEM STILL OCCURS
This class is supposed to run all tasks in the list, sleep and then wake up and repeat the process infinitely. For some reason though, after the first sleep, the sleepThread.RunWorkerAsync() call gets called twice for some reason. I can obviously solve this by:
if (!sleepThread.IsBusy) { sleepThread.RunWorkerAsync(); }
but that feels like a work around.
Here is the main routine class:
public class ServiceRoutine
{
private static volatile ServiceRoutine instance;
private static object instanceLock = new object();
private static object listLock = new object();
private static readonly List<Task> taskList = new List<Task>()
{
new UpdateWaferQueueTask(),
new UpdateCommentsTask(),
new UpdateFromTestDataTask(),
new UpdateFromTestStationLogsTask(),
new UpdateFromWatchmanLogsTask(),
new UpdateStationsStatusTask()
};
private List<Task> runningTasks;
private BackgroundWorker sleepThread;
private Logger log;
private ServiceRoutine()
{
log = new Logger();
runningTasks = new List<Task>();
sleepThread = new BackgroundWorker();
sleepThread.WorkerReportsProgress = false;
sleepThread.WorkerSupportsCancellation = false;
sleepThread.DoWork += (sender, e) =>
{
int sleepTime = ConfigReader.Instance.GetSleepTime();
log.Log(Logger.LogType.Info,
"service sleeping for " + sleepTime / 1000 + " seconds");
Thread.Sleep(sleepTime);
};
sleepThread.RunWorkerCompleted += (sender, e) => { Run(); };
}
public static ServiceRoutine Instance
{
get
{
if (instance == null)
{
lock (instanceLock)
{
if (instance == null)
{
instance = new ServiceRoutine();
}
}
}
return instance;
}
}
public void Run()
{
foreach (Task task in taskList)
{
lock (listLock)
{
runningTasks.Add(task);
task.TaskComplete += (completedTask) =>
{
runningTasks.Remove(completedTask);
if (runningTasks.Count <= 0)
{
sleepThread.RunWorkerAsync();
}
};
task.Execute();
}
}
}
}
this is called like this:
ServiceRoutine.Instance.Run();
from the service start method. Here is the Task class as well:
public abstract class Task
{
private Logger log;
protected BackgroundWorker thread;
public delegate void TaskPointer(Task task);
public TaskPointer TaskComplete;
public Task()
{
log = new Logger();
thread = new BackgroundWorker();
thread.WorkerReportsProgress = false;
thread.DoWork += WorkLoad;
thread.RunWorkerCompleted += FinalizeTask;
}
protected abstract string Name { get; }
protected abstract void WorkLoad(object sender, DoWorkEventArgs e);
private string GetInnerMostException(Exception ex)
{
string innerMostExceptionMessage = string.Empty;
if (ex.InnerException == null) { innerMostExceptionMessage = ex.Message; }
else
{
while (ex.InnerException != null)
{
innerMostExceptionMessage = ex.InnerException.Message;
}
}
return innerMostExceptionMessage;
}
protected void FinalizeTask(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (e.Error != null)
{
string errorMessage = GetInnerMostException(e.Error);
log.Log(Logger.LogType.Error, this.Name + " failed: " + errorMessage);
}
else
{
log.Log(Logger.LogType.Info, "command complete: " + this.Name);
}
}
catch (Exception ex)
{
string errorMessage = GetInnerMostException(ex);
log.Log(Logger.LogType.Error, this.Name + " failed: " + errorMessage);
}
finally { TaskComplete(this); }
}
public void Execute()
{
log.Log(Logger.LogType.Info, "starting: " + this.Name);
thread.RunWorkerAsync();
}
}
The question is, why is sleepThread.RunWorkerAsync() getting called twice and is there a better way to get this work without checking if the thread is busy before calling it?
You are facing a race condition here. The problem is in the TaskComplete callback. Last two tasks remove themselves from the runningTasks list before executing the if condition. When it is executed, the list count is zero. You should lock the list before changing its. The lock needs to be taken in the TaskComplete callback:
runningTasks.Add(task);
task.TaskComplete += (completedTask) =>
{
lock (runningTasks)
{
runningTasks.Remove(completedTask);
if (runningTasks.Count <= 0)
{
sleepThread.RunWorkerAsync();
}
}
};
task.Execute();
SOLVED
I tried several different locking techniques on the runningTasks list but nothing worked. After changing runningTasks to a BlockingCollection, everything worked perfectly.
Here is the new add/remove implementation using a BlockingCollection instead of a List:
foreach (Task task in taskList)
{
runningTasks.Add(task);
task.TaskComplete += (completedTask) =>
{
runningTasks.TryTake(out completedTask);
if (runningTasks.Count <= 0 && completedTask != null)
{
sleepThread.RunWorkerAsync();
}
};
task.Execute();
}
I'm trying to access information on a web browser from another thread. When trying to access the browser.DocumentTitle, I get this error:
The name DocumentTitle does not exist in the current context
I can successfully navigate to webpages inside the DoWork or ProcessWebPage methods but I cannot access the GetTitle function without crashing. I have been working on this part alone for days and simply cannot figure it out.
Here is the problem code:
BROWSER CODE
class BrowserInterface : Form
{
WebBrowser browser;
Thread thread;
State state;
public State State { get { return state; } }
public BrowserInterface()
{
Initialize();
}
void Initialize()
{
browser = new WebBrowser();
state = State.Null;
state = State.Initializing;
thread = new Thread(StartThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while (state == State.Initializing) Thread.Sleep(20);
}
void StartThread()
{
browser = new WebBrowser();
browser.Dock = DockStyle.Fill;
browser.Name = "webBrowser";
browser.ScrollBarsEnabled = false;
browser.TabIndex = 0;
browser.DocumentCompleted +=
new WebBrowserDocumentCompletedEventHandler(this.Web_Completed);
Form form = new Form();
form.Controls.Add(browser);
form.Name = "Browser";
state = State.Null;
Application.Run(form);
}
public void Navigate(string url)
{
state = State.Navigating;
if (browser.IsDisposed)
Initialize();
browser.Navigate(url);
}
public string GetTitle()
{
if (InvokeRequired)
{
BeginInvoke(new MethodInvoker(() => GetTitle()));
}
return browser.DocumentTitle;
}
private void Web_Completed(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br = sender as WebBrowser;
if (br.Url == e.Url)
state = State.Completed;
}
}
enum State
{
Initializing,
Null,
Navigating,
Completed
}
OTHER THREAD
class Controller
{
public int ThreadsAllowed;
private ManualResetEvent[] resetEvent;
private BrowserInterface[] browser;
static Thread mainThread;
bool run;
bool exit;
public Controller(int threadsAllowed)
{
ThreadsAllowed = threadsAllowed;
resetEvent = new ManualResetEvent[ThreadsAllowed];
browser = new BrowserInterface[ThreadsAllowed];
for (int i = 0; i < ThreadsAllowed; i++)
{
resetEvent[i] = new ManualResetEvent(true);
browser[i] = new BrowserInterface();
}
ThreadPool.SetMaxThreads(ThreadsAllowed, ThreadsAllowed);
mainThread = new Thread(RunThread);
mainThread.Start();
run = false;
exit = false;
}
public void Run()
{
run = true;
}
void RunThread()
{
while (true)
{
while (!run) Thread.Sleep(20);
while (mode == ScoutMode.Off) Thread.Sleep(100);
//wait for the last set to complete
WaitHandle.WaitAll(resetEvent);
if (exit)
break;
for (int i = 0; i < ThreadsAllowed; i++)
ThreadPool.QueueUserWorkItem(DoWork, i);
}
}
void DoWork(object o)
{
int i = (int)o;
if(browser[i].state == State.null)
{
…
… navigation code that works …
…
return;
}
else if(browser[i].state == State.Completed)
ProcessWebPage(i);
}
void ProcessWebPage(int i)
{
string title;
try
{
title = browser[i].GetTitle();
}
catch { return; }
}
}
What hurts my eye is your GetTitle function. When using MethodInvoker, you're dealing with methods of void type, that is, you cannot get return value from the function. That's why you need a delegate which will return you the value.
Also, you have to have else statement, so to not try to return the value when invoking is in fact required.
class BrowserInterface : Form
{
/* ... */
private delegate string StringDelegate();
public string GetTitle()
{
/*
if (InvokeRequired)
{
BeginInvoke(new MethodInvoker(() => GetTitle()));
}
return browser.DocumentTitle;
*/
if (InvokeRequired)
{
object result = Invoke(new StringDelegate(GetTitle));
return (string)result;
}
else
return browser.DocumentTitle;
}
/* ... */
}
At first, use browsers invoke instead of forms one. And the main problem that after invokation you will return to code and try to access browser.DocumentTitle as background thread. To avoid this, add else construction.
public string GetTitle()
{
if (this.browser.InvokeRequired)
{
this.browser.Invoke(new MethodInvoker(() => GetTitle()));
}
else
{
return browser.DocumentTitle;
}
}