RasConnectionNotification after computer resumes from sleep - c#

I've got a project called DotRas on CodePlex that exposes a component called RasConnectionWatcher which uses the RasConnectionNotification Win32 API to receive notifications when connections on a machine change. One of my users recently brought to my attention that if the machine comes out of sleep mode, and attempts to redial the connection, the connection goes into a loop indicating the connection is already being dialed even though it isn't. This loop will not end until the application is restarted, even if done through a synchronous call which all values on the structs are unique for that specific call, and none of it is retained once the call completes.
I've done as much as I can to fix the problem, but I fear the problem is something I've done with the RasConnectionNotification API and using ThreadPool.RegisterWaitForSingleObject which might be blocking something else in Windows.
The below method is used to register 1 of the 4 change types the API supports, and the handle to associate with it to monitor. During runtime, the below method would be called 4 times during initialization to register all 4 change types.
private void Register(NativeMethods.RASCN changeType, RasHandle handle)
{
AutoResetEvent waitObject = new AutoResetEvent(false);
int ret = SafeNativeMethods.Instance.RegisterConnectionNotification(handle, waitObject.SafeWaitHandle, changeType);
if (ret == NativeMethods.SUCCESS)
{
RasConnectionWatcherStateObject stateObject = new RasConnectionWatcherStateObject(changeType);
stateObject.WaitObject = waitObject;
stateObject.WaitHandle = ThreadPool.RegisterWaitForSingleObject(waitObject, new WaitOrTimerCallback(this.ConnectionStateChanged), stateObject, Timeout.Infinite, false);
this._stateObjects.Add(stateObject);
}
}
The event passed into the API gets signaled when Windows detects a change in the connections on the machine. The callback used just takes the change type registered from the state object and then processes it to determine exactly what changed.
private void ConnectionStateChanged(object obj, bool timedOut)
{
lock (this.lockObject)
{
if (this.EnableRaisingEvents)
{
try
{
// Retrieve the active connections to compare against the last state that was checked.
ReadOnlyCollection<RasConnection> connections = RasConnection.GetActiveConnections();
RasConnection connection = null;
switch (((RasConnectionWatcherStateObject)obj).ChangeType)
{
case NativeMethods.RASCN.Disconnection:
connection = FindEntry(this._lastState, connections);
if (connection != null)
{
this.OnDisconnected(new RasConnectionEventArgs(connection));
}
if (this.Handle != null)
{
// The handle that was being monitored has been disconnected.
this.Handle = null;
}
this._lastState = connections;
break;
}
}
catch (Exception ex)
{
this.OnError(new System.IO.ErrorEventArgs(ex));
}
}
}
}
}
Everything works perfectly, other than when the machine comes out of sleep. Now the strange thing is when this happens, if a MessageBox is displayed (even for 1 ms and closed by using SendMessage) it will work. I can only imagine something I've done is blocking something else in Windows so that it can't continue processing while the event is being processed by the component.
I've stripped down a lot of the code here, the full source can be found at:
http://dotras.codeplex.com/SourceControl/changeset/view/68525#1344960
I've come for help from people much smarter than myself, I'm outside of my comfort zone trying to fix this problem, any assistance would be greatly appreciated!
Thanks! - Jeff

After a lot of effort, I tracked down the problem. Thankfully it wasn't a blocking issue in Windows.
For those curious, basically once the machine came out of sleep the developer was attempting to immediately dial a connection (via the Disconnected event). Since the network interfaces hadn't finished initializing, an error was returned and the connection handle was not being closed. Any attempts to close the connection would throw an error indicating the connection was already closed, even though it wasn't. Since the handle was left open, any subsequent attempts to dial the connection would cause an actual error.
I just had to make an adjustment in the HangUp code to hide the error thrown when a connection is closed that has already been closed.

Related

ObjectDisposedException: Safe handle has been closed

So this is a rather small question with a big explanation. As is noted by the title I am getting an unhandled exception telling me my Safe handle has been closed. What I'll probably have to do is edit this post a few times with more and more code to help me diagnose what the problem is.
I'm using POS for .NET to make a Service Object for my RFID and MSR device. Although my devices are the same, I have 2 different Virtual COM Port chips that communicate to those devices. One by Silicon labs, the other by FTDI. I wanted to use the plug and play features with POS for .NET so I gave it both my Hardware ID's. Because it is plug and play I have the full hardware path available to me which I can then create a SafeFileHandle using a call to PInvoke and using that SafeFileHandle I create a FileStream. The FTDI chip doesn't let me talk to the devices directly like that so I have to get the friendly name of the device then use mutex to pull out the COM port then create a SerialPort instance. That step works fine and great. As a FYI I have tried to use the Friendly name of both chips to get the COM port and the Silicon Labs one (for some strange reason) doesn't get listed using SetupAPI.GetDeviceDetails using the Ports GUID. I'm not sure on that one since in Device Manager the Silicon labs Device Class Guid is the Ports GUID.
Well since both the SerialPort and the FileStream have a Stream object I decided to use that to read and write to that port. The problem with that is if I send a RFID command to the MSR device the MSR device doesn't respond back with anything. So if I use this code int fromReader = ReaderStream.ReadByte(); my thread is blocked. It's a blocking call and requires a minimum of 1 byte to proceed. So I looked around and it appears the only solution is to use a separate thread and set a timeout. If the timeout happens then abort the thread.
Thread t = new Thread(new ThreadStart(ReadFromStream));
t.Start();
if (!t.Join(timeout))
{
t.Abort();
}
(t.Abort has been surrounded with a try/catch to no avail, since it didn't fix the problem I removed it)
ReadFromStream is Abstract method in RFID Device. Here is one of the implementations
protected override void ReadFromStream()
{
var commandLength = USN3170Constants.MIN_RESPONSE_LENGTH;
var response = new System.Collections.Generic.List<byte>(USN3170Constants.MIN_RESPONSE_LENGTH);
for (int i = 0; i <= commandLength; i++)
{
int fromReader = ReaderStream.ReadByte();
if (fromReader == -1) break; //at end of stream
response.Add((byte)fromReader);
if (response.Count > USN3170Constants.DATA_LENGTH_INDEX && response[USN3170Constants.DATA_LENGTH_INDEX] > 0)
{
commandLength = response[USN3170Constants.DATA_LENGTH_INDEX] + 3;
}
}
streamBuffer = response.ToArray();
}
(int fromReader = ReaderStream.ReadByte(); was surrounded with a try/catch. Only thing it caught was the aborted thread exception, so I took it out)
The above code is where I suspect the problem lies. The strange thing is, though, is that I have a unit test which I feel mimics rather well the Microsoft Test App.
(FYI QUADPORT is the FTDI chipset)
PosExplorer posExplorer;
DeviceCollection smartCardRWs;
[Test]
public void TestQuadPortOpen()
{
posExplorer = new PosExplorer();
smartCardRWs = posExplorer.GetDevices(DeviceType.SmartCardRW, DeviceCompatibilities.CompatibilityLevel1);
//if using quadport one item is the MSR and the other is the RFID
//because of that one of them will fail. Currently the first Device in the collection is the the RFID, and the second is MSR
Assert.GreaterOrEqual(smartCardRWs.Count, 2);
//Hardware Id: QUADPORT\QUAD_SERIAL_INTERFACE
foreach(DeviceInfo item in smartCardRWs)
{
Assert.AreEqual("QUADPORT\\QUAD_SERIAL_INTERFACE", item.HardwareId);
}
SmartCardRW rfidDevice = (SmartCardRW)posExplorer.CreateInstance(smartCardRWs[0]);
SmartCardRW msrDevice = (SmartCardRW)posExplorer.CreateInstance(smartCardRWs[1]);
rfidDevice.Open();
Assert.AreNotEqual(ControlState.Closed, rfidDevice.State);
rfidDevice.Close();
try
{
msrDevice.Open();
Assert.Fail("MSR Device is not a RFID Device");
}
catch
{
Assert.AreEqual(ControlState.Closed, msrDevice.State);
}
rfidDevice = null;
msrDevice = null;
}
When I run that test I do not get the SafeFileHandle exception. In fact the test passes.
So I am at a loss as to how to track down this bug. Since I'll be using this Service Object in a different program that I am also creating I'll probably end up using this code from this test in that program. However I feel that the Microsoft Test App is more or less the "Golden Standard". Is it really... probably not. But it does work good for my purposes, SO I feel it is a problem with my code and not theirs.
Any tricks on how I can narrow this down? FYI I've tried using the debugger but walking the Open Code the error does not occur. I also walked the Update Status Timer and it also does not throw the error. Once I hit continue then I'll get the exception. I turned of Just My Code and Loaded Symbols and it tells me "Source Information is missing from teh debug information for this module"
This problem (and in particular the reference to a SerialPort instance) sounds suspiciously like the problem documented at http://connect.microsoft.com/VisualStudio/feedback/details/140018/serialport-crashes-after-disconnect-of-usb-com-port.
As I understand it, in the case of a non-permanent SerialPort (like one associated with a USB device, for example) when the port "goes away" unexpectedly the underlying Stream associated with it gets disposed. If there is an active read or write operation on the port at the time a subsequent call to SerialPort.Close can lead to the exception you mention, however the exception is occurring in Microsoft's code running on a different thread and cannot be caught from within your code. (It will still be seen by any "last chance" exception handler you have bound to the UnhandledException event on the AppDomain.)
There seem to be two basic workaround styles in the linked document. In both instances, after opening the port you store a reference to the BaseStream instance for the open port. One workaround then suppresses garbage collection on that base stream. The other explicitly calls Close on the base stream, capturing any exceptions thrown during that operation, before calling Close on the SerialPort.
EDIT: For what it's worth, under the .NET framework V4.5, it appears that none of the documented workarounds on the Microsoft Connect site fully resolve the problem although they may be reducing the frequency with which it occurs. :-(
I had the same error when I used a thread to read from a SerialPort. Calling Interrupt on the thread occasionally caused the uncatchable ObjectDisposedException. After hours of debugging and carefully reading this:
https://blogs.msdn.microsoft.com/bclteam/2006/10/10/top-5-serialport-tips-kim-hamilton/
I realized that the problem is just this:
NET 2.0 (and above) isn’t letting you get away with some things, such as attempting to cancel a SerialPort read by interrupting the thread accessing the SerialPort.
So before you call Thread.Interrupt() you have to close the COM... This will cause a catchable exception on the ReadByte operation.
Or you may use the ReadTimeout property on the SerialPort to avoid using a thread just to have a timeout.
I would like to post my case in which I had a similar issue trying to read from a serial port (virtual com driven by a Moxa RS232 to ethernet).
Since I did have no chance to catch the ObjectDisposedException, the only solution was to increase the ReadTimeout property which was originally set to -1 (continuous reading).
Setting the ReadTimeout to 100 millis solved this issue in my case.
EDIT
It is not the definitive solution: it can happen that if you close the application during a read attempt you can get the same uncatchable exception.
My final solution is to kill the process of the application directly in the FormClosing event :
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
Process p = Process.GetCurrentProcess();
p.Kill();
}
Please take a look at this:
https://github.com/jcurl/SerialPortStream
I replaced System.IO.Ports with RJPC.IO.Ports, fixed up a couple parameter differences in the initialization, and all the problems went away with this issue.

HttpNotificationChannel Open() throwing InvalidOperationException ("Failed to open channel")

I'm writing a Windows Phone 7 application which utilises Push Notifications and have a class which is responsible for managing interactions between the MS Notification Servers and my service in the cloud. However when I'm attempting to open the channel on my device HttpNotificationChannel is throwing an InvalidOperationException with the message "Failed to open channel". According to MSDN I should try opening the channel again.
My snippet of code to open the push notification is following the standard pattern of;
public class HttpNotification {
private const string kChannelName = "MyApp.PushNotification";
private HttpNotificationChannel _Channel;
public void Register() {
try {
_Channel = HttpNotificationChannel.Find(kChannelName);
if (_Channel == null) {
_Channel = new HttpNotificationChannel(kChannelName);
InstallEventHandlers();
// This line throws
_Channel.Open();
} else {
InstallEventHandlers();
};
} catch (InvalidOperationException ex) {
MessageBox.Show(string.Format("Failed to initialise Push Notifications - {0}", ex.Message));
};
}
}
I'm not sure exactly what MSDN means by "try opening the channel again". I've wrapped the call to Open() in a try/catch and snoozing 5 seconds between attempts but it doesn't succeed. I've also tried the same approach around the entire method (ie. Do the call to HttpNotificationChannel.Find() each time it throws) to no avail.
I know this is a tad bit vague - but was wondering if anyone has any suggestions on handling this? This same code works flawlessly in the emulator, but fails every time on my actual device, even after an un-install and re-install of my application. Given that this is my actual phone, I'm a little reticent to do a hardware reset in the hope that it solves this issue, and don't feel comfortable releasing the application to the marketplace with this issue haunting me.
Update: An additional point, I'm using an unauthenticated channel, so there's no certificate installed for my cloud-based service.
Update #2: Further, I just tried deploying the Microsoft Phone Push Recipe to my device and it's also throwing the same exception.
So from your comment I understand that it does work on your emulator but not on your phone right?
Did you by any chance use the channel name in another/prior application?
The thing is that the emulator reset back to it's default state everyime it closes, your phone does not. A particular channel name can only be used by a single application. So if the channel name was used by another application on the same phone before it is still registered to that app and you can't access it from your app.
Conversely an app can also regsiter no more than one channel so if there is allready one by another name associated with it you cannot register a new one until you unregister the old one and reboot your device. Also there is no way to request which channel is associated with your app.
Ultimately when I got stuck in this loop I changed the name of the channel and my applications ProductID registered in the WMAppManifest.xml and it worked again form me
<App xmlns="" ProductID="{d57ef66e-f46c-4b48-ac47-22b1e924184b}"
Update
My computer crashed this weekend, thank god for WHS and backups.
Anyway below is my sourcecode. I notice a two differences.
First off I created a method called RepeatAttemptExecuteMethod() to which I pass the entire executing code as a delegate. The 10 floating somewhere at the end is the amount of times it has to retry. If you only retried the .Open method every 5 seconds the difference might be in that I also call the Find and New methods again...
Another difference I see is that my code assumes that the _appChannel.ChannelUri can be null. In which case it waits for the channel to raise an event and then does the work asociated with a actual channel being there. But since your samplecode doesn't do any of that sort of work I doubt it will be what you are looking for
protected override void Load(PhoneApplicationPage parent)
{
Verkeer.Helper.ExternalResources.RepeatAttemptExecuteMethod(() =>
{
_appChannel = HttpNotificationChannel.Find(CHANNELNAME);
if (_appChannel == null)
{
_appChannel = new HttpNotificationChannel(CHANNELNAME);
SetUpDelegates();
}
else
{
SetUpDelegates();
//if (_appChannel.ChannelUri != null) this.NotificationChannel = _appChannel.ChannelUri;
}
if (_appChannel.ChannelUri != null) this.NotificationChannel = _appChannel.ChannelUri;
else
{
try
{
_appChannel.Open();
}
catch { }
}
BindToShellTile();
App.ViewModel.TrafficInfo.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(TrafficInfo_PropertyChanged);
if (App.ViewModel.TrafficInfo.TrafficImage != null && this.NotificationChannel != null)
{
CreateTiles();
}
},10);
}
private void BindToShellTile()
{
if (!_appChannel.IsShellTileBound && App.ViewModel.PanItemSettings.AutomaticallyUpdateTile)
{
Collection<Uri> ListOfAllowedDomains = new Collection<Uri> { new Uri("http://m.anwb.nl/") };
_appChannel.BindToShellTile(ListOfAllowedDomains);
}
}
void TrafficInfo_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "TrafficImage")
{
if (App.ViewModel.PanItemSettings.AutomaticallyUpdateTile && this.NotificationChannel != null)
{
CreateTiles();
}
}
}
#slaad .. here are few things that I would check, unless you have already tried these:
Your actual device does have data connectivity, right? doh :)
How are you storing an existing Channel in Isolated Storage? Make sure your Find() is working & that you are not trying to recreate a channel that exists leading to exception.
Check if your Channel creation has issues with domain name or certs. Try this link
Check every step of your process against this
Sorry, not being of much more help than this.

How to serialize access to an asynchronous resource?

Can anyone point me to a good working solution to the following problem?
The application I'm working on needs to communicate over TCP to software running on another system. Some of the requests I send to that system can take a long time to complete (up to 15sec).
In my application I have a number of threads, including the main UI thread, which can access the service which communicates with the remote system. There is only a single instance of the service which is accessed by all threads.
I need to only allow a single request to be processed at a time, i.e. it needs to be serialized, otherwise bad things happen with the TCP comms.
Attempted Solutions so far
Initially I tried using lock() with a static object to protect each 'command' method, as follows:
lock (_cmdLock)
{
SetPosition(position);
}
However I found that sometimes it wouldn't release the lock, even though there are timeouts on the remote system and on the TCP comms. Additionally, if two calls came in from the same thread (e.g. a user double clicked a button) then it would get past the lock - after reading up about locking again I know that the same thread won't wait for the lock.
I then tried to use AutoResetEvents to only allow a single call through at a time. But without the locking it wouldn't work with multiple threads. The following is the code I used to send a command (from the calling thread) and process a command request (running in the background on its own thread)
private static AutoResetEvent _cmdProcessorReadyEvent = new AutoResetEvent(false);
private static AutoResetEvent _resultAvailableEvent = new AutoResetEvent(false);
private static AutoResetEvent _sendCommandEvent = new AutoResetEvent(false);
// This method is called to send each command and can run on different threads
private bool SendCommand(Command cmd)
{
// Wait for processor thread to become ready for next cmd
if (_cmdProcessorReadyEvent.WaitOne(_timeoutSec + 500))
{
lock (_sendCmdLock)
{
_currentCommand = cmd;
}
// Tell the processor thread that there is a command present
_sendCommandEvent.Set();
// Wait for a result from the processor thread
if (!_resultAvailableEvent.WaitOne(_timeoutSec + 500))
_lastCommandResult.Timeout = true;
}
return _lastCommandResult.Success;
}
// This method runs in a background thread while the app is running
private void ProcessCommand()
{
try
{
do
{
// Indicate that we are ready to process another commnad
_cmdProcessorReadyEvent.Set();
_sendCommandEvent.WaitOne();
lock (_sendCmdLock)
{
_lastCommandResult = new BaseResponse(false, false, "No Command");
RunCOMCommand(_currentCommand);
}
_resultAvailableEvent.Set();
} while (_processCommands);
}
catch (Exception ex)
{
_lastCommandResult.Success = false;
_lastCommandResult.Timeout = false;
_lastCommandResult.LastError = ex.Message;
}
}
I haven't tried implementing a queue of command requests as the calling code expects everything to be synchronous - i.e. the previous command must have completed before I sent the next one.
Additional Background
The software running on the remote system is a 3rd party product and I don't have access to it, it is used to control a laser marking machine with an integrated XY table.
I'm actually using a legacy VB6 DLL to communicate with the laser as it has all the code for formatting commands and processing the responses. This VB6 DLL uses a WinSock control for the comms.
I'm not sure why a queueing solution wouldn't work.
Why not put each request, plus details for a callback with result, on a queue ? Your application would queue these requests, and the module interfacing to your 3rd party system can take each queue item in turn, process, and return the result.
I think it's a cleaner separation of concerns between modules rather than implementing locking around request dispatch etc. Your requestor is largely oblivious of the serialisation constraints, and the 3rd-party interfacing module can look after serialisation, managing timeouts and other errors etc.
Edit: In the Java world we have BlockingQueues which are synchronised for consumers/publishers and make this sort of thing quite easy. I'm not sure if you have the same in the C# world. A quick search suggests not, but there's source code floating around for this sort of thing (if anyone in the C# world can shed some light that would be appreciated)

WCF exception received on closing connection with callbacks in use

I am using a netNamedPipeBinding to perform inter-process WCF communication from a windows app to a windows service.
Now my app is running well in all other accounts (after fighting off my fair share of WCF exceptions as anybody who has worked with WCF would know..) but this error is one that is proving to be quite resilient.
To paint a picture of my scenario: my windows service could be queued to do some work at any given time through a button pressed in the windows app and it then talks over the netNamedPipeBinding which is a binding that supports callbacks (two-way communication) if you are not familiar and initiates a request to perform this work, (in this case a file upload procedure) it also throws the callbacks (events) every few seconds ranging from file progress to transfer speed etc. etc. back to the windows app, so there is some fairly tight client-server integration; this is how I receive my progress of what's running in my windows service back into my windows app.
Now, all is great, the WCF gods are relatively happy with me right now apart from one nasty exception which I receive every time I shutdown the app prematurely (which is a perfectly valid scenario). Whilst a transfer is in progress, and callbacks are firing pretty heavily, I receive this error:
System.ServiceModel.ProtocolException:
The channel received an unexpected input message with Action
'http://tempuri.org/ITransferServiceContract/TransferSpeedChangedCallback'
while closing. You should only close your channel when you are not expecting
any more input messages.
Now I understand that error, but unfortunately I cannot guarantee to close my channel after never receiving any more input messsages, as the user may shutdown the app at any time therefore the work will still be continuing in the background of the windows service (kind of like how a virus scanner operates). The user should be able to start and close the win management tool app as much as they like with no interference.
Now the error, I receive immediately after performing my Unsubscribe() call which is the second last call before terminating the app and what I believe is the preferred way to disconnect a WCF client. All the unsubscribe does before closing the connection is simply removes the client id from an array which was stored locally on the win service wcf service (as this is an instance SHARED by both the win service and windows app as the win service can perform work at scheduled events by itself) and after the client id array removal I perform, what I hope (feel) should be a clean disconnection.
The result of this, besides receiving an exception, is my app hangs, the UI is in total lock up, progress bars and everything mid way, with all signs pointing to having a race condition or WCF deadlock [sigh], but I am pretty thread-savvy now and I think this is a relatively isolated situation and reading the exception as-is, I don't think it's a 'thread' issue per-se, as it states more an issue of early disconnection which then spirals all my threads into mayhem, perhaps causing the lock up.
My Unsubscribe() approach on the client looks like this:
public void Unsubscribe()
{
try
{
// Close existing connections
if (channel != null &&
channel.State == CommunicationState.Opened)
{
proxy.Unsubscribe();
}
}
catch (Exception)
{
// This is where we receive the 'System.ServiceModel.ProtocolException'.
}
finally
{
Dispose();
}
}
And my Dispose() method, which should perform the clean disconnect:
public void Dispose()
{
// Dispose object
if (channel != null)
{
try
{
// Close existing connections
Close();
// Attempt dispose object
((IDisposable)channel).Dispose();
}
catch (CommunicationException)
{
channel.Abort();
}
catch (TimeoutException)
{
channel.Abort();
}
catch (Exception)
{
channel.Abort();
throw;
}
}
}
And the WCF service Subscription() counterpart and class attributes (for reference) on the windows service server (nothing tricky here and my exception occurs client side):
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TransferService : LoggableBase, ITransferServiceContract
{
public void Unsubscribe()
{
if (clients.ContainsKey(clientName))
{
lock (syncObj)
{
clients.Remove(clientName);
}
}
#if DEBUG
Console.WriteLine(" + {0} disconnected.", clientName);
#endif
}
...
}
Interface of:
[ServiceContract(
CallbackContract = typeof(ITransferServiceCallbackContract),
SessionMode = SessionMode.Required)]
public interface ITransferServiceContract
{
[OperationContract(IsInitiating = true)]
bool Subscribe();
[OperationContract(IsOneWay = true)]
void Unsubscribe();
...
}
Interface of callback contract, it doesn't do anything very exciting, just calls events via delegates etc. The reason I included this is to show you my attributes. I did alleviate one set of deadlocks already by including UseSynchronizationContext = false:
[CallbackBehavior(UseSynchronizationContext = false,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TransferServiceCallback : ITransferServiceCallbackContract
{ ... }
Really hope somebody can help me! Thanks a lot =:)
OH my gosh, I found the issue.
That exception had nothing to do with the underyling app hang, that was just a precautionary exception which you can safely catch.
You would not believe it, I spent about 6 hours on and off on this bug, it turned out to be the channel.Close() locking up waiting for pending WCF requests to complete (which never would complete until the transfer has finished! which defeats the purpose!)
I just went brute-force breakpointing line after line, my issue was if I was too slow..... it would never hang, because somehow the channel would be available to close (even before the transfer had finished) so I had to breakpoint F5 and then quickly step to catch the hang, and that's the line it ended on. I now simply apply a timeout value to the Close() operation and catch it with a TimeoutException and then hard abort the channel if it cannot shut down in a timely fashion!
See the fix code:
private void Close()
{
if (channel != null &&
channel.State == CommunicationState.Opened)
{
// If cannot cleanly close down the app in 3 seconds,
// channel is locked due to channel heavily in use
// through callbacks or the like.
// Throw TimeoutException
channel.Close(new TimeSpan(0, 0, 0, 3));
}
}
public void Dispose()
{
// Dispose object
if (channel != null)
{
try
{
// Close existing connections
// *****************************
// This is the close operation where we perform
//the channel close and timeout check and catch the exception.
Close();
// Attempt dispose object
((IDisposable)channel).Dispose();
}
catch (CommunicationException)
{
channel.Abort();
}
catch (TimeoutException)
{
channel.Abort();
}
catch (Exception)
{
channel.Abort();
throw;
}
}
}
I am so happy to have this bug finally over and done with! My app is now shutting down cleanly after a 3 second timeout regardless of the current WCF service state, I hope I could have helped someone else who ever finds themselves suffering a similar issue.
Graham

C# COM port communication problem

I have a C# code which communicates with three different COM ports. The COM ports are actually three serial port to USB converters.
The code each time switches 'off' and 'on' the devices to which it is communicating, then initializes the three com ports, tries to send and read data and then closes the com port. This keeps continuing for a pre-defined number of loops.
My problem is that after about 8 or 9 iterations, the COM port communication stops working. Sometime it throws an error saying the port is closed, sometime it does not throw any exception but it is actually not reading or writing anything from the com port. Some point it was only writing but not reading back the data.
What might be the reason and any tips to debug this problem?
EDIT:
The port abruptly closes or stops working even in the middle of the program as shown below:
SerialPort.Write("ss");
SerialPort.Read("ss"); // FAILS!!
Some part of the code I am using
public string Read(string readCommand)
{
string str = "";
_port.WriteLine("\r");
_port.WriteLine(readCommand + "\r");
Thread.Sleep(0x3e8);
str = _port.ReadExisting();
return str;
}
public void Write(string command)
{
_port.WriteLine(command + "\r");
Thread.Sleep(100);
if (_port.ReadExisting() == string.Empty)
{
throw new IOException("Error writing to COM");
}
}
public void Initialize()
{
if (_port == null)
{
_port = new SerialPort(this.PortName.ToString(), this.BaudRate, this.Parity, this.DataBits, this.StopBits);
_port.Handshake = this.Handshake;
}
try
{
if (!_port.IsOpen)
{
_port.Open();
if (Read("") == string.Empty)
{
throw new IOException("Device not connected or powered on");
}
}
}
catch (Exception)
{
this.Close();
}
}
Thanks...
_port.WriteLine(command + "\r");
Thread.Sleep(100);
if (_port.ReadExisting() == string.Empty)
{
throw new IOException("Error writing to COM");
}
That's evil code and bound to throw sooner or later. Windows cannot provide a service guarantee like that. Or for that matter the device itself, especially when you power it on and off. Use SerialPort.ReadTimeout, set it to at least 2 seconds. And make a blocking call, like ReadLine().
catch (Exception)
{
this.Close();
}
That's tops the previous snippet. You have no idea what's going wrong when that runs. And your code will try to use a closed port. Just delete the statements, it does nothing but harm.
Do not close the ports until your program ends. SerialPort uses a background thread to watch for events on the port, that thread needs to shutdown after the Close() call before you can open the port again. How long it takes to shutdown is unpredictable, it could be seconds worst case. There's no point in closing the port, it isn't going to be useful to anything else.
You need to use SetCommTimeouts (not sure what the .NET wrapper is, I gave up on the .NET serial classes long ago and call the Win32 API directly) to force the USB/serial converter to send the data back to your program.
By default it may try to collect a block equal in size to a USB transfer block, for efficiency.
Its tough to tell exactly what the problem might be without see some of the code. My guess would be that you are not waiting long enough for the COM port to close after reopening it. Note from the SerialPort.Close page, that:
The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly.
Can you just open the COM ports and leave them open until you are done? For example from this post:
using (SerialPort serialPort = new SerialPort("COM1", 9600))
{
serialPort.Open();
while (true)
{
Thread.Sleep(1000);
// serialPort.Write();
Thread.Sleep(1000);
// serialPort.Read();
// break at some point to end
}
serialPort.Close();
}

Categories