I'm developing a Windows Form app with C# and .NET Framework 4.0.
I'm using Task to run a long running task and I need to update UI with some log messages every time my task process a code.
There is a Queue processing that code, I need to show that a code has been processed.
private Task taskReadCodeAuto;
private delegate void RefreshTextBox();
private Queue CodesReceived;
public MainForm()
{
InitializeComponent();
logMessages = new List<string>();
CodesReceived = new Queue();
taskReadCodeAuto = new Task(() => ProcessCodesReceived());
}
private void ProcessCodesReceived()
{
int result;
try
{
while (CodesReceived.Count > 0)
{
string code = CodesReceived.Dequeue().ToString();
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), string.Format("Sending code {0} to ReadCodeAuto...", code));
if (trzic == null)
{
result =
TRZIC.ReadCodeAuto(
ConnStringTextBox.Text,
byte.Parse(AggregationNumeric.Value.ToString()),
code);
}
else
{
result =
trzic.ReadCodeAuto(
byte.Parse(AggregationNumeric.Value.ToString()),
code);
}
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), string.Format("Code sent {0}. Result: {1}", code, result));
}
}
catch (Exception ex)
{
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "Error: " + ex.Message);
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
finally
{
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "END BG-WORKER");
}
}
private void InsertProfileMessage(string time, string message)
{
string profileString =
string.Format("{0} - {1}", time, message);
logMessages.Add(profileString);
if (this.InvokeRequired)
{
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
}
else
{
RefreshTextBoxResults(profileString + "\n");
}
}
private void RefreshTextBoxResults(string text)
{
LogTextBox.AppendText(text);
}
My problem is that I don't know how to pass the text to show on LogTextBox using Invoke.
How can I do it?
Use the overload of Invoke which takes an Object[] as a parameter for the arguments to be supplied to your method.
You can add the parameters after the invoke:
Action<string> d = RefreshTextBoxResults;
this.Invoke(d, profileString + "\n");
Or invoke an action where the parameter is already included (which is this case is suitable regarding re usability)
Action d= () =>RefreshTextBoxResults(profileString + "\n");
if (this.InvokeRequired)
{
Invoke(d);
}
else
{
d();
}
PS, if you want to use your RefreshTextBox delegate instead of an Action, the RefreshTextBox delegate should be altered to include a string parameter
You would have to use the overload of Invoke which uses an array:
MSDN documentation
Pass the text value like below.
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d,new object[] {“Pass value here”});
Related
I would like to cancel a Task.run in a clean and simple way here is my code:
bool NTAG_isHere = false;
// CODE (...)
private async Task Dump_NTAG(object sender, EventArgs eventArgs)
{
// while NTAG PRESENT DUMP START:
await Task.Run(() => {while (NTAG_isHere()) { } });
// CODE (...)
// If NTAG NOT PRESENT or not detected stop the dump and kill thread:
if(!NTAG_isHere)
{
// kill the thread
}
{
Thank you,
Edit 4, my complete method :
private void Dump_NTAG(object sender, EventArgs eventArgs)
{
try
{
richTextBox_debug.Invoke(new UpdateTextCallback(CleanText), new object[] {string.Empty});
WriteLine(NTAG_isHere());
if (!NTAG_isHere())
{
Task.Factory.StartNew(() =>
{
richTextBox_debug.Invoke(new UpdateTextCallback(CleanText), new object[] { string.Empty });
byte[] dump = new byte[540];
int i;
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { "START" + Environment.NewLine + Environment.NewLine });
for (i = 0; i < 135; i++)
{
string Result = arduino.SendCommand("/READ " + i);
string[] SplitResult = Result.Split('/', ' ');
if (SplitResult.Length > 1)
{
if (Result.Split('/', ' ')[1] == "ERROR")
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { string.Format("NFC_Error" + Result.Substring(1) + Environment.NewLine) });
else
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { string.Format("NFC_Unknown_Response" + Result + Environment.NewLine) });
i = 135;
}
else
{
string page = "Page";
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { string.Format(page + " {0} : {1}", i, Result) + Environment.NewLine });
}
}
if (i == 135) { richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { "Dump success !" + Environment.NewLine + Environment.NewLine }); }
arduino.SendCommand("/NTAG_HALT");
arduino.Close();
});
}
else
{
MessageBox.Show("ERROR !!");
}
}
catch (Exception e)
{
WriteLine(e.Message);
}
}
my concern is that i can no longer dump, the boolean would still be wrong with this new code, but my method NTAG_isHere () returns good value, so is not in question. and that never do I pass in MessageBox.Show ("ERROR !!"); with or without ntag on the player
looking at your code now , you can simply do this , there is no need of cancellation thing , just check the method returns true than run task else dont
private void Read_amiibo(object sender, EventArgs eventArgs)
{
bool ishere = NTAG_isHere();
//store reference of local thread
if(ishere)
{
Task.Factory.StartNew(() =>
{
//do operation
}
}
else {
//exit
}
}
one point to add here : if you make use of async/await your call get return when await encounters and code below will not get executed
Below solution will work in case when there is no async/await
make use of CancellationTokenSource with task that will do for you, CancellationToken you can pass as argument to run method and monitor it for cancellation.
This is cleaner way given by Microsoft team.
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Run( () => {
//perform task
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
} , token);
if(!NTAG_isHere)
{
// kill the thread
tokenSource.Cancel();
}
other way not much recommended ,
//store reference of local thread
Thread threadToCancel = null;
Task.Factory.StartNew(() =>
{
//Capture the thread
threadToCancel = Thread.CurrentThread;
}
//you can put timer also , means check value after some time to avoid while loop
while(true) {
if(NTAG_isHere){
threadToCancel.Abort();
}
}
Discussed this approach here : Abort/Cancel Task
reference check here : Task.Run Method (Action, CancellationToken)
I'm stuck in TAPI programming. I've created a program to monitor activity of phone call. Everything is working fine, but now I want to implement a functionality to accept and reject a call from web directly.
what I've done is below:
namespace Shared
{
public partial class Site : System.Web.UI.MasterPage
{
public static ITAddress ln;
static int index = -1;
static int line;
static ITAddress[] ia;
protected void Page_Load(object sender, EventArgs e)
{
#region TAPI
TAPIClass tobj;
int[] registertoken;
tobj = new TAPI3Lib.TAPIClass();
tobj.Initialize();
IEnumAddress ea = tobj.EnumerateAddresses();
uint lines;
uint arg3 = 0;
int TotalLines = 0;
lines = 0;
foreach (TAPI3Lib.ITAddress ad in (tobj.Addresses as TAPI3Lib.ITCollection))
{
TotalLines++;
}
callnotification cn = new callnotification();
tobj.ITTAPIEventNotification_Event_Event += new TAPI3Lib.ITTAPIEventNotification_EventEventHandler(cn.Event);
tobj.EventFilter = (int)(TAPI_EVENT.TE_CALLNOTIFICATION |
TAPI_EVENT.TE_DIGITEVENT |
TAPI_EVENT.TE_PHONEEVENT |
TAPI_EVENT.TE_CALLSTATE |
TAPI_EVENT.TE_GENERATEEVENT |
TAPI_EVENT.TE_GATHERDIGITS |
TAPI_EVENT.TE_REQUEST);
registertoken = new int[TotalLines];
ia = new TAPI3Lib.ITAddress[TotalLines];
for (int i = 0; i = 0)
{
ln = ia[line];
}
IEnumCall ec = ln.EnumerateCalls();
uint arg = 0;
ITCallInfo ici;
try
{
ec.Next(1, out ici, ref arg);
ITBasicCallControl2 bc = (TAPI3Lib.ITBasicCallControl2)ici;
if (ici != null && ici.CallState == CALL_STATE.CS_OFFERING)
{
if (bc != null)
{
bc.Answer();
}
}
}
catch (Exception ex)
{
COMException comEx = ex as COMException;
if (comEx != null)
comEx.ErrorCode.ToString();
else
{
string aa = ex.Message;
}
}
//addtolist("Call Offering from " + callernumber + " to Ext " + viaextension + " via DID " + DIDNumber);
break;
case TAPI3Lib.CALL_STATE.CS_IDLE:
//addtolist("Call is created!");
break;
}
break;
}
}
catch (Exception ex)
{
//MessageBox.Show(ex.StackTrace.ToString());
}
}
}
#endregion
}
}
I'm always getting ITBasicCallControl2 bc NULL and when I press the Accept button, nothing happens.
Okay, so the first thing you have to do in order to successfully answer calls, is to register your line with owner rights.
tobj.RegisterCallNotifications(ln, true, true, TapiConstants.TAPIMEDIATYPE_AUDIO, 2);
After that, you can either iterate through every call using EnumerateCalls(), or you can implement the ITTAPIEventNotification interface to get notified if there is a change in callstate (for example).
One way or the other, at some point, you have found the call you want to answer. Now you need to make sure, that the call is in an alerting state (CS_OFFERING for inbound calls), before you can finally call the answer method.
try
{
ec.Next(1, out ici, ref arg);
if (ici != null && ici.CallState == CALL_STATE.CS_OFFERING)
{
ITBasicCallControl2 bc = (TAPI3Lib.ITBasicCallControl2)ici;
if (bc != null)
{
bc.Answer();
}
}
}
catch (Exception exp)
{
COMException comEx = exp as COMException;
if (comEx != null)
MessageBox.Show(comEx.ErrorCode.ToString());
else
MessageBox.Show(exp.Message);
}
If the call you want to answer is not in the callstate CS_CONNECTED, the method will throw a COMException with an error code of 0x800040010.
For further information on the ITTAPIEventNotification interface, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms732506(v=vs.85).aspx
EDIT:
If you want to detect new incoming calls, I'd recommend to use the TE_CALLNOTIFICATION-Event, because it is triggered only once per new incoming call.
The TE_CALLSTATE-Event will be triggered every time the callstate changes.
Now, i've updated the callnotification class:
public class callnotification : TAPI3Lib.ITTAPIEventNotification
{
public InboundCall OnNewIncomingCall;
public void Event(TAPI_EVENT TapiEvent, object pEvent)
{
switch (TapiEvent)
{
case TAPI_EVENT.TE_CALLNOTIFICATION:
this.OnCallNotification((ITCallNotificationEvent)pEvent);
break;
}
}
private void OnCallNotification(ITCallNotificationEvent callNotification)
{
ITCallInfo ici = callNotification.Call;
if (ici != null && ici.CallState == CALL_STATE.CS_OFFERING)
this.OnNewIncomingCall(ici);
}
}
I've also declared a delegate Method to use if there is a new inbound call:
public delegate void InboundCall(ITCallInfo ici);
So your initialization of the callnotification event could look like this:
callnotification cn = new callnotification();
cn.OnNewIncomingCall += this.OnNewIncomingCall;
And finally, in the OnNewIncomingCallMethod, you can answer the call:
private void OnNewIncomingCall(ITCallInfo ici)
{
ITBasicCallControl bcc = (ITBasicCallControl)ici;
if (bcc != null)
{
string caller = ici.get_CallInfoString(CALLINFO_STRING.CIS_CALLERIDNUMBER);
DialogResult dlg = MessageBox.Show(string.Format("New incoming call from {0}\r\nDo you wish to answer the call now?", caller), "New incoming call", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dlg == System.Windows.Forms.DialogResult.Yes)
bcc.Answer();
}
}
I've tested your code with the additions of mine and it worked fine. Should you be having any exceptions when answering or during the initialization, let me know.
I want to call a method in a specific time of the day without any need to request to any page :
UPDATE: i done it with the following class and without Task Schedule or something else
Something like windows schedule
I did it in a class:
public class Class1 {
private const string DummyCacheItemKey = "GagaGuguGigi";
protected void Application_Start(Object sender, EventArgs e) {
var Result = RegisterCacheEntry();
if (!Result) {
Debug.WriteLine("The DummyCacheItem is Alive!");
}
}
public bool RegisterCacheEntry() {
if (null != HttpContext.Current.Cache[DummyCacheItemKey])
return false;
try {
HttpContext.Current.Cache.Add(DummyCacheItemKey, "Test", null, DateTime.MaxValue, TimeSpan.FromMinutes(1), CacheItemPriority.Normal, new CacheItemRemovedCallback(CacheItemRemovedCallback));
}catch( Exception Ex) {
Debug.WriteLine("Exeption Error: " + Ex);
}
return true;
}
public void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason) {
Debug.WriteLine("Cache item callback: " + DateTime.Now.ToString() + " Removed!");
try {
HitPage();
}catch(Exception Ex) {
Debug.WriteLine("HitPage Was unsuccessful: " + Ex);
}
// Do the service works
DoWork();
//SendMail();
}
private const string DummyPageUrl = "http://localhost:53509/Page.cshtml";
private void HitPage() {
WebClient client = new WebClient();
client.DownloadData(DummyPageUrl);
}
protected void Application_BeginRequest(Object sender, EventArgs e) {
// If the dummy page is hit, then it means we want to add another item
// in cache
if (HttpContext.Current.Request.Url.ToString() == DummyPageUrl) {
// Add the item in cache and when succesful, do the work.
RegisterCacheEntry();
}
}
private void DoWork() {
Debug.WriteLine("Begin DoWork...");
Debug.WriteLine("Running as: " +
WindowsIdentity.GetCurrent().Name);
DoSomeFileWritingStuff();
Debug.WriteLine("End DoWork...");
}
private void DoSomeFileWritingStuff() {
Debug.WriteLine("Writing to file...");
try {
using (StreamWriter writer =
new StreamWriter(#"c:\temp\Cachecallback.txt", true)) {
writer.WriteLine("Cache Callback: {0}", DateTime.Now);
writer.Close();
}
} catch (Exception x) {
Debug.WriteLine("Error: " + x);
}
Debug.WriteLine("File write successful");
}
}
And here is the explanation of why i did this?
Is there a simpler way to do it?
use "Task Scheduler" to run the programme in a specific time.
Find it by typing "task scheduler" in start menu.
It depends what you are actually trying to do here. If you just need to execute some code at given times, use windows scheduler.
If you for some reason need to do this from yur web application you could use http://www.quartz-scheduler.net/ and host it within your web application. Just make sure your application pool is set to always run so the task scheduler stays alive.
24 hours timer is working perfectly
var DailyTime = "16:59:00";
var timeParts = DailyTime.Split(new char[1] { ':' });
var dateNow = DateTime.Now;
var date = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day,
int.Parse(timeParts[0]), int.Parse(timeParts[1]), int.Parse(timeParts[2]));
TimeSpan ts;
if (date > dateNow)
ts = date - dateNow;
else
{
date = date.AddDays(1);
ts = date - dateNow;
}
//waits certan time and run the code
Task.Delay(ts).ContinueWith((x) => OnTimer());
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 using C# .net 4.0 VS 2010.
I got a code in a form that basically adds a Task on form load in order to run a UDP Listener (on infinite loop). Whenever the Listener gets something from UDP socket, i add a line and the message to the multiline-textbox (this.textBox4.Text).
However i get an exception saying "Cross-thread operation not valid: "Contol 'textBox4' accessed from a thread other than the thread it was created on."
I didn't want to end the loop just to pass the value. Is there a way to do this? Here are my codes:
//main form load menu
private void frm_Menu_Load(object sender, EventArgs e)
{
Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
.... // more code here
}
//async function
public Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
{
return Task.Factory.StartNew<int>(() =>
{
if (WhatToDo == "OpenYourEars")
goListening();
... // more codes here
return 1;
});
}
//Listening on UDP socket
public void goListening()
{
bool done = false;
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
string received_data;
byte[] receive_byte_array;
try
{
while (!done)
{
receive_byte_array = listener.Receive(ref groupEP);
received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
// display on TextBox4
this.textBox4.Text = "a\r\nb";
this.textBox4.Text = received_data.ToString().Trim();
}
}
catch (Exception e)
{
//gives "Contol 'textBox4' accessed from a thread other than
//the thread it was created on." when receiving a message.
MessageBox.Show(e.ToString());
}
listener.Close();
}
Version 2 - After answers by #cremor and #George87
private void frm_Menu_Load(object sender, EventArgs e)
{
MyValue = "Menu,7";
Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
.... // more code here
}
private Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
{
return Task.Factory.StartNew<int>(() =>
{
if (WhatToDo == "OpenYourEars")
goListening();
.... // more codes here
return 1;
});
}
//Listening
private void goListening()
{
bool done = false;
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
string received_data;
byte[] receive_byte_array;
try
{
while (!done)
{
receive_byte_array = listener.Receive(ref groupEP);
received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
string aa = received_data.ToString().Trim();
if ( aa != "")
{
SetText("a\r\nb");
SetText(received_data.ToString().Trim());
aa = "";
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
listener.Close();
}
private delegate void SetTextCallback(string text);
private void SetText(string text)
{
try
{
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.BeginInvoke(d, new object[] { text });
}
else
{
SetText(text);
}
this.textBox4.Text = text;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
....
UI controls can only be changed by the thread they were created in. You need to check InvokeRequired (WinForms) or Dispatcher.CheckAccess() (WPF) and then call Invoke/BeginInvoke.
Normally when using c# and use multi threading you should use delegates to make things work and not violate Cross-thread politics.
In other words you are not allowed to use objects defined in one thread from other threads. To make this happen you should use delegates to force the owning thread to perform the task for the calling thread.
Instead of:
// display on TextBox4
this.textBox4.Text = "a\r\nb";
you could use this:
define this methods:
delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
SetText(text);
}
this.textBox1.Text = text;
}
and call them from the tread like this
SetText("a\r\nb");
You can try changing your async function to use the current syncronisation context
return Task.Factory.StartNew<int>(() =>
{
if (WhatToDo == "OpenYourEars")
goListening();
return 1;
},
new CancellationToken(),
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());