/****************************************************************************
**
** Copyright (C) 2016 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 <qt_windows.h>

#include "qnativesocketengine_winrt_p.h"

#include <qcoreapplication.h>
#include <qabstracteventdispatcher.h>
#include <qsocketnotifier.h>
#include <qdatetime.h>
#include <qnetworkinterface.h>
#include <qelapsedtimer.h>
#include <qthread.h>
#include <qabstracteventdispatcher.h>
#include <qfunctions_winrt.h>

#include <private/qthread_p.h>
#include <private/qabstractsocket_p.h>
#include <private/qeventdispatcher_winrt_p.h>

#ifndef QT_NO_SSL
#include <QSslSocket>
#endif

#include <functional>
#include <wrl.h>
#include <windows.foundation.collections.h>
#include <windows.storage.streams.h>
#include <windows.networking.h>
#include <windows.networking.sockets.h>
#include <robuffer.h>

using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Storage::Streams;
using namespace ABI::Windows::Networking;
using namespace ABI::Windows::Networking::Connectivity;
using namespace ABI::Windows::Networking::Sockets;
#if _MSC_VER >= 1900
using namespace ABI::Windows::Security::EnterpriseData;
#endif

typedef ITypedEventHandler<StreamSocketListener *, StreamSocketListenerConnectionReceivedEventArgs *> ClientConnectedHandler;
typedef ITypedEventHandler<DatagramSocket *, DatagramSocketMessageReceivedEventArgs *> DatagramReceivedHandler;
typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> SocketReadCompletedHandler;
typedef IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32> SocketWriteCompletedHandler;
typedef IAsyncOperationWithProgress<IBuffer *, UINT32> IAsyncBufferOperation;

QT_BEGIN_NAMESPACE

Q_LOGGING_CATEGORY(lcNetworkSocket, "qt.network.socket");
Q_LOGGING_CATEGORY(lcNetworkSocketVerbose, "qt.network.socket.verbose");

#if _MSC_VER >= 1900
static HRESULT qt_winrt_try_create_thread_network_context(QString host, ComPtr<IThreadNetworkContext> &context)
{
    HRESULT hr;
    ComPtr<IProtectionPolicyManagerStatics> protectionPolicyManager;

    hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(),
                              &protectionPolicyManager);
    RETURN_HR_IF_FAILED("Could not access ProtectionPolicyManager statics.");

    ComPtr<IHostNameFactory> hostNameFactory;
    hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
                              &hostNameFactory);
    RETURN_HR_IF_FAILED("Could not access HostName factory.");

    ComPtr<IHostName> hostName;
    HStringReference hostRef(reinterpret_cast<LPCWSTR>(host.utf16()), host.length());
    hr = hostNameFactory->CreateHostName(hostRef.Get(), &hostName);
    RETURN_HR_IF_FAILED("Could not create hostname.");

    ComPtr<IAsyncOperation<HSTRING>> op;
    hr = protectionPolicyManager->GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName.Get(), &op);
    RETURN_HR_IF_FAILED("Could not get identity operation.");

    HSTRING hIdentity;
    hr = QWinRTFunctions::await(op, &hIdentity);
    RETURN_HR_IF_FAILED("Could not wait for identity operation.");

    // Implies there is no need for a network context for this address
    if (hIdentity == nullptr)
        return S_OK;

    hr = protectionPolicyManager->CreateCurrentThreadNetworkContext(hIdentity, &context);
    RETURN_HR_IF_FAILED("Could not create thread network context");

    return S_OK;
}
#endif // _MSC_VER >= 1900

typedef QHash<qintptr, IStreamSocket *> TcpSocketHash;

struct SocketHandler
{
    SocketHandler() : socketCount(0) {}
    qintptr socketCount;
    TcpSocketHash pendingTcpSockets;
};

Q_GLOBAL_STATIC(SocketHandler, gSocketHandler)

struct SocketGlobal
{
    SocketGlobal()
    {
        HRESULT hr;
        hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
                                  &bufferFactory);
        Q_ASSERT_SUCCEEDED(hr);
    }

    ComPtr<IBufferFactory> bufferFactory;
};
Q_GLOBAL_STATIC(SocketGlobal, g)

#define READ_BUFFER_SIZE 65536

static inline QString qt_QStringFromHString(const HString &string)
{
    UINT32 length;
    PCWSTR rawString = string.GetRawBuffer(&length);
    return QString::fromWCharArray(rawString, length);
}

class SocketEngineWorker : public QObject
{
    Q_OBJECT
public:
    SocketEngineWorker(QNativeSocketEnginePrivate *engine)
            : enginePrivate(engine)
    {
        qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << engine;
    }

    ~SocketEngineWorker()
    {
        qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
        if (Q_UNLIKELY(initialReadOp)) {
            qCDebug(lcNetworkSocket) << Q_FUNC_INFO << "Closing initial read operation";
            ComPtr<IAsyncInfo> info;
            HRESULT hr = initialReadOp.As(&info);
            Q_ASSERT_SUCCEEDED(hr);
            if (info) {
                hr = info->Cancel();
                Q_ASSERT_SUCCEEDED(hr);
                hr = info->Close();
                Q_ASSERT_SUCCEEDED(hr);
            }
        }

        if (readOp) {
            qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing read operation";
            ComPtr<IAsyncInfo> info;
            HRESULT hr = readOp.As(&info);
            Q_ASSERT_SUCCEEDED(hr);
            if (info) {
                hr = info->Cancel();
                Q_ASSERT_SUCCEEDED(hr);
                hr = info->Close();
                Q_ASSERT_SUCCEEDED(hr);
            }
        }

        if (connectOp) {
            qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing connect operation";
            ComPtr<IAsyncInfo> info;
            HRESULT hr = connectOp.As(&info);
            Q_ASSERT_SUCCEEDED(hr);
            if (info) {
                hr = info->Cancel();
                Q_ASSERT_SUCCEEDED(hr);
                hr = info->Close();
                Q_ASSERT_SUCCEEDED(hr);
            }
        }
    }

signals:
    void connectOpFinished(bool success, QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString);
    void newDataReceived();
    void socketErrorOccured(QAbstractSocket::SocketError error);

public:
    void startReading()
    {
        qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
        ComPtr<IBuffer> buffer;
        HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer);
        Q_ASSERT_SUCCEEDED(hr);
        ComPtr<IInputStream> stream;
        hr = tcpSocket->get_InputStream(&stream);
        Q_ASSERT_SUCCEEDED(hr);
        hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, initialReadOp.GetAddressOf());
        Q_ASSERT_SUCCEEDED(hr);
        enginePrivate->socketState = QAbstractSocket::ConnectedState;
        hr = initialReadOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketEngineWorker::onReadyRead).Get());
        Q_ASSERT_SUCCEEDED(hr);
    }

    HRESULT onConnectOpFinished(IAsyncAction *action, AsyncStatus)
    {
        qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
        HRESULT hr = action->GetResults();
        if (FAILED(hr)) {
            if (hr == HRESULT_FROM_WIN32(WSAETIMEDOUT)) {
                emit connectOpFinished(false, QAbstractSocket::NetworkError, WinRTSocketEngine::ConnectionTimeOutErrorString);
                return S_OK;
            } else if (hr == HRESULT_FROM_WIN32(WSAEHOSTUNREACH)) {
                emit connectOpFinished(false, QAbstractSocket::HostNotFoundError, WinRTSocketEngine::HostUnreachableErrorString);
                return S_OK;
            } else if (hr == HRESULT_FROM_WIN32(WSAECONNREFUSED)) {
                emit connectOpFinished(false, QAbstractSocket::ConnectionRefusedError, WinRTSocketEngine::ConnectionRefusedErrorString);
                return S_OK;
            } else {
                emit connectOpFinished(false, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString);
                return S_OK;
            }
        }

        // The callback might be triggered several times if we do not cancel/reset it here
        if (connectOp) {
            ComPtr<IAsyncInfo> info;
            hr = connectOp.As(&info);
            Q_ASSERT_SUCCEEDED(hr);
            if (info) {
                hr = info->Cancel();
                Q_ASSERT_SUCCEEDED(hr);
                hr = info->Close();
                Q_ASSERT_SUCCEEDED(hr);
            }
            hr = connectOp.Reset();
            Q_ASSERT_SUCCEEDED(hr);
        }

        emit connectOpFinished(true, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString);
        return S_OK;
    }

    HRESULT OnNewDatagramReceived(IDatagramSocket *, IDatagramSocketMessageReceivedEventArgs *args)
    {
        qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO;
        WinRtDatagram datagram;
        QHostAddress returnAddress;
        ComPtr<IHostName> remoteHost;
        HRESULT hr = args->get_RemoteAddress(&remoteHost);
        RETURN_OK_IF_FAILED("Could not obtain remote host");
        HString remoteHostString;
        hr = remoteHost->get_CanonicalName(remoteHostString.GetAddressOf());
        RETURN_OK_IF_FAILED("Could not obtain remote host's canonical name");
        returnAddress.setAddress(qt_QStringFromHString(remoteHostString));
        datagram.header.senderAddress = returnAddress;
        HString remotePort;
        hr = args->get_RemotePort(remotePort.GetAddressOf());
        RETURN_OK_IF_FAILED("Could not obtain remote port");
        datagram.header.senderPort = qt_QStringFromHString(remotePort).toInt();

        ComPtr<IDataReader> reader;
        hr = args->GetDataReader(&reader);
        RETURN_OK_IF_FAILED("Could not obtain data reader");
        quint32 length;
        hr = reader->get_UnconsumedBufferLength(&length);
        RETURN_OK_IF_FAILED("Could not obtain unconsumed buffer length");
        datagram.data.resize(length);
        hr = reader->ReadBytes(length, reinterpret_cast<BYTE *>(datagram.data.data()));
        RETURN_OK_IF_FAILED("Could not read datagram");

        QMutexLocker locker(&mutex);
        // Notify the engine about new datagrams being present at the next event loop iteration
        if (emitDataReceived)
            emit newDataReceived();
        pendingDatagrams << datagram;

        return S_OK;
    }

    HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
    {
        qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO;
        if (asyncInfo == initialReadOp.Get()) {
            initialReadOp.Reset();
        } else if (asyncInfo == readOp.Get()) {
            readOp.Reset();
        } else {
            Q_ASSERT(false);
        }

        // A read in UnconnectedState will close the socket and return -1 and thus tell the caller,
        // that the connection was closed. The socket cannot be closed here, as the subsequent read
        // might fail then.
        if (status == Error || status == Canceled) {
            qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Remote host closed";
            emit socketErrorOccured(QAbstractSocket::RemoteHostClosedError);
            return S_OK;
        }

        ComPtr<IBuffer> buffer;
        HRESULT hr = asyncInfo->GetResults(&buffer);
        if (FAILED(hr)) {
            qErrnoWarning(hr, "Failed to get read results buffer");
            emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
            return S_OK;
        }

        UINT32 bufferLength;
        hr = buffer->get_Length(&bufferLength);
        if (FAILED(hr)) {
            qErrnoWarning(hr, "Failed to get buffer length");
            emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
            return S_OK;
        }
        // A zero sized buffer length signals, that the remote host closed the connection. The socket
        // cannot be closed though, as the following read might have socket descriptor -1 and thus and
        // the closing of the socket won't be communicated to the caller. So only the error is set. The
        // actual socket close happens inside of read.
        if (!bufferLength) {
            qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Remote host closed";
            emit socketErrorOccured(QAbstractSocket::RemoteHostClosedError);
            return S_OK;
        }

        ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
        hr = buffer.As(&byteArrayAccess);
        if (FAILED(hr)) {
            qErrnoWarning(hr, "Failed to get cast buffer");
            emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
            return S_OK;
        }
        byte *data;
        hr = byteArrayAccess->Buffer(&data);
        if (FAILED(hr)) {
            qErrnoWarning(hr, "Failed to access buffer data");
            emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
            return S_OK;
        }

        QByteArray newData(reinterpret_cast<const char*>(data), qint64(bufferLength));

        QMutexLocker readLocker(&mutex);
        emit newDataReceived();
        pendingData.append(newData);
        readLocker.unlock();

        hr = QEventDispatcherWinRT::runOnXamlThread([buffer, this]() {
            UINT32 readBufferLength;
            ComPtr<IInputStream> stream;
            HRESULT hr = tcpSocket->get_InputStream(&stream);
            if (FAILED(hr)) {
                qErrnoWarning(hr, "Failed to obtain input stream");
                emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
                return S_OK;
            }

            // Reuse the stream buffer
            hr = buffer->get_Capacity(&readBufferLength);
            if (FAILED(hr)) {
                qErrnoWarning(hr, "Failed to get buffer capacity");
                emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
                return S_OK;
            }
            hr = buffer->put_Length(0);
            if (FAILED(hr)) {
                qErrnoWarning(hr, "Failed to set buffer length");
                emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
                return S_OK;
            }

            hr = stream->ReadAsync(buffer.Get(), readBufferLength, InputStreamOptions_Partial, &readOp);
            if (FAILED(hr)) {
                qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer.");
                emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
                return S_OK;
            }
            hr = readOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketEngineWorker::onReadyRead).Get());
            if (FAILED(hr)) {
                qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback.");
                emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
                return S_OK;
            }
            return S_OK;
        });
        Q_ASSERT_SUCCEEDED(hr);
        return S_OK;
    }

    void setTcpSocket(ComPtr<IStreamSocket> socket) { tcpSocket = socket; }

private:
    friend class QNativeSocketEngine;
    ComPtr<IStreamSocket> tcpSocket;

    QList<WinRtDatagram> pendingDatagrams;
    bool emitDataReceived = true;
    QByteArray pendingData;

    // Protects pendingData/pendingDatagrams which are accessed from native callbacks
    QMutex mutex;

    ComPtr<IAsyncAction> connectOp;
    ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> initialReadOp;
    ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> readOp;

    QNativeSocketEnginePrivate *enginePrivate;
};

static QByteArray socketDescription(const QAbstractSocketEngine *s)
{
    QByteArray result;
    if (const QObject *o = s->parent()) {
        const QString name = o->objectName();
        if (!name.isEmpty()) {
            result += '"';
            result += name.toLocal8Bit();
            result += "\"/";
        }
        result += o->metaObject()->className();
    }
    return result;
}

// Common constructs
#define Q_CHECK_VALID_SOCKETLAYER(function, returnValue) do { \
    if (!isValid()) { \
        qWarning(""#function" was called on an uninitialized socket device"); \
        return returnValue; \
    } } while (0)
#define Q_CHECK_INVALID_SOCKETLAYER(function, returnValue) do { \
    if (isValid()) { \
        qWarning(""#function" was called on an already initialized socket device"); \
        return returnValue; \
    } } while (0)
#define Q_CHECK_STATE(function, checkState, returnValue) do { \
    if (d->socketState != (checkState)) { \
        qWarning(""#function" was not called in "#checkState); \
        return (returnValue); \
    } } while (0)
#define Q_CHECK_NOT_STATE(function, checkState, returnValue) do { \
    if (d->socketState == (checkState)) { \
        qWarning(""#function" was called in "#checkState); \
        return (returnValue); \
    } } while (0)
#define Q_CHECK_STATES(function, state1, state2, returnValue) do { \
    if (d->socketState != (state1) && d->socketState != (state2)) { \
        qWarning(""#function" was called" \
                 " not in "#state1" or "#state2); \
        return (returnValue); \
    } } while (0)
#define Q_CHECK_STATES3(function, state1, state2, state3, returnValue) do { \
    if (d->socketState != (state1) && d->socketState != (state2) && d->socketState != (state3)) { \
        qWarning(""#function" was called" \
                 " not in "#state1", "#state2" or "#state3); \
        return (returnValue); \
    } } while (0)
#define Q_CHECK_TYPE(function, type, returnValue) do { \
    if (d->socketType != (type)) { \
        qWarning(#function" was called by a" \
                 " socket other than "#type""); \
        return (returnValue); \
    } } while (0)
#define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a)

template <typename T>
static AsyncStatus opStatus(const ComPtr<T> &op)
{
    ComPtr<IAsyncInfo> info;
    HRESULT hr = op.As(&info);
    Q_ASSERT_SUCCEEDED(hr);
    AsyncStatus status;
    hr = info->get_Status(&status);
    Q_ASSERT_SUCCEEDED(hr);
    return status;
}

static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
{
    qCDebug(lcNetworkSocket) << Q_FUNC_INFO << data << len;
    ComPtr<IBuffer> buffer;
    HRESULT hr = g->bufferFactory->Create(len, &buffer);
    Q_ASSERT_SUCCEEDED(hr);
    hr = buffer->put_Length(len);
    Q_ASSERT_SUCCEEDED(hr);
    ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
    hr = buffer.As(&byteArrayAccess);
    Q_ASSERT_SUCCEEDED(hr);
    byte *bytes;
    hr = byteArrayAccess->Buffer(&bytes);
    Q_ASSERT_SUCCEEDED(hr);
    memcpy(bytes, data, len);
    ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
    hr = stream->WriteAsync(buffer.Get(), &op);
    RETURN_IF_FAILED("Failed to write to stream", return -1);
    UINT32 bytesWritten;
    hr = QWinRTFunctions::await(op, &bytesWritten);
    RETURN_IF_FAILED("Failed to write to stream", return -1);
    return bytesWritten;
}

QNativeSocketEngine::QNativeSocketEngine(QObject *parent)
    : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << parent;
    qRegisterMetaType<WinRtDatagram>();
    qRegisterMetaType<WinRTSocketEngine::ErrorString>();
    Q_D(QNativeSocketEngine);
#ifndef QT_NO_SSL
    if (parent)
        d->sslSocket = qobject_cast<QSslSocket *>(parent->parent());
#endif

    connect(this, &QNativeSocketEngine::connectionReady,
            this, &QNativeSocketEngine::connectionNotification, Qt::QueuedConnection);
    connect(this, &QNativeSocketEngine::readReady,
            this, &QNativeSocketEngine::processReadReady, Qt::QueuedConnection);
    connect(this, &QNativeSocketEngine::writeReady,
            this, &QNativeSocketEngine::writeNotification, Qt::QueuedConnection);
    connect(d->worker, &SocketEngineWorker::connectOpFinished,
            this, &QNativeSocketEngine::handleConnectOpFinished, Qt::QueuedConnection);
    connect(d->worker, &SocketEngineWorker::newDataReceived, this, &QNativeSocketEngine::handleNewData, Qt::QueuedConnection);
    connect(d->worker, &SocketEngineWorker::socketErrorOccured,
            this, &QNativeSocketEngine::handleTcpError, Qt::QueuedConnection);
}

QNativeSocketEngine::~QNativeSocketEngine()
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    close();
}

bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << type << protocol;
    Q_D(QNativeSocketEngine);
    if (isValid())
        close();

    // Create the socket
    if (!d->createNewSocket(type, protocol))
        return false;

    if (type == QAbstractSocket::UdpSocket) {
        // Set the broadcasting flag if it's a UDP socket.
        if (!setOption(BroadcastSocketOption, 1)) {
            d->setError(QAbstractSocket::UnsupportedSocketOperationError,
                WinRTSocketEngine::BroadcastingInitFailedErrorString);
            close();
            return false;
        }

        // Set some extra flags that are interesting to us, but accept failure
        setOption(ReceivePacketInformation, 1);
        setOption(ReceiveHopLimit, 1);
    }


    // Make sure we receive out-of-band data
    if (type == QAbstractSocket::TcpSocket
        && !setOption(ReceiveOutOfBandData, 1)) {
        qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data");
    }


    d->socketType = type;
    d->socketProtocol = protocol;
    return true;
}

bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << socketDescriptor << socketState;
    Q_D(QNativeSocketEngine);

    if (isValid())
        close();

    // Currently, only TCP sockets are initialized this way.
    IStreamSocket *socket = gSocketHandler->pendingTcpSockets.take(socketDescriptor);
    d->socketDescriptor = qintptr(socket);
    d->socketType = QAbstractSocket::TcpSocket;

    if (!d->socketDescriptor || !d->fetchConnectionParameters()) {
        d->setError(QAbstractSocket::UnsupportedSocketOperationError,
            WinRTSocketEngine::InvalidSocketErrorString);
        d->socketDescriptor = -1;
        return false;
    }

    // Start processing incoming data
    if (d->socketType == QAbstractSocket::TcpSocket) {
        HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, socket, this]() {
            d->worker->setTcpSocket(socket);
            d->worker->startReading();
            return S_OK;
        });
        if (FAILED(hr))
            return false;
    } else {
        d->socketState = socketState;
    }

    return true;
}

qintptr QNativeSocketEngine::socketDescriptor() const
{
    Q_D(const QNativeSocketEngine);
    return d->socketDescriptor;
}

bool QNativeSocketEngine::isValid() const
{
    Q_D(const QNativeSocketEngine);
    return d->socketDescriptor != -1;
}

bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << address << port;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHost(), false);
    Q_CHECK_STATES3(QNativeSocketEngine::connectToHost(), QAbstractSocket::BoundState,
        QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
    const QString addressString = address.toString();
    return connectToHostByName(addressString, port);
}

bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << name << port;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHostByName(), false);
    Q_CHECK_STATES3(QNativeSocketEngine::connectToHostByName(), QAbstractSocket::BoundState,
        QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
    HRESULT hr;

#if _MSC_VER >= 1900
    ComPtr<IThreadNetworkContext> networkContext;
    if (!qEnvironmentVariableIsEmpty("QT_WINRT_USE_THREAD_NETWORK_CONTEXT")) {
        qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Creating network context";
        hr = qt_winrt_try_create_thread_network_context(name, networkContext);
        if (FAILED(hr)) {
            setError(QAbstractSocket::ConnectionRefusedError, QLatin1String("Could not create thread network context."));
            d->socketState = QAbstractSocket::ConnectedState;
            return true;
        }
    }
#endif // _MSC_VER >= 1900

    HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(name.utf16()));
    ComPtr<IHostNameFactory> hostNameFactory;
    hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
                                      &hostNameFactory);
    Q_ASSERT_SUCCEEDED(hr);
    ComPtr<IHostName> remoteHost;
    hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
    RETURN_FALSE_IF_FAILED("QNativeSocketEngine::connectToHostByName: Could not create hostname.");

    const QString portString = QString::number(port);
    HStringReference portReference(reinterpret_cast<LPCWSTR>(portString.utf16()));
    if (d->socketType == QAbstractSocket::TcpSocket)
        hr = d->tcpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->worker->connectOp);
    else if (d->socketType == QAbstractSocket::UdpSocket)
        hr = d->udpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->worker->connectOp);
    if (hr == E_ACCESSDENIED) {
        qErrnoWarning(hr, "QNativeSocketEngine::connectToHostByName: Unable to connect to host (%s:%hu/%s). "
                          "Please check your manifest capabilities.",
                      qPrintable(name), port, socketDescription(this).constData());
        return false;
    }
    Q_ASSERT_SUCCEEDED(hr);

#if _MSC_VER >= 1900
    if (networkContext != nullptr) {
        qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing network context";
        ComPtr<IClosable> networkContextCloser;
        hr = networkContext.As(&networkContextCloser);
        Q_ASSERT_SUCCEEDED(hr);
        hr = networkContextCloser->Close();
        Q_ASSERT_SUCCEEDED(hr);
    }
#endif // _MSC_VER >= 1900

    d->socketState = QAbstractSocket::ConnectingState;
    QEventDispatcherWinRT::runOnXamlThread([d, &hr]() {
        hr = d->worker->connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
                                         d->worker, &SocketEngineWorker::onConnectOpFinished).Get());
        RETURN_OK_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
        return S_OK;
    });
    if (FAILED(hr))
        return false;

    return d->socketState == QAbstractSocket::ConnectedState;
}

bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << address << port;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bind(), false);
    Q_CHECK_STATE(QNativeSocketEngine::bind(), QAbstractSocket::UnconnectedState, false);

    HRESULT hr;
    // runOnXamlThread may only return S_OK (will assert otherwise) so no need to check its result.
    // hr is set inside the lambda though. If an error occurred hr will point that out.
    bool specificErrorSet = false;
    QEventDispatcherWinRT::runOnXamlThread([address, d, &hr, port, &specificErrorSet, this]() {
        ComPtr<IHostName> hostAddress;

        if (address != QHostAddress::Any && address != QHostAddress::AnyIPv4 && address != QHostAddress::AnyIPv6) {
            ComPtr<IHostNameFactory> hostNameFactory;
            hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
                                      &hostNameFactory);
            RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not obtain hostname factory");
            const QString addressString = address.toString();
            HStringReference addressRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
            hr = hostNameFactory->CreateHostName(addressRef.Get(), &hostAddress);
            RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create hostname.");
        }

        QString portQString = port ? QString::number(port) : QString();
        HStringReference portString(reinterpret_cast<LPCWSTR>(portQString.utf16()));

        ComPtr<IAsyncAction> op;
        if (d->socketType == QAbstractSocket::TcpSocket) {
            if (!d->tcpListener) {
                hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(),
                                        &d->tcpListener);
                RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create tcp listener");
            }

            hr = d->tcpListener->add_ConnectionReceived(
                        Callback<ClientConnectedHandler>(d, &QNativeSocketEnginePrivate::handleClientConnection).Get(),
                        &d->connectionToken);
            RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not register client connection callback");
            hr = d->tcpListener->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op);
        } else if (d->socketType == QAbstractSocket::UdpSocket) {
            hr = d->udpSocket()->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op);
        }
        if (hr == E_ACCESSDENIED) {
            qErrnoWarning(hr, "Unable to bind socket (%s:%hu/%s). Please check your manifest capabilities.",
                          qPrintable(address.toString()), port, socketDescription(this).constData());
            d->setError(QAbstractSocket::SocketAccessError,
                     WinRTSocketEngine::AccessErrorString);
            d->socketState = QAbstractSocket::UnconnectedState;
            specificErrorSet = true;
            return S_OK;
        }
        RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Unable to bind socket");

        hr = QWinRTFunctions::await(op);
        if (hr == 0x80072741) { // The requested address is not valid in its context
            d->setError(QAbstractSocket::SocketAddressNotAvailableError,
                     WinRTSocketEngine::AddressNotAvailableErrorString);
            d->socketState = QAbstractSocket::UnconnectedState;
            specificErrorSet = true;
            return S_OK;
        // Only one usage of each socket address (protocol/network address/port) is normally permitted
        } else if (hr == 0x80072740) {
            d->setError(QAbstractSocket::AddressInUseError,
                WinRTSocketEngine::AddressInuseErrorString);
            d->socketState = QAbstractSocket::UnconnectedState;
            specificErrorSet = true;
            return S_OK;
        }
        RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not wait for bind to finish");
        return S_OK;
    });
    if (FAILED(hr)) {
        if (!specificErrorSet) {
            d->setError(QAbstractSocket::UnknownSocketError,
                     WinRTSocketEngine::UnknownSocketErrorString);
            d->socketState = QAbstractSocket::UnconnectedState;
        }
        return false;
    }

    d->socketState = QAbstractSocket::BoundState;
    return d->fetchConnectionParameters();
}

bool QNativeSocketEngine::listen()
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false);
    Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false);
#if QT_CONFIG(sctp)
    Q_CHECK_TYPES(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket,
        QAbstractSocket::SctpSocket, false);
#else
    Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
#endif

    if (d->tcpListener && d->socketDescriptor != -1) {
        d->socketState = QAbstractSocket::ListeningState;
        return true;
    }
    return false;
}

int QNativeSocketEngine::accept()
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
    Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, -1);
#if QT_CONFIG(sctp)
    Q_CHECK_TYPES(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket,
        QAbstractSocket::SctpSocket, -1);
#else
    Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, -1);
#endif

    if (d->socketDescriptor == -1 || d->pendingConnections.isEmpty()) {
        d->setError(QAbstractSocket::TemporaryError, WinRTSocketEngine::TemporaryErrorString);
        return -1;
    }

    if (d->socketType == QAbstractSocket::TcpSocket) {
        IStreamSocket *socket = d->pendingConnections.takeFirst();

        SocketHandler *handler = gSocketHandler();
        handler->pendingTcpSockets.insert(++handler->socketCount, socket);
        return handler->socketCount;
    }

    return -1;
}

void QNativeSocketEngine::close()
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    Q_D(QNativeSocketEngine);

    if (d->closingDown)
        return;

    if (d->pendingReadNotification)
        processReadReady();

    d->closingDown = true;

    d->notifyOnRead = false;
    d->notifyOnWrite = false;
    d->notifyOnException = false;
    d->emitReadReady = false;

    HRESULT hr;
    if (d->socketType == QAbstractSocket::TcpSocket) {
        hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
            HRESULT hr;
            // To close the connection properly (not with a hard reset) all pending read operation have to
            // be finished or cancelled. The API isn't available on Windows 8.1 though.
            ComPtr<IStreamSocket3> socket3;
            hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket3));
            Q_ASSERT_SUCCEEDED(hr);

            ComPtr<IAsyncAction> action;
            hr = socket3->CancelIOAsync(&action);
            Q_ASSERT_SUCCEEDED(hr);
            hr = QWinRTFunctions::await(action, QWinRTFunctions::YieldThread, 5000);
            // If there is no pending IO (no read established before) the function will fail with
            // "function was called at an unexpected time" which is fine.
            // Timeout is fine as well. The result will be the socket being hard reset instead of
            // being closed gracefully
            if (hr != E_ILLEGAL_METHOD_CALL && hr != ERROR_TIMEOUT)
                Q_ASSERT_SUCCEEDED(hr);
            return S_OK;
        });
        Q_ASSERT_SUCCEEDED(hr);
    }

    if (d->socketDescriptor != -1) {
        ComPtr<IClosable> socket;
        if (d->socketType == QAbstractSocket::TcpSocket) {
            hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket));
            Q_ASSERT_SUCCEEDED(hr);
            hr = d->tcpSocket()->Release();
            Q_ASSERT_SUCCEEDED(hr);
        } else if (d->socketType == QAbstractSocket::UdpSocket) {
            hr = d->udpSocket()->QueryInterface(IID_PPV_ARGS(&socket));
            Q_ASSERT_SUCCEEDED(hr);
            hr = d->udpSocket()->Release();
            Q_ASSERT_SUCCEEDED(hr);
        }

        if (socket) {
            hr = socket->Close();
            Q_ASSERT_SUCCEEDED(hr);
        }
        d->socketDescriptor = -1;
    }
    d->socketState = QAbstractSocket::UnconnectedState;
    d->hasSetSocketError = false;
    d->localPort = 0;
    d->localAddress.clear();
    d->peerPort = 0;
    d->peerAddress.clear();
    d->inboundStreamCount = d->outboundStreamCount = 0;
}

bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)
{
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false);
    Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false);
    Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false);
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << groupAddress << iface;
    Q_UNIMPLEMENTED();
    return false;
}

bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)
{
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false);
    Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false);
    Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false);
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << groupAddress << iface;
    Q_UNIMPLEMENTED();
    return false;
}

QNetworkInterface QNativeSocketEngine::multicastInterface() const
{
    Q_D(const QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::multicastInterface(), QNetworkInterface());
    Q_CHECK_TYPE(QNativeSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface());
    Q_UNIMPLEMENTED();
    return QNetworkInterface();
}

bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface)
{
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setMulticastInterface(), false);
    Q_CHECK_TYPE(QNativeSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false);
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << iface;
    Q_UNIMPLEMENTED();
    return false;
}

qint64 QNativeSocketEngine::bytesAvailable() const
{
    Q_D(const QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bytesAvailable(), -1);
    Q_CHECK_NOT_STATE(QNativeSocketEngine::bytesAvailable(), QAbstractSocket::UnconnectedState, -1);
    if (d->socketType != QAbstractSocket::TcpSocket)
        return -1;

    QMutexLocker locker(&d->worker->mutex);
    const qint64 bytesAvailable = d->worker->pendingData.length();

    qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << bytesAvailable;
    return bytesAvailable;
}

qint64 QNativeSocketEngine::read(char *data, qint64 maxlen)
{
    qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << maxlen;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::read(), -1);
    Q_CHECK_STATES(QNativeSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1);
    if (d->socketType != QAbstractSocket::TcpSocket)
        return -1;

    // There will be a read notification when the socket was closed by the remote host. If that
    // happens and there isn't anything left in the buffer, we have to return -1 in order to signal
    // the closing of the socket.
    QMutexLocker mutexLocker(&d->worker->mutex);
    if (d->worker->pendingData.isEmpty() && d->socketState != QAbstractSocket::ConnectedState) {
        close();
        return -1;
    }

    QByteArray readData;
    const int copyLength = qMin(maxlen, qint64(d->worker->pendingData.length()));
    if (maxlen >= d->worker->pendingData.length()) {
        qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Reading full buffer";
        readData = d->worker->pendingData;
        d->worker->pendingData.clear();
        d->emitReadReady = true;
    } else {
        qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Reading part of the buffer ("
            << copyLength << "of" << d->worker->pendingData.length() << "bytes";
        readData = d->worker->pendingData.left(maxlen);
        d->worker->pendingData.remove(0, maxlen);
        if (d->notifyOnRead) {
            d->pendingReadNotification = true;
            emit readReady();
        }
    }
    mutexLocker.unlock();

    memcpy(data, readData, copyLength);
    qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Read" << copyLength << "bytes";
    return copyLength;
}

qint64 QNativeSocketEngine::write(const char *data, qint64 len)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << data << len;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::write(), -1);
    Q_CHECK_STATE(QNativeSocketEngine::write(), QAbstractSocket::ConnectedState, -1);

    HRESULT hr = E_FAIL;
    ComPtr<IOutputStream> stream;
    if (d->socketType == QAbstractSocket::TcpSocket)
        hr = d->tcpSocket()->get_OutputStream(&stream);
    else if (d->socketType == QAbstractSocket::UdpSocket)
        hr = d->udpSocket()->get_OutputStream(&stream);
    Q_ASSERT_SUCCEEDED(hr);

    qint64 bytesWritten = writeIOStream(stream, data, len);
    if (bytesWritten < 0)
        d->setError(QAbstractSocket::SocketAccessError, WinRTSocketEngine::AccessErrorString);
    else if (bytesWritten > 0 && d->notifyOnWrite)
        emit writeReady();

    return bytesWritten;
}

qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header,
                                         PacketHeaderOptions)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << maxlen;
#ifndef QT_NO_UDPSOCKET
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1);
    Q_CHECK_STATES(QNativeSocketEngine::readDatagram(), QAbstractSocket::BoundState,
        QAbstractSocket::ConnectedState, -1);

    QMutexLocker locker(&d->worker->mutex);
    if (d->socketType != QAbstractSocket::UdpSocket || d->worker->pendingDatagrams.isEmpty()) {
        if (header)
            header->clear();
        return -1;
    }

    WinRtDatagram datagram = d->worker->pendingDatagrams.takeFirst();
    if (header)
        *header = datagram.header;

    QByteArray readOrigin;
    if (maxlen < datagram.data.length())
        readOrigin = datagram.data.left(maxlen);
    else
        readOrigin = datagram.data;
    if (d->worker->pendingDatagrams.isEmpty()) {
        qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "That's all folks";
        d->worker->emitDataReceived = true;
        d->emitReadReady = true;
    }

    locker.unlock();
    memcpy(data, readOrigin, qMin(maxlen, qint64(datagram.data.length())));
    return readOrigin.length();
#else
    Q_UNUSED(data)
    Q_UNUSED(maxlen)
    Q_UNUSED(header)
    return -1;
#endif // QT_NO_UDPSOCKET
}

qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << data << len;
#ifndef QT_NO_UDPSOCKET
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1);
    Q_CHECK_STATES(QNativeSocketEngine::writeDatagram(), QAbstractSocket::BoundState,
        QAbstractSocket::ConnectedState, -1);

    if (d->socketType != QAbstractSocket::UdpSocket)
        return -1;

    ComPtr<IHostName> remoteHost;
    ComPtr<IHostNameFactory> hostNameFactory;

    HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
                                    &hostNameFactory);
    Q_ASSERT_SUCCEEDED(hr);
    const QString addressString = header.destinationAddress.toString();
    HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
    hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
    RETURN_IF_FAILED("QNativeSocketEngine::writeDatagram: Could not create hostname.", return -1);

    ComPtr<IAsyncOperation<IOutputStream *>> streamOperation;
    ComPtr<IOutputStream> stream;
    const QString portString = QString::number(header.destinationPort);
    HStringReference portRef(reinterpret_cast<LPCWSTR>(portString.utf16()));
    hr = d->udpSocket()->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation);
    Q_ASSERT_SUCCEEDED(hr);

    hr = QWinRTFunctions::await(streamOperation, stream.GetAddressOf());
    Q_ASSERT_SUCCEEDED(hr);

    return writeIOStream(stream, data, len);
#else
    Q_UNUSED(data)
    Q_UNUSED(len)
    Q_UNUSED(header)
    return -1;
#endif // QT_NO_UDPSOCKET
}

bool QNativeSocketEngine::hasPendingDatagrams() const
{
    Q_D(const QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::hasPendingDatagrams(), false);
    Q_CHECK_NOT_STATE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UnconnectedState, false);
    Q_CHECK_TYPE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false);

    QMutexLocker locker(&d->worker->mutex);
    return d->worker->pendingDatagrams.length() > 0;
}

qint64 QNativeSocketEngine::pendingDatagramSize() const
{
    Q_D(const QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::pendingDatagramSize(), -1);
    Q_CHECK_TYPE(QNativeSocketEngine::pendingDatagramSize(), QAbstractSocket::UdpSocket, -1);

    QMutexLocker locker(&d->worker->mutex);
    if (d->worker->pendingDatagrams.isEmpty())
        return -1;

    return d->worker->pendingDatagrams.at(0).data.length();
}

qint64 QNativeSocketEngine::bytesToWrite() const
{
    return 0;
}

qint64 QNativeSocketEngine::receiveBufferSize() const
{
    Q_D(const QNativeSocketEngine);
    return d->option(QAbstractSocketEngine::ReceiveBufferSocketOption);
}

void QNativeSocketEngine::setReceiveBufferSize(qint64 bufferSize)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << bufferSize;
    Q_D(QNativeSocketEngine);
    d->setOption(QAbstractSocketEngine::ReceiveBufferSocketOption, bufferSize);
}

qint64 QNativeSocketEngine::sendBufferSize() const
{
    Q_D(const QNativeSocketEngine);
    return d->option(QAbstractSocketEngine::SendBufferSocketOption);
}

void QNativeSocketEngine::setSendBufferSize(qint64 bufferSize)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << bufferSize;
    Q_D(QNativeSocketEngine);
    d->setOption(QAbstractSocketEngine::SendBufferSocketOption, bufferSize);
}

int QNativeSocketEngine::option(QAbstractSocketEngine::SocketOption option) const
{
    Q_D(const QNativeSocketEngine);
    return d->option(option);
}

bool QNativeSocketEngine::setOption(QAbstractSocketEngine::SocketOption option, int value)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << option << value;
    Q_D(QNativeSocketEngine);
    return d->setOption(option, value);
}

bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << msecs;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false);
    Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForRead(),
                      QAbstractSocket::UnconnectedState, false);

    if (timedOut)
        *timedOut = false;

    QElapsedTimer timer;
    timer.start();
    while (msecs > timer.elapsed()) {
        // Servers with active connections are ready for reading
        if (!d->currentConnections.isEmpty())
            return true;

        // If we are a client, we are ready to read if our buffer has data
        QMutexLocker locker(&d->worker->mutex);
        if (!d->worker->pendingData.isEmpty())
            return true;

        // Nothing to do, wait for more events
        d->eventLoop.processEvents();
    }

    d->setError(QAbstractSocket::SocketTimeoutError,
                WinRTSocketEngine::TimeOutErrorString);

    if (timedOut)
        *timedOut = true;
    return false;
}

bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << msecs;
    Q_UNUSED(timedOut);
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);
    Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForWrite(),
        QAbstractSocket::UnconnectedState, false);

    if (d->socketState == QAbstractSocket::ConnectingState) {
        HRESULT hr = QWinRTFunctions::await(d->worker->connectOp, QWinRTFunctions::ProcessMainThreadEvents);
        if (SUCCEEDED(hr)) {
            handleConnectOpFinished(true, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString);
            return true;
        }
    }
    return false;
}

bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, int msecs, bool *timedOut)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << checkRead << checkWrite << msecs;
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForReadOrWrite(), false);
    Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(),
        QAbstractSocket::UnconnectedState, false);

    Q_UNUSED(readyToRead);
    Q_UNUSED(readyToWrite);
    Q_UNUSED(timedOut);
    return false;
}

bool QNativeSocketEngine::isReadNotificationEnabled() const
{
    Q_D(const QNativeSocketEngine);
    return d->notifyOnRead;
}

void QNativeSocketEngine::setReadNotificationEnabled(bool enable)
{
    qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable;
    Q_D(QNativeSocketEngine);
    d->notifyOnRead = enable;
}

bool QNativeSocketEngine::isWriteNotificationEnabled() const
{
    Q_D(const QNativeSocketEngine);
    return d->notifyOnWrite;
}

void QNativeSocketEngine::setWriteNotificationEnabled(bool enable)
{
    qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable;
    Q_D(QNativeSocketEngine);
    d->notifyOnWrite = enable;
    if (enable && d->socketState == QAbstractSocket::ConnectedState) {
        if (bytesToWrite())
            return; // will be emitted as a result of bytes written
        writeNotification();
    }
}

bool QNativeSocketEngine::isExceptionNotificationEnabled() const
{
    Q_D(const QNativeSocketEngine);
    return d->notifyOnException;
}

void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable)
{
    qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable;
    Q_D(QNativeSocketEngine);
    d->notifyOnException = enable;
}

void QNativeSocketEngine::establishRead()
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    Q_D(QNativeSocketEngine);

    HRESULT hr;
    hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
        d->worker->setTcpSocket(d->tcpSocket());
        d->worker->startReading();
        return S_OK;
    });
    Q_ASSERT_SUCCEEDED(hr);
}

void QNativeSocketEngine::handleConnectOpFinished(bool success, QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << success << error << errorString;
    Q_D(QNativeSocketEngine);
    disconnect(d->worker, &SocketEngineWorker::connectOpFinished,
            this, &QNativeSocketEngine::handleConnectOpFinished);
    if (!success) {
        d->setError(error, errorString);
        d->socketState = QAbstractSocket::UnconnectedState;
        close();
        return;
    }

    d->socketState = QAbstractSocket::ConnectedState;
    d->fetchConnectionParameters();
    emit connectionReady();

    if (d->socketType != QAbstractSocket::TcpSocket)
        return;

#ifndef QT_NO_SSL
    // Delay the reader so that the SSL socket can upgrade
    if (d->sslSocket)
        QObject::connect(qobject_cast<QSslSocket *>(d->sslSocket), &QSslSocket::encrypted, this, &QNativeSocketEngine::establishRead);
    else
#endif
        establishRead();
}

void QNativeSocketEngine::handleNewData()
{
    qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO;
    Q_D(QNativeSocketEngine);

    if (d->notifyOnRead && d->emitReadReady) {
        if (d->socketType == QAbstractSocket::UdpSocket && !d->worker->emitDataReceived)
            return;
        qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Emitting readReady";
        d->pendingReadNotification = true;
        emit readReady();
        d->worker->emitDataReceived = false;
        d->emitReadReady = false;
    }
}

void QNativeSocketEngine::handleTcpError(QAbstractSocket::SocketError error)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << error;
    Q_D(QNativeSocketEngine);
    WinRTSocketEngine::ErrorString errorString;
    switch (error) {
    case QAbstractSocket::RemoteHostClosedError:
        errorString = WinRTSocketEngine::RemoteHostClosedErrorString;
        break;
    default:
        errorString = WinRTSocketEngine::UnknownSocketErrorString;
    }

    d->setError(error, errorString);
    close();
}

void QNativeSocketEngine::processReadReady()
{
    Q_D(QNativeSocketEngine);
    if (d->closingDown)
        return;

    d->pendingReadNotification = false;
    readNotification();
}

bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << socketType << socketProtocol;
    Q_UNUSED(socketProtocol);
    HRESULT hr;

    switch (socketType) {
    case QAbstractSocket::TcpSocket: {
        ComPtr<IStreamSocket> socket;
        hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &socket);
        RETURN_FALSE_IF_FAILED("createNewSocket: Could not create socket instance");
        socketDescriptor = qintptr(socket.Detach());
        break;
    }
    case QAbstractSocket::UdpSocket: {
        ComPtr<IDatagramSocket> socket;
        hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &socket);
        RETURN_FALSE_IF_FAILED("createNewSocket: Could not create socket instance");
        socketDescriptor = qintptr(socket.Detach());
        QEventDispatcherWinRT::runOnXamlThread([&hr, this]() {
            hr = udpSocket()->add_MessageReceived(Callback<DatagramReceivedHandler>(worker, &SocketEngineWorker::OnNewDatagramReceived).Get(), &connectionToken);
            if (FAILED(hr)) {
                qErrnoWarning(hr, "createNewSocket: Could not add \"message received\" callback");
                return hr;
            }
            return S_OK;
        });
        if (FAILED(hr))
            return false;
        break;
    }
    default:
        qWarning("Invalid socket type");
        return false;
    }

    this->socketType = socketType;

    // Make the socket nonblocking.
    if (!setOption(QAbstractSocketEngine::NonBlockingSocketOption, 1)) {
        setError(QAbstractSocket::UnsupportedSocketOperationError, WinRTSocketEngine::NonBlockingInitFailedErrorString);
        q_func()->close();
        return false;
    }

    return true;
}

QNativeSocketEnginePrivate::QNativeSocketEnginePrivate()
    : QAbstractSocketEnginePrivate()
    , notifyOnRead(true)
    , notifyOnWrite(true)
    , notifyOnException(false)
    , closingDown(false)
    , socketDescriptor(-1)
    , worker(new SocketEngineWorker(this))
    , sslSocket(nullptr)
    , connectionToken( { -1 } )
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
}

QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate()
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    if (socketDescriptor == -1 || connectionToken.value == -1)
        return;

    HRESULT hr;
    if (socketType == QAbstractSocket::UdpSocket)
        hr = udpSocket()->remove_MessageReceived(connectionToken);
    else if (socketType == QAbstractSocket::TcpSocket)
        hr = tcpListener->remove_ConnectionReceived(connectionToken);
    Q_ASSERT_SUCCEEDED(hr);

    worker->deleteLater();
}

void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString) const
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << error << errorString;
    if (hasSetSocketError) {
        // Only set socket errors once for one engine; expect the
        // socket to recreate its engine after an error. Note: There's
        // one exception: SocketError(11) bypasses this as it's purely
        // a temporary internal error condition.
        // Another exception is the way the waitFor*() functions set
        // an error when a timeout occurs. After the call to setError()
        // they reset the hasSetSocketError to false
        return;
    }
    if (error != QAbstractSocket::SocketError(11))
        hasSetSocketError = true;

    socketError = error;

    switch (errorString) {
    case WinRTSocketEngine::NonBlockingInitFailedErrorString:
        socketErrorString = QNativeSocketEngine::tr("Unable to initialize non-blocking socket");
        break;
    case WinRTSocketEngine::BroadcastingInitFailedErrorString:
        socketErrorString = QNativeSocketEngine::tr("Unable to initialize broadcast socket");
        break;
    // should not happen anymore
    case WinRTSocketEngine::NoIpV6ErrorString:
        socketErrorString = QNativeSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support");
        break;
    case WinRTSocketEngine::RemoteHostClosedErrorString:
        socketErrorString = QNativeSocketEngine::tr("The remote host closed the connection");
        break;
    case WinRTSocketEngine::TimeOutErrorString:
        socketErrorString = QNativeSocketEngine::tr("Network operation timed out");
        break;
    case WinRTSocketEngine::ResourceErrorString:
        socketErrorString = QNativeSocketEngine::tr("Out of resources");
        break;
    case WinRTSocketEngine::OperationUnsupportedErrorString:
        socketErrorString = QNativeSocketEngine::tr("Unsupported socket operation");
        break;
    case WinRTSocketEngine::ProtocolUnsupportedErrorString:
        socketErrorString = QNativeSocketEngine::tr("Protocol type not supported");
        break;
    case WinRTSocketEngine::InvalidSocketErrorString:
        socketErrorString = QNativeSocketEngine::tr("Invalid socket descriptor");
        break;
    case WinRTSocketEngine::HostUnreachableErrorString:
        socketErrorString = QNativeSocketEngine::tr("Host unreachable");
        break;
    case WinRTSocketEngine::NetworkUnreachableErrorString:
        socketErrorString = QNativeSocketEngine::tr("Network unreachable");
        break;
    case WinRTSocketEngine::AccessErrorString:
        socketErrorString = QNativeSocketEngine::tr("Permission denied");
        break;
    case WinRTSocketEngine::ConnectionTimeOutErrorString:
        socketErrorString = QNativeSocketEngine::tr("Connection timed out");
        break;
    case WinRTSocketEngine::ConnectionRefusedErrorString:
        socketErrorString = QNativeSocketEngine::tr("Connection refused");
        break;
    case WinRTSocketEngine::AddressInuseErrorString:
        socketErrorString = QNativeSocketEngine::tr("The bound address is already in use");
        break;
    case WinRTSocketEngine::AddressNotAvailableErrorString:
        socketErrorString = QNativeSocketEngine::tr("The address is not available");
        break;
    case WinRTSocketEngine::AddressProtectedErrorString:
        socketErrorString = QNativeSocketEngine::tr("The address is protected");
        break;
    case WinRTSocketEngine::DatagramTooLargeErrorString:
        socketErrorString = QNativeSocketEngine::tr("Datagram was too large to send");
        break;
    case WinRTSocketEngine::SendDatagramErrorString:
        socketErrorString = QNativeSocketEngine::tr("Unable to send a message");
        break;
    case WinRTSocketEngine::ReceiveDatagramErrorString:
        socketErrorString = QNativeSocketEngine::tr("Unable to receive a message");
        break;
    case WinRTSocketEngine::WriteErrorString:
        socketErrorString = QNativeSocketEngine::tr("Unable to write");
        break;
    case WinRTSocketEngine::ReadErrorString:
        socketErrorString = QNativeSocketEngine::tr("Network error");
        break;
    case WinRTSocketEngine::PortInuseErrorString:
        socketErrorString = QNativeSocketEngine::tr("Another socket is already listening on the same port");
        break;
    case WinRTSocketEngine::NotSocketErrorString:
        socketErrorString = QNativeSocketEngine::tr("Operation on non-socket");
        break;
    case WinRTSocketEngine::InvalidProxyTypeString:
        socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation");
        break;
    case WinRTSocketEngine::TemporaryErrorString:
        socketErrorString = QNativeSocketEngine::tr("Temporary error");
        break;
    case WinRTSocketEngine::UnknownSocketErrorString:
        socketErrorString = QNativeSocketEngine::tr("Unknown error");
        break;
    }
}

int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt) const
{
    ComPtr<IStreamSocketControl> control;
    if (socketType == QAbstractSocket::TcpSocket) {
        if (FAILED(tcpSocket()->get_Control(&control))) {
            qWarning("QNativeSocketEnginePrivate::option: Could not obtain socket control");
            return -1;
        }
    }
    switch (opt) {
    case QAbstractSocketEngine::NonBlockingSocketOption:
    case QAbstractSocketEngine::BroadcastSocketOption:
    case QAbstractSocketEngine::ReceiveOutOfBandData:
        return 1;
    case QAbstractSocketEngine::SendBufferSocketOption:
        if (socketType == QAbstractSocket::UdpSocket)
            return -1;

        UINT32 bufferSize;
        if (FAILED(control->get_OutboundBufferSizeInBytes(&bufferSize))) {
            qWarning("Could not obtain OutboundBufferSizeInBytes information vom socket control");
            return -1;
        }
        return bufferSize;
    case QAbstractSocketEngine::LowDelayOption:
        if (socketType == QAbstractSocket::UdpSocket)
            return -1;

        boolean noDelay;
        if (FAILED(control->get_NoDelay(&noDelay))) {
            qWarning("Could not obtain NoDelay information from socket control");
            return -1;
        }
        return noDelay;
    case QAbstractSocketEngine::KeepAliveOption:
        if (socketType == QAbstractSocket::UdpSocket)
            return -1;

        boolean keepAlive;
        if (FAILED(control->get_KeepAlive(&keepAlive))) {
            qWarning("Could not obtain KeepAlive information from socket control");
            return -1;
        }
        return keepAlive;
    case QAbstractSocketEngine::ReceiveBufferSocketOption:
    case QAbstractSocketEngine::AddressReusable:
    case QAbstractSocketEngine::BindExclusively:
    case QAbstractSocketEngine::MulticastTtlOption:
    case QAbstractSocketEngine::MulticastLoopbackOption:
    case QAbstractSocketEngine::TypeOfServiceOption:
    case QAbstractSocketEngine::MaxStreamsSocketOption:
    case QAbstractSocketEngine::PathMtuInformation:
    default:
        return -1;
    }
    return -1;
}

bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption opt, int v)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << opt << v;
    ComPtr<IStreamSocketControl> control;
    if (socketType == QAbstractSocket::TcpSocket) {
        if (FAILED(tcpSocket()->get_Control(&control))) {
            qWarning("QNativeSocketEnginePrivate::setOption: Could not obtain socket control");
            return false;
        }
    }
    switch (opt) {
    case QAbstractSocketEngine::NonBlockingSocketOption:
    case QAbstractSocketEngine::BroadcastSocketOption:
    case QAbstractSocketEngine::ReceiveOutOfBandData:
        return v != 0;
    case QAbstractSocketEngine::SendBufferSocketOption:
        if (socketType == QAbstractSocket::UdpSocket)
            return false;

        if (FAILED(control->put_OutboundBufferSizeInBytes(v))) {
            qWarning("Could not set OutboundBufferSizeInBytes");
            return false;
        }
        return true;
    case QAbstractSocketEngine::LowDelayOption: {
        if (socketType == QAbstractSocket::UdpSocket)
            return false;

        boolean noDelay = v;
        if (FAILED(control->put_NoDelay(noDelay))) {
            qWarning("Could not obtain NoDelay information from socket control");
            return false;
        }
        return true;
    }
    case QAbstractSocketEngine::KeepAliveOption: {
        if (socketType == QAbstractSocket::UdpSocket
                || socketState != QAbstractSocket::UnconnectedState)
            return false;

        boolean keepAlive = v;
        if (FAILED(control->put_KeepAlive(keepAlive))) {
            qWarning("Could not set KeepAlive value");
            return false;
        }
        return true;
    }
    case QAbstractSocketEngine::ReceiveBufferSocketOption:
    case QAbstractSocketEngine::AddressReusable:
    case QAbstractSocketEngine::BindExclusively:
    case QAbstractSocketEngine::MulticastTtlOption:
    case QAbstractSocketEngine::MulticastLoopbackOption:
    case QAbstractSocketEngine::TypeOfServiceOption:
    case QAbstractSocketEngine::MaxStreamsSocketOption:
    case QAbstractSocketEngine::PathMtuInformation:
    default:
        return false;
    }
    return false;
}

bool QNativeSocketEnginePrivate::fetchConnectionParameters()
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    localPort = 0;
    localAddress.clear();
    peerPort = 0;
    peerAddress.clear();
    inboundStreamCount = outboundStreamCount = 0;

    HRESULT hr;
    if (socketType == QAbstractSocket::TcpSocket) {
        ComPtr<IHostName> hostName;
        HString tmpHString;
        ComPtr<IStreamSocketInformation> info;
        hr = tcpSocket()->get_Information(&info);
        Q_ASSERT_SUCCEEDED(hr);
        hr = info->get_LocalAddress(&hostName);
        Q_ASSERT_SUCCEEDED(hr);
        if (hostName) {
            hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            localAddress.setAddress(qt_QStringFromHString(tmpHString));
            hr = info->get_LocalPort(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            localPort = qt_QStringFromHString(tmpHString).toInt();
        }
        if (!localPort && tcpListener) {
            ComPtr<IStreamSocketListenerInformation> listenerInfo = 0;
            hr = tcpListener->get_Information(&listenerInfo);
            Q_ASSERT_SUCCEEDED(hr);
            hr = listenerInfo->get_LocalPort(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            localPort = qt_QStringFromHString(tmpHString).toInt();
            localAddress = QHostAddress::Any;
        }
        info->get_RemoteAddress(&hostName);
        if (hostName) {
            hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            peerAddress.setAddress(qt_QStringFromHString(tmpHString));
            hr = info->get_RemotePort(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            peerPort = qt_QStringFromHString(tmpHString).toInt();
            inboundStreamCount = outboundStreamCount = 1;
        }
    } else if (socketType == QAbstractSocket::UdpSocket) {
        ComPtr<IHostName> hostName;
        HString tmpHString;
        ComPtr<IDatagramSocketInformation> info;
        hr = udpSocket()->get_Information(&info);
        Q_ASSERT_SUCCEEDED(hr);
        hr = info->get_LocalAddress(&hostName);
        Q_ASSERT_SUCCEEDED(hr);
        if (hostName) {
            hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            localAddress.setAddress(qt_QStringFromHString(tmpHString));
            hr = info->get_LocalPort(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            localPort = qt_QStringFromHString(tmpHString).toInt();
        }

        hr = info->get_RemoteAddress(&hostName);
        Q_ASSERT_SUCCEEDED(hr);
        if (hostName) {
            hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            peerAddress.setAddress(qt_QStringFromHString(tmpHString));
            hr = info->get_RemotePort(tmpHString.GetAddressOf());
            Q_ASSERT_SUCCEEDED(hr);
            peerPort = qt_QStringFromHString(tmpHString).toInt();
            inboundStreamCount = outboundStreamCount = 1;
        }
    }
    return true;
}

HRESULT QNativeSocketEnginePrivate::handleClientConnection(IStreamSocketListener *listener, IStreamSocketListenerConnectionReceivedEventArgs *args)
{
    qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
    Q_Q(QNativeSocketEngine);
    Q_UNUSED(listener)
    IStreamSocket *socket;
    args->get_Socket(&socket);
    pendingConnections.append(socket);
    emit q->connectionReady();
    if (notifyOnRead)
        emit q->readReady();
    return S_OK;
}

QT_END_NAMESPACE

#include "qnativesocketengine_winrt.moc"
