I am writing a program which sends and recieves messages to another application.
Some messages have to be sent at certain intervals. For example, I have four messages which get sent, one should be sent every 8 seconds, two are to be sent every 0.5 seconds and one should be sent every 1 second.
The messages seem to be sending fine, however they are all sending at the same time. I used a timestamp to determine that they all seem to be sent every 11 seconds. Below is the code I have implimented:
Here are where the timers are declared:
System.Timers.Timer statusTimer = new System.Timers.Timer(1000);
System.Timers.Timer heightTimer = new System.Timers.Timer(500);
System.Timers.Timer keepAliveTimer = new System.Timers.Timer(8000);
System.Timers.Timer longHeightTimer = new System.Timers.Timer(500);
And here in the main window they are started (this is a WPF project):
statusTimer.Start();
heightTimer.Start();
keepAliveTimer.Start();
longHeightTimer.Start();
statusTimer.Elapsed += delegate { sendStatusMessage(); };
heightTimer.Elapsed += delegate { sendHeightMessage(); };
keepAliveTimer.Elapsed += delegate { sendKeepAliveMessage(); };
longHeightTimer.Elapsed += delegate { sendLongWarpMessage(); };
Here is an example of a method which is should be fired when the time is elapsed (there is not much point in showing them all as they are all basically the same):
public void sendStatusMessage()
{
sendMessage(status_msg.TranslateToMessage());
Dispatcher.Invoke(DispatcherPriority.Normal, (System.Action)delegate //change the priority so you can use UI
{
statusMsgLbl.Content = "Status Msg Sent: " + DateTime.Now.ToLongTimeString();
});
}
And finally this the method which sends the message, as bytes, to the other application using a network stream:
public void sendMessage(byte[] toSend)
{
try
{
lock (NSLock)
{
for (int i = 0; i < toSend.Length; i++)
{
nwStream.WriteByte(toSend[i]);
nwStream.Flush();
}
}
}
catch { MessageBox.Show("Unable to send message"); }
}
You may try to use a DispatcherTimer with an async Tick event handler:
DispatcherTimer statusTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1.0)
};
...
statusTimer.Tick += async (s, e) =>
{
var message = status_msg.TranslateToMessage());
await nwStream.WriteAsync(message, 0, message.Length);
statusMsgLbl.Content = "Status Msg Sent: " + DateTime.Now.ToLongTimeString();
};
statusTimer.Start();
The date is the same because its the date of code running in Invoke, you have to capture date before invoking:
public void sendStatusMessage()
{
sendMessage(status_msg.TranslateToMessage());
var text = "Status Msg Sent: " + DateTime.Now.ToLongTimeString();
Dispatcher.Invoke(() => statusMsgLbl.Content = text);
Related
I read/write data to serial port and I want to see reading on listbox right away. I created a new thread to send command to serial port. I keep the main thread empty, so, it can update the UI and also serial port event handler wont be interrupted with something else.(I am not sure is it right approach?)
The following code works with while (!dataRecieved) { Thread.Sleep(4000); } but does not works with while (!dataRecieved) { Thread.Sleep(100); }.
The problem is if I use 100ms sleep, serial port event handler fire only once and then program stops!(If I debug with breakpoint 100ms works because I create additional time when stepping into the code.) If I wait 4000ms the program works. Also, I check the time between sending data and receiving data from serial port is 200ms. So, 100ms is reasonable.
Here is the code:
public bool dataRecieved = false;
public Form1()
{
InitializeComponent();
}
public void AppendTextBox(string value)
{
this.Invoke((MethodInvoker)delegate { richTextBox1.Text += value + "\n";});
}
private void button1_Click(object sender, EventArgs e)
{
serialPort1.Open();
Thread testThread = new Thread(() => sendThread());
testThread.Start();
}
public void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
data = serialPort1.ReadLine();
dataRecieved = true;
}
public void sendThread()
{
for(int i = 0; i<10; i++)
{
serialPort1.WriteLine("AT" + i);
// Following line creates odd situation:
// if Thread.Sleep(100), I receive only first data, then program stops(serial port handler doesnt fire!).
// if Thread.Sleep(4000), I receive all data, successfuly works.
// But I do not want to wait 4000ms, because I receive answer from device in 200ms.
while (!dataRecieved) { Thread.Sleep(100); }
AppendTextBox("Received" + "AT" + i);
dataRecieved = false;
}
}
Where I am wrong? Can you please provide a solution?
I even didn't use a new Thead for write and read on SerialPort. You just need use update control in Invoke() is ok. Below is my update on richTextBox. You can change form richTextBox to your listbox.
public void update_RichTextBox(string message)
{
Invoke(new System.Action(() =>
{
txtReceivedData.Text += message;
txtReceivedData.Refresh();
txtReceivedData.SelectionStart = txtReceivedData.Text.Length;
txtReceivedData.ScrollToCaret();
}));
}
and the way to use above void:
if (ComPort.IsOpen)
{
ComPort.Write(_inputdata + "\r");
Form1._Form1.update_RichTextBox(_inputdata + "\r");
string _receviedData = ComPort.ReadExisting();
Form1._Form1.update_RichTextBox(respond);
ComPort.DiscardInBuffer();//delete all data in device's received buffer
ComPort.DiscardOutBuffer();// delete all data in transmit buffer
}
else
{
MessageBox.Show("haven't yet open COM port");
return "FLASE";
}
I use something I call "Cross Thread Linker"
#region Cross Thread Linker
public bool ControlInvokeRequired(Control c, Action a)
{
if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
else return false;
return true;
}
void Update_RichTextBox(RichTextBox rtb, string Text)
{
if (ControlInvokeRequired(rtb, () => Update_RichTextBox(rtb, Text))) return;
rtb.AppendText(Text + Environment.NewLine);
}
#endregion
Then:
Update_RichTextBox(richTextBox1, "Text to append");
Sorry for abstract question, but I'm looking for some samples/advices/articles on type of applications which does some equivalent operations in cycle, and every iteration of cycle should expose its result in certain portion of time (for instance, 10 seconds).
My application does synchronization of data between external WCF service and local database. In every iteration an application retrieves changes of data passing request to WCF service and puts changes to database and vice versa. One of most hard requirement for this application is that iterations should fire every ten seconds.
So here is the issues arises. How can I guarantee that iteration will finish for no more than 10 seconds?
I guess this type of applications called real-time applications (in maner of real-time OS).
DAL components that we use acts randomly on connection timeout behavior. So DB operations may take longer time than 10 seconds.
Here is the estimated code of one iteration:
Stopwatch s1 = new Stopwatch();
s1.Start();
Parallel.ForEach(Global.config.databases, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (l) =>
{
Console.WriteLine("Started for {0}", l.key.name);
DB db = new DB(l.connectionString);
DateTime lastIterationTS = GetPreviousIterationTS(l.id);
ExternalService serv = new ExternalService(l.id);
List<ChangedData> ChangedDataDb = db.GetChangedData(DateTime.Now.AddSeconds((lastIterationTS == DateTime.MinValue) ? -300 : -1 * (DateTime.Now - lastIterationTS).Seconds));
List<Data> ChangedDataService = serv.GetModifiedData();
Action syncDBChanges = new Action(() =>
{
// Изменения в БД
foreach (ChangedData d in ChangedDataDb)
{
try
{
// ...
// analyzing & syncing
}
catch (Exception e)
{
logger.InfoEx("Exception_SyncDatabase", e.ToString());
}
}
}
);
Action syncService = new Action(() =>
{
foreach (Data d in ChangedDataService)
{
try
{
// ...
// analyzing & syncing
}
catch (Exception e)
{
logger.InfoEx("Exception_SyncService", e.ToString());
}
}
});
List<WaitHandle> handles = new List<WaitHandle>();
IAsyncResult ar1 = syncDBChanges.BeginInvoke(syncDBChanges.EndInvoke, null);
IAsyncResult ar2 = syncService.BeginInvoke(syncService.EndInvoke, null);
handles.Add(ar1.AsyncWaitHandle);
handles.Add(ar2.AsyncWaitHandle);
WaitHandle.WaitAll(handles.ToArray(), (int)((Global.config.syncModifiedInterval - 1) * 1000));
SetCurrentIterationTS(l.id);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
logger.InfoEx("Exception_Iteration", e.ToString());
continue;
}
}
logger.InfoEx("end_Iteration", IterationContextParams);
}
);
s1.Stop();
Console.WriteLine("Main iteration done for {0}...", s1.Elapsed);
You can consider a couple of options...
Kill the iteration if it exceeds more than 10 seconds and hope that the next iteration can complete process. The issue with this approach is that there is a good possibility that the none of the iterations will complete and therefore the synchronization process will never occur. I would recommend the following option...
If the iteration takes more than 10 seconds, wait for it to complete and skip the next iteration(s). This way you ensure the process completes atleast once. The following is a simplified code sample for reference...
class Updater
{
Timer timer = new Timer();
public object StateLock = new object();
public string State;
public Updater()
{
timer.Elapsed += timer_Elapsed;
timer.Interval = 10000;
timer.AutoReset = true;
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (State != "Running")
{
Process();
}
}
private void Process()
{
try
{
lock (StateLock)
{
State = "Running";
}
// Process
lock (StateLock)
{
State = "";
}
}
catch
{
throw;
}
}
}
...
class Program
{
static void Main(string[] args)
{
Updater updater = new Updater();
Console.ReadLine();
}
}
Quartz.net is an excellent scheduler for the .NET platform, which I think could suit your needs.
If you want to kill a job, you can implement IInterruptableJob. You should be able to add some cleanup code in the Interupt method to dispose of any db connections.
If you want finish a job, but only start another job if the last one is completed (which I think is the better option), you can implement IStatefulJob interface
I usually separate the update cycle from the actual timer
The timer does two things:
1) if the update is not running starts it.
2) if the service is already running set a flag for it to continue running.
The update cycle:
1)set running flag
2) do the update
3) set running flag to false
4) if continue running is set go to 1).
You might want to read up on the variety of Timer objects available in .Net: http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
I personally like System.Threading.Timer because you can easily use lambdas, and it allows a state object to be passed if you create a separate callback.
I would also recommend using the System.Threading.Tasks library, because it allows you to gracefully handle cancellations in the case that the timer elapses before your work is completed. Msdn example: http://msdn.microsoft.com/en-us/library/dd537607.aspx
Here's an example of using these components together in a 10 minute timer:
Note: to do this with your sql database you'll need to set Asynchronous Processing=true; and MultipleActiveResultSets=True;
CancellationTokenSource cancelSource = new CancellationTokenSource();
System.Threading.Timer timer = new System.Threading.Timer(callback =>
{
//start sync
Task syncTask = Task.Factory.StartNew(syncAction =>
{
using (SqlConnection conn =
new SqlConnection(
ConfigurationManager.ConnectionStrings["db"].ConnectionString))
{
conn.Open();
using (SqlCommand syncCommand = new SqlCommand
{
CommandText = "SELECT getdate() \n WAITFOR DELAY '00:11'; ",
CommandTimeout = 600,
Transaction = conn.BeginTransaction(),
Connection = conn
})
{
try
{
IAsyncResult t = syncCommand.BeginExecuteNonQuery();
SpinWait.SpinUntil(() =>
(t.IsCompleted || cancelSource.Token.IsCancellationRequested));
if (cancelSource.Token.IsCancellationRequested && !t.IsCompleted)
syncCommand.Transaction.Rollback();
}
catch (TimeoutException timeoutException)
{
syncCommand.Transaction.Rollback();
//log a failed sync attepmt here
Console.WriteLine(timeoutException.ToString());
}
finally
{
syncCommand.Connection.Close();
}
}
}
}, null, cancelSource.Token);
//set up a timer for processing in the interim, save some time for rollback
System.Threading.Timer spinTimer = new System.Threading.Timer(c => {
cancelSource.Cancel();
}, null, TimeSpan.FromMinutes(9), TimeSpan.FromSeconds(5));
//spin here until the spintimer elapses;
//this is optional, but would be useful for debugging.
SpinWait.SpinUntil(()=>(syncTask.IsCompleted || cancelSource.Token.IsCancellationRequested));
if (syncTask.IsCompleted || cancelSource.Token.IsCancellationRequested)
spinTimer.Dispose();
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(10));
Perhaps try this. Please make sure you do not create and use any new threads in DoWork() method.
class DatabaseUpdater
{
private readonly Timer _timer;
private List<Thread> _threads;
private readonly List<DatabaseConfig> _dbConfigs;
public DatabaseUpdater(int seconds, List<DatabaseConfig> dbConfigs)
{
_timer = new Timer(seconds * 1000);
_timer.Elapsed += TimerElapsed;
_dbConfigs = dbConfigs;
}
public void Start()
{
StartThreads();
_timer.Start();
}
public void Stop()
{
_timer.Stop();
StopThreads();
}
void TimerElapsed(object sender, ElapsedEventArgs e)
{
StopThreads();
StartThreads();
}
private void StartThreads()
{
var newThreads = new List<Thread>();
foreach (var config in _dbConfigs)
{
var thread = new Thread(DoWork);
thread.Start(config);
newThreads.Add(thread);
}
_threads = newThreads;
}
private void StopThreads()
{
if (_threads == null) return;
var oldThreads = _threads;
foreach (var thread in oldThreads)
{
thread.Abort();
}
}
static void DoWork(object objConfig)
{
var dbConfig = objConfig as DatabaseConfig;
if (null == dbConfig) return;
var n = GetRandomNumber();
try
{
ConsoleWriteLine("Sync started for : {0} - {1} sec work.", dbConfig.Id, n);
// update/sync db
Thread.Sleep(1000 * n);
ConsoleWriteLine("Sync finished for : {0} - {1} sec work.", dbConfig.Id, n);
}
catch (Exception ex)
{
// cancel/rollback db transaction
ConsoleWriteLine("Sync cancelled for : {0} - {1} sec work.",
dbConfig.Id, n);
}
}
static readonly Random Random = new Random();
[MethodImpl(MethodImplOptions.Synchronized)]
static int GetRandomNumber()
{
return Random.Next(5, 20);
}
[MethodImpl(MethodImplOptions.Synchronized)]
static void ConsoleWriteLine(string format, params object[] arg)
{
Console.WriteLine(format, arg);
}
}
static void Main(string[] args)
{
var configs = new List<DatabaseConfig>();
for (var i = 1; i <= 3; i++)
{
configs.Add(new DatabaseConfig() { Id = i });
}
var databaseUpdater = new DatabaseUpdater(10, configs);
databaseUpdater.Start();
Console.ReadKey();
databaseUpdater.Stop();
}
i'm working with ping Librarry in net 3.5 to check the presence of IP.
take a look at the code below:
public void PingIP(string IP)
{
var ping = new Ping();
ping.PingCompleted += new PingCompletedEventHandler(ping_PingCompleted); //here the event handler of ping
ping.SendAsync(IP,"a");
}
void ping_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Reply.Status == IPStatus.Success)
{
//On Ping Success
}
}
Then i execute the code through Thread or backgroundworker.
private void CheckSomeIP()
{
for (int a = 1; a <= 255; a++)
{
PingIP("192.168.1." + a);
}
}
System.Threading.Thread checkip = new System.Threading.Thread(CheckSomeIP);
checkip.Start();
Well, here is the problem:
If i start the thread then i would to close the application (Close with Controlbox at corner), i would get "App Crash"
although i have closed/abort the thread.
I think the problem is event handler? as they still working when i'm closing the Application so that i will get "App Crash"
What would be the best way to solve this case?
I think, on a successfull Ping, you are trying to update the interface from within the Thread, which will cause an CrossThreadingOperation exception.
Search the Web for ThreadSave / delegates:
public void PingIP(string IP)
{
var ping = new Ping();
ping.PingCompleted += new PingCompletedEventHandler(ping_PingCompleted); //here the event handler of ping
ping.SendAsync(IP,"a");
}
delegate void updateTextBoxFromThread(String Text);
void updateTextBox(String Text){
if (this.textbox1.InvokeRequired){
//textbox created by other thread.
updateTextBoxFromThread d = new updateTextBoxFromThread(updateTextBox);
this.invoke(d, new object[] {Text});
}else{
//running on same thread. - invoking the delegate will lead to this part.
this.textbox1.text = Text;
}
}
void ping_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Reply.Status == IPStatus.Success)
{
updateTextBox(Text);
}
}
Also on "quitting" the application, you may want to cancel al running threads. Therefore you need to keep the reference on every thread you start somewhere in your application. in the formClosing-Event of your Main-Form, you can force all (running) threads to stop.
I am calling my background worker by the following code:
private void UpdateDataTimer_Tick(object sender, EventArgs e)
{
if (!serialPortWorker.IsBusy)
{
serialPortWorker.RunWorkerAsync();
}
}
My DoWork event is as follows:
private void serialPortWorker_DoWork(object sender, DoWorkEventArgs e)
{
//Configures serial port
connection.BaudRate = 19200;
connection.DataReceived += new SerialDataReceivedEventHandler(DataReceivedEvent);
//Sends the commands for opening diagnostics
string[] init_commands = { "STRING", "STRING", "STRING", "STRING", "STRING" };
foreach (string command in init_commands)
{
connection.WriteLine(command + connection.NewLine);
Thread.Sleep(1000);
}
const string constant_message_section = "G03";
string[] command_list = { "62", "64", "5C" };
//Writes all commands to all radio addresses
foreach (int address in radioAddresses)
{
foreach (string command in command_list)
{
for (int i = 0; i < MAX_ATTEMPTS; i++)
{
connection.WriteLine(constant_message_section + address.ToString("X4") + command);
Thread.Sleep(500);
}
}
}
Thread.Sleep(1000); //Give a little time for all responses to come in
}
For some reason, after a few hundred calls of the UpdateDataTimer_Tick event, it will not run the serialPortWorker any more. I put a debugger at if (!serialPortWorker.IsBusy), and it indicated that the serialPortWorker is still busy. It must be hanging up somewhere in the DoWork event, right? Any ideas why?
For those that are interested, the data received event is as follows:
public void DataReceivedEvent(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string receive = sp.ReadLine();
try
{
Debug.Logger.WriteToDebug("Data Received Serial Port: " + receive);
}
catch { }
try
{
int unit_address = Int32.Parse(receive.Substring(1, 4), System.Globalization.NumberStyles.HexNumber);
if (radioAddresses.Contains(unit_address))
{
int radio_index = radioAddresses.IndexOf(unit_address) + 1;
int max_index = radio_index * 3;
integrityMonitor[radio_index] = DateTime.Now; //Last updated time
int message_data = 0;
if (receive.Contains("66"))
{
//Stuff
}
else if (receive.Contains("61"))
{
//Stuff
}
else if (receive.Contains("55"))
{
//Stuff
}
}
}
catch { }
}
Ok, since nobody has left an answer, I will. The issue was the line
connection.DataReceived += new SerialDataReceivedEventHandler(DataReceivedEvent);
being called during every tick of the timer in background worker. This would cause numerous instances of the event handler, which would eventually cause the background worker to lock up and report busy all the time. To correct this problem, I needed to put in
connection.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedEvent);
in order to avoid having numerous event handlers processing the data received event. This has resolved my problem.
So I am trying to create a basic WinForm Application that uses SNMP, from snmpsharpnet.
I have two buttons 'Eye' and 'Jitter' that when one is pressed a timer starts which executes SNMP code inside the timer handler every minute.
I am trying to write the SNMP output to a textbox from the timer handler but everything I try either leads to thread exceptions or a continuous running process when I exit the program.
I have tried so many different things to fix those two errors that I may be screwing everything up but here is the code I have:
using System;
using System.Net;
using SnmpSharpNet;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public static bool stop = false;
static bool min = false, sec = false, eye = false, jitter = false;
static string ipAdd = "";
static System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
static int alarmCounter = 1;
static bool exitFlag = false;
static TextBox textbox;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textbox = outputBox;
}
private void IPtext_TextChanged(object sender, EventArgs e)
{
ipAdd = IPtext.Text;
}
private void stopButton_Click(object sender, EventArgs e)
{
stop = true;
timer.Stop();
}
// This is the method to run when the timer is raised.
private static void TimerEventProcessor(Object myObject,
EventArgs myEventArgs)
{
timer.Stop();
// If stop button has not been pressed then continue timer.
if (stop == false)
{
textbox.Clear();
// Restarts the timer and increments the counter.
alarmCounter += 1;
timer.Enabled = true;
/*
textbox.Invoke(
new MethodInvoker(
delegate { textbox.AppendText("fsjdaò"); }));
*/
System.IO.StreamWriter file;
if (eye == true)
{
file = new System.IO.StreamWriter("c:/Users/bshellnut/Desktop/Eye.txt", true);
}
else
{
file = new System.IO.StreamWriter("c:/Users/bshellnut/Desktop/Jitter.txt", true);
}
// SNMP community name
OctetString community = new OctetString("public");
// Define agent parameters class
AgentParameters param = new AgentParameters(community);
// Set SNMP version to 2 (GET-BULK only works with SNMP ver 2 and 3)
param.Version = SnmpVersion.Ver2;
// Construct the agent address object
// IpAddress class is easy to use here because
// it will try to resolve constructor parameter if it doesn't
// parse to an IP address
IpAddress agent = new IpAddress(ipAdd);
// Construct target
UdpTarget target = new UdpTarget((IPAddress)agent, 161, 2000, 1);
// Define Oid that is the root of the MIB
// tree you wish to retrieve
Oid rootOid;
if (eye == true)
{
rootOid = new Oid("1.3.6.1.4.1.128.5.2.10.14"); // ifDescr
}
else
{
rootOid = new Oid("1.3.6.1.4.1.128.5.2.10.15");
}
// This Oid represents last Oid returned by
// the SNMP agent
Oid lastOid = (Oid)rootOid.Clone();
// Pdu class used for all requests
Pdu pdu = new Pdu(PduType.GetBulk);
// In this example, set NonRepeaters value to 0
pdu.NonRepeaters = 0;
// MaxRepetitions tells the agent how many Oid/Value pairs to return
// in the response.
pdu.MaxRepetitions = 5;
// Loop through results
while (lastOid != null)
{
// When Pdu class is first constructed, RequestId is set to 0
// and during encoding id will be set to the random value
// for subsequent requests, id will be set to a value that
// needs to be incremented to have unique request ids for each
// packet
if (pdu.RequestId != 0)
{
pdu.RequestId += 1;
}
// Clear Oids from the Pdu class.
pdu.VbList.Clear();
// Initialize request PDU with the last retrieved Oid
pdu.VbList.Add(lastOid);
// Make SNMP request
SnmpV2Packet result;
try
{
result = (SnmpV2Packet)target.Request(pdu, param);
//textbox.Text = "";
}
catch (SnmpSharpNet.SnmpException)
{
textbox.Invoke(
new MethodInvoker(
delegate { textbox.AppendText("Could not connect to the IP Provided."); }));
timer.Stop();
//outputBox.Text += "Could not connect to the IP Provided.";
break;
}
// You should catch exceptions in the Request if using in real application.
// If result is null then agent didn't reply or we couldn't parse the reply.
if (result != null)
{
// ErrorStatus other then 0 is an error returned by
// the Agent - see SnmpConstants for error definitions
if (result.Pdu.ErrorStatus != 0)
{
// agent reported an error with the request
/*Console.WriteLine("Error in SNMP reply. Error {0} index {1}",
result.Pdu.ErrorStatus,
result.Pdu.ErrorIndex);*/
textbox.Invoke(
new MethodInvoker(
delegate { textbox.AppendText("Error in SNMP reply. " + "Error " + result.Pdu.ErrorStatus + " index " + result.Pdu.ErrorIndex); }));
//outputBox.Text = "Error in SNMP reply. " + "Error " + result.Pdu.ErrorStatus + " index " + result.Pdu.ErrorIndex;
lastOid = null;
break;
}
else
{
// Walk through returned variable bindings
foreach (Vb v in result.Pdu.VbList)
{
// Check that retrieved Oid is "child" of the root OID
if (rootOid.IsRootOf(v.Oid))
{
/*Console.WriteLine("{0} ({1}): {2}",
v.Oid.ToString(),
SnmpConstants.GetTypeName(v.Value.Type),
v.Value.ToString());*/
textbox.Invoke(
new MethodInvoker(
delegate { textbox.AppendText(v.Oid.ToString() + " " + SnmpConstants.GetTypeName(v.Value.Type) +
" " + v.Value.ToString() + Environment.NewLine); }));
//outputBox.Text += v.Oid.ToString() + " " + SnmpConstants.GetTypeName(v.Value.Type) +
//" " + v.Value.ToString() + Environment.NewLine;
file.WriteLine(v.Oid.ToString() + " " + SnmpConstants.GetTypeName(v.Value.Type) +
" " + v.Value.ToString(), true);
if (v.Value.Type == SnmpConstants.SMI_ENDOFMIBVIEW)
lastOid = null;
else
lastOid = v.Oid;
}
else
{
// we have reached the end of the requested
// MIB tree. Set lastOid to null and exit loop
lastOid = null;
}
}
}
}
else
{
//Console.WriteLine("No response received from SNMP agent.");
textbox.Invoke(
new MethodInvoker(
delegate { textbox.AppendText("No response received from SNMP agent."); }));
//outputBox.Text = "No response received from SNMP agent.";
}
}
target.Close();
file.Close();
}
else
{
// Stops the timer.
exitFlag = true;
}
}
private void eyeButton_Click(object sender, EventArgs e)
{
outputBox.Text = "Connecting...";
eye = true;
jitter = false;
stop = false;
timer.Tick += new EventHandler(TimerEventProcessor);
// Sets the timer interval to 5 seconds.
timer.Interval = 5000;
timer.Start();
// Runs the timer, and raises the event.
while (exitFlag == false)
{
// Processes all the events in the queue.
Application.DoEvents();
}
}
private void jitterButton_Click(object sender, EventArgs e)
{
outputBox.Text = "Connecting...";
eye = false;
jitter = true;
stop = false;
timer.Tick += new EventHandler(TimerEventProcessor);
// Sets the timer interval to 5 seconds.
timer.Interval = 5000;
timer.Start();
// Runs the timer, and raises the event.
while (exitFlag == false)
{
// Processes all the events in the queue.
Application.DoEvents();
}
}
private void Seconds_CheckedChanged(object sender, EventArgs e)
{
min = false;
sec = true;
}
private void Minutes_CheckedChanged(object sender, EventArgs e)
{
min = true;
sec = false;
}
}
}
I believe you are running into a deadlock on the UI thread.
TimerEventProcessor() is called by your instance of System.Windows.Forms.Timer, which runs on the UI thread. When the timer goes off, I believe it puts a message into the UI thread's message queue to call your TimerEventProcessor() method. That method in turn calls textbox.Invoke(), which puts another message into the queue and then waits for it to be processed.
Your UI thread is now stuck, as it is in the middle of processing a message, but must wait until another message is processed before it can continue. The calls to Application.DoEvents() do you no good, as they are not being called once your program enters TimerEventProcessor(). (They're also unnecessary, since your button click handlers wouldn't be blocking the UI thread anyway.)
Since the timer runs on the UI thread, you can get rid of the textbox.Invoke() calls and just access textbox directly.
Summary:
Replace your calls to textbox.Invoke() with direct access to textbox
Remove your calls to Application.DoEvents()
Note: if you got the Application.DoEvents() logic from the MSDN example for using a timer, they put it there so that the application doesn't quit once the Main function completes.
Update: You can see if this is truly the issue by replacing your calls to textbox.Invoke with the following code. If this code works, then you definitely have a UI message queue deadlocking issue. Also, if this does resolve the issue, I would not recommend keeping this as the solution, but rather, addressing the deadlocking as suggested above.
// Make the request asynchronously
System.IAsyncResult asyncResult = textbox.BeginInvoke(
new MethodInvoker(
delegate { /* insert delegate code here */ }));
// Process the message queue until this request has been completed
while(!asyncResult.IsCompleted) Application.DoEvents();
// Clean up our async request
textbox.EndInvoke(asyncResult);
Since you seem to have your code working, you can ignore the above test code.
Your problem is not related to the timer and all the Invoke statements you use are not necessary. The System.Windows.Forms.Timer class operates in the UI thread. Look here: http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx