QtBase  v6.3.1
qnetworklistmanagerevents.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
41 
42 #ifdef SUPPORTS_WINRT
43 #include <winrt/base.h>
44 // Workaround for Windows SDK bug.
45 // See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47
46 namespace winrt::impl
47 {
48  template <typename Async>
49  auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout);
50 }
51 
52 #include <winrt/Windows.Networking.Connectivity.h>
53 #endif
54 
56 
57 namespace {
58 template<typename T>
59 bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
60 {
61  if (riid == __uuidof(T)) {
62  *ppvObject = static_cast<T *>(from);
63  from->AddRef();
64  return true;
65  }
66  return false;
67 }
68 }
69 
71 {
72  auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
73  IID_INetworkListManager, &networkListManager);
74  if (FAILED(hr)) {
75  qCWarning(lcNetInfoNLM) << "Could not get a NetworkListManager instance:"
77  return;
78  }
79 
80  ComPtr<IConnectionPointContainer> connectionPointContainer;
81  hr = networkListManager.As(&connectionPointContainer);
82  if (SUCCEEDED(hr)) {
83  hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents,
84  &connectionPoint);
85  }
86  if (FAILED(hr)) {
87  qCWarning(lcNetInfoNLM) << "Failed to get connection point for network list manager events:"
89  }
90 }
91 
93 {
94  Q_ASSERT(ref == 0);
95 }
96 
97 HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject)
98 {
99  if (!ppvObject)
100  return E_INVALIDARG;
101 
102  return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
103  || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject)
104  ? S_OK
105  : E_NOINTERFACE;
106 }
107 
108 HRESULT STDMETHODCALLTYPE
109 QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
110 {
111  // This function is run on a different thread than 'monitor' is created on, so we need to run
112  // it on that thread
113  emit connectivityChanged(newConnectivity);
114  return S_OK;
115 }
116 
118 {
119  if (!connectionPoint) {
120  qCWarning(lcNetInfoNLM, "Initialization failed, can't start!");
121  return false;
122  }
123  auto hr = connectionPoint->Advise(this, &cookie);
124  if (FAILED(hr)) {
125  qCWarning(lcNetInfoNLM) << "Failed to subscribe to network connectivity events:"
126  << errorStringFromHResult(hr);
127  return false;
128  }
129 
130  // Update connectivity since it might have changed since this class was constructed
131  NLM_CONNECTIVITY connectivity;
132  hr = networkListManager->GetConnectivity(&connectivity);
133  if (FAILED(hr))
134  qCWarning(lcNetInfoNLM) << "Could not get connectivity:" << errorStringFromHResult(hr);
135  else
136  emit connectivityChanged(connectivity);
137 
138 #ifdef SUPPORTS_WINRT
139  using namespace winrt::Windows::Networking::Connectivity;
140  // Register for changes in the network and store a token to unregister later:
141  token = NetworkInformation::NetworkStatusChanged(
142  [this](const winrt::Windows::Foundation::IInspectable sender) {
143  Q_UNUSED(sender);
144  emitWinRTUpdates();
145  });
146  // Emit initial state
147  emitWinRTUpdates();
148 #endif
149 
150  return true;
151 }
152 
154 {
155  Q_ASSERT(connectionPoint);
156  auto hr = connectionPoint->Unadvise(cookie);
157  if (FAILED(hr)) {
158  qCWarning(lcNetInfoNLM) << "Failed to unsubscribe from network connectivity events:"
159  << errorStringFromHResult(hr);
160  return false;
161  }
162  cookie = 0;
163 
164 #ifdef SUPPORTS_WINRT
165  using namespace winrt::Windows::Networking::Connectivity;
166  // Pass the token we stored earlier to unregister:
167  NetworkInformation::NetworkStatusChanged(token);
168  token = {};
169 #endif
170  return true;
171 }
172 
174 {
175  if (!networkListManager)
176  return false;
177  ComPtr<IEnumNetworks> networks;
178  HRESULT hr =
179  networkListManager->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, networks.GetAddressOf());
180  if (FAILED(hr) || networks == nullptr)
181  return false;
182 
183  // @note: This checks all connected networks, but that might not be necessary
184  ComPtr<INetwork> network;
185  hr = networks->Next(1, network.GetAddressOf(), nullptr);
186  while (SUCCEEDED(hr) && network != nullptr) {
187  ComPtr<IPropertyBag> propertyBag;
188  hr = network.As(&propertyBag);
189  if (SUCCEEDED(hr) && propertyBag != nullptr) {
190  VARIANT variant;
191  VariantInit(&variant);
192  const auto scopedVariantClear = qScopeGuard([&variant]() { VariantClear(&variant); });
193 
194  const wchar_t *versions[] = { NA_InternetConnectivityV6, NA_InternetConnectivityV4 };
195  for (const auto version : versions) {
196  hr = propertyBag->Read(version, &variant, nullptr);
197  if (SUCCEEDED(hr)
198  && (V_UINT(&variant) & NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
199  == NLM_INTERNET_CONNECTIVITY_WEBHIJACK) {
200  return true;
201  }
202  }
203  }
204 
205  hr = networks->Next(1, network.GetAddressOf(), nullptr);
206  }
207 
208  return false;
209 }
210 
211 #ifdef SUPPORTS_WINRT
212 namespace {
213 using namespace winrt::Windows::Networking::Connectivity;
214 // NB: this isn't part of "network list manager", but sadly NLM doesn't have an
215 // equivalent API (at least not that I've found...)!
216 [[nodiscard]]
217 QNetworkInformation::TransportMedium getTransportMedium(const ConnectionProfile &profile)
218 {
219  if (profile.IsWwanConnectionProfile())
221  if (profile.IsWlanConnectionProfile())
223 
224  NetworkAdapter adapter = profile.NetworkAdapter();
225  if (adapter == nullptr)
227 
228  // Note: Bluetooth is given an iana iftype of 6, which is the same as Ethernet.
229  // In Windows itself there is clearly a distinction between a Bluetooth PAN
230  // and an Ethernet LAN, though it is not clear how they make this distinction.
231  auto fromIanaId = [](quint32 ianaId) -> QNetworkInformation::TransportMedium {
232  // https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib
233  switch (ianaId) {
234  case 6:
236  case 71: // Should be handled before entering this lambda
238  }
240  };
241 
242  return fromIanaId(adapter.IanaInterfaceType());
243 }
244 
245 [[nodiscard]] bool getMetered(const ConnectionProfile &profile)
246 {
247  ConnectionCost cost = profile.GetConnectionCost();
248  NetworkCostType type = cost.NetworkCostType();
249  return type == NetworkCostType::Fixed || type == NetworkCostType::Variable;
250 }
251 } // unnamed namespace
252 
253 void QNetworkListManagerEvents::emitWinRTUpdates()
254 {
255  using namespace winrt::Windows::Networking::Connectivity;
256  ConnectionProfile profile = NetworkInformation::GetInternetConnectionProfile();
257  if (profile == nullptr)
258  return;
259  emit transportMediumChanged(getTransportMedium(profile));
260  emit isMeteredChanged(getMetered(profile));
261 }
262 #endif
263 
void transportMediumChanged(QNetworkInformation::TransportMedium)
void connectivityChanged(NLM_CONNECTIVITY)
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override
HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override
int const char * version
Definition: zlib.h:814
Token token
Definition: keywords.cpp:453
bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
unsigned int quint32
Definition: qglobal.h:288
#define qCWarning(category,...)
QT_BEGIN_NAMESPACE QString errorStringFromHResult(HRESULT hr)
GLenum type
Definition: qopengl.h:270
GLbitfield GLuint64 timeout
[4]
GLint ref
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition: qscopeguard.h:93
#define emit
Definition: qtmetamacros.h:85
IUIViewSettingsInterop __RPC__in REFIID riid
long HRESULT
Q_UNUSED(salary)
[21]
QObject::connect nullptr
QVariant variant
[1]
Definition: main.cpp:38