From 8c04a9d4da007870ed0535047153b13d92ab5d9f Mon Sep 17 00:00:00 2001 From: Mike Corsaro Date: Tue, 23 Jan 2024 13:07:37 -0800 Subject: [PATCH] [WinUI] Add workaround for Connectivity check on Win10 (#19261) * Implemented a Win10 work-around for connectivity thread issue Reimplement `ConnectivityImplementation.ConnectionProfiles` to use native .net core APIs * Remove prop bag ID * Replace network availability changed event with native .net API * Remove explicit file include * Fix file naming * Implemented a Win10 work-around for connectivity thread issue Reimplement `ConnectivityImplementation.ConnectionProfiles` to use native .net core APIs * Remove prop bag ID * Replace network availability changed event with native .net API * Remove explicit file include * Fix file naming * * Revert change to remove `Windows.Networking.Connectivity.NetworkInformation.NetworkStatusChanged` * Update Connectivity.uwp.cs --------- Co-authored-by: Mike Corsaro Co-authored-by: Matthew Leibowitz --- .../Connectivity/Connectivity.Native.uwp.cs | 173 ++++++++++++++++++ .../src/Connectivity/Connectivity.uwp.cs | 110 ++++++----- 2 files changed, 240 insertions(+), 43 deletions(-) create mode 100644 src/Essentials/src/Connectivity/Connectivity.Native.uwp.cs diff --git a/src/Essentials/src/Connectivity/Connectivity.Native.uwp.cs b/src/Essentials/src/Connectivity/Connectivity.Native.uwp.cs new file mode 100644 index 000000000000..033aa62301d4 --- /dev/null +++ b/src/Essentials/src/Connectivity/Connectivity.Native.uwp.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Microsoft.Maui.Networking +{ +#if WINDOWS + internal class ConnectivityNativeHelper + { + internal static string CNetworkListManagerCoClassGuid = "DCB00C01-570F-4A9B-8D69-199FDBA5723B"; + + internal enum NLM_ENUM_NETWORK : int + { + NLM_ENUM_NETWORK_CONNECTED = 0x01, + NLM_ENUM_NETWORK_DISCONNECTED = 0x02, + NLM_ENUM_NETWORK_ALL = 0x03 + } + + internal enum NLM_NETWORK_CATEGORY + { + NLM_NETWORK_CATEGORY_PUBLIC = 0x00, + NLM_NETWORK_CATEGORY_PRIVATE = 0x01, + NLM_NETWORK_CATEGORY_DOMAIN_AUTHENTICATED = 0x02 + } + + [Flags] + internal enum NLM_CONNECTIVITY + { + NLM_CONNECTIVITY_DISCONNECTED = 0, + NLM_CONNECTIVITY_IPV4_NOTRAFFIC = 0x1, + NLM_CONNECTIVITY_IPV6_NOTRAFFIC = 0x2, + NLM_CONNECTIVITY_IPV4_SUBNET = 0x10, + NLM_CONNECTIVITY_IPV4_LOCALNETWORK = 0x20, + NLM_CONNECTIVITY_IPV4_INTERNET = 0x40, + NLM_CONNECTIVITY_IPV6_SUBNET = 0x100, + NLM_CONNECTIVITY_IPV6_LOCALNETWORK = 0x200, + NLM_CONNECTIVITY_IPV6_INTERNET = 0x400 + } + + [ComImport] + [Guid("DCB00000-570F-4A9B-8D69-199FDBA5723B")] + [InterfaceType(ComInterfaceType.InterfaceIsDual)] + internal interface INetworkListManager + { + IEnumNetworks GetNetworks(NLM_ENUM_NETWORK flags); + + INetwork GetNetwork(Guid guid); + + IEnumNetworkConnections GetNetworkConnections(); + + void GetNetworkConnection(); + + bool IsConnectedToInternet { get; } + + bool IsConnected { get; } + + void GetConnectivity(); + } + + [ComImport] + [Guid("DCB00003-570F-4A9B-8D69-199FDBA5723B")] + [InterfaceType(ComInterfaceType.InterfaceIsDual)] + internal interface IEnumNetworks : IEnumerable + { + } + + [ComImport] + [Guid("DCB00006-570F-4A9B-8D69-199FDBA5723B")] + [InterfaceType(ComInterfaceType.InterfaceIsDual)] + internal interface IEnumNetworkConnections : IEnumerable + { + } + + [ComImport] + [Guid("DCB00002-570F-4A9B-8D69-199FDBA5723B")] + [InterfaceType(ComInterfaceType.InterfaceIsDual)] + internal interface INetwork + { + /// + /// Get the name of the network. + /// + /// The network name. + string GetName(); + + /// + /// Rename this network. This change takes effect immediately. + /// + /// + void SetName(string szNetworkNewName); + + /// + /// Get the network description. + /// + /// Network description. + string GetDescription(); + + /// + /// Set the network description. This change takes effect immediately. + /// + /// The network description. + /// /// + void SetDescription(string szDescription); + + /// + /// Get the network ID. + /// + /// The network Id. + Guid GetNetworkId(); + + /// + /// Returns the domain type of a network. + /// + /// Domain Type + Int32 GetDomainType(); + + /// + /// Returns an enumeration of all network connections for a network + /// + /// Network Enumeration + IEnumNetworkConnections GetNetworkConnections(); + + /// + /// Returns the local date and time when the network was created and connected. + /// + /// + /// + /// + /// + void GetTimeCreatedAndConnected( + out uint pdwLowDateTimeCreated, + out uint pdwHighDateTimeCreated, + out uint pdwLowDateTimeConnected, + out uint pdwHighDateTimeConnected); + + /// + /// Specifies if the network has internet connectivity. + /// + /// + bool IsConnectedToInternet(); + + /// + /// Specifies if the network has any network connectivity. + /// + /// + bool IsConnected(); + + /// + /// Returns the connectivity state of the network. + /// + /// + NLM_CONNECTIVITY GetConnectivity(); + + /// + /// Returns the category of a network. + /// + /// + NLM_NETWORK_CATEGORY GetCategory(); + + void SetCategory(NLM_NETWORK_CATEGORY NewCategory); + } + + internal static INetworkListManager GetNetworkListManager() + { + Type netProfMgrClass = Type.GetTypeFromCLSID(new Guid(CNetworkListManagerCoClassGuid)); + +#pragma warning disable IL2072 + return (INetworkListManager)Activator.CreateInstance(netProfMgrClass); +#pragma warning restore + } + } +#endif +} diff --git a/src/Essentials/src/Connectivity/Connectivity.uwp.cs b/src/Essentials/src/Connectivity/Connectivity.uwp.cs index 2ec285ab3e08..57f85afd168f 100644 --- a/src/Essentials/src/Connectivity/Connectivity.uwp.cs +++ b/src/Essentials/src/Connectivity/Connectivity.uwp.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; +using System.Net.NetworkInformation; using Windows.Networking.Connectivity; namespace Microsoft.Maui.Networking @@ -21,18 +20,54 @@ public NetworkAccess NetworkAccess { get { - var profile = NetworkInformation.GetInternetConnectionProfile(); - if (profile == null) - return NetworkAccess.Unknown; + if (OperatingSystem.IsWindowsVersionAtLeast(11)) + { + var profile = NetworkInformation.GetInternetConnectionProfile(); + if (profile == null) + return NetworkAccess.Unknown; - var level = profile.GetNetworkConnectivityLevel(); - return level switch + var level = profile.GetNetworkConnectivityLevel(); + return level switch + { + NetworkConnectivityLevel.LocalAccess => NetworkAccess.Local, + NetworkConnectivityLevel.InternetAccess => NetworkAccess.Internet, + NetworkConnectivityLevel.ConstrainedInternetAccess => NetworkAccess.ConstrainedInternet, + _ => NetworkAccess.None, + }; + } + else { - NetworkConnectivityLevel.LocalAccess => NetworkAccess.Local, - NetworkConnectivityLevel.InternetAccess => NetworkAccess.Internet, - NetworkConnectivityLevel.ConstrainedInternetAccess => NetworkAccess.ConstrainedInternet, - _ => NetworkAccess.None, - }; + // Windows 10 workaround for https://github.com/microsoft/WindowsAppSDK/issues/2965 + var networkList = ConnectivityNativeHelper.GetNetworkListManager(); + var enumNetworks = networkList.GetNetworks(ConnectivityNativeHelper.NLM_ENUM_NETWORK.NLM_ENUM_NETWORK_CONNECTED); + var connectivity = ConnectivityNativeHelper.NLM_CONNECTIVITY.NLM_CONNECTIVITY_DISCONNECTED; + + foreach (ConnectivityNativeHelper.INetwork networkInterface in enumNetworks) + { + if (networkInterface.IsConnected()) + { + connectivity = networkInterface.GetConnectivity(); + break; + } + } + + if ((connectivity & (ConnectivityNativeHelper.NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV4_INTERNET | ConnectivityNativeHelper.NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV6_INTERNET)) != 0) + { + return NetworkAccess.Internet; + } + else if ((connectivity & (ConnectivityNativeHelper.NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV4_LOCALNETWORK | ConnectivityNativeHelper.NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV6_LOCALNETWORK)) != 0) + { + return NetworkAccess.Local; + } + else if ((connectivity & (ConnectivityNativeHelper.NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV4_NOTRAFFIC | ConnectivityNativeHelper.NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV6_NOTRAFFIC)) != 0) + { + return NetworkAccess.Local; + } + else + { + return NetworkAccess.None; + } + } } } @@ -40,43 +75,32 @@ public IEnumerable ConnectionProfiles { get { - var networkInterfaceList = NetworkInformation.GetConnectionProfiles(); - foreach (var interfaceInfo in networkInterfaceList.Where(nii => nii.GetNetworkConnectivityLevel() != NetworkConnectivityLevel.None)) + var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); + foreach (var nic in networkInterfaces) { - var type = ConnectionProfile.Unknown; - - try + if (nic.OperationalStatus is not OperationalStatus.Up || + nic.NetworkInterfaceType is NetworkInterfaceType.Loopback || + nic.NetworkInterfaceType is NetworkInterfaceType.Tunnel) { - var adapter = interfaceInfo.NetworkAdapter; - if (adapter == null) - continue; - - // http://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib - switch (adapter.IanaInterfaceType) - { - case 6: - type = ConnectionProfile.Ethernet; - break; - case 71: - type = ConnectionProfile.WiFi; - break; - case 243: - case 244: - type = ConnectionProfile.Cellular; - break; - - // xbox wireless, can skip - case 281: - continue; - } + continue; } - catch (Exception ex) + + var interfaceType = ConnectionProfile.Unknown; + switch (nic.NetworkInterfaceType) { - // TODO Add Logging? - Debug.WriteLine($"Unable to get Network Adapter, returning Unknown: {ex.Message}"); + case NetworkInterfaceType.Ethernet: + interfaceType = ConnectionProfile.Ethernet; + break; + case NetworkInterfaceType.Wireless80211: + interfaceType = ConnectionProfile.WiFi; + break; + case NetworkInterfaceType.Wwanpp: + case NetworkInterfaceType.Wwanpp2: + interfaceType = ConnectionProfile.Cellular; + break; } - yield return type; + yield return interfaceType; } } }