Why won't NetworkChange events fire - c#

I am fairly new to all this but I have looked through questions and can't find anything that answers my question.
I am writing a simple service that checks whether it can reach one of our servers whenever there is a change to it's network settings.
I am using the two events under the NetworkChange class, NetworkAddressChanged and NetworkAvailabiltyChanged.
When either fire the service tries to ping the server and depending on the result changes the ProxyEnable setting.
The Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Net.NetworkInformation;
using Microsoft.Win32;
namespace ProxyManager
{
public partial class ProxyManager : ServiceBase
{
static RegistryKey rk = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\Internet Settings",true);
static string currentProxy = rk.GetValue("ProxyEnable").ToString();
static string newProxy;
public ProxyManager()
{
InitializeComponent();
NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);
NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
newProxy = "0";
}
protected override void OnStart(string[] args)
{
}
void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
ProxySwitcher();
EventLog evt = new EventLog("ProxyManager");
string message = "Proxy Manager, Newtwork availabilty changed. Proxy switched to " + newProxy.ToString();
evt.Source = "Proxy Manager";
evt.WriteEntry(message,EventLogEntryType.Information);
}
void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
{
ProxySwitcher();
EventLog evt = new EventLog("ProxyManager");
string message = "Proxy Manager, Newtwork address changed. Proxy switched to " + newProxy.ToString();
evt.Source = "Proxy Manager";
evt.WriteEntry(message,EventLogEntryType.Information);
}
void ProxySwitcher()
{
if (currentProxy == "0")
{
newProxy = "1";
}
else
{
newProxy = "0";
}
try
{
Ping myPing = new Ping();
string host = "FILE SERVER";
byte[] buffer = new byte[32];
int timeout = 1000;
PingOptions pingOptions = new PingOptions();
PingReply reply = myPing.Send(host, timeout, buffer, pingOptions);
if (reply.Status == IPStatus.Success)
{
if (currentProxy == "0")
{
rk.SetValue("ProxyEnable", newProxy);
}
}
else
{
if (currentProxy == "1")
{
rk.SetValue("ProxyEnable", newProxy);
}
}
}
catch (PingException pingEx)
{
rk.SetValue("ProxyEnable", 0);
}
}
protected override void OnStop()
{
}
}
}
My problem is that when the service is installed and running, the events are either not being triggered or not being picked up. There related events are not being logged and the proxy is not switching.
I tried the code, minus the event handlers in a console app that I could run at will and that worked fine, depending on the network availability.
I am running Windows 7 x86, but am coding in .NET 3.5.
Any help will be gratefully received.

I wrote a rather large WMI application and this is the one feature that I could never get to work well with WMI. Sorry to disagree with Ted but .NET WMI support is terrible, particularly with dealing with exceptions properly and when dealing with pre-Vista operating systems.
Two caveats when dealing with these events. First, they only work if subscribed on the main thread. However, since your log file gets created then you appear to have verified that the event is being triggered. The second problem is in the assumption your code makes about the current state of the network.
From the look of your code you are starting with Proxy = 0, so no proxy. Then when you receive a network change event you toggle the proxy. Consider what happens when your service starts and the network is "down" already, which might very well happen during bootup depending on when your service starts in relation to the network stack initialization. You will be setting "no proxy" when the NIC is down, then setting "Proxy" when the NIC is up. This seems to be inverted to what you were trying to accomplish.
The fix is simple, in the event you need to check the state of the network to know why the change event was fired. So, in your NetworkAvailabilityChanged event you are going to want to check NetworkInterface.GetIsNetworkAvailable() which returns true as long as there is one network interface that isn't loopback or ms-tunnel.
For the NetworkAddressChanged event presumably you are going to want to check the address to see if it's valid or not to determine if your proxy needs to be invoked. You can do this by grabbing the
UnicastIPAddressInformationCollection like this...
NetworkInterface[] niList = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface ni in niList)
{
switch (ni.NetworkInterfaceType)
{
case NetworkInterfaceType.Ethernet: // 10baseT
case NetworkInterfaceType.FastEthernetT: // 100baseT
case NetworkInterfaceType.GigabitEthernet:
GatewayIPAddressInformationCollection gwIPColl = ni.GetIPProperties().GatewayAddresses;
UnicastIPAddressInformation uniIPInfo = null;
UnicastIPAddressInformationCollection IPcoll = ni.GetIPProperties().UnicastAddresses;
if (IPcoll.Count <= 0)
{
LogFile.LogMe("No valid unicast IP address");
broken = true;
break; // Cannot continue if we don't have an IP in the colletion
}
.
.
.
or something similar depending on how complicated your want to make it. When I check for network address validity I check the Unicast address, Netmask (.IPv4Mask) and the defined gateway from GatewayIPAddressInformationCollection (.GetIPProperties().GatewayAddresses)
Hopefully that helps you out a little bit.

You may try logging to a file instead of the event log. There are permission issues with the event log and when you are running the service, the security model may be preventing you from writing to the log.

Consider using WMI.
Some sample VBScripts on http://msdn.microsoft.com/en-us/library/aa394595(v=vs.85).aspx
I know from experience that WMI works, but never tried it with "NetworkChange" object.

Related

Notify when network connection changes like network connected or disconnected

I want to show popup window when a connection is lost or back for that I am us below code
using System.Net.NetworkInformation;
using Tulpep.NotificationWindow;
public TaskList()
{
InitializeComponent();
NetworkChange.NetworkAvailabilityChanged += AvailabilityChanged;
load();
}
private void AvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
PopupNotifier popup = new PopupNotifier();
if (e.IsAvailable)
{
popup.ContentText = "Network connected!";
popup.Popup();
}
else
{
popup.ContentText = "Network disconnected!";
popup.Popup();
}
}
But it cannot show a notification. I also apply to debug but it is not hit the debugger what is wrong here I don't know please help me in my code
One option would be to use the Ping method periodically. That call will return a result, and when it fails that might indicate you've disconnected. However, this does rely on you having a reliable IP (or url) to use.
The basic test goes like this:
var reliableIP = "127.0.0.1";
var stillConnected = new System.Net.NetworkInformation.Ping().Send(reliableIP, 500).Status == System.Net.NetworkInformation.IPStatus.Success;
How you use that will depend upon the kind of monitoring you want.

HoloLens unable to send or receive data via BT and TCP

I am working on HoloLens (Unity-UWP) and trying to make a connection with PC (UWP) or Android phone work (Xamarin). So far I tried client and host with both Bluetooth and TCP (even two versions with different libraries) on Android and UWP. I kept the code entirely separated from user interface, so that it is easier to use, to understand and modular. An Action<string> is used to output results (error logs and sent messages).
Everything that is not on the HoloLens works fine (even though it's exactly the same code). It worked from PC (UWP) to Android with client and host switched. But it doesn't even work between HoloLens and PC (UWP). The behavior ranged from crashes (mostly for Bluetooth) to instant disconnection. The last tests resulted in disconnection once bytes are about to be received. It could even read the first 4 bytes (uint for the length of the following UTF-8 message), but then it was disconnected. The other devices seemed to work fine.
What I know: Capabilities are set, the code works, the issue is likely something that is common for everything that has to do with networking and HoloLens.
So the question is, is Unity or HoloLens incompatible with something I am using? What I used which is worth mentioning: StreamSocket, BinaryWriter, BinaryReader, Task (async, await). Or is HoloLens actively blocking communication with applications on other devices? I know it can connect to devices with Bluetooth and that it can connect via TCP, and it looks like people succeed to get it to work. Are there known issues? Or is there something with Unity that causes this - a bad setting maybe? Do I have to use async methods or only sync? Are there incompatibility issues with Tasks/Threads and Unity? Is this possibly the issue (inability to consent to permissions)?
Another thing to note is that I cannot ping HoloLens via its IP by using the cmd, even though the IP is correct.
I'd appreciate any advice, answer or guess. I can provide more information if requested (see also the comments below). I would suggest to focus on the TCP connection as it seemed to be working better and appears to be more "basic." Here is the code:
using System;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Windows.Networking;
using Windows.Networking.Sockets;
#region Common
public abstract class TcpCore
{
protected StreamSocket Socket;
protected BinaryWriter BWriter;
protected BinaryReader BReader;
protected Task ReadingTask;
public bool DetailedInfos { get; set; } = false;
public bool Listening { get; protected set; }
public ActionSingle<string> MessageOutput { get; protected set; } = new ActionSingle<string> (); // Used for message and debug output. They wrap an Action and allow safer use.
public ActionSingle<string> LogOutput { get; protected set; } = new ActionSingle<string> ();
protected const string USED_PORT = "1337";
protected readonly Encoding USED_ENCODING = Encoding.UTF8;
public abstract void Disconnect ();
protected void StartCommunication ()
{
Stream streamOut = Socket.OutputStream.AsStreamForWrite ();
Stream streamIn = Socket.InputStream.AsStreamForRead ();
BWriter = new BinaryWriter (streamOut); //{ AutoFlush = true };
BReader = new BinaryReader (streamIn);
LogOutput.Trigger ("Connection established.");
ReadingTask = new Task (() => StartReading ());
ReadingTask.Start ();
}
public void SendMessage (string message)
{
// There's no need to send a zero length message.
if (string.IsNullOrEmpty (message)) return;
// Make sure that the connection is still up and there is a message to send.
if (Socket == null || BWriter == null) { LogOutput.Trigger ("Cannot send message: No clients connected."); return; }
uint length = (uint) message.Length;
byte[] countBuffer = BitConverter.GetBytes (length);
byte[] buffer = USED_ENCODING.GetBytes (message);
if (DetailedInfos) LogOutput.Trigger ("Sending: " + message);
BWriter.Write (countBuffer);
BWriter.Write (buffer);
BWriter.Flush ();
}
protected void StartReading ()
{
if (DetailedInfos) LogOutput.Trigger ("Starting to listen for input.");
Listening = true;
while (Listening)
{
try
{
if (DetailedInfos) LogOutput.Trigger ("Starting a listen iteration.");
// Based on the protocol we've defined, the first uint is the size of the message. [UInt (4)] + [Message (1*n)] - The UInt describes the length of the message (=n).
uint length = BReader.ReadUInt32 ();
if (DetailedInfos) LogOutput.Trigger ("ReadLength: " + length.ToString ());
MessageOutput.Trigger ("A");
byte[] messageBuffer = BReader.ReadBytes ((int) length);
MessageOutput.Trigger ("B");
string message = USED_ENCODING.GetString (messageBuffer);
MessageOutput.Trigger ("Received Message: " + message);
}
catch (Exception e)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus (e.HResult) == SocketErrorStatus.Unknown)
{
// Seems to occur on disconnects. Let's not throw().
Listening = false;
Disconnect ();
LogOutput.Trigger ("Unknown error occurred: " + e.Message);
break;
}
else
{
Listening = false;
Disconnect ();
break;
}
}
}
LogOutput.Trigger ("Stopped to listen for input.");
}
}
#endregion
#region Client
public class GTcpClient : TcpCore
{
public async void Connect (string target, string port = USED_PORT) // Target is IP address.
{
try
{
Socket = new StreamSocket ();
HostName serverHost = new HostName (target);
await Socket.ConnectAsync (serverHost, port);
LogOutput.Trigger ("Connection successful to: " + target + ":" + port);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
#region Server
public class GTcpServer : TcpCore
{
private StreamSocketListener socketListener;
public bool AutoResponse { get; set; } = false;
public async void StartServer ()
{
try
{
//Create a StreamSocketListener to start listening for TCP connections.
socketListener = new StreamSocketListener ();
//Hook up an event handler to call when connections are received.
socketListener.ConnectionReceived += ConnectionReceived;
//Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await socketListener.BindServiceNameAsync (USED_PORT);
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
private void ConnectionReceived (StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
listener.Dispose ();
Socket = args.Socket;
if (DetailedInfos) LogOutput.Trigger ("Connection received from: " + Socket.Information.RemoteAddress + ":" + Socket.Information.RemotePort);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection Received error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (socketListener != null) { socketListener.Dispose (); socketListener = null; }
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
Coincidentially, I just implemented a BT connection between HoloLens and an UWP app. I followed the sample at https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BluetoothRfcommChat.
As capabilities, I set "Bluetooth" (of course), "Internet (Client & Server)" and "Private Networks (Client & Server)". The steps on the server side then are:
Create an RfcommServiceProvider for your own or an existing (eg OBEX object push) service ID.
Create a StreamSocketListener and wire its ConnectionReceived Event.
Bind the service Name on the listener: listener.BindServiceNameAsync(provider.ServiceId.AsString(), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
If you have a custom service ID, set its name along with other attributes you may want to configure. See the sample linked above for this. I think, this is mostly optional.
Start advertising the BT service: provider.StartAdvertising(listener, true);
Once a client connects, there is a StreamSocket in the StreamSocketListenerConnectionReceivedEventArgs that you can use to create a DataReader and DataWriter on like on any other stream. If you only want to allow one client, you can also stop advertising now.
On the client side, you would:
Show the DevicePicker and let the user select the peer device. Do not forget setting a filter like picker.Filter.SupportedDeviceSelectors.Add(BluetoothDevice.GetDeviceSelectorFromPairingState(true)); You can also allow unpaired devices, but you need to call PairAsync before you can continue in step 2. Also, I think there is no way to circumvent the user consent dialogue in this case, so I would recommend pairing before. To be honest, I did not check whether the unpaired stuff works on HoloLens.
You get a DeviceInformation instance from the picker, which you can use to obtain a BT device like await BluetoothDevice.FromIdAsync(info.Id);
Get the services from the device like device.GetRfcommServicesAsync(BluetoothCacheMode.Uncached); and select the one you are interested in. Note that I found that the built-in filtering did not behave as expected, so I just enumerated the result and compared the UUIDs manually. I believe that the UWP implementation performs a case-sensitive string comparison at some point, which might lead to the requested service not showing up although it is there.
Once you found your service, which I will call s from now on, create a StreamSocket and connect using socket.ConnectAsync(s.ConnectionHostName, s.ConnectionServiceName, SocketProtectionLevel.PlainSocket);
Again, you can not create the stream readers and writers like on the server side.
The answer is Threading.
For whoever may have similar issues, I found the solution. It was due to Unity itself, not HoloLens specifically. My issue was that I wrote my code separately in an own class instead of commingle it with UI code, which would have made it 1. unreadable and 2. not modular to use. So I tried a better coding approach (in my opinion). Everybody could download it and easily integrate it and have basic code for text messaging. While this was no issue for Xamarin and UWP, it was an issue for Unity itself (and there the Unity-UWP solution as well).
The receiving end of Bluetooth and TCP seemed to create an own thread (maybe even something else, but I didn't do it actively), which was unable to write on the main thread in Unity, which solely handles GameObjects (like the output log). Thus I got weird log outputs when I tested it on HoloLens.
I created a new TCP code which works for Unity but not the Unity-UWP solution, using TcpClient/TcpListener, in order to try another version with TCP connection. Luckily when I ran that in the editor it finally pointed on the issue itself: The main thread could not be accessed, which would have written into the UI - the text box for the log output. In order to solve that, I just had to use Unity's Update() method in order to set the text to the output. The variables themselves still could be accessed, but not the GameObjects.

testing internet connection on a cellular network

I have an application that was written for remote trucks to use on cell service. Before I do anything, I am checking the internet with this class:
using System.Net;
namespace SSS.ServicesConfig.MiscClasses
{
public class VerifyInternetAccess
{
public static bool HasInternet()
{
try
{
using (var client = new WebClient())
using (var stream = client.OpenRead("http://www.google.com"))
{
return true;
}
}
catch
{
return false;
}
}
}
}
In some cases, the light on the external cellular device has a green light as if it has internet. My test class comes back false so it thinks it does not have internet.
The driver can then open up internet explorer, close internet explorer, promptly run my application and it passes the test above.
The users are saying that IE is 'waking up' the internet so that it can transfer.
Doesn't my class do essentially the same thing? If not, how can I 'wake up' the internet connection as IE does?
You didn't state if you're restricted to a certain mobile OS but this works on a normal box.
I try to leverage two features of the System.Net.NetworkInformation namespace.
I start with registering for the NetworkChangedEvent. By calling GetIsNetworkAvailable you get an idea if there is at least one other NIC present that is not the loopback interface.
If there is no connection I try to wake-up the network layer by getting pinging a host.
I use the Dns.GetHostEntry to obtain all IP Adresses known for a host. Next I try to Ping the address one by one.
Be aware that not all hosts allow ICMP traffic which would lead to timeouts in all circumstances. If however in the meantime the network becomes available the NetworkChanged event should have been fired and set the HasConnection to true
public class VerifyInternetAccess
{
private static bool HasConnection = false;
static VerifyInternetAccess()
{
NetworkChange.NetworkAvailabilityChanged += (o, ca) =>
{
HasConnection = ca.IsAvailable;
};
HasConnection = NetworkInterface.GetIsNetworkAvailable();
}
public static bool HasInternet()
{
bool hasEnded = false;
if (!HasConnection)
{
// let's try to wake up...
using (var ping = new Ping())
{
var iphost = Dns.GetHostEntry("www.google.com");
foreach (var addr in iphost.AddressList)
{
var reply = ping.Send(addr);
if (reply.Status == IPStatus.Success)
{
HasConnection = true;
break;
}
}
}
}
return HasConnection;
}
}

What is the prefered or accepted method for testing proxy settings?

I have a lot of trouble with the internet connectivity in the program I am working on and it all seems to spawn from some issue with the proxy settings. Most of the issues at this point are fixed, but the issue I am having now is that my method of testing the proxy settings makes some users wait for long periods of time.
Here is what I do:
System.Net.WebClient webClnt = new System.Net.WebClient();
webClnt.Proxy = proxy;
webClnt.Credentials = proxy.Credentials;
byte[] tempBytes;
try
{
tempBytes = webClnt.DownloadData(url.Address);
}
catch
{
//Invalid proxy settings
//Code to handle the exception goes here
}
This is the only way that I've found to test if the proxy settings are correct. I tried making a web service call to our web service, but no proxy settings are needed when making the call. It will work even if I have bogus proxy settings. The above method, though, has no timeout member that I can set that I can find and I use the DownloadData as opposed to the DownloadDataAsync because I need to wait til the method is done so that I can know if the settings are correct before continuing on in the program.
Any suggestions on a better method or a work around for this method is appreciated.
Mike
EDIT: I tried something else, but no luck. I used the DownloadDataAsync method to download the data in a separate thread which raises the DownloadDataCompleted event of the WebClient when finished. While I wait for the event to get called I have a loop: while(DateTime.Now < downloadStart.AddMinutes(timeout) && !TestIsDone) {} The DownloadDataCompleted event sets the TestIsDone member to true when the event is called. The problem here is if the proxy settings are bad the Event never gets called, no exception is thrown, and the program waits for the entire timeout period before continuing. Here is the code for this approach:
public static bool TestProxy(System.Net.WebProxy proxy)
{
ProxySettingsTestDone = false; //public static var
string address = //url to some arbitrary data on our server
System.Net.WebClient webClnt = new System.Net.WebClient();
webClnt.Proxy = proxy;
webClnt.Credentials = proxy.Credentials;
try
{
webClnt.DownloadDataCompleted += new System.Net.DownloadDataCompletedEventHandler(DownloadDataCallback);
webClnt.DownloadDataAsync(new Uri(address));
//Timeout period
DateTime dnldStartTime = DateTime.Now;
while (DateTime.Now < dnldStartTime.AddMinutes(1.0) && !ProxySettingsTestDone)
{ }
if (!ProxySettingsTestDone) //Exceded timeout
{
throw new System.Net.WebException("Invalid Proxy Settings");
}
}
catch (System.Net.WebException e)
{
if (e.Status == System.Net.WebExceptionStatus.ProxyNameResolutionFailure)
{
//Proxy failed, server may or may not be there
Util.ConnectivityErrorMsg = e.Message;
return false;
}
else if (e.Status == System.Net.WebExceptionStatus.ProtocolError)
{
//File not found, server is down, but proxy settings succeded
ServerUp = false;
Util.ConnectivityErrorMsg = e.Message;
return true;
}
return false;
}
Util.ConnectivityErrorMsg = "";
return true;
}
private static void DownloadDataCallback(object sender, System.Net.DownloadDataCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
ProxySettingsTestDone = true;
else
throw new System.Net.WebException("Invalid Proxy Settings");
}
Sorry about the long post. I wanted to update this question with the information that I found after testing this new approach.
Thanks,
Mike
You can run the proxycheck in a seperate thread. And consider the check to be failed if the thread takes too long.
Or you could use WebRequest, it allows you set a timeout:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://url");
request.Proxy = proxy;
request.Timeout = 2000;
If the request has not finished within the given timeout a WebException with the Status property set to WebExceptionStatus.Timeout will be thrown.
Every method mentioned here are valid. But the most important one is to test the Proxy connection using the same Windows user account for the process that you want to test. Many proxies has specific privileges for each Windows user.
A recent post Testing IP:Port proxies explains proxy check using a simple Python script. The script checks for availability and correctness of proxy servers.

How to capture a serial port that disappears because the usb cable gets unplugged

I have a c# winforms program and it opens up a serial port. The problem happens when the end user unplugs the usb cable and then the device disappears. After this the program will crash and want to report the error to microsoft.
Is there a way to capture this event and shut down gracefully?
Yes, there is a way to capture the event. Unfortunately, there can be a long delay between the time the device is removed and the time the program receives any notification.
The approach is to trap com port events such as ErrorReceived and to catch the WM_DEVICECHANGE message.
Not sure why your program is crashing; you should take a look at the stack to see where this is happening.
You can use WMI (Windows Management Instrumentation) to receive notification on USB events.
I did exactly that two years ago, monitoring for plugging and unplugging of a specific usb device.
Unfortunately, the code stays with my former employer, but I found one example at bytes.com:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Management;
class UsbWatcher
{
public static void Main()
{
WMIEvent wEvent = new WMIEvent();
ManagementEventWatcher watcher = null;
WqlEventQuery query;
ManagementOperationObserver observer = new ManagementOperationObserver();
ManagementScope scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
try
{
query = new WqlEventQuery();
query.EventClassName = "__InstanceCreationEvent";
query.WithinInterval = new TimeSpan(0,0,10);
query.Condition = #"TargetInstance ISA 'Win32_USBControllerDevice' ";
watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived
+= new EventArrivedEventHandler(wEvent.UsbEventArrived);
watcher.Start();
}
catch (Exception e)
{
//handle exception
}
}
I don't remember if I modified the query to receive events only for e specific device, or if I filtered out events from other devices in my event handler. For further information you may want to have a look at the MSDN WMI .NET Code Directory.
EDIT
I found some more info on the event handler, it looks roughly like this:
protected virtual void OnUsbConnected(object Sender, EventArrivedEventArgs Arguments)
{
PropertyData TargetInstanceData = Arguments.NewEvent.Properties["TargetInstance"];
if (TargetInstanceData != null)
{
ManagementBaseObject TargetInstanceObject = (ManagementBaseObject)TargetInstanceData.Value;
if (TargetInstanceObject != null)
{
string dependent = TargetInstanceObject.Properties["Dependent"].Value.ToString();
string deviceId = dependent.Substring(dependent.IndexOf("DeviceID=") + 10);
// device id string taken from windows device manager
if (deviceId = "USB\\\\VID_0403&PID_6001\\\\12345678\"")
{
// Device is connected
}
}
}
}
You may want to add some exception handling, though.
In registry at:
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
is actual list of ports. If your port disappeared it means it was unplugged.
Real example: (Try to remove your USB and press F5 in registry editor)
Windows Registry Editor Version 5.00
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM]
"Winachsf0"="COM10"
"\\Device\\mxuport0"="COM1"
"\\Device\\Serial2"="COM13"
COM10 - My fax modem
COM1 - USB - moxa usb serial converter
COM13 - USB - Profilic serial converter
Regards
Although the answers already given provide a good starting point, I would like to add some working examples for .net 4.5 and also an example of capturing a type of usb device.
In Treb's answer, he used the 'Win32_USBControllerDevice'. This may or may not be the best condition for your query, depending on what you want to accomplish. The device id from the Win32_USBControllerDevice is unique to each device. So if you're looking for a unique id that identifies a single device, then that's exactly what you want. But if you're looking for a certain type of device, you could use 'Win32_PnPEntity' and access the Description property. Here is an example of getting a certain type of device by its description:
using System;
using System.ComponentModel.Composition;
using System.Management;
public class UsbDeviceMonitor
{
private ManagementEventWatcher plugInWatcher;
private ManagementEventWatcher unPlugWatcher;
private const string MyDeviceDescription = #"My Device Description";
~UsbDeviceMonitor()
{
Dispose();
}
public void Dispose()
{
if (plugInWatcher != null)
try
{
plugInWatcher.Dispose();
plugInWatcher = null;
}
catch (Exception) { }
if (unPlugWatcher == null) return;
try
{
unPlugWatcher.Dispose();
unPlugWatcher = null;
}
catch (Exception) { }
}
public void Start()
{
const string plugInSql = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PnPEntity'";
const string unpluggedSql = "SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PnPEntity'";
var scope = new ManagementScope("root\\CIMV2") {Options = {EnablePrivileges = true}};
var pluggedInQuery = new WqlEventQuery(plugInSql);
plugInWatcher = new ManagementEventWatcher(scope, pluggedInQuery);
plugInWatcher.EventArrived += HandlePluggedInEvent;
plugInWatcher.Start();
var unPluggedQuery = new WqlEventQuery(unpluggedSql);
unPlugWatcher = new ManagementEventWatcher(scope, unPluggedQuery);
unPlugWatcher.EventArrived += HandleUnPluggedEvent;
unPlugWatcher.Start();
}
private void HandleUnPluggedEvent(object sender, EventArrivedEventArgs e)
{
var description = GetDeviceDescription(e.NewEvent);
if (description.Equals(MyDeviceDescription))
// Take actions here when the device is unplugged
}
private void HandlePluggedInEvent(object sender, EventArrivedEventArgs e)
{
var description = GetDeviceDescription(e.NewEvent);
if (description.Equals(MyDeviceDescription))
// Take actions here when the device is plugged in
}
private static string GetDeviceDescription(ManagementBaseObject newEvent)
{
var targetInstanceData = newEvent.Properties["TargetInstance"];
var targetInstanceObject = (ManagementBaseObject) targetInstanceData.Value;
if (targetInstanceObject == null) return "";
var description = targetInstanceObject.Properties["Description"].Value.ToString();
return description;
}
}
Some links that might be useful for researching which classes to use in your sql statements:
Win32 Classes - In the example above, the 'Win32_PnPEntity' class was used.
WMI System Classes - In the example above, the __InstanceCreationEvent and __InstanceDeletionEvent classes were used.
You could try to handle ErrorReceived.
private void buttonStart_Click(object sender, EventArgs e)
{
port.ErrorReceived += new System.IO.Ports.SerialErrorReceivedEventHandler(port_ErrorReceived);
}
void port_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
{
// TODO: handle the problem here
}
Additionally, you could check whether the port exists before proceeding. You may want to check it once in a while, maybe just before reading/writing.
string[] ports = System.IO.Ports.SerialPort.GetPortNames();
if (ports.Contains("COM7:"))
{
// TODO: Can continue
}
else
{
// TODO: Cannot, terminate properly
}
You should also place try-catch blocks for all your serial port operations. It should help to prevent unexpected terminations.
You may want to try to run the app in debug mode under your IDE and simulate the error. If an exception is throw, you would be able to identify where the problem becomes most evident. From there, you could probably try to find more specific solutions.
If your try statement isn't catching the exception then let's hope Microsoft will inspect the dumps.
There are some SetupDi APIs (I think ... it's been a while) that permit you to be advised of device arrivals and removals, but it won't help if you already crashed because the removed device was in the middle of your read or write operation.

Categories

Resources