1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwinrtfiledialoghelper.h"
41 #include "qwinrtfileengine.h"
42 
43 #include <QtCore/qcoreapplication.h>
44 #include <QtCore/QEventLoop>
45 #include <QtCore/QMap>
46 #include <QtCore/QVector>
47 #include <QtCore/qfunctions_winrt.h>
48 #include <private/qeventdispatcher_winrt_p.h>
49 
50 #include <functional>
51 #include <wrl.h>
52 #include <windows.foundation.h>
53 #include <windows.storage.pickers.h>
54 #include <Windows.Applicationmodel.Activation.h>
55 
56 using namespace Microsoft::WRL;
57 using namespace Microsoft::WRL::Wrappers;
58 using namespace ABI::Windows::ApplicationModel::Activation;
59 using namespace ABI::Windows::Foundation;
60 using namespace ABI::Windows::Foundation::Collections;
61 using namespace ABI::Windows::Storage;
62 using namespace ABI::Windows::Storage::Pickers;
63 
64 typedef IAsyncOperationCompletedHandler<StorageFile *> SingleFileHandler;
65 typedef IAsyncOperationCompletedHandler<IVectorView<StorageFile *> *> MultipleFileHandler;
66 typedef IAsyncOperationCompletedHandler<StorageFolder *> SingleFolderHandler;
67 
68 QT_BEGIN_NAMESPACE
69 
70 // Required for save file picker
71 class WindowsStringVector : public RuntimeClass<IVector<HSTRING>>
72 {
73 public:
GetAt(quint32 index,HSTRING * item)74     HRESULT __stdcall GetAt(quint32 index, HSTRING *item)
75     {
76         *item = impl.at(int(index));
77         return S_OK;
78     }
get_Size(quint32 * size)79     HRESULT __stdcall get_Size(quint32 *size)
80     {
81         *size = quint32(impl.size());
82         return S_OK;
83     }
GetView(IVectorView<HSTRING> ** view)84     HRESULT __stdcall GetView(IVectorView<HSTRING> **view)
85     {
86         *view = nullptr;
87         return E_NOTIMPL;
88     }
IndexOf(HSTRING value,quint32 * index,boolean * found)89     HRESULT __stdcall IndexOf(HSTRING value, quint32 *index, boolean *found)
90     {
91         *found = false;
92         for (int i = 0; i < impl.size(); ++i) {
93             qint32 result;
94             HRESULT hr = WindowsCompareStringOrdinal(impl.at(i), value, &result);
95             if (FAILED(hr))
96                 return hr;
97             if (result == 0) {
98                 *index = quint32(i);
99                 *found = true;
100                 break;
101             }
102         }
103         return S_OK;
104     }
SetAt(quint32 index,HSTRING item)105     HRESULT __stdcall SetAt(quint32 index, HSTRING item)
106     {
107         HSTRING newItem;
108         HRESULT hr = WindowsDuplicateString(item, &newItem);
109         if (FAILED(hr))
110             return hr;
111         impl[int(index)] = newItem;
112         return S_OK;
113     }
InsertAt(quint32 index,HSTRING item)114     HRESULT __stdcall InsertAt(quint32 index, HSTRING item)
115     {
116         HSTRING newItem;
117         HRESULT hr = WindowsDuplicateString(item, &newItem);
118         if (FAILED(hr))
119             return hr;
120         impl.insert(int(index), newItem);
121         return S_OK;
122     }
RemoveAt(quint32 index)123     HRESULT __stdcall RemoveAt(quint32 index)
124     {
125         WindowsDeleteString(impl.takeAt(int(index)));
126         return S_OK;
127     }
Append(HSTRING item)128     HRESULT __stdcall Append(HSTRING item)
129     {
130         HSTRING newItem;
131         HRESULT hr = WindowsDuplicateString(item, &newItem);
132         if (FAILED(hr))
133             return hr;
134         impl.append(newItem);
135         return S_OK;
136     }
RemoveAtEnd()137     HRESULT __stdcall RemoveAtEnd()
138     {
139         WindowsDeleteString(impl.takeLast());
140         return S_OK;
141     }
Clear()142     HRESULT __stdcall Clear()
143     {
144         foreach (const HSTRING &item, impl)
145             WindowsDeleteString(item);
146         impl.clear();
147         return S_OK;
148     }
149 private:
150     QVector<HSTRING> impl;
151 };
152 
153 template<typename T>
initializePicker(HSTRING runtimeId,T ** picker,const QSharedPointer<QFileDialogOptions> & options)154 static bool initializePicker(HSTRING runtimeId, T **picker, const QSharedPointer<QFileDialogOptions> &options)
155 {
156     HRESULT hr;
157 
158     ComPtr<IInspectable> basePicker;
159     hr = RoActivateInstance(runtimeId, &basePicker);
160     RETURN_FALSE_IF_FAILED("Failed to instantiate file picker");
161     hr = basePicker.Get()->QueryInterface(IID_PPV_ARGS(picker));
162     RETURN_FALSE_IF_FAILED("Failed to cast file picker");
163 
164     if (options->isLabelExplicitlySet(QFileDialogOptions::Accept)) {
165         const QString labelText = options->labelText(QFileDialogOptions::Accept);
166         HStringReference labelTextRef(reinterpret_cast<const wchar_t *>(labelText.utf16()),
167                                       uint(labelText.length()));
168         hr = (*picker)->put_CommitButtonText(labelTextRef.Get());
169         RETURN_FALSE_IF_FAILED("Failed to set commit button text");
170     }
171 
172     return true;
173 }
174 
175 template<typename T>
initializeOpenPickerOptions(T * picker,const QSharedPointer<QFileDialogOptions> & options)176 static bool initializeOpenPickerOptions(T *picker, const QSharedPointer<QFileDialogOptions> &options)
177 {
178     HRESULT hr;
179     hr = picker->put_ViewMode(options->viewMode() == QFileDialogOptions::Detail
180                               ? PickerViewMode_Thumbnail : PickerViewMode_List);
181     RETURN_FALSE_IF_FAILED("Failed to set picker view mode");
182 
183     ComPtr<IVector<HSTRING>> filters;
184     hr = picker->get_FileTypeFilter(&filters);
185     RETURN_FALSE_IF_FAILED("Failed to get file type filters list");
186     for (const QString &namedFilter : options->nameFilters()) {
187         for (const QString &filter : QPlatformFileDialogHelper::cleanFilterList(namedFilter)) {
188             // Remove leading star
189             const int offset = (filter.length() > 1 && filter.startsWith(QLatin1Char('*'))) ? 1 : 0;
190             HStringReference filterRef(reinterpret_cast<const wchar_t *>(filter.utf16() + offset),
191                                        uint(filter.length() - offset));
192             hr = filters->Append(filterRef.Get());
193             if (FAILED(hr)) {
194                 qWarning("Failed to add named file filter \"%s\": %s",
195                          qPrintable(filter), qPrintable(qt_error_string(hr)));
196             }
197         }
198     }
199     // The file dialog won't open with an empty list - add a default wildcard
200     quint32 size;
201     hr = filters->get_Size(&size);
202     RETURN_FALSE_IF_FAILED("Failed to get file type filters list size");
203     if (!size) {
204         hr = filters->Append(HString::MakeReference(L"*").Get());
205         RETURN_FALSE_IF_FAILED("Failed to add default wildcard to file type filters list");
206     }
207 
208     return true;
209 }
210 
pickFiles(IFileOpenPicker * picker,QWinRTFileDialogHelper * helper,bool singleFile)211 static bool pickFiles(IFileOpenPicker *picker, QWinRTFileDialogHelper *helper, bool singleFile)
212 {
213     Q_ASSERT(picker);
214     Q_ASSERT(helper);
215     HRESULT hr;
216     hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper, singleFile]() {
217         HRESULT hr;
218         if (singleFile) {
219             ComPtr<IAsyncOperation<StorageFile *>> op;
220             hr = picker->PickSingleFileAsync(&op);
221             RETURN_HR_IF_FAILED("Failed to open single file picker");
222             hr = op->put_Completed(Callback<SingleFileHandler>(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get());
223             RETURN_HR_IF_FAILED("Failed to attach file picker callback");
224         } else {
225             ComPtr<IAsyncOperation<IVectorView<StorageFile *> *>> op;
226             hr = picker->PickMultipleFilesAsync(&op);
227             RETURN_HR_IF_FAILED("Failed to open multi file picker");
228             hr = op->put_Completed(Callback<MultipleFileHandler>(helper, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get());
229             RETURN_HR_IF_FAILED("Failed to attach multi file callback");
230         }
231         return S_OK;
232     });
233     return SUCCEEDED(hr);
234 }
235 
pickFolder(IFolderPicker * picker,QWinRTFileDialogHelper * helper)236 static bool pickFolder(IFolderPicker *picker, QWinRTFileDialogHelper *helper)
237 {
238     Q_ASSERT(picker);
239     Q_ASSERT(helper);
240     HRESULT hr;
241     hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper]() {
242         HRESULT hr;
243         ComPtr<IAsyncOperation<StorageFolder *>> op;
244         hr = picker->PickSingleFolderAsync(&op);
245         RETURN_HR_IF_FAILED("Failed to open folder picker");
246         hr = op->put_Completed(Callback<SingleFolderHandler>(helper, &QWinRTFileDialogHelper::onSingleFolderPicked).Get());
247         RETURN_HR_IF_FAILED("Failed to attach folder picker callback");
248         return S_OK;
249     });
250     return SUCCEEDED(hr);
251 }
252 
pickSaveFile(IFileSavePicker * picker,QWinRTFileDialogHelper * helper)253 static bool pickSaveFile(IFileSavePicker *picker, QWinRTFileDialogHelper *helper)
254 {
255     Q_ASSERT(picker);
256     Q_ASSERT(helper);
257     HRESULT hr;
258     hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper]() {
259         HRESULT hr;
260         ComPtr<IAsyncOperation<StorageFile *>> op;
261         hr = picker->PickSaveFileAsync(&op);
262         RETURN_HR_IF_FAILED("Failed to open save file picker");
263         hr = op->put_Completed(Callback<SingleFileHandler>(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get());
264         RETURN_HR_IF_FAILED("Failed to attach save file picker callback");
265         return S_OK;
266     });
267     return SUCCEEDED(hr);
268 }
269 
270 class QWinRTFileDialogHelperPrivate
271 {
272 public:
273     bool shown;
274     QEventLoop loop;
275 
276     // Input
277     QUrl directory;
278     QUrl saveFileName;
279     QString selectedNameFilter;
280 
281     // Output
282     QList<QUrl> selectedFiles;
283 };
284 
QWinRTFileDialogHelper()285 QWinRTFileDialogHelper::QWinRTFileDialogHelper()
286     : QPlatformFileDialogHelper(), d_ptr(new QWinRTFileDialogHelperPrivate)
287 {
288     Q_D(QWinRTFileDialogHelper);
289 
290     d->shown = false;
291 }
292 
exec()293 void QWinRTFileDialogHelper::exec()
294 {
295     Q_D(QWinRTFileDialogHelper);
296 
297     if (!d->shown)
298         show(Qt::Dialog, Qt::ApplicationModal, nullptr);
299     d->loop.exec();
300 }
301 
show(Qt::WindowFlags windowFlags,Qt::WindowModality windowModality,QWindow * parent)302 bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
303 {
304     Q_UNUSED(windowFlags)
305     Q_UNUSED(windowModality)
306     Q_UNUSED(parent)
307     Q_D(QWinRTFileDialogHelper);
308 
309     HRESULT hr;
310     const QSharedPointer<QFileDialogOptions> dialogOptions = options();
311     switch (dialogOptions->acceptMode()) {
312     default:
313     case QFileDialogOptions::AcceptOpen: {
314         switch (dialogOptions->fileMode()) {
315         case QFileDialogOptions::AnyFile:
316         case QFileDialogOptions::ExistingFile:
317         case QFileDialogOptions::ExistingFiles: {
318             ComPtr<IFileOpenPicker> picker;
319             if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FileOpenPicker).Get(),
320                                   picker.GetAddressOf(), dialogOptions)) {
321                 return false;
322             }
323             if (!initializeOpenPickerOptions(picker.Get(), dialogOptions))
324                 return false;
325 
326             if (!pickFiles(picker.Get(), this, dialogOptions->fileMode() == QFileDialogOptions::ExistingFile))
327                 return false;
328 
329             break;
330         }
331         case QFileDialogOptions::Directory:
332         case QFileDialogOptions::DirectoryOnly: {
333             ComPtr<IFolderPicker> picker;
334             if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FolderPicker).Get(),
335                                   picker.GetAddressOf(), dialogOptions)) {
336                 return false;
337             }
338             if (!initializeOpenPickerOptions(picker.Get(), dialogOptions))
339                 return false;
340 
341             if (!pickFolder(picker.Get(), this))
342                 return false;
343 
344             break;
345         }
346         }
347         break;
348     }
349     case QFileDialogOptions::AcceptSave: {
350         ComPtr<IFileSavePicker> picker;
351         if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FileSavePicker).Get(),
352                               picker.GetAddressOf(), dialogOptions)) {
353             return false;
354         }
355 
356         if (!dialogOptions->nameFilters().isEmpty()) {
357             ComPtr<IMap<HSTRING, IVector<HSTRING> *>> choices;
358             hr = picker->get_FileTypeChoices(&choices);
359             RETURN_FALSE_IF_FAILED("Failed to get file extension choices");
360             const QStringList nameFilters = dialogOptions->nameFilters();
361             for (const QString &namedFilter : nameFilters) {
362                 ComPtr<IVector<HSTRING>> entry = Make<WindowsStringVector>();
363                 const QStringList cleanFilter = QPlatformFileDialogHelper::cleanFilterList(namedFilter);
364                 for (const QString &filter : cleanFilter) {
365                     // Remove leading star
366                     const int starOffset = (filter.length() > 1 && filter.startsWith(QLatin1Char('*'))) ? 1 : 0;
367                     HStringReference filterRef(reinterpret_cast<const wchar_t *>(filter.utf16() + starOffset),
368                                                uint(filter.length() - starOffset));
369                     hr = entry->Append(filterRef.Get());
370                     if (FAILED(hr)) {
371                         qWarning("Failed to add named file filter \"%s\": %s",
372                                  qPrintable(filter), qPrintable(qt_error_string(hr)));
373                     }
374                 }
375                 const int offset = namedFilter.indexOf(QLatin1String(" ("));
376                 const QString filterTitle = namedFilter.mid(0, offset);
377                 HStringReference namedFilterRef(reinterpret_cast<const wchar_t *>(filterTitle.utf16()),
378                                                 uint(filterTitle.length()));
379                 boolean replaced;
380                 hr = choices->Insert(namedFilterRef.Get(), entry.Get(), &replaced);
381                 // Only print a warning as * or *.* is not a valid choice on Windows 10
382                 // but used on a regular basis on all other platforms
383                 if (FAILED(hr)) {
384                     qWarning("Failed to insert file extension choice entry: %s: %s",
385                              qPrintable(filterTitle), qPrintable(qt_error_string(hr)));
386                 }
387             }
388         }
389 
390         QString suffix = dialogOptions->defaultSuffix();
391         if (!suffix.isEmpty()) {
392             if (!suffix.startsWith(QLatin1Char('.')))
393                 suffix.prepend(QLatin1Char('.'));
394             HStringReference nativeSuffix(reinterpret_cast<const wchar_t *>(suffix.utf16()),
395                                           uint(suffix.length()));
396             hr = picker->put_DefaultFileExtension(nativeSuffix.Get());
397             RETURN_FALSE_IF_FAILED_WITH_ARGS("Failed to set default file extension \"%s\"", qPrintable(suffix));
398         }
399 
400         QString suggestedName = QFileInfo(d->saveFileName.toLocalFile()).fileName();
401         if (suggestedName.isEmpty() && dialogOptions->initiallySelectedFiles().size() > 0)
402             suggestedName = QFileInfo(dialogOptions->initiallySelectedFiles().first().toLocalFile())
403                                     .fileName();
404         if (suggestedName.isEmpty()) {
405             const auto fileInfo = QFileInfo(dialogOptions->initialDirectory().toLocalFile());
406             if (!fileInfo.isDir())
407                 suggestedName = fileInfo.fileName();
408         }
409         if (!suggestedName.isEmpty()) {
410             HStringReference nativeSuggestedName(reinterpret_cast<const wchar_t *>(suggestedName.utf16()),
411                                                  uint(suggestedName.length()));
412             hr = picker->put_SuggestedFileName(nativeSuggestedName.Get());
413             RETURN_FALSE_IF_FAILED("Failed to set suggested file name");
414         }
415 
416         if (!pickSaveFile(picker.Get(), this))
417             return false;
418 
419         break;
420     }
421     }
422 
423     d->shown = true;
424     return true;
425 }
426 
hide()427 void QWinRTFileDialogHelper::hide()
428 {
429     Q_D(QWinRTFileDialogHelper);
430 
431     if (!d->shown)
432         return;
433 
434     d->shown = false;
435 }
436 
setDirectory(const QUrl & directory)437 void QWinRTFileDialogHelper::setDirectory(const QUrl &directory)
438 {
439     Q_D(QWinRTFileDialogHelper);
440     d->directory = directory;
441 }
442 
directory() const443 QUrl QWinRTFileDialogHelper::directory() const
444 {
445     Q_D(const QWinRTFileDialogHelper);
446     return d->directory;
447 }
448 
selectFile(const QUrl & saveFileName)449 void QWinRTFileDialogHelper::selectFile(const QUrl &saveFileName)
450 {
451     Q_D(QWinRTFileDialogHelper);
452     d->saveFileName = saveFileName;
453 }
454 
selectedFiles() const455 QList<QUrl> QWinRTFileDialogHelper::selectedFiles() const
456 {
457     Q_D(const QWinRTFileDialogHelper);
458     return d->selectedFiles;
459 }
460 
selectNameFilter(const QString & selectedNameFilter)461 void QWinRTFileDialogHelper::selectNameFilter(const QString &selectedNameFilter)
462 {
463     Q_D(QWinRTFileDialogHelper);
464     d->selectedNameFilter = selectedNameFilter;
465 }
466 
selectedNameFilter() const467 QString QWinRTFileDialogHelper::selectedNameFilter() const
468 {
469     Q_D(const QWinRTFileDialogHelper);
470     return d->selectedNameFilter;
471 }
472 
onSingleFilePicked(IAsyncOperation<StorageFile * > * args,AsyncStatus status)473 HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation<StorageFile *> *args, AsyncStatus status)
474 {
475     Q_D(QWinRTFileDialogHelper);
476 
477     QEventLoopLocker locker(&d->loop);
478     d->shown = false;
479     d->selectedFiles.clear();
480     if (status == Canceled || status == Error) {
481         emit reject();
482         return S_OK;
483     }
484 
485     HRESULT hr;
486     ComPtr<IStorageFile> file;
487     hr = args->GetResults(&file);
488     Q_ASSERT_SUCCEEDED(hr);
489     return onFilePicked(file.Get());
490 }
491 
onMultipleFilesPicked(IAsyncOperation<IVectorView<StorageFile * > * > * args,AsyncStatus status)492 HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorView<StorageFile *> *> *args, AsyncStatus status)
493 {
494     Q_D(QWinRTFileDialogHelper);
495 
496     QEventLoopLocker locker(&d->loop);
497     d->shown = false;
498     d->selectedFiles.clear();
499     if (status == Canceled || status == Error) {
500         emit reject();
501         return S_OK;
502     }
503 
504     HRESULT hr;
505     ComPtr<IVectorView<StorageFile *>> fileList;
506     hr = args->GetResults(&fileList);
507     RETURN_HR_IF_FAILED("Failed to get file list");
508     return onFilesPicked(fileList.Get());
509 }
510 
onSingleFolderPicked(IAsyncOperation<StorageFolder * > * args,AsyncStatus status)511 HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation<StorageFolder *> *args, AsyncStatus status)
512 {
513     Q_D(QWinRTFileDialogHelper);
514 
515     QEventLoopLocker locker(&d->loop);
516     d->shown = false;
517     d->selectedFiles.clear();
518     if (status == Canceled || status == Error) {
519         emit reject();
520         return S_OK;
521     }
522 
523     HRESULT hr;
524     ComPtr<IStorageFolder> folder;
525     hr = args->GetResults(&folder);
526     Q_ASSERT_SUCCEEDED(hr);
527     return onFolderPicked(folder.Get());
528 }
529 
onFilesPicked(IVectorView<StorageFile * > * files)530 HRESULT QWinRTFileDialogHelper::onFilesPicked(IVectorView<StorageFile *> *files)
531 {
532     HRESULT hr;
533     quint32 size;
534     hr = files->get_Size(&size);
535     Q_ASSERT_SUCCEEDED(hr);
536     if (!size) {
537         emit reject();
538         return S_OK;
539     }
540 
541     for (quint32 i = 0; i < size; ++i) {
542         ComPtr<IStorageFile> file;
543         hr = files->GetAt(i, &file);
544         Q_ASSERT_SUCCEEDED(hr);
545         appendFile(file.Get());
546     }
547 
548     emit accept();
549     return S_OK;
550 }
551 
onFolderPicked(IStorageFolder * folder)552 HRESULT QWinRTFileDialogHelper::onFolderPicked(IStorageFolder *folder)
553 {
554     if (!folder) {
555         emit reject();
556         return S_OK;
557     }
558 
559     appendFile(folder);
560     emit accept();
561     return S_OK;
562 }
563 
onFilePicked(IStorageFile * file)564 HRESULT QWinRTFileDialogHelper::onFilePicked(IStorageFile *file)
565 {
566     if (!file) {
567         emit reject();
568         return S_OK;
569     }
570 
571     appendFile(file);
572     emit accept();
573     return S_OK;
574 }
575 
appendFile(IInspectable * file)576 void QWinRTFileDialogHelper::appendFile(IInspectable *file)
577 {
578     Q_D(QWinRTFileDialogHelper);
579 
580     HRESULT hr;
581     ComPtr<IStorageItem> item;
582     hr = file->QueryInterface(IID_PPV_ARGS(&item));
583     Q_ASSERT_SUCCEEDED(hr);
584 
585     HString path;
586     hr = item->get_Path(path.GetAddressOf());
587     Q_ASSERT_SUCCEEDED(hr);
588 
589     quint32 pathLen;
590     const wchar_t *pathStr = path.GetRawBuffer(&pathLen);
591     const QString filePath = QString::fromWCharArray(pathStr, pathLen);
592     QWinRTFileEngineHandler::registerFile(filePath, item.Get());
593     d->selectedFiles.append(QUrl::fromLocalFile(filePath));
594 }
595 
596 QT_END_NAMESPACE
597