summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/winrt/qwinrtfileengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/winrt/qwinrtfileengine.cpp')
-rw-r--r--src/plugins/platforms/winrt/qwinrtfileengine.cpp505
1 files changed, 505 insertions, 0 deletions
diff --git a/src/plugins/platforms/winrt/qwinrtfileengine.cpp b/src/plugins/platforms/winrt/qwinrtfileengine.cpp
new file mode 100644
index 0000000000..3a4aa519cc
--- /dev/null
+++ b/src/plugins/platforms/winrt/qwinrtfileengine.cpp
@@ -0,0 +1,505 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtfileengine.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QHash>
+#include <QtCore/qfunctions_winrt.h>
+
+#include <wrl.h>
+#include <windows.storage.h>
+#include <robuffer.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Storage;
+using namespace ABI::Windows::Storage::Streams;
+
+typedef IAsyncOperationCompletedHandler<IRandomAccessStream *> StreamCompletedHandler;
+typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> StreamReadCompletedHandler;
+
+QT_BEGIN_NAMESPACE
+
+#define RETURN_AND_SET_ERROR_IF_FAILED(error, ret) \
+ setError(error, qt_error_string(hr)); \
+ if (FAILED(hr)) \
+ return ret;
+
+Q_GLOBAL_STATIC(QWinRTFileEngineHandler, handlerInstance)
+
+class QWinRTFileEngineHandlerPrivate
+{
+public:
+ QHash<QString, ComPtr<IStorageItem>> files;
+};
+
+class QWinRTFileEnginePrivate
+{
+public:
+ QWinRTFileEnginePrivate(const QString &fileName, IStorageItem *file)
+ : fileName(fileName), file(file)
+ {
+ HRESULT hr;
+ hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
+ IID_PPV_ARGS(&bufferFactory));
+ Q_ASSERT_SUCCEEDED(hr);
+
+ lastSeparator = fileName.size() - 1;
+ for (int i = lastSeparator; i >= 0; --i) {
+ if (fileName.at(i).unicode() == '/' || fileName.at(i).unicode() == '\\') {
+ lastSeparator = i;
+ break;
+ }
+ }
+
+ firstDot = fileName.size();
+ for (int i = lastSeparator; i > fileName.size(); ++i) {
+ if (fileName.at(i).unicode() == '.') {
+ firstDot = i;
+ break;
+ }
+ }
+ }
+
+ ComPtr<IBufferFactory> bufferFactory;
+
+ QString fileName;
+ int lastSeparator;
+ int firstDot;
+ ComPtr<IStorageItem> file;
+ ComPtr<IRandomAccessStream> stream;
+
+ qint64 pos;
+
+private:
+ QWinRTFileEngineHandler *q_ptr;
+ Q_DECLARE_PUBLIC(QWinRTFileEngineHandler)
+};
+
+
+QWinRTFileEngineHandler::QWinRTFileEngineHandler()
+ : d_ptr(new QWinRTFileEngineHandlerPrivate)
+{
+}
+
+QWinRTFileEngineHandler::~QWinRTFileEngineHandler()
+{
+}
+
+void QWinRTFileEngineHandler::registerFile(const QString &fileName, IStorageItem *file)
+{
+ handlerInstance->d_func()->files.insert(QDir::cleanPath(fileName), file);
+}
+
+IStorageItem *QWinRTFileEngineHandler::registeredFile(const QString &fileName)
+{
+ return handlerInstance->d_func()->files.value(fileName).Get();
+}
+
+QAbstractFileEngine *QWinRTFileEngineHandler::create(const QString &fileName) const
+{
+ Q_D(const QWinRTFileEngineHandler);
+
+ QHash<QString, ComPtr<IStorageItem>>::const_iterator file = d->files.find(fileName);
+ if (file != d->files.end())
+ return new QWinRTFileEngine(fileName, file.value().Get());
+
+ return Q_NULLPTR;
+}
+
+static HRESULT getDestinationFolder(const QString &fileName, const QString newFileName,
+ IStorageItem *file, IStorageFolder **folder)
+{
+ HRESULT hr;
+ ComPtr<IAsyncOperation<StorageFolder *>> op;
+ QFileInfo newFileInfo(newFileName);
+#ifndef Q_OS_WINPHONE
+ QFileInfo fileInfo(fileName);
+ if (fileInfo.dir() == newFileInfo.dir()) {
+ ComPtr<IStorageItem2> item;
+ hr = file->QueryInterface(IID_PPV_ARGS(&item));
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = item->GetParentAsync(&op);
+ } else
+#else
+ Q_UNUSED(fileName);
+ Q_UNUSED(file)
+#endif
+ {
+ ComPtr<IStorageFolderStatics> folderFactory;
+ hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_StorageFolder).Get(),
+ IID_PPV_ARGS(&folderFactory));
+ Q_ASSERT_SUCCEEDED(hr);
+
+ const QString newFilePath = QDir::toNativeSeparators(newFileInfo.absolutePath());
+ HStringReference nativeNewFilePath(reinterpret_cast<LPCWSTR>(newFilePath.utf16()),
+ newFilePath.length());
+ hr = folderFactory->GetFolderFromPathAsync(nativeNewFilePath.Get(), &op);
+ }
+ if (FAILED(hr))
+ return hr;
+ return QWinRTFunctions::await(op, folder);
+}
+
+QWinRTFileEngine::QWinRTFileEngine(const QString &fileName, IStorageItem *file)
+ : d_ptr(new QWinRTFileEnginePrivate(fileName, file))
+{
+}
+
+QWinRTFileEngine::~QWinRTFileEngine()
+{
+}
+
+bool QWinRTFileEngine::open(QIODevice::OpenMode openMode)
+{
+ Q_D(QWinRTFileEngine);
+
+ FileAccessMode fileAccessMode = (openMode & QIODevice::WriteOnly)
+ ? FileAccessMode_ReadWrite : FileAccessMode_Read;
+
+ HRESULT hr;
+ ComPtr<IStorageFile> file;
+ hr = d->file.As(&file);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::OpenError, false);
+
+ ComPtr<IAsyncOperation<IRandomAccessStream *>> op;
+ hr = file->OpenAsync(fileAccessMode, &op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::OpenError, false);
+
+ hr = QWinRTFunctions::await(op, d->stream.GetAddressOf());
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::OpenError, false);
+
+ return SUCCEEDED(hr);
+}
+
+bool QWinRTFileEngine::close()
+{
+ Q_D(QWinRTFileEngine);
+
+ if (!d->stream)
+ return false;
+
+ ComPtr<IClosable> closable;
+ HRESULT hr = d->stream.As(&closable);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = closable->Close();
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::UnspecifiedError, false);
+ d->stream.Reset();
+ return SUCCEEDED(hr);
+}
+
+qint64 QWinRTFileEngine::size() const
+{
+ Q_D(const QWinRTFileEngine);
+
+ if (!d->stream)
+ return 0;
+
+ UINT64 size;
+ HRESULT hr;
+ hr = d->stream->get_Size(&size);
+ RETURN_IF_FAILED("Failed to get file size", return 0);
+
+ return qint64(size);
+}
+
+qint64 QWinRTFileEngine::pos() const
+{
+ Q_D(const QWinRTFileEngine);
+ return d->pos;
+}
+
+bool QWinRTFileEngine::seek(qint64 pos)
+{
+ Q_D(QWinRTFileEngine);
+
+ if (!d->stream)
+ return false;
+
+ HRESULT hr = d->stream->Seek(pos);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::PositionError, false);
+ d->pos = pos;
+ return SUCCEEDED(hr);
+}
+
+bool QWinRTFileEngine::remove()
+{
+ Q_D(QWinRTFileEngine);
+
+ ComPtr<IAsyncAction> op;
+ HRESULT hr = d->file->DeleteAsync(StorageDeleteOption_Default, &op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RemoveError, false);
+
+ hr = QWinRTFunctions::await(op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RemoveError, false);
+ return SUCCEEDED(hr);
+}
+
+bool QWinRTFileEngine::copy(const QString &newName)
+{
+ Q_D(QWinRTFileEngine);
+
+ HRESULT hr;
+ ComPtr<IStorageFolder> destinationFolder;
+ hr = getDestinationFolder(d->fileName, newName, d->file.Get(), destinationFolder.GetAddressOf());
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
+
+ ComPtr<IStorageFile> file;
+ hr = d->file.As(&file);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
+
+ const QString destinationName = QFileInfo(newName).fileName();
+ HStringReference nativeDestinationName(reinterpret_cast<LPCWSTR>(destinationName.utf16()), destinationName.length());
+ ComPtr<IAsyncOperation<StorageFile *>> op;
+ hr = file->CopyOverloadDefaultOptions(destinationFolder.Get(), nativeDestinationName.Get(), &op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
+
+ ComPtr<IStorageFile> newFile;
+ hr = QWinRTFunctions::await(op, newFile.GetAddressOf());
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
+ return SUCCEEDED(hr);
+}
+
+bool QWinRTFileEngine::rename(const QString &newName)
+{
+ Q_D(QWinRTFileEngine);
+
+ HRESULT hr;
+ ComPtr<IStorageFolder> destinationFolder;
+ hr = getDestinationFolder(d->fileName, newName, d->file.Get(), destinationFolder.GetAddressOf());
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
+
+ const QString destinationName = QFileInfo(newName).fileName();
+ HStringReference nativeDestinationName(reinterpret_cast<LPCWSTR>(destinationName.utf16()), destinationName.length());
+ ComPtr<IAsyncAction> op;
+ hr = d->file->RenameAsyncOverloadDefaultOptions(nativeDestinationName.Get(), &op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
+ return SUCCEEDED(hr);
+}
+
+bool QWinRTFileEngine::renameOverwrite(const QString &newName)
+{
+ Q_D(QWinRTFileEngine);
+
+ HRESULT hr;
+ ComPtr<IStorageFolder> destinationFolder;
+ hr = getDestinationFolder(d->fileName, newName, d->file.Get(), destinationFolder.GetAddressOf());
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
+
+ const QString destinationName = QFileInfo(newName).fileName();
+ HStringReference nativeDestinationName(reinterpret_cast<LPCWSTR>(destinationName.utf16()), destinationName.length());
+ ComPtr<IAsyncAction> op;
+ hr = d->file->RenameAsync(nativeDestinationName.Get(), NameCollisionOption_ReplaceExisting, &op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
+ return SUCCEEDED(hr);
+}
+
+QAbstractFileEngine::FileFlags QWinRTFileEngine::fileFlags(FileFlags type) const
+{
+ Q_D(const QWinRTFileEngine);
+
+ FileFlags flags = ExistsFlag|ReadOwnerPerm|ReadUserPerm|WriteOwnerPerm|WriteUserPerm;
+
+ HRESULT hr;
+ FileAttributes attributes;
+ hr = d->file->get_Attributes(&attributes);
+ RETURN_IF_FAILED("Failed to get file attributes", return flags);
+ if (attributes & FileAttributes_ReadOnly)
+ flags ^= WriteUserPerm;
+ if (attributes & FileAttributes_Directory)
+ flags |= DirectoryType;
+ else
+ flags |= FileType;
+
+ return type & flags;
+}
+
+bool QWinRTFileEngine::setPermissions(uint perms)
+{
+ Q_UNUSED(perms);
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+QString QWinRTFileEngine::fileName(FileName type) const
+{
+ Q_D(const QWinRTFileEngine);
+
+ switch (type) {
+ default:
+ case DefaultName:
+ case AbsoluteName:
+ case CanonicalName:
+ break;
+ case BaseName:
+ return d->lastSeparator < 0
+ ? d->fileName : d->fileName.mid(d->lastSeparator, d->firstDot - d->lastSeparator);
+ case PathName:
+ case AbsolutePathName:
+ case CanonicalPathName:
+ return d->fileName.mid(0, d->lastSeparator);
+ case LinkName:
+ case BundleName:
+ return QString();
+ }
+ return d->fileName;
+}
+
+QDateTime QWinRTFileEngine::fileTime(FileTime type) const
+{
+ Q_D(const QWinRTFileEngine);
+
+ HRESULT hr;
+ DateTime dateTime = { 0 };
+ switch (type) {
+ case CreationTime:
+ hr = d->file->get_DateCreated(&dateTime);
+ RETURN_IF_FAILED("Failed to get file creation time", return QDateTime());
+ break;
+ case ModificationTime:
+ case AccessTime: {
+ ComPtr<IAsyncOperation<FileProperties::BasicProperties *>> op;
+ hr = d->file->GetBasicPropertiesAsync(&op);
+ RETURN_IF_FAILED("Failed to initiate file properties", return QDateTime());
+ ComPtr<FileProperties::IBasicProperties> properties;
+ hr = QWinRTFunctions::await(op, properties.GetAddressOf());
+ RETURN_IF_FAILED("Failed to get file properties", return QDateTime());
+ hr = type == ModificationTime ? properties->get_DateModified(&dateTime)
+ : properties->get_ItemDate(&dateTime);
+ RETURN_IF_FAILED("Failed to get file date", return QDateTime());
+ }
+ break;
+ }
+
+ SYSTEMTIME systemTime;
+ FileTimeToSystemTime((const FILETIME *)&dateTime, &systemTime);
+ QDate date(systemTime.wYear, systemTime.wMonth, systemTime.wDay);
+ QTime time(systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
+ return QDateTime(date, time);
+}
+
+qint64 QWinRTFileEngine::read(char *data, qint64 maxlen)
+{
+ Q_D(QWinRTFileEngine);
+
+ if (!d->stream)
+ return -1;
+
+ ComPtr<IInputStream> stream;
+ HRESULT hr = d->stream.As(&stream);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
+
+ UINT32 length = qBound(quint64(0), quint64(maxlen), quint64(UINT_MAX));
+ ComPtr<IBuffer> buffer;
+ hr = d->bufferFactory->Create(length, &buffer);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
+
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> op;
+ hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
+
+ hr = QWinRTFunctions::await(op, buffer.GetAddressOf());
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
+
+ hr = buffer->get_Length(&length);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
+
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
+ hr = buffer.As(&byteArrayAccess);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
+
+ byte *bytes;
+ hr = byteArrayAccess->Buffer(&bytes);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
+ memcpy(data, bytes, length);
+ return qint64(length);
+}
+
+qint64 QWinRTFileEngine::write(const char *data, qint64 maxlen)
+{
+ Q_D(QWinRTFileEngine);
+
+ if (!d->stream)
+ return -1;
+
+ ComPtr<IOutputStream> stream;
+ HRESULT hr = d->stream.As(&stream);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+
+ UINT32 length = qBound(quint64(0), quint64(maxlen), quint64(UINT_MAX));
+ ComPtr<IBuffer> buffer;
+ hr = d->bufferFactory->Create(length, &buffer);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+ hr = buffer->put_Length(length);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
+ hr = buffer.As(&byteArrayAccess);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+
+ byte *bytes;
+ hr = byteArrayAccess->Buffer(&bytes);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+ memcpy(bytes, data, length);
+
+ ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
+ hr = stream->WriteAsync(buffer.Get(), &op);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+
+ hr = QWinRTFunctions::await(op, &length);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+
+ ComPtr<IAsyncOperation<bool>> flushOp;
+ hr = stream->FlushAsync(&flushOp);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+ boolean flushed;
+ hr = QWinRTFunctions::await(flushOp, &flushed);
+ RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
+
+ return qint64(length);
+}
+
+QT_END_NAMESPACE