qt6-bb10/src/network/socket/qnativesocketengine_winrt.cpp

1397 lines
50 KiB
C++

/****************************************************************************
**
** 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;
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
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_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)
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)
static inline QString qt_QStringFromHString(const HString &string)
{
UINT32 length;
PCWSTR rawString = string.GetRawBuffer(&length);
return QString::fromWCharArray(rawString, length);
}
#define READ_BUFFER_SIZE 65536
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)
{
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)
{
#ifndef QT_NO_SSL
Q_D(QNativeSocketEngine);
if (parent)
d->sslSocket = qobject_cast<QSslSocket *>(parent->parent());
#endif
connect(this, SIGNAL(connectionReady()), SLOT(connectionNotification()), Qt::QueuedConnection);
connect(this, SIGNAL(readReady()), SLOT(readNotification()), Qt::QueuedConnection);
connect(this, SIGNAL(writeReady()), SLOT(writeNotification()), Qt::QueuedConnection);
}
QNativeSocketEngine::~QNativeSocketEngine()
{
close();
}
bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
{
Q_D(QNativeSocketEngine);
if (isValid())
close();
// Create the socket
if (!d->createNewSocket(type, protocol))
return false;
d->socketType = type;
d->socketProtocol = protocol;
return true;
}
bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState 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,
d->InvalidSocketErrorString);
d->socketDescriptor = -1;
return false;
}
// Start processing incoming data
if (d->socketType == QAbstractSocket::TcpSocket) {
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([d, socket, this]() {
ComPtr<IBuffer> buffer;
HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer);
RETURN_HR_IF_FAILED("initialize(): Could not create buffer");
ComPtr<IInputStream> stream;
hr = socket->get_InputStream(&stream);
RETURN_HR_IF_FAILED("initialize(): Could not obtain input stream");
hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, d->readOp.GetAddressOf());
if (FAILED(hr)) {
qErrnoWarning(hr, "initialize(): Failed to read from the socket buffer (%s).",
socketDescription(this).constData());
return E_FAIL;
}
hr = d->readOp->put_Completed(Callback<SocketReadCompletedHandler>(d, &QNativeSocketEnginePrivate::handleReadyRead).Get());
if (FAILED(hr)) {
qErrnoWarning(hr, "initialize(): Failed to set socket read callback (%s).",
socketDescription(this).constData());
return E_FAIL;
}
return S_OK;
});
if (hr == E_FAIL)
return false;
Q_ASSERT_SUCCEEDED(hr);
}
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)
{
const QString addressString = address.toString();
return connectToHostByName(addressString, port);
}
bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port)
{
Q_D(QNativeSocketEngine);
HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(name.utf16()));
ComPtr<IHostNameFactory> hostNameFactory;
HRESULT 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->connectOp);
else if (d->socketType == QAbstractSocket::UdpSocket)
hr = d->udpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->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);
d->socketState = QAbstractSocket::ConnectingState;
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
return d->connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
d, &QNativeSocketEnginePrivate::handleConnectToHost).Get());
});
Q_ASSERT_SUCCEEDED(hr);
return d->socketState == QAbstractSocket::ConnectedState;
}
bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port)
{
Q_D(QNativeSocketEngine);
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([address, d, port, this]() {
HRESULT hr;
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_HR_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_HR_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_HR_IF_FAILED("QNativeSocketEngine::bind: Could not create tcp listener");
}
hr = d->tcpListener->add_ConnectionReceived(
Callback<ClientConnectedHandler>(d, &QNativeSocketEnginePrivate::handleClientConnection).Get(),
&d->connectionToken);
RETURN_HR_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());
return hr;
}
RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Unable to bind socket");
hr = QWinRTFunctions::await(op);
RETURN_HR_IF_FAILED("QNativeSocketEngine::bind: Could not wait for bind to finish");
return S_OK;
});
Q_ASSERT_SUCCEEDED(hr);
d->socketState = QAbstractSocket::BoundState;
return d->fetchConnectionParameters();
}
bool QNativeSocketEngine::listen()
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false);
Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false);
Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
if (d->tcpListener && d->socketDescriptor != -1) {
d->socketState = QAbstractSocket::ListeningState;
return true;
}
return false;
}
int QNativeSocketEngine::accept()
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, -1);
Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, -1);
if (d->socketDescriptor == -1 || d->pendingConnections.isEmpty()) {
d->setError(QAbstractSocket::TemporaryError, QNativeSocketEnginePrivate::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()
{
Q_D(QNativeSocketEngine);
if (d->closingDown)
return;
d->closingDown = true;
d->notifyOnRead = false;
d->notifyOnWrite = false;
d->notifyOnException = false;
HRESULT hr;
if (d->connectOp) {
ComPtr<IAsyncInfo> info;
hr = d->connectOp.As(&info);
Q_ASSERT_SUCCEEDED(hr);
if (info) {
hr = info->Cancel();
Q_ASSERT_SUCCEEDED(hr);
hr = info->Close();
Q_ASSERT_SUCCEEDED(hr);
}
}
#if _MSC_VER >= 1900
// 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);
Q_ASSERT_SUCCEEDED(hr);
#endif // _MSC_VER >= 1900
if (d->readOp) {
ComPtr<IAsyncInfo> info;
hr = d->readOp.As(&info);
Q_ASSERT_SUCCEEDED(hr);
if (info) {
hr = info->Cancel();
Q_ASSERT_SUCCEEDED(hr);
hr = info->Close();
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_UNUSED(groupAddress);
Q_UNUSED(iface);
Q_UNIMPLEMENTED();
return false;
}
bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)
{
Q_UNUSED(groupAddress);
Q_UNUSED(iface);
Q_UNIMPLEMENTED();
return false;
}
QNetworkInterface QNativeSocketEngine::multicastInterface() const
{
Q_UNIMPLEMENTED();
return QNetworkInterface();
}
bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface)
{
Q_UNUSED(iface);
Q_UNIMPLEMENTED();
return false;
}
qint64 QNativeSocketEngine::bytesAvailable() const
{
Q_D(const QNativeSocketEngine);
if (d->socketType != QAbstractSocket::TcpSocket)
return -1;
return d->readBytes.size() - d->readBytes.pos();
}
qint64 QNativeSocketEngine::read(char *data, qint64 maxlen)
{
Q_D(QNativeSocketEngine);
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.
if (d->readBytes.pos() == d->readBytes.size() && d->socketState != QAbstractSocket::ConnectedState) {
close();
return -1;
}
QMutexLocker mutexLocker(&d->readMutex);
return d->readBytes.read(data, maxlen);
}
qint64 QNativeSocketEngine::write(const char *data, qint64 len)
{
Q_D(QNativeSocketEngine);
if (!isValid())
return -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, QNativeSocketEnginePrivate::AccessErrorString);
else if (bytesWritten > 0 && d->notifyOnWrite)
emit writeReady();
return bytesWritten;
}
qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header,
PacketHeaderOptions)
{
Q_D(QNativeSocketEngine);
if (d->socketType != QAbstractSocket::UdpSocket || d->pendingDatagrams.isEmpty()) {
if (header)
header->clear();
return -1;
}
WinRtDatagram datagram = d->pendingDatagrams.takeFirst();
if (header)
*header = datagram.header;
QByteArray readOrigin;
// Do not read the whole datagram. Put the rest of it back into the "queue"
if (maxlen < datagram.data.length()) {
QByteArray readOrigin = datagram.data.left(maxlen);
datagram.data = datagram.data.remove(0, maxlen);
d->pendingDatagrams.prepend(datagram);
} else {
readOrigin = datagram.data;
}
memcpy(data, readOrigin, qMin(maxlen, qint64(datagram.data.length())));
return readOrigin.length();
}
qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
{
Q_D(QNativeSocketEngine);
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);
}
bool QNativeSocketEngine::hasPendingDatagrams() const
{
Q_D(const QNativeSocketEngine);
return d->pendingDatagrams.length() > 0;
}
qint64 QNativeSocketEngine::pendingDatagramSize() const
{
Q_D(const QNativeSocketEngine);
if (d->pendingDatagrams.isEmpty())
return -1;
return d->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)
{
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)
{
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)
{
Q_D(QNativeSocketEngine);
return d->setOption(option, value);
}
bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
{
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->readMutex);
if (!d->readBytes.atEnd())
return true;
// Nothing to do, wait for more events
d->eventLoop.processEvents();
}
d->setError(QAbstractSocket::SocketTimeoutError,
QNativeSocketEnginePrivate::TimeOutErrorString);
if (timedOut)
*timedOut = true;
return false;
}
bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
{
Q_UNUSED(msecs);
Q_UNUSED(timedOut);
Q_D(QNativeSocketEngine);
if (d->socketState == QAbstractSocket::ConnectingState) {
HRESULT hr = QWinRTFunctions::await(d->connectOp, QWinRTFunctions::ProcessMainThreadEvents);
if (SUCCEEDED(hr)) {
d->handleConnectionEstablished(d->connectOp.Get());
return true;
}
}
return false;
}
bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, int msecs, bool *timedOut)
{
Q_UNUSED(readyToRead);
Q_UNUSED(readyToWrite);
Q_UNUSED(checkRead);
Q_UNUSED(checkWrite);
Q_UNUSED(msecs);
Q_UNUSED(timedOut);
return false;
}
bool QNativeSocketEngine::isReadNotificationEnabled() const
{
Q_D(const QNativeSocketEngine);
return d->notifyOnRead;
}
void QNativeSocketEngine::setReadNotificationEnabled(bool enable)
{
Q_D(QNativeSocketEngine);
d->notifyOnRead = enable;
}
bool QNativeSocketEngine::isWriteNotificationEnabled() const
{
Q_D(const QNativeSocketEngine);
return d->notifyOnWrite;
}
void QNativeSocketEngine::setWriteNotificationEnabled(bool 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)
{
Q_D(QNativeSocketEngine);
d->notifyOnException = enable;
}
void QNativeSocketEngine::establishRead()
{
Q_D(QNativeSocketEngine);
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
ComPtr<IInputStream> stream;
HRESULT hr = d->tcpSocket()->get_InputStream(&stream);
RETURN_HR_IF_FAILED("establishRead(): Failed to get socket input stream");
ComPtr<IBuffer> buffer;
hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer);
RETURN_HR_IF_FAILED("establishRead(): Failed to create buffer");
hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, &d->readOp);
RETURN_HR_IF_FAILED("establishRead(): Failed to initiate socket read");
hr = d->readOp->put_Completed(Callback<SocketReadCompletedHandler>(d, &QNativeSocketEnginePrivate::handleReadyRead).Get());
RETURN_HR_IF_FAILED("establishRead(): Failed to register read callback");
return S_OK;
});
Q_ASSERT_SUCCEEDED(hr);
}
bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &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);
Q_ASSERT_SUCCEEDED(hr);
socketDescriptor = qintptr(socket.Detach());
break;
}
case QAbstractSocket::UdpSocket: {
ComPtr<IDatagramSocket> socket;
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &socket);
Q_ASSERT_SUCCEEDED(hr);
socketDescriptor = qintptr(socket.Detach());
hr = QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr = udpSocket()->add_MessageReceived(Callback<DatagramReceivedHandler>(this, &QNativeSocketEnginePrivate::handleNewDatagram).Get(), &connectionToken);
return hr;
});
Q_ASSERT_SUCCEEDED(hr);
break;
}
default:
qWarning("Invalid socket type");
return false;
}
this->socketType = socketType;
// Make the socket nonblocking.
if (!setOption(QAbstractSocketEngine::NonBlockingSocketOption, 1)) {
setError(QAbstractSocket::UnsupportedSocketOperationError, NonBlockingInitFailedErrorString);
q_func()->close();
return false;
}
return true;
}
QNativeSocketEnginePrivate::QNativeSocketEnginePrivate()
: QAbstractSocketEnginePrivate()
, notifyOnRead(true)
, notifyOnWrite(true)
, notifyOnException(false)
, closingDown(false)
, socketDescriptor(-1)
, sslSocket(Q_NULLPTR)
, connectionToken( { -1 } )
{
}
QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate()
{
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);
}
void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, ErrorString errorString) const
{
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 NonBlockingInitFailedErrorString:
socketErrorString = QNativeSocketEngine::tr("Unable to initialize non-blocking socket");
break;
case BroadcastingInitFailedErrorString:
socketErrorString = QNativeSocketEngine::tr("Unable to initialize broadcast socket");
break;
// should not happen anymore
case NoIpV6ErrorString:
socketErrorString = QNativeSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support");
break;
case RemoteHostClosedErrorString:
socketErrorString = QNativeSocketEngine::tr("The remote host closed the connection");
break;
case TimeOutErrorString:
socketErrorString = QNativeSocketEngine::tr("Network operation timed out");
break;
case ResourceErrorString:
socketErrorString = QNativeSocketEngine::tr("Out of resources");
break;
case OperationUnsupportedErrorString:
socketErrorString = QNativeSocketEngine::tr("Unsupported socket operation");
break;
case ProtocolUnsupportedErrorString:
socketErrorString = QNativeSocketEngine::tr("Protocol type not supported");
break;
case InvalidSocketErrorString:
socketErrorString = QNativeSocketEngine::tr("Invalid socket descriptor");
break;
case HostUnreachableErrorString:
socketErrorString = QNativeSocketEngine::tr("Host unreachable");
break;
case NetworkUnreachableErrorString:
socketErrorString = QNativeSocketEngine::tr("Network unreachable");
break;
case AccessErrorString:
socketErrorString = QNativeSocketEngine::tr("Permission denied");
break;
case ConnectionTimeOutErrorString:
socketErrorString = QNativeSocketEngine::tr("Connection timed out");
break;
case ConnectionRefusedErrorString:
socketErrorString = QNativeSocketEngine::tr("Connection refused");
break;
case AddressInuseErrorString:
socketErrorString = QNativeSocketEngine::tr("The bound address is already in use");
break;
case AddressNotAvailableErrorString:
socketErrorString = QNativeSocketEngine::tr("The address is not available");
break;
case AddressProtectedErrorString:
socketErrorString = QNativeSocketEngine::tr("The address is protected");
break;
case DatagramTooLargeErrorString:
socketErrorString = QNativeSocketEngine::tr("Datagram was too large to send");
break;
case SendDatagramErrorString:
socketErrorString = QNativeSocketEngine::tr("Unable to send a message");
break;
case ReceiveDatagramErrorString:
socketErrorString = QNativeSocketEngine::tr("Unable to receive a message");
break;
case WriteErrorString:
socketErrorString = QNativeSocketEngine::tr("Unable to write");
break;
case ReadErrorString:
socketErrorString = QNativeSocketEngine::tr("Network error");
break;
case PortInuseErrorString:
socketErrorString = QNativeSocketEngine::tr("Another socket is already listening on the same port");
break;
case NotSocketErrorString:
socketErrorString = QNativeSocketEngine::tr("Operation on non-socket");
break;
case InvalidProxyTypeString:
socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation");
break;
case TemporaryErrorString:
socketErrorString = QNativeSocketEngine::tr("Temporary error");
break;
case 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:
default:
return -1;
}
return -1;
}
bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption opt, int 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:
default:
return false;
}
return false;
}
bool QNativeSocketEnginePrivate::fetchConnectionParameters()
{
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)
{
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;
}
HRESULT QNativeSocketEnginePrivate::handleConnectToHost(IAsyncAction *action, AsyncStatus)
{
handleConnectionEstablished(action);
return S_OK;
}
void QNativeSocketEnginePrivate::handleConnectionEstablished(IAsyncAction *action)
{
Q_Q(QNativeSocketEngine);
if (wasDeleted || !connectOp) // Protect against a late callback
return;
HRESULT hr = action->GetResults();
switch (hr) {
case 0x8007274c: // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString);
socketState = QAbstractSocket::UnconnectedState;
break;
case 0x80072751: // A socket operation was attempted to an unreachable host.
setError(QAbstractSocket::HostNotFoundError, HostUnreachableErrorString);
socketState = QAbstractSocket::UnconnectedState;
break;
case 0x8007274d: // No connection could be made because the target machine actively refused it.
setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
socketState = QAbstractSocket::UnconnectedState;
break;
default:
if (FAILED(hr)) {
setError(QAbstractSocket::UnknownSocketError, UnknownSocketErrorString);
socketState = QAbstractSocket::UnconnectedState;
}
break;
}
// 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);
}
socketState = QAbstractSocket::ConnectedState;
emit q->connectionReady();
if (socketType != QAbstractSocket::TcpSocket)
return;
// Delay the reader so that the SSL socket can upgrade
if (sslSocket)
QObject::connect(qobject_cast<QSslSocket *>(sslSocket), &QSslSocket::encrypted, q, &QNativeSocketEngine::establishRead);
else
q->establishRead();
}
HRESULT QNativeSocketEnginePrivate::handleReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
{
Q_Q(QNativeSocketEngine);
if (wasDeleted || isDeletingChildren)
return S_OK;
// 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) {
setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString);
socketState = QAbstractSocket::UnconnectedState;
if (notifyOnRead)
emit q->readReady();
return S_OK;
}
ComPtr<IBuffer> buffer;
HRESULT hr = asyncInfo->GetResults(&buffer);
RETURN_OK_IF_FAILED("Failed to get read results buffer");
UINT32 bufferLength;
hr = buffer->get_Length(&bufferLength);
Q_ASSERT_SUCCEEDED(hr);
// 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) {
setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString);
socketState = QAbstractSocket::UnconnectedState;
if (notifyOnRead)
emit q->readReady();
return S_OK;
}
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
hr = buffer.As(&byteArrayAccess);
Q_ASSERT_SUCCEEDED(hr);
byte *data;
hr = byteArrayAccess->Buffer(&data);
Q_ASSERT_SUCCEEDED(hr);
readMutex.lock();
if (readBytes.atEnd()) // Everything has been read; the buffer is safe to reset
readBytes.close();
if (!readBytes.isOpen())
readBytes.open(QBuffer::ReadWrite|QBuffer::Truncate);
qint64 readPos = readBytes.pos();
readBytes.seek(readBytes.size());
Q_ASSERT(readBytes.atEnd());
readBytes.write(reinterpret_cast<const char*>(data), qint64(bufferLength));
readBytes.seek(readPos);
readMutex.unlock();
if (notifyOnRead)
emit q->readReady();
hr = QEventDispatcherWinRT::runOnXamlThread([buffer, q, this]() {
UINT32 readBufferLength;
ComPtr<IInputStream> stream;
HRESULT hr = tcpSocket()->get_InputStream(&stream);
RETURN_HR_IF_FAILED("handleReadyRead(): Could not obtain input stream");
// Reuse the stream buffer
hr = buffer->get_Capacity(&readBufferLength);
RETURN_HR_IF_FAILED("handleReadyRead(): Could not obtain buffer capacity");
hr = buffer->put_Length(0);
RETURN_HR_IF_FAILED("handleReadyRead(): Could not set buffer length");
hr = stream->ReadAsync(buffer.Get(), readBufferLength, InputStreamOptions_Partial, &readOp);
if (FAILED(hr)) {
qErrnoWarning(hr, "handleReadyRead(): Could not read into socket stream buffer (%s).",
socketDescription(q).constData());
return S_OK;
}
hr = readOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &QNativeSocketEnginePrivate::handleReadyRead).Get());
if (FAILED(hr)) {
qErrnoWarning(hr, "handleReadyRead(): Failed to set socket read callback (%s).",
socketDescription(q).constData());
return S_OK;
}
return S_OK;
});
Q_ASSERT_SUCCEEDED(hr);
return S_OK;
}
HRESULT QNativeSocketEnginePrivate::handleNewDatagram(IDatagramSocket *socket, IDatagramSocketMessageReceivedEventArgs *args)
{
Q_Q(QNativeSocketEngine);
Q_UNUSED(socket);
WinRtDatagram datagram;
QHostAddress returnAddress;
ComPtr<IHostName> remoteHost;
HRESULT hr = args->get_RemoteAddress(&remoteHost);
RETURN_OK_IF_FAILED("Could not obtain remote host");
HString remoteHostString;
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");
pendingDatagrams.append(datagram);
if (notifyOnRead)
emit q->readReady();
return S_OK;
}
QT_END_NAMESPACE