summaryrefslogtreecommitdiffstats
path: root/src/network/kernel/qnetconmonitor_win.cpp
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2019-04-12 15:46:35 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2019-07-08 12:12:45 +0200
commitdc431b7e8548e97d19770285a6160ecfa8874815 (patch)
treebf494c7852e2efe997b0f9f452968ba1217575d7 /src/network/kernel/qnetconmonitor_win.cpp
parent682e4795fe7a5ae64268212b23ff96706a0822d9 (diff)
Introduce QNetwork(Connection|Status)Monitor to Windows
Currently not available for mingw because it's missing all the interfaces. But should start working once it has them. Change-Id: I231e8b69e008c5300a83087fe9cd071acd0687cb Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/kernel/qnetconmonitor_win.cpp')
-rw-r--r--src/network/kernel/qnetconmonitor_win.cpp710
1 files changed, 710 insertions, 0 deletions
diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp
new file mode 100644
index 0000000000..2eab0a062f
--- /dev/null
+++ b/src/network/kernel/qnetconmonitor_win.cpp
@@ -0,0 +1,710 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetconmonitor_p.h"
+
+#include "private/qobject_p.h"
+
+#include <QtCore/quuid.h>
+#include <QtCore/qmetaobject.h>
+
+#include <QtNetwork/qnetworkinterface.h>
+
+#include <objbase.h>
+#include <netlistmgr.h>
+#include <wrl/client.h>
+#include <wrl/wrappers/corewrappers.h>
+#include <comdef.h>
+#include <iphlpapi.h>
+
+#include <algorithm>
+
+using namespace Microsoft::WRL;
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor");
+
+namespace {
+QString errorStringFromHResult(HRESULT hr)
+{
+ _com_error error(hr);
+ return QString::fromWCharArray(error.ErrorMessage());
+}
+
+template<typename T>
+bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
+{
+ if (riid == __uuidof(T)) {
+ *ppvObject = static_cast<T *>(from);
+ from->AddRef();
+ return true;
+ }
+ return false;
+}
+
+QNetworkInterface getInterfaceFromHostAddress(const QHostAddress &local)
+{
+ QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
+ auto it = std::find_if(
+ interfaces.cbegin(), interfaces.cend(), [&local](const QNetworkInterface &iface) {
+ const auto &entries = iface.addressEntries();
+ return std::any_of(entries.cbegin(), entries.cend(),
+ [&local](const QNetworkAddressEntry &entry) {
+ return entry.ip().isEqual(local,
+ QHostAddress::TolerantConversion);
+ });
+ });
+ if (it == interfaces.cend()) {
+ qCWarning(lcNetMon, "Could not find the interface for the local address.");
+ return {};
+ }
+ return *it;
+}
+} // anonymous namespace
+
+class QNetworkConnectionEvents final : public INetworkConnectionEvents
+{
+public:
+ QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor);
+ ~QNetworkConnectionEvents();
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
+
+ ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
+ ULONG STDMETHODCALLTYPE Release() override
+ {
+ if (--ref == 0) {
+ delete this;
+ return 0;
+ }
+ return ref;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ NetworkConnectionConnectivityChanged(GUID connectionId, NLM_CONNECTIVITY connectivity) override;
+ HRESULT STDMETHODCALLTYPE NetworkConnectionPropertyChanged(
+ GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags) override;
+
+ Q_REQUIRED_RESULT
+ bool setTarget(const QNetworkInterface &iface);
+ Q_REQUIRED_RESULT
+ bool startMonitoring();
+ Q_REQUIRED_RESULT
+ bool stopMonitoring();
+
+private:
+ ComPtr<INetworkConnection> getNetworkConnectionFromAdapterGuid(QUuid guid);
+
+ QUuid currentConnectionId{};
+
+ ComPtr<INetworkListManager> networkListManager;
+ ComPtr<IConnectionPoint> connectionPoint;
+
+ QNetworkConnectionMonitorPrivate *monitor = nullptr;
+
+ QAtomicInteger<ULONG> ref = 1; // start at 1 for our own initial reference
+ DWORD cookie = 0;
+};
+
+class QNetworkConnectionMonitorPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QNetworkConnectionMonitor);
+
+public:
+ QNetworkConnectionMonitorPrivate();
+ ~QNetworkConnectionMonitorPrivate();
+
+ Q_REQUIRED_RESULT
+ bool setTargets(const QHostAddress &local, const QHostAddress &remote);
+ Q_REQUIRED_RESULT
+ bool startMonitoring();
+ void stopMonitoring();
+
+ void setConnectivity(NLM_CONNECTIVITY newConnectivity);
+
+private:
+ ComPtr<QNetworkConnectionEvents> connectionEvents;
+ // We can assume we have access to internet/subnet when this class is created because
+ // connection has already been established to the peer:
+ NLM_CONNECTIVITY connectivity =
+ NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET
+ | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET);
+
+ bool sameSubnet = false;
+ bool monitoring = false;
+ bool comInitFailed = false;
+ bool remoteIsIPv6 = false;
+};
+
+QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor)
+ : monitor(monitor)
+{
+ auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
+ IID_INetworkListManager, &networkListManager);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:"
+ << errorStringFromHResult(hr);
+ return;
+ }
+
+ ComPtr<IConnectionPointContainer> connectionPointContainer;
+ hr = networkListManager.As(&connectionPointContainer);
+ if (SUCCEEDED(hr)) {
+ hr = connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents,
+ &connectionPoint);
+ }
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to get connection point for network events:"
+ << errorStringFromHResult(hr);
+ }
+}
+
+QNetworkConnectionEvents::~QNetworkConnectionEvents()
+{
+ Q_ASSERT(ref == 0);
+}
+
+ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAdapterGuid(QUuid guid)
+{
+ ComPtr<IEnumNetworkConnections> connections;
+ auto hr = networkListManager->GetNetworkConnections(connections.GetAddressOf());
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to enumerate network connections:"
+ << errorStringFromHResult(hr);
+ return nullptr;
+ }
+ ComPtr<INetworkConnection> connection = nullptr;
+ do {
+ hr = connections->Next(1, connection.GetAddressOf(), nullptr);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to get next network connection in enumeration:"
+ << errorStringFromHResult(hr);
+ break;
+ }
+ if (connection) {
+ GUID adapterId;
+ hr = connection->GetAdapterId(&adapterId);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to get adapter ID from network connection:"
+ << errorStringFromHResult(hr);
+ continue;
+ }
+ if (guid == adapterId)
+ return connection;
+ }
+ } while (connection);
+ return nullptr;
+}
+
+HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::QueryInterface(REFIID riid, void **ppvObject)
+{
+ if (!ppvObject)
+ return E_INVALIDARG;
+
+ return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
+ || QueryInterfaceImpl<INetworkConnectionEvents>(this, riid, ppvObject)
+ ? S_OK
+ : E_NOINTERFACE;
+}
+
+HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionConnectivityChanged(
+ GUID connectionId, NLM_CONNECTIVITY newConnectivity)
+{
+ // This function is run on a different thread than 'monitor' is created on, so we need to run
+ // it on that thread
+ QMetaObject::invokeMethod(monitor->q_ptr,
+ [this, connectionId, newConnectivity, monitor = this->monitor]() {
+ if (connectionId == currentConnectionId)
+ monitor->setConnectivity(newConnectivity);
+ },
+ Qt::QueuedConnection);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionPropertyChanged(
+ GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags)
+{
+ Q_UNUSED(connectionId);
+ Q_UNUSED(flags);
+ return E_NOTIMPL;
+}
+
+bool QNetworkConnectionEvents::setTarget(const QNetworkInterface &iface)
+{
+ // Unset this in case it's already set to something
+ currentConnectionId = QUuid{};
+
+ NET_LUID luid;
+ if (ConvertInterfaceIndexToLuid(iface.index(), &luid) != NO_ERROR) {
+ qCWarning(lcNetMon, "Could not get the LUID for the interface.");
+ return false;
+ }
+ GUID guid;
+ if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) {
+ qCWarning(lcNetMon, "Could not get the GUID for the interface.");
+ return false;
+ }
+ ComPtr<INetworkConnection> connection = getNetworkConnectionFromAdapterGuid(guid);
+ if (!connection) {
+ qCWarning(lcNetMon, "Could not get the INetworkConnection instance for the adapter GUID.");
+ return false;
+ }
+ auto hr = connection->GetConnectionId(&guid);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to get the connection's GUID:" << errorStringFromHResult(hr);
+ return false;
+ }
+ currentConnectionId = guid;
+
+ return true;
+}
+
+bool QNetworkConnectionEvents::startMonitoring()
+{
+ if (currentConnectionId.isNull()) {
+ qCWarning(lcNetMon, "Can not start monitoring, set targets first");
+ return false;
+ }
+ if (!connectionPoint) {
+ qCWarning(lcNetMon,
+ "We don't have the connection point, cannot start listening to events!");
+ return false;
+ }
+
+ auto hr = connectionPoint->Advise(this, &cookie);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:"
+ << errorStringFromHResult(hr);
+ return false;
+ }
+ return true;
+}
+
+bool QNetworkConnectionEvents::stopMonitoring()
+{
+ auto hr = connectionPoint->Unadvise(cookie);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to unsubscribe from network connection events:"
+ << errorStringFromHResult(hr);
+ return false;
+ }
+ cookie = 0;
+ currentConnectionId = QUuid{};
+ return true;
+}
+
+QNetworkConnectionMonitorPrivate::QNetworkConnectionMonitorPrivate()
+{
+ auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
+ comInitFailed = true;
+ return;
+ }
+
+ connectionEvents = new QNetworkConnectionEvents(this);
+}
+
+QNetworkConnectionMonitorPrivate::~QNetworkConnectionMonitorPrivate()
+{
+ if (comInitFailed)
+ return;
+ connectionEvents.Reset();
+ CoUninitialize();
+}
+
+bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local,
+ const QHostAddress &remote)
+{
+ if (comInitFailed)
+ return false;
+
+ QNetworkInterface iface = getInterfaceFromHostAddress(local);
+ if (!iface.isValid())
+ return false;
+ const auto &addressEntries = iface.addressEntries();
+ auto it = std::find_if(
+ addressEntries.cbegin(), addressEntries.cend(),
+ [&local](const QNetworkAddressEntry &entry) { return entry.ip() == local; });
+ if (Q_UNLIKELY(it == addressEntries.cend())) {
+ qCWarning(lcNetMon, "The address entry we were working with disappeared");
+ return false;
+ }
+ sameSubnet = remote.isInSubnet(local, it->prefixLength());
+ remoteIsIPv6 = remote.protocol() == QAbstractSocket::IPv6Protocol;
+
+ return connectionEvents->setTarget(iface);
+}
+
+void QNetworkConnectionMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity)
+{
+ Q_Q(QNetworkConnectionMonitor);
+ const bool reachable = q->isReachable();
+ connectivity = newConnectivity;
+ const bool newReachable = q->isReachable();
+ if (reachable != newReachable)
+ emit q->reachabilityChanged(newReachable);
+}
+
+bool QNetworkConnectionMonitorPrivate::startMonitoring()
+{
+ Q_ASSERT(connectionEvents);
+ Q_ASSERT(!monitoring);
+ if (connectionEvents->startMonitoring())
+ monitoring = true;
+ return monitoring;
+}
+
+void QNetworkConnectionMonitorPrivate::stopMonitoring()
+{
+ Q_ASSERT(connectionEvents);
+ Q_ASSERT(monitoring);
+ if (connectionEvents->stopMonitoring())
+ monitoring = false;
+}
+
+QNetworkConnectionMonitor::QNetworkConnectionMonitor()
+ : QObject(*new QNetworkConnectionMonitorPrivate)
+{
+}
+
+QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local,
+ const QHostAddress &remote)
+ : QObject(*new QNetworkConnectionMonitorPrivate)
+{
+ setTargets(local, remote);
+}
+
+QNetworkConnectionMonitor::~QNetworkConnectionMonitor() = default;
+
+bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote)
+{
+ if (isMonitoring()) {
+ qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
+ return false;
+ }
+ if (local.isNull()) {
+ qCWarning(lcNetMon, "Invalid (null) local address, cannot create a reachability target");
+ return false;
+ }
+ // Silently return false for loopback addresses instead of printing warnings later
+ if (remote.isLoopback())
+ return false;
+
+ return d_func()->setTargets(local, remote);
+}
+
+bool QNetworkConnectionMonitor::startMonitoring()
+{
+ Q_D(QNetworkConnectionMonitor);
+ if (isMonitoring()) {
+ qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
+ return false;
+ }
+ return d->startMonitoring();
+}
+
+bool QNetworkConnectionMonitor::isMonitoring() const
+{
+ return d_func()->monitoring;
+}
+
+void QNetworkConnectionMonitor::stopMonitoring()
+{
+ Q_D(QNetworkConnectionMonitor);
+ if (!isMonitoring()) {
+ qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!");
+ return;
+ }
+ d->stopMonitoring();
+}
+
+bool QNetworkConnectionMonitor::isReachable()
+{
+ Q_D(QNetworkConnectionMonitor);
+ NLM_CONNECTIVITY required = d->sameSubnet
+ ? (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_SUBNET : NLM_CONNECTIVITY_IPV4_SUBNET)
+ : (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_INTERNET : NLM_CONNECTIVITY_IPV4_INTERNET);
+ return d_func()->connectivity & required;
+}
+
+class QNetworkListManagerEvents final : public INetworkListManagerEvents
+{
+public:
+ QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor);
+ ~QNetworkListManagerEvents();
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
+
+ ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
+ ULONG STDMETHODCALLTYPE Release() override
+ {
+ if (--ref == 0) {
+ delete this;
+ return 0;
+ }
+ return ref;
+ }
+
+ HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override;
+
+ Q_REQUIRED_RESULT
+ bool start();
+ Q_REQUIRED_RESULT
+ bool stop();
+
+private:
+ ComPtr<INetworkListManager> networkListManager = nullptr;
+ ComPtr<IConnectionPoint> connectionPoint = nullptr;
+
+ QNetworkStatusMonitorPrivate *monitor = nullptr;
+
+ QAtomicInteger<ULONG> ref = 1; // start at 1 for our own initial reference
+ DWORD cookie = 0;
+};
+
+class QNetworkStatusMonitorPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QNetworkStatusMonitor);
+
+public:
+ QNetworkStatusMonitorPrivate();
+ ~QNetworkStatusMonitorPrivate();
+
+ Q_REQUIRED_RESULT
+ bool start();
+ void stop();
+
+ void setConnectivity(NLM_CONNECTIVITY newConnectivity);
+
+private:
+ friend class QNetworkListManagerEvents;
+
+ ComPtr<QNetworkListManagerEvents> managerEvents;
+ NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED;
+
+ bool monitoring = false;
+ bool comInitFailed = false;
+};
+
+QNetworkListManagerEvents::QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor)
+ : monitor(monitor)
+{
+ auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
+ IID_INetworkListManager, &networkListManager);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:"
+ << errorStringFromHResult(hr);
+ return;
+ }
+
+ // Set initial connectivity
+ hr = networkListManager->GetConnectivity(&monitor->connectivity);
+ if (FAILED(hr))
+ qCWarning(lcNetMon) << "Could not get connectivity:" << errorStringFromHResult(hr);
+
+ ComPtr<IConnectionPointContainer> connectionPointContainer;
+ hr = networkListManager.As(&connectionPointContainer);
+ if (SUCCEEDED(hr)) {
+ hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents,
+ &connectionPoint);
+ }
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to get connection point for network list manager events:"
+ << errorStringFromHResult(hr);
+ }
+}
+
+QNetworkListManagerEvents::~QNetworkListManagerEvents()
+{
+ Q_ASSERT(ref == 0);
+}
+
+HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject)
+{
+ if (!ppvObject)
+ return E_INVALIDARG;
+
+ return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
+ || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject)
+ ? S_OK
+ : E_NOINTERFACE;
+}
+
+HRESULT STDMETHODCALLTYPE
+QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
+{
+ // This function is run on a different thread than 'monitor' is created on, so we need to run
+ // it on that thread
+ QMetaObject::invokeMethod(monitor->q_ptr,
+ [newConnectivity, monitor = this->monitor]() {
+ monitor->setConnectivity(newConnectivity);
+ },
+ Qt::QueuedConnection);
+ return S_OK;
+}
+
+bool QNetworkListManagerEvents::start()
+{
+ if (!connectionPoint) {
+ qCWarning(lcNetMon, "Initialization failed, can't start!");
+ return false;
+ }
+ auto hr = connectionPoint->Advise(this, &cookie);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:"
+ << errorStringFromHResult(hr);
+ return false;
+ }
+ return true;
+}
+
+bool QNetworkListManagerEvents::stop()
+{
+ Q_ASSERT(connectionPoint);
+ auto hr = connectionPoint->Unadvise(cookie);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to unsubscribe from network connectivity events:"
+ << errorStringFromHResult(hr);
+ return false;
+ }
+ cookie = 0;
+ return true;
+}
+
+QNetworkStatusMonitorPrivate::QNetworkStatusMonitorPrivate()
+{
+ auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
+ comInitFailed = true;
+ return;
+ }
+ managerEvents = new QNetworkListManagerEvents(this);
+}
+
+QNetworkStatusMonitorPrivate::~QNetworkStatusMonitorPrivate()
+{
+ if (comInitFailed)
+ return;
+ if (monitoring)
+ stop();
+ managerEvents.Reset();
+ CoUninitialize();
+}
+
+void QNetworkStatusMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity)
+{
+ Q_Q(QNetworkStatusMonitor);
+
+ const bool oldAccessibility = q->isNetworkAccesible();
+ connectivity = newConnectivity;
+ const bool accessibility = q->isNetworkAccesible();
+ if (oldAccessibility != accessibility)
+ emit q->onlineStateChanged(accessibility);
+}
+
+bool QNetworkStatusMonitorPrivate::start()
+{
+ if (comInitFailed)
+ return false;
+ Q_ASSERT(managerEvents);
+ Q_ASSERT(!monitoring);
+ if (managerEvents->start())
+ monitoring = true;
+ return monitoring;
+}
+
+void QNetworkStatusMonitorPrivate::stop()
+{
+ Q_ASSERT(managerEvents);
+ Q_ASSERT(monitoring);
+ if (managerEvents->stop())
+ monitoring = false;
+}
+
+QNetworkStatusMonitor::QNetworkStatusMonitor() : QObject(*new QNetworkStatusMonitorPrivate) {}
+
+QNetworkStatusMonitor::~QNetworkStatusMonitor() {}
+
+bool QNetworkStatusMonitor::start()
+{
+ if (isMonitoring()) {
+ qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
+ return false;
+ }
+
+ return d_func()->start();
+}
+
+void QNetworkStatusMonitor::stop()
+{
+ if (!isMonitoring()) {
+ qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!");
+ return;
+ }
+
+ d_func()->stop();
+}
+
+bool QNetworkStatusMonitor::isMonitoring() const
+{
+ return d_func()->monitoring;
+}
+
+bool QNetworkStatusMonitor::isNetworkAccesible()
+{
+ return d_func()->connectivity
+ & (NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET
+ | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET);
+}
+
+bool QNetworkStatusMonitor::isEnabled()
+{
+ return true;
+}
+
+void QNetworkStatusMonitor::reachabilityChanged(bool online)
+{
+ Q_UNUSED(online);
+ Q_UNREACHABLE();
+}
+
+QT_END_NAMESPACE