2 namespace System.Net.NetworkInformation {
5 using System.Net.NetworkInformation;
6 using System.Net.Sockets;
7 using System.Collections;
8 using System.Collections.Specialized;
9 using System.ComponentModel;
10 using System.Threading;
11 using System.Runtime.InteropServices;
14 internal enum StartIPOptions {Both = 3, None = 0, StartIPv4 = 1, StartIPv6 = 2}
16 public class NetworkAvailabilityEventArgs:EventArgs{
19 internal NetworkAvailabilityEventArgs(bool isAvailable){
20 this.isAvailable = isAvailable;
23 public bool IsAvailable{
30 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors")]
31 public class NetworkChange{
32 #region designer support for System.Windows.dll
33 //introduced for supporting design-time loading of System.Windows.dll
34 [Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.", true)]
35 [EditorBrowsable(EditorBrowsableState.Never)]
36 public NetworkChange() { }
38 //introduced for supporting design-time loading of System.Windows.dll
39 [Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.", true)]
40 [EditorBrowsable(EditorBrowsableState.Never)]
41 public static void RegisterNetworkChange(NetworkChange nc) { }
44 static public event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged{
46 AvailabilityChangeListener.Start(value);
49 AvailabilityChangeListener.Stop(value);
55 static public event NetworkAddressChangedEventHandler NetworkAddressChanged{
57 AddressChangeListener.Start(value);
60 AddressChangeListener.Stop(value);
67 static internal bool CanListenForNetworkChanges {
73 internal static class AvailabilityChangeListener{
75 static object syncObject = new object();
76 static private ListDictionary s_availabilityCallerArray = new ListDictionary();
77 static NetworkAddressChangedEventHandler addressChange = ChangedAddress;
78 static volatile bool isAvailable = false;
79 private static ContextCallback s_RunHandlerCallback = new ContextCallback(RunHandlerCallback);
82 private static void RunHandlerCallback(object state)
84 ((NetworkAvailabilityChangedEventHandler) state)(null, new NetworkAvailabilityEventArgs(isAvailable));
88 private static void ChangedAddress(object sender, EventArgs eventArgs) {
91 bool isAvailableNow = SystemNetworkInterface.InternalGetIsNetworkAvailable();
93 if (isAvailableNow != isAvailable) {
94 isAvailable = isAvailableNow;
96 DictionaryEntry[] callerArray = new DictionaryEntry[s_availabilityCallerArray.Count];
97 s_availabilityCallerArray.CopyTo(callerArray, 0);
99 for (int i = 0; i < callerArray.Length; i++)
101 NetworkAvailabilityChangedEventHandler handler = (NetworkAvailabilityChangedEventHandler) callerArray[i].Key;
102 ExecutionContext context = (ExecutionContext) callerArray[i].Value;
105 handler(null, new NetworkAvailabilityEventArgs(isAvailable));
109 ExecutionContext.Run(context.CreateCopy(), s_RunHandlerCallback, handler);
118 internal static void Start(NetworkAvailabilityChangedEventHandler caller){
121 if (s_availabilityCallerArray.Count == 0) {
122 isAvailable = NetworkInterface.GetIsNetworkAvailable();
123 AddressChangeListener.UnsafeStart(addressChange);
126 if ((caller != null) && (!s_availabilityCallerArray.Contains(caller))) {
127 s_availabilityCallerArray.Add(caller, ExecutionContext.Capture());
133 internal static void Stop(NetworkAvailabilityChangedEventHandler caller)
136 s_availabilityCallerArray.Remove(caller);
137 if(s_availabilityCallerArray.Count == 0){
138 AddressChangeListener.Stop(addressChange);
145 //helper class for detecting address change events.
146 internal unsafe static class AddressChangeListener{
148 static private ListDictionary s_callerArray = new ListDictionary();
149 static private ContextCallback s_runHandlerCallback = new ContextCallback(RunHandlerCallback);
150 static private RegisteredWaitHandle s_registeredWait;
152 //need to keep the reference so it isn't GC'd before the native call executes
153 static private bool s_isListening = false;
154 static private bool s_isPending = false;
155 static private SafeCloseSocketAndEvent s_ipv4Socket = null;
156 static private SafeCloseSocketAndEvent s_ipv6Socket = null;
157 static private WaitHandle s_ipv4WaitHandle = null;
158 static private WaitHandle s_ipv6WaitHandle = null;
160 //callback fired when an address change occurs
161 private static void AddressChangedCallback(object stateObject, bool signaled) {
162 lock (s_callerArray) {
164 //the listener was cancelled, which would only happen if we aren't listening
168 if (!s_isListening) {
172 s_isListening = false;
174 // Need to copy the array so the callback can call start and stop
175 DictionaryEntry[] callerArray = new DictionaryEntry[s_callerArray.Count];
176 s_callerArray.CopyTo(callerArray, 0);
180 //wait for the next address change
181 StartHelper(null, false, (StartIPOptions)stateObject);
183 catch (NetworkInformationException nie)
185 if (Logging.On) Logging.Exception(Logging.Web, "AddressChangeListener", "AddressChangedCallback", nie);
188 for (int i = 0; i < callerArray.Length; i++)
190 NetworkAddressChangedEventHandler handler = (NetworkAddressChangedEventHandler) callerArray[i].Key;
191 ExecutionContext context = (ExecutionContext) callerArray[i].Value;
194 handler(null, EventArgs.Empty);
198 ExecutionContext.Run(context.CreateCopy(), s_runHandlerCallback, handler);
204 private static void RunHandlerCallback(object state)
206 ((NetworkAddressChangedEventHandler) state)(null, EventArgs.Empty);
211 internal static void Start(NetworkAddressChangedEventHandler caller)
213 StartHelper(caller, true, StartIPOptions.Both);
216 internal static void UnsafeStart(NetworkAddressChangedEventHandler caller)
218 StartHelper(caller, false, StartIPOptions.Both);
221 private static void StartHelper(NetworkAddressChangedEventHandler caller, bool captureContext, StartIPOptions startIPOptions)
223 lock (s_callerArray) {
224 // setup changedEvent and native overlapped struct.
225 if(s_ipv4Socket == null){
226 Socket.InitializeSockets();
230 if(Socket.OSSupportsIPv4){
232 s_ipv4Socket = SafeCloseSocketAndEvent.CreateWSASocketWithEvent(AddressFamily.InterNetwork, SocketType.Dgram, (ProtocolType)0, true, false);
233 UnsafeNclNativeMethods.OSSOCK.ioctlsocket(s_ipv4Socket, IoctlSocketConstants.FIONBIO,ref blocking);
234 s_ipv4WaitHandle = s_ipv4Socket.GetEventHandle();
237 if(Socket.OSSupportsIPv6){
239 s_ipv6Socket = SafeCloseSocketAndEvent.CreateWSASocketWithEvent(AddressFamily.InterNetworkV6, SocketType.Dgram, (ProtocolType)0, true, false);
240 UnsafeNclNativeMethods.OSSOCK.ioctlsocket(s_ipv6Socket,IoctlSocketConstants.FIONBIO,ref blocking);
241 s_ipv6WaitHandle = s_ipv6Socket.GetEventHandle();
245 if ((caller != null) && (!s_callerArray.Contains(caller))) {
246 s_callerArray.Add(caller, captureContext ? ExecutionContext.Capture() : null);
249 //if s_listener is not null, it means we are already actively listening
250 if (s_isListening || s_callerArray.Count == 0) {
257 SocketError errorCode;
259 if(Socket.OSSupportsIPv4 && (startIPOptions & StartIPOptions.StartIPv4) !=0){
260 s_registeredWait = ThreadPool.UnsafeRegisterWaitForSingleObject(
262 new WaitOrTimerCallback(AddressChangedCallback),
263 StartIPOptions.StartIPv4,
267 errorCode = (SocketError) UnsafeNclNativeMethods.OSSOCK.WSAIoctl_Blocking(
268 s_ipv4Socket.DangerousGetHandle(),
269 (int) IOControlCode.AddressListChange,
272 SafeNativeOverlapped.Zero, IntPtr.Zero);
274 if (errorCode != SocketError.Success) {
275 NetworkInformationException exception = new NetworkInformationException();
276 if (exception.ErrorCode != (uint)SocketError.WouldBlock) {
281 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(s_ipv4Socket, s_ipv4Socket.GetEventHandle().SafeWaitHandle, AsyncEventBits.FdAddressListChange);
282 if (errorCode != SocketError.Success) {
283 throw new NetworkInformationException();
287 if(Socket.OSSupportsIPv6 && (startIPOptions & StartIPOptions.StartIPv6) !=0){
288 s_registeredWait = ThreadPool.UnsafeRegisterWaitForSingleObject(
290 new WaitOrTimerCallback(AddressChangedCallback),
291 StartIPOptions.StartIPv6,
295 errorCode = (SocketError) UnsafeNclNativeMethods.OSSOCK.WSAIoctl_Blocking(
296 s_ipv6Socket.DangerousGetHandle(),
297 (int) IOControlCode.AddressListChange,
300 SafeNativeOverlapped.Zero, IntPtr.Zero);
302 if (errorCode != SocketError.Success) {
303 NetworkInformationException exception = new NetworkInformationException();
304 if (exception.ErrorCode != (uint)SocketError.WouldBlock) {
309 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(s_ipv6Socket, s_ipv6Socket.GetEventHandle().SafeWaitHandle, AsyncEventBits.FdAddressListChange);
310 if (errorCode != SocketError.Success) {
311 throw new NetworkInformationException();
316 s_isListening = true;
324 internal static void Stop(object caller)
327 s_callerArray.Remove(caller);
328 if (s_callerArray.Count == 0 && s_isListening) {
329 s_isListening = false;
332 } //ends ignoreaddresschanges
336 public delegate void NetworkAddressChangedEventHandler(object sender, EventArgs e);
337 public delegate void NetworkAvailabilityChangedEventHandler(object sender, NetworkAvailabilityEventArgs e);