C# Event Not Triggered Unless Invoke Is Run on Task - c#

I have a custom event handler that is supposed to post events that happen due to network events such as a device connecting or disconnecting etc. In the code for handling TCPClient Exceptions I run this code
case SocketError.HostUnreachable:
connectionStatus = NetworkingEventType.Error;
networkEvent = new GUINetworkEventArgs(connectionStatus, ipAddress, DateTime.Now, e.SocketErrorCode);
//GlobalVariables.raiseGUINetworkEvent(this, networkEvent);
Task.Run(() => GlobalVariables.raiseGUINetworkEvent(this, networkEvent));
ServerClientMessageCarrier.BroadcastToClients(networkEvent.ServerClientMsg, ServerClientMessageType.GUISettings);
boardStatus = connectionStatusList.Where(status => status.ipAddress.Equals(ipAddress)).FirstOrDefault();
if (boardStatus != null)
{
boardStatus.status = connectionStatus;
}
connectionStatusList.Remove(boardStatus);
Debug.WriteLine("NetworkCore.cs connectToControllerBoard(): Socket Exception for " + ipAddress + ": No code to handle (" + e.SocketErrorCode.ToString() + ")");
Log.Logger.Fatal("NetworkCore.cs Unexpected SocketErrorCode Exception " + ipAddress + "with Error Code:" + e.SocketErrorCode.ToString() + "\n" + e.Message.ToString());
//GlobalVariables.pauseAutoReconnectList.Add(ipAddress);
MessageBox.Show("Fatal Network Error Detected. Host is unreachable. This could be caused by an unplugged network cable. Please reconnect and restart afterwards. Pausing Reconnecting until reset.","Fatal Network Error");
//connectToControllerBoard(ipAddress, port, frequency);
return;
public static void raiseGUINetworkEvent(object sender, GUINetworkEventArgs args)
{
GUINetworkNotification?.Invoke(sender, args);
Debug.WriteLine("GlobalVariables.cs: raiseGUINetworkEvent - " + args.networkStatusEvent.ToString() + " from " + args.ipAddress);
}
What is kind of strange is if I do not put this line inside of a Task. It does not work, none of the other parts of code that subscribe to the event receive any notification.
Task.Run(() => GlobalVariables.raiseGUINetworkEvent(this, networkEvent));
"GlobalVariables" is a static class I use to house all my Event Handlers and such so any part of the program can subscribe and invoke events globally.
I was wondering why this is? My guess is that since this Exception Handling code is done on it's own Task and it has a return line a few lines down its shutting down the Task and the function to invoke the event is cancelled? But as far as I can tell Invoke is not a cancellable async function? It should just run synchronously but apparently this is not the case from my experimentation.
I would appreciate if anyone has some insight on why this works the way it does because it is quite confusing to me. I want to learn why, so I can correct any other part of my code that might be affected by the nature of how this works.

Related

C# winforms freezing: Serialport, Timer, Thread

Edit: Keeping the original question for continuity.
I then edited the question with replacement code for the ReadLine() method by using ReadExisting instead. It works however I still have the same freeze, where the app becomes unresponsive. Debug says it's locking (it takes a while to freeze, sometimes seconds, sometimes minutes) in the while () {} function where I wait for the complete message. More explanations below:
-- obsolete --
What is a good way to handle serialport.readtimeout exception?
try
{
serialPort1.Write(Command_);
if (!IsWriteComm_)
{
Response_ = serialPort1.ReadLine().Replace("\r", "");
}
}
catch (TimeoutException err)
{
DateTime d = DateTime.Now;
rtboxDiag.AppendText("\n" + d.ToString("HH:mm:ss") + ": ");
rtboxDiag.AppendText(err.Message);
if (!serialPort1.IsOpen)
InitConnection();
return Textbox_;
}
this bit of code is exectuted on a timer tick event.
I was having a weird "crash" of the app with an IO exception
"The I/O operation has been aborted because of either a thread exit or an application request."
no matter what I do I am not able to "recover" meaning, I am no longer able to poll data from the serial port.
I added this exception catch and it does log the exception. weirdly enough the test on !serialport.isopen is false (meaning the port is still open).
What might be a hint is: this error does STOP the timer somehow, this is not something I am doing in code. so I am suspecting something related to the timer, rather than the serialport, but I could be wrong.
Closing the port manually, and reconnecting does not fix the problem.
Disconnecting and reconnecting the USB does not fix the problem.
however, closing the app, and relaunching the app does fix the problem (without even disconnecting the MCU or power cycling the MCU/hardware).
-- /obsolete --
edit: the problem is appearing after a few seconds, sometimes minutes of flawless operations. I cannot repeat the issue using a serialport terminal polling the data the same way, at the same frequency. It seems the problem is not coming from the hardware itself.
cheers
Edit: I have yet to test the following modification, not sure if it will fix this problem (I doubt), but at least it's an attempt at not using .readline() which from what I've gathered is not good practice.
anyway here it is:
try
{
serialPort1.Write(Command_);
if (!IsWriteComm_)
{
while (!SerialRxCplt) ;
Response_ = SerialRxResponse.Replace("\r", "").Replace("\n", "");
SerialRxCplt = false;
//Response_ = serialPort1.ReadLine().Replace("\r", "");
}
}
catch (TimeoutException err)
{
DateTime d = DateTime.Now;
rtboxDiag.AppendText("\n" + d.ToString("HH:mm:ss") + ": ");
rtboxDiag.AppendText(err.Message);
if (!serialPort1.IsOpen)
InitConnection();
return Textbox_;
}
and I have the datareceived event enabled:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
var serialPort = (System.IO.Ports.SerialPort)sender;
string dataReceived = serialPort.ReadExisting();
ProcessSerialData(dataReceived);
}
and this is how I am processing the data, and manually "waiting" for the \n character which tells me when the data has been fully received.
private void ProcessSerialData(string data)
{
SerialRxBuffer += data;
if (SerialRxBuffer.Contains("\n"))
{
SerialRxCplt = true;
SerialRxResponse = SerialRxBuffer;
SerialRxBuffer = "";
}
else
{
SerialRxCplt = false;
}
}
any input is welcome.
I have added "stuff" for debugging inside that while loop and it does work fine for a while and then freezes, no error or exception is thrown there. For some reason I have a feeling it's not related to the serial port.
I have even added this:
try
{
serialPort1.Write(Command_);
if (!IsWriteComm_)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
while (!SerialRxCplt || Timer2StopWatchMilli > 5)
{
Timer2StopWatchMilli = stopWatch.Elapsed.TotalMilliseconds;
ExceptionMessage = Timer2StopWatchMilli.ToString();
IsException = true;
}
stopWatch.Stop();
if (!SerialRxCplt)
return Textbox_;
Response_ = SerialRxResponse.Replace("\r", "").Replace("\n", "");
SerialRxCplt = false;
//Response_ = serialPort1.ReadLine().Replace("\r", "");
}
}
the ExceptionMessage and IsException help me have an idea of what's happening in that loop. And in normal operations, it is what you would except, increments in the order of 0.0x milliseconds. Data is being processed correctly. When it freezes, nothing looks abnormal. I initially thought I was somehow getting "stuck" in an infinite loop but that || Timer2StopWatchMilli > 5 should get me out of it, acting as some sort of timeout.
one extra piece of info: when it freezes, the one CPU core is fully loaded. (I have a 6core CPU, and it's 16-17% in the task manager - memory usage is low < 30MB)
Any help is welcome
I fixed it by clearing RX/TX and stream buffers after each successful transaction.
I think data was being sent to the PC faster than it was able to read causing data to eventually accumulating on the Rx Buffer.
private void SerialPortClearBuffers()
{
serialPort1.DiscardOutBuffer();
serialPort1.DiscardInBuffer();
serialPort1.BaseStream.Flush();
}

SQLDependency.Stop() - called but when QN Subscription times out, a message is dropped on the queue

Scenario
Static Service Broker Queue and Service
Use these static queues for SQLDependency subscription
Rough outline of code
Using this blog post as a template the code roughly follows this pattern
SqlDependency.Start(this.dbConnectionString, this.notificationQueueName);
Configure Dependency targeting specific service (see code below)
private async void ConfigureDependencyUsingStoreProcedureAndSpecificQueue()
{
if (null != this.sampleSqlDependency)
{
this.sampleSqlDependency.OnChange -= null;
}
if (null != this.sampleSqlCommand)
{
this.sampleSqlCommand.Dispose();
}
if (null != this.sampleSqlConnection)
{
this.sampleSqlConnection.Dispose();
}
this.sampleSqlDependency = null;
this.sampleSqlCommand = null;
this.sampleSqlConnection = null;
//// Create connection.
this.sampleSqlConnection = new SqlConnection(this.dbConnectionString);
//// Create command.
this.sampleSqlCommand = new SqlCommand { Connection = this.sampleSqlConnection };
this.sampleSqlCommand.CommandType = CommandType.StoredProcedure;
this.sampleSqlCommand.CommandText = this.notificationStoredProcedure;
this.sampleSqlCommand.Notification = null;
//// Create Sql Dependency.
this.sampleSqlDependency = new SqlDependency(this.sampleSqlCommand, "service=" + this.notificationServiceName +"; Local database=" + this.databaseName, this.notificationTimeout);
this.sampleSqlDependency.OnChange += this.SqlDependencyOnChange;
await this.sampleSqlCommand.Connection.OpenAsync();
await this.sampleSqlCommand.ExecuteReaderAsync(CommandBehavior.CloseConnection);
if (null != this.sampleSqlCommand)
{
this.sampleSqlCommand.Dispose();
}
if (null != this.sampleSqlConnection)
{
this.sampleSqlConnection.Dispose();
}
Handle SqlDependencyOnChange event as below. Calling the ConfigureDependency code again
private void SqlDependencyOnChange(object sender, SqlNotificationEventArgs eventArgs)
{
if (eventArgs.Info == SqlNotificationInfo.Invalid)
{
Console.WriteLine("The above notification query is not valid.");
}
else
{
Console.WriteLine("\nNotification Time: {0}", DateTime.Now);
Console.WriteLine("\nNotification Info: " + eventArgs.Info);
Console.WriteLine("Notification source: " + eventArgs.Source);
Console.WriteLine("Notification type: " + eventArgs.Type + "\n");
}
switch (optionSelected)
{
case "1":
this.ConfigureDependencyUsingStoreProcedureAndDefaultQueue();
break;
case "2":
this.ConfigureDependencyUsingStoreProcedureAndSpecificQueue();
break;
case "3":
this.ConfigureDependencyUsingTextQueryAndDefaultQueue();
break;
case "4":
this.ConfigureDependencyUsingTextQueryAndSpecificQueue();
break;
}
}
Upon app shutdown call SqlDependency.Stop(this.dbConnectionString, this.notificationQueueName);. This returns true which according to the documentation means the listener was completely stopped.
Issue Faced
What I then see is that when the subscription reaches it's timeout period, it fires and drops a message onto the dependency queue waiting to be consumed.
If these messages stay in the queue, on next startup the app throws The given key was not present in the dictionary.
Also if I call SQLDependency.Stop() and leave the app running, it still consumes the QN fires for timeouts.
What step am I missing here as I am likely to face issues if messages are getting dropped on the static queue causing the The given key was not present in the dictionary exception.
Thanks
This returns true which according to the documentation means the listener was completely stopped.
This makes no promise with regard to the server state, nor the queue state. These will outlive your volatile application state, and at the next startup, you will find notifications from when your app was offline/shut down. You will have to code accordingly, with this expectation in place (eg. ignore keys that are not in the dictionary).
Note that even if SqlDependency.Stop() would attempt to stop the pending subscribed notifications, it is impossible to guarantee success as there could be notifications in transit (ie. pending delivery via Service Broker, notification may be in sys.transmission_queue). Waiting for all in transit notifications to drain delivery is also not feasible.
Additionally, your application would not handle backup-restore scenarios, as it is now. A restored backup may contain pending notification requests which will be invalidated upon restore and fire the notification message. When the application connects, it will find those unexpected notifications.
And ultimately, the application would not be able to handle its own disconnects/crashes. If you are relying on SqlDependency.Stop() to succeed before you can start successfully next time, you won't be able to start at all if you could not call SqlDependency.Stop() (=> any non-graceful stop).
For all these reasons, you must be able to handle keys not in the dictionary.

blocking listen prevents disconnect

Overview of Problem:
I need to connect to an IRC Server. Once connected, the program will send a message to the channel, and a response will occur over multiple lines back. I need to read these lines and store in a variable for later use. A special character at the end of the message (]) will define the end of the message over multiple lines. Once we have received this character, the IRC session should disconnect and processing should continue.
Situation:
I am using the Smartirc4net library. Calling irc.Disconnect() takes about 40 seconds to disconnect the session. Once we've received the ] character, the session should be disconnected, Listen() should not be blocking, and the rest of the program should continue to run.
Research:
I have found this: smartirc4net listens forever, can't exit thread, and I think it might be the same issue, however, I am unsure of what I need to do to resolve the problem.
Code:
public class IrcCommunicator
{
public IrcClient irc = new IrcClient();
string data;
public string Data { get { return data; } }
// this method we will use to analyse queries (also known as private messages)
public void OnQueryMessage(object sender, IrcEventArgs e)
{
data += e.Data.Message;
if (e.Data.Message.Contains("]"))
{
irc.Disconnect(); //THIS TAKES 40 SECONDS!!!
}
}
public void RunCommand()
{
irc.OnQueryMessage += new IrcEventHandler(OnQueryMessage);
string[] serverlist;
serverlist = new string[] { "127.0.0.1" };
int port = 6667;
string channel = "#test";
try
{
irc.Connect(serverlist, port);
}
catch (ConnectionException e)
{
// something went wrong, the reason will be shown
System.Console.WriteLine("couldn't connect! Reason: " + e.Message);
}
try
{
// here we logon and register our nickname and so on
irc.Login("test", "test");
// join the channel
irc.RfcJoin(channel);
irc.SendMessage(SendType.Message, "test", "!query");
// here we tell the IRC API to go into a receive mode, all events
// will be triggered by _this_ thread (main thread in this case)
// Listen() blocks by default, you can also use ListenOnce() if you
// need that does one IRC operation and then returns, so you need then
// an own loop
irc.Listen();
// when Listen() returns our IRC session is over, to be sure we call
// disconnect manually
irc.Disconnect();
}
catch (Exception e)
{
// this should not happen by just in case we handle it nicely
System.Console.WriteLine("Error occurred! Message: " + e.Message);
System.Console.WriteLine("Exception: " + e.StackTrace);
}
}
}
IrcBot bot = new IrcBot();
bot.RunCommand();
ViewBag.IRC = bot.Data;
As you can see, once this
Thank you for your time to look at this code and read my problem description. If you have any thoughts, or other suggestions, please let me know.
Mike
I was able to successfully disconnect straight away by calling RfcQuit() within OnQueryMessage(), before irc.Disconnect();

Apply Stop on a service that is Stopped or has another state

I am using the following function to stop my windows service (with a timeout):
public int StopService()
{
ServiceController service = new ServiceController(serviceName);
try
{
TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
return 1;
}
catch (Exception ex)
{
logger.Log(LoggingLevel.Error,
this.GetType().Name + ":" + MethodBase.GetCurrentMethod().Name,
string.Format("{0}", ex.Message));
return -1;
}
}
I check the status of the service. And if it is not ServiceControllerStatus.Stopped I call this function.
I have tested and the program works when the status of the server is Running or Stopped. But I would like to know what happens when the status is other states such as StopPending, StartPending, etc. If I tell my service to stop while the state is one of the above, will the Stop command still do its job?
It depends on the service and what it permits at the time of the call to Stop().
At the Win32 level, each service must regularly call SetServiceStatus() to inform the Service Control Manager (SCM) what commands it will accept. Your call to Stop() will only succeed when the service has explicitly said that it allows that operation. In my experience, services in a pending state do not usually allow the normal range of operations so a call to Stop() will likely fail.
Note that it is easy to check what is permitted in C#, as illustrated in this sample code under the Examples heading. You can prefix your call to Stop() with CanStop(), and maybe wait a while if the service isn't quite ready to be stopped yet. Contingencies abound.
You can try like below, below example for StopPending ,
while (service.Status == ServiceControllerStatus.StopPending) //If it is stop pending, wait for it
{
System.Threading.Thread.Sleep(30 * 1000); // thread sleep for 30 seconds
service.Refresh(); // refreshing status
if (service.Status == ServiceControllerStatus.Stopped)
{
Comments = "Service " + serviceName + " stopped successfully. ";
}
}
Let me know whether it works or not. Please also provide if you have(had) any better solution

Need help in "Socket Programming" using Visual C# (Dot Net Framework 4.0)?

Recently, I was given an assignment...
"To develop a Windows Forms application which can be installed on various windows machines at an office or enterprise. There would be a database in just one machine(ALPHA machine).. This database would be used by applications on other Beta machines to access data. The application would itself manage to check if it is an Alpha or a Beta (Has Database file with it?) and hence has to act as a server or a client."
I can do everything except the Network and Inter-Application Communication requirements. So, I started to learn Socket Programming over the Internet and I have gone through this link...
The idea I am working on is...
To have the client send the message to server.
To have the server accept this message and put this message in queue.
Read the message to get Client's IP Address and the its Request for Data.
Apply this request on database and get the result.
Convert the result in string.
Send it to the requesting client.
I can manage to perform steps 3,4 & 5. I am stuck on 1, 2 & 6.
Towards this...
I have created a function for Server as well as for client who return the Sockets when called. I create a separate function as I like my code to be clean, tidy and understandable after years.
Check my code below...
For Server...
private Socket GetServerReady()
{
IPEndPoint RemoteEP = new IPEndPoint(IPAddress.Any, 8000);
Socket newSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newSock.Connect(RemoteEP);
newSock.Listen(10);
return (newSock);
}
You will notice there is no Accept() method anywhere, This is because I wish to call it like below for further use...
Socket CltSock = GetServerReady().Accept();
The Code for Client is...
private Socket GetClientReady()
{
IPEndPoint RemoteEP = new IPEndPoint(IPAddress.Parse(txtBxHost2.Text.Trim()), 8000);
Socket ServerSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ServerSock.Connect(RemoteEP);
return (ServerSock);
}
Finally, The questions are....
"Where is the appropriate place to call the functions I wrote above?"
"Should I call the Server and Client Function in Form_Load() Event?"
"What must be the next step towards my main intention which is point 1,2 & 6 mentioned above?"
I don't expect the full code that I can just copy as it is. Just the correct procedure and a little detail over the concept would do.
I would be using just a single PC for testing purpose. Also, another limitation is, It would all be coded in a single application. I don't want to write two separate applications for client & server.
I hope I made it all clear for you to understand.
Thanks a Lot.
Awaiting the response.
I was struggling to get things done and somehow managed to get the solution.
Below is my solution:
Server Side code:
(I put this code in a function which loops back the execution if any exception is caught)
private void Looper()
{
int i = 0;
int AttemptCount = 1;
while (i == 0)
{
try
{
TcpListener tL = new TcpListener(Network.GetLocalIPAddress(), 56009);
tL.Start(10);
Socket tS = tL.AcceptSocket();
if (tS.Connected)
{
NetworkStream nS = new NetworkStream(tS);
StreamReader Reader = new StreamReader(nS);
Output = Reader.ReadToEnd().Trim();
Reader.Close();
nS.Close();
tS.Close();
tL.Stop();
//If Done, End Execution
i = 1;
}
else
{
MessageBox.Show("The connection to the client is broken or failed..!!\n\nPlease check connection and try again.","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
catch (SystemException ex)
{
//If Not, Loop Execution Again
if (MessageBox.Show("Exception: " + ex.Message + "\n\nAttempt Count: " + AttemptCount + "\n\nDo you want to terminate the transmission?", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes)
{
i = 1;
ResetTimer.Stop();
}
else
{
i = 0;
AttemptCount++;
}
}
}
}
When above function is called, The server waits to accept any incoming socket. If there is any error somewhere due to port re-usage or anything, It loops back itself and resets the server. (So, we don't have to manually call the server function again & again.)
Once the server accepts any incoming socket, the execution ends up successfully. Lot's of time we don't want to keep invoking server even after a successful reception. So, I, instead of calling this function in a button "click_event", I called it in a timer Tick_Event. So, the human need is eliminated at server side.
This leads to a problem. Once the server starts waiting to accept, It is in blocking mode.
It hangs all the processes and controls in same thread. So, I moved the call to above function to BackgroundWorker's "Do_Work" Event.
Check below Code:
private void GetServerReady()
{
if (!bW.IsBusy)
{
bW.RunWorkerAsync();
txtBxHistory.Text += "\r\n" + Output;
}
}
private void bW_DoWork(object sender, DoWorkEventArgs e)
{
Looper();
}
private void ResetTimer_Tick(object sender, EventArgs e)
{
GetServerReady();
}
"bW" is "BackgroundWorker".
"Output" is a variable I defined globally.
The reason we need a variable is that,
BackgroundWorker has its own thread to execute the code placed in its "Do_Work" Event. So, a TextBox from our application's thread can't be used by BackgroundWorker to store the received output. Doing this to a variable and then setting TextBox's Text property to this variable does the trick.
Client Side code:
private void btnSend_Click(object sender, EventArgs e)
{
TcpClient socketForServer;
try
{
socketForServer = new TcpClient(txtBxDestIP.Text.Trim(), 56009);
}
catch
{
MessageBox.Show("Failed to connect to server at " + txtBxDestIP.Text.Trim() + ":999", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
NetworkStream networkStream = socketForServer.GetStream();
StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
try
{
string InputString;
InputString = Network.GetLocalIPAddress() + ": " + txtBxData.Text;
streamWriter.Write(InputString);
streamWriter.Flush();
socketForServer.Close();
txtBxHistory.Text += "\r\nMe: " + txtBxData.Text.Trim();
txtBxData.Clear();
}
catch
{
MessageBox.Show("Exception reading from Server.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
streamWriter.Close();
networkStream.Close();
socketForServer.Close();
}
"txtBxDestIP" is a TextBox having the Destination IP address as Text.
"txtBxData" is a TextBox having the text to be sent.
This code works flawless for me. With above solution I can achieve all my motives from step 1 to 6 (Mentioned in the question above.)
I hope it helps others too. Please suggest if there is a better and efficient way to perform this.
Thanks.
Regards.

Categories