I'm developing a program that receives some data and after processing I want to print it.It should be automatic printing.The data is added as records to printList queue.Then a thread is supposed to print them one by one.here's the code :
private void button4_Click(object sender, EventArgs e)
{
rwl.AcquireWriterLock(10);
try
{
if (automaticPrint == false)
{
automaticPrint = true;
_automaticPrintThread = new Thread(new ThreadStart(AutomaticPrintA4));
_automaticPrintThread.IsBackground = true;
_automaticPrintThread.Start();
}
else
{
automaticPrint = false;
if (_automaticPrintThread != null)
{
_automaticPrintThread.Join(1);
}
}
}
finally
{
rwl.ReleaseWriterLock();
}
}
private void AutomaticPrintA4()
{
try
{
this.AutomaticPrintA4Delegate();
}
catch (Exception e)
{
this._automaticPrintThread.Abort();
MessageBox.Show(e.StackTrace);
}
}
private void AutomaticPrintA4Delegate()
{
try
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(AutomaticPrintA4Delegate));
else
{
rwl.AcquireReaderLock(100);
Telerik.Reporting.Processing.ReportProcessor rp = new Telerik.Reporting.Processing.ReportProcessor();
System.Drawing.Printing.PrinterSettings ps = new System.Drawing.Printing.PrinterSettings();
try
{
while (automaticPrint)
{
rwlprintlist.AcquireReaderLock(10);
try
{
if (Session.printList != null)
{
if (Session.printList.Count != 0)
{
if (Session.printList[0] != null)
{
rp.PrintReport(new Report(Session.printList[0]), ps);
LockCookie lc = rwlprintlist.UpgradeToWriterLock(10);
Session.printList.RemoveAt(0);
rwlprintlist.DowngradeFromWriterLock(ref lc);
}
}
else
{
Thread.Sleep(1000);
}
}
else
{
Thread.Sleep(1000);
}
}
finally
{
rwlprintlist.ReleaseReaderLock();
}
}
}
finally
{
rwl.ReleaseReaderLock();
}
}
}
catch (Exception e)
{
MessageBox.Show("Print : " + e.StackTrace + e.Message);
}
}
A button click controls automaticprint variable.
rwl controls access to automatic print and rwlprintlist controls access to printList.
but my main gui hangs whenever I start this thread.I don't know why?
P.S. any other design ideas would be appreciated.
In your background worker you call AutomaticPrintA4Delegate. This method switches to the UI thread with
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(AutomaticPrintA4Delegate));
So the print out is running in the UI thread and that is the reason, why your UI is "hanging".
Related
Hi,
I'm trying to write a UDP-client that listens to a port, and then display the incoming data in a textbox.
I have written a class; UDP_Receive() that starts a backgroundworker that listens to a specified port and receives the data. The code is based on this question; C# .Net receiving UDp packets in separater thread and application exit
I think I have solved the issues with the blocking .receive() by following the accepted answer in this question; How can I safely terminate UdpClient.receive() in case of timeout?
My question is how do I get the data I receive in the backgroundworkerthread (that I created in my class) back to the mainthread, so that I can display it in a textbox?
I thought of using Invoke, but I don't have any references to the textbox in my class.
textBox_UdpPositionInData.Invoke(new EventHandler(delegate
{
textBox_UdpPositionInData.Text = receivedData;
}));
I found a very similar question about a tcp-server, but I can't see how to apply that solution here Send data from a background thread to the main thread
Any thoughts or suggestions are of course much appreciated!
Many Thanks!
internal class UDP_Receive
{
private int _portToListen = 2003;
private volatile bool listening;
BackgroundWorker _backgroundWorker;
//Constructor
public UDP_Receive()
{
Debug.WriteLine("Constructor: UDP_Receive");
this.listening = false;
}
public void StartListener()
{
if ( (_backgroundWorker==null) || (!_backgroundWorker.IsBusy))
{
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += listenForUDPPackages_DoWork;
_backgroundWorker.RunWorkerCompleted +=listenForUDPPackages_RunWorkerCompleted;
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.RunWorkerAsync();
Debug.WriteLine("Creates a new thread: " + _backgroundWorker.ToString() );
// We are listening
this.listening = true;
}
}
public void StopListener()
{
// The user cancelled the UDP Port listening
this.listening = false;
// Cancel the backgroundworker
_backgroundWorker.CancelAsync();
// Debug
Debug.WriteLine("Stops current thread: " + _backgroundWorker.ToString());
}
public bool IsListening
{
get { return this.listening; }
}
public int PortToListen
{
get { return this._portToListen; }
set { this._portToListen = value; }
}
private void listenForUDPPackages_DoWork(object sender, DoWorkEventArgs ev)
{
UdpClient? listener = null;
try
{
listener = new UdpClient(_portToListen);
}
catch (SocketException)
{
//do nothing
}
if (listener != null)
{
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, _portToListen);
try
{
while (this.listening)
{
Debug.WriteLine("Waiting for UDP broadcast to port " + _portToListen);
byte[] receivedBytes = new byte[1024];
string receivedData;
bool timeTracker = TrackFunction(TimeSpan.FromSeconds(2), () =>
{
receivedBytes = listener.Receive(ref groupEP);
});
Debug.WriteLine("Timetracker result: " + timeTracker.ToString());
if (receivedBytes == null || receivedBytes.Length == 0)
{
// We did not recieve any data
Debug.WriteLine("No data received befor Time out ");
}
else
{
// We managed to receive some data!
// No we want to process the data and then send the result to the Thread that initiated the class.
receivedData = Encoding.Default.GetString(receivedBytes);
Debug.WriteLine("Data received: " + receivedData);
}
}
catch (Exception e)
{
Debug.WriteLine("Exception: " + e.ToString());
}
finally
{
listener.Close();
Debug.WriteLine("Finally: Done listening for UDP broadcast");
}
}
else
{
Debug.WriteLine("Error: UdpClient(_portToListen) returned null");
}
}
private void listenForUDPPackages_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e == null || e.Result == null)
{
Debug.WriteLine("listenForUDPPackages_RunWorkerCompleted e = null");
}
else
{
if (e.Cancelled)
{
Debug.WriteLine("Operation was canceled");
}
else if (e.Error != null)
{
Debug.WriteLine("Error: " + e.Error.Message);
}
else
{
Debug.WriteLine("Result: " + e.Result.ToString());
}
}
}
private static bool TrackFunction(TimeSpan timeSpan, Action codeBlock)
{
try
{
Task task = Task.Factory.StartNew(() => codeBlock());
task.Wait(timeSpan);
return task.IsCompleted;
}
catch (AggregateException ae)
{
throw ae.InnerExceptions[0];
}
}
}
You can write this kind of code to keep the UI separate from the UDP_Receive class:
internal class UDP_Receive
{
private Action<string> _invoke;
public UDP_Receive(Action<string> invoke)
{
_invoke = invoke;
}
public void Foo()
{
_invoke("Hello");
}
}
When you declare the class you then do it like this:
var ur = new UDP_Receive(t => textBox.Invoke(() => textBox.Text = t));
Now it's just a matter of calling ur.Foo() (in my example) to update the UI.
I'm having an issue where I want to be able to modify a textbox from another class.
I've tried searching and testing solutions, none seemed to do the job. (Invoking for an example..)
Code from class cThread:
class cThread
{
public bool closed = false;
private TcpClient client;
private StreamReader ins;
private StreamWriter ots;
Form1 meow = new Form1();
public cThread(TcpClient client, StreamReader ins, StreamWriter ots)
{
this.client = client;
this.ins = ins;
this.ots = ots;
}
public void run()
{
try
{
string responseLine;
responseLine = meow.bunifuCustomTextbox2.Text;
while ((responseLine = ins.ReadLine()) != null)
{
Console.WriteLine(responseLine);
meow.bunifuCustomTextbox3.Text = responseLine + " test";
if (responseLine.IndexOf("*** Adios") != -1)
{
break;
}
}
closed = true;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
Environment.Exit(0);
}
}
This causes an error: Cross-Thread operation not valid: accessed from a thread other than the thread it was created on.
I have also tried this solution:
public void run()
{
try
{
string responseLine;
meow.bunifuCustomTextbox2.Invoke(new MethodInvoker(delegate { responseLine = meow.bunifuCustomTextbox2.Text; }));
while ((responseLine = ins.ReadLine()) != null)
{
Console.WriteLine(responseLine);
meow.bunifuCustomTextbox3.Invoke(new MethodInvoker(delegate { meow.bunifuCustomTextbox3.Text = meow.bunifuCustomTextbox2.Text; }));
meow.bunifuCustomTextbox3.Text = responseLine + " test";
if (responseLine.IndexOf("*** Adios") != -1)
{
break;
}
}
closed = true;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
Environment.Exit(0);
}
}
This did not work either. Same error.
What I'm expecting to happen is when the user types in a message in bunifuCustomTextbox2, it'll then be set to responseLine which I want to lively be updated by bunifuCustomTextbox3.
Because this is going to be a multiplayer chat and I'm converting this code from a consoleapp to winforms..
sorry if im being a dumb :(
When you access an UI resource from another thread you should have the Dispatcher thread handle that:
Dispatcher.Invoke(() => { /* UI changes */ });
I have a problem with thread, I want to display a text on my txtoutput(textbox) when I receive a SMS I have done that but doesn't work.
private void Output(string text)
{
this.expander.IsExpanded = true; // Exception catched: The calling thread can not access this object because a different thread owns it.
if (txtOutput.Dispatcher.CheckAccess())
{
txtOutput.AppendText(text);
txtOutput.AppendText("\r\n");
}
else
{
this.txtOutput.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate
{
// txtOutput.AppendText += text Environment.NewLine;
txtOutput.AppendText(text);
txtOutput.AppendText("\r\n");
});
}
}
You're setting the text of txtOutput in a correct way (CheckAccess() and BeginInvoke). Do the same with expander.
try this instead
private void Output(string text)
{
if (txtOutput.Dispatcher.CheckAccess())
{
this.expander.IsExpanded = true;
txtOutput.AppendText(text);
txtOutput.AppendText("\r\n");
}
else
{
this.txtOutput.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate
{
this.expander.IsExpanded = true;
// txtOutput.AppendText += text Environment.NewLine;
txtOutput.AppendText(text);
txtOutput.AppendText("\r\n");
});
}
}
Improved version:
private void Output(string text)
{
if (!txtOutput.Dispatcher.CheckAccess())
{
this.txtOutput.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate
{
Output(text); //Call this function again on the correct thread!
});
return;
}
this.expander.IsExpanded = true;
txtOutput.AppendText(text);
txtOutput.AppendText("\r\n");
}
I want to playback a sound file in two or three external sound cards at the same time and I think that using threads is the solution but I really didn't know how to use it in the playback code.
This is the event makes on button play:
public partial class PlaybackForm : Form
{
IWavePlayer waveOut;
string fileName = null;
WaveStream mainOutputStream;
WaveChannel32 volumeStream;
int _deviceNum;
int _deviceNum1;
Thread t1;
Thread t2;
public PlaybackForm(int deviceNum,int deviceNum1)
{
InitializeComponent();
_deviceNum = deviceNum;
_deviceNum1 = deviceNum1;
}
private void buttonPlay_Click(object sender, EventArgs e)
{
if (waveOut != null)
{
if (waveOut.PlaybackState == PlaybackState.Playing)
{
return;
}
else if (waveOut.PlaybackState == PlaybackState.Paused)
{
waveOut.Play();
return;
}
}
// we are in a stopped state
// TODO: only re-initialise if necessary
if (String.IsNullOrEmpty(fileName))
{
toolStripButtonOpenFile_Click(sender, e);
}
if (String.IsNullOrEmpty(fileName))
{
return;
}
try
{
CreateWaveOut();
}
catch (Exception driverCreateException)
{
MessageBox.Show(String.Format("{0}", driverCreateException.Message));
return;
}
mainOutputStream = CreateInputStream(fileName);
trackBarPosition.Maximum = (int)mainOutputStream.TotalTime.TotalSeconds;
labelTotalTime.Text = String.Format("{0:00}:{1:00}", (int)mainOutputStream.TotalTime.TotalMinutes,
mainOutputStream.TotalTime.Seconds);
trackBarPosition.TickFrequency = trackBarPosition.Maximum / 30;
try
{
waveOut.Init(mainOutputStream);
}
catch (Exception initException)
{
MessageBox.Show(String.Format("{0}", initException.Message), "Error Initializing Output");
return;
}
// not doing Volume on IWavePlayer any more
volumeStream.Volume = volumeSlider1.Volume;
waveOut.Play();
}
And this is how to create the waveout:
private void CreateWaveOut()
{
CloseWaveOut();
int latency = (int)comboBoxLatency.SelectedItem;
//if (radioButtonWaveOut.Checked)
{
//WaveCallbackInfo callbackInfo = checkBoxWaveOutWindow.Checked ?
WaveCallbackInfo callbackInfo = WaveCallbackInfo.FunctionCallback();
// WaveCallbackInfo callbackInfo = WaveCallbackInfo.FunctionCallback();
// WaveCallbackInfo.NewWindow(): WaveCallbackInfo.FunctionCallback();
WaveOut outputDevice = new WaveOut(callbackInfo);
outputDevice.DesiredLatency = latency;
outputDevice.DeviceNumber = _deviceNum;
waveOut = outputDevice;
}
}
I declared two deviceNum but until now I can playsound only in one device,that's why I want to use thread.
Can you help me please
Thank you in advance
Do something like that:
using System.Threading;
...
private void buttonPlay_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.PlaySound), 1);
ThreadPool.QueueUserWorkItem(new WaitCallback(this.PlaySound), 2);
}
private void PlaySound(object obj)
{
int deviceNumber = (int)obj;
// Do the stuff you used to do in buttonPlay_Click
WaveOut myWaveOut = CreateWaveOut(deviceNumber);
...
}
private WaveOut CreateWaveOut(int deviceNumber)
{
...
WaveOut outputDevice = new WaveOut(callbackInfo);
outputDevice.DesiredLatency = latency;
outputDevice.DeviceNumber = _deviceNum;
return outputDevice;
}
I've got a method called Import that imports the data of an xml file into the database. It looks like this:
private void SavingXMLFile()
{
//Save the file:
if (rootElement != null)
{
try
{
using (FileStream fsHosp = new FileStream("Data/ConfigOrgHospital.xml", FileMode.Truncate, FileAccess.Write))
{
using (XmlWriter x = XmlWriter.Create(fsHosp))
{
SerializeHospitalData(x, rootElement);
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
try
{
Import("Hospitals");
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
Then it goes to the import method:
public void Import(string sourceFile)
{
IProgressReporter progressReporter = new ProgressReporter();
BackgroundThreading.RunInBackground<int>((object input) =>
{
foreach (IConfigRunner runner in Runners.OrderBy(r => r.Priority))
{
runner.DoImport(progressReporter, sourceFile);
}
return 0;
}, (answer, error, w) =>
{
if (error != null)
{
//ShowError(error);
}
else
{
//AddProgress("Export ready");
}
//DoReady();
}, (error) =>
{
//ShowError(error);
});
}
Then it does a DoImport:
public interface IConfigRunner
{
int Priority { get; }
void DoExport(IProgressReporter progress);
void DoImport(IProgressReporter progress, string filename);
}
[Export]
public void DoImport(IProgressReporter progress, string filename = null)
{
if (filename.Equals("Hospitals"))
{
if (File.Exists(HOSPITALFILE))
{
progress.AddProgress("Importing " + HOSPITALFILE);
using (OrgEntities orgEntityModel = ModelFactory.GetOrgEntities())
{
Tools.ValidateXml(HOSPITALFILE, "Xsd\\hospital.xsd");
XmlSerializer inSerializer = new XmlSerializer(typeof(HospitalRoot));
TextReader reader = new StreamReader(HOSPITALFILE);
HospitalRoot root = (HospitalRoot)inSerializer.Deserialize(reader);
reader.Close();
try
{
OrgHospitalXml.Insert(orgEntityModel, root.Hospitals);
}
catch (ImportException e)
{
progress.AddProgress(e.Message + ": " + e.Item);
throw e;
}
}
}
}
Is there any way that I can show the progression of this in a progressbar? Or how to find all the UI Threads? Thx in advance
BackgroundWorker:
public static class BackgroundThreading
{
public static BackgroundWorker RunInBackground<T>(object param, Func<object, T> call, Action<T, Exception, BackgroundWorker> callBack, Action<Exception> errorHandler = null) where T : new()
{
BackgroundWorker worker = new BackgroundWorker();
DoWorkEventHandler workHandler = null;
RunWorkerCompletedEventHandler completeHandler = null;
workHandler = delegate(object s, DoWorkEventArgs args)
{
args.Result = call(args.Argument);
args.Cancel = worker.CancellationPending;
};
completeHandler = delegate(object s, RunWorkerCompletedEventArgs args)
{
if (!args.Cancelled)
{
if (args.Error != null)
{
if (!(args.Error is FaultException))
{
T result = new T();
callBack(result, args.Error, (BackgroundWorker)s);
}
else
{
if (errorHandler == null)
{
string message;
if (args.Error.InnerException != null)
{
message = args.Error.InnerException.Message;
}
else
{
message = args.Error.Message;
}
Logger.LogError("SVC", Logger.SVC_ERROR_001, new object[1] { message });
throw args.Error;
}
else
{
errorHandler(args.Error);
}
}
}
else
{
callBack((T)args.Result, null, (BackgroundWorker)s);
}
((BackgroundWorker)s).DoWork -= workHandler;
((BackgroundWorker)s).RunWorkerCompleted -= completeHandler;
}
};
worker.DoWork += workHandler;
worker.RunWorkerCompleted += completeHandler;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync(param);
return worker;
}
public static BackgroundWorker RunInBackground<T>(Func<object, T> call, Action<T, Exception, BackgroundWorker> callBack, Action<Exception> errorHandler = null) where T : new()
{
return RunInBackground<T>(null, call, callBack, errorHandler);
}
Use BackgroundWorker http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
ProgressChanged event is automatically run on UI thread while your actual code runs in a different thread.
As far as i remember you need to hook up to the progress changed event and update your progress bar in this.
You then need to call worker.reportProgress(x).
Another option is to set your progress bar to marque and call Application.DoEvents every now and then but this may well be frowned upon by other members