diff --git a/dev/Interop/StoragePickers/FileOpenPicker.cpp b/dev/Interop/StoragePickers/FileOpenPicker.cpp index b61792f9ac..809aedd2ef 100644 --- a/dev/Interop/StoragePickers/FileOpenPicker.cpp +++ b/dev/Interop/StoragePickers/FileOpenPicker.cpp @@ -50,6 +50,28 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation PickerCommon::ValidateStringNoEmbeddedNulls(value); m_commitButtonText = value; } + winrt::hstring FileOpenPicker::Title() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_title; + } + void FileOpenPicker::Title(winrt::hstring const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + PickerCommon::ValidateStringNoEmbeddedNulls(value); + m_title = value; + } + winrt::hstring FileOpenPicker::SettingsIdentifier() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_settingsIdentifier; + } + void FileOpenPicker::SettingsIdentifier(winrt::hstring const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + PickerCommon::ValidateStringNoEmbeddedNulls(value); + m_settingsIdentifier = value; + } winrt::Windows::Foundation::Collections::IMap> FileOpenPicker::FileTypeChoices() { THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); @@ -59,6 +81,28 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { return m_fileTypeFilter; } + winrt::Windows::Foundation::IReference FileOpenPicker::DefaultFileTypeIndex() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + if (!m_defaultFileTypeIndex.has_value()) + { + return nullptr; + } + + return winrt::box_value(*m_defaultFileTypeIndex).as>(); + } + void FileOpenPicker::DefaultFileTypeIndex(winrt::Windows::Foundation::IReference const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + if (value) + { + m_defaultFileTypeIndex = value.Value(); + } + else + { + m_defaultFileTypeIndex.reset(); + } + } winrt::hstring FileOpenPicker::SuggestedFolder() { THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); @@ -86,9 +130,15 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { parameters.HWnd = winrt::Microsoft::UI::GetWindowFromWindowId(m_windowId); parameters.CommitButtonText = m_commitButtonText; + parameters.Title = m_title; + parameters.SettingsIdentifier = m_settingsIdentifier; parameters.SuggestedFolder = m_suggestedFolder; parameters.SuggestedStartLocation = m_suggestedStartLocation; parameters.SuggestedStartFolder = m_suggestedStartFolder; + + // Note: the user-defined index must be written in parameters before capturing filter spec for the open picker. + // for the fileTypeFilter may override DefaultFileTypeIndex to select "All Files" if the index is not specified by the user. + parameters.DefaultFileTypeIndex = m_defaultFileTypeIndex; parameters.CaptureFilterSpecData(m_fileTypeFilter.GetView(), m_fileTypeChoices.GetView()); } diff --git a/dev/Interop/StoragePickers/FileOpenPicker.h b/dev/Interop/StoragePickers/FileOpenPicker.h index c5ff80bc27..556498b867 100644 --- a/dev/Interop/StoragePickers/FileOpenPicker.h +++ b/dev/Interop/StoragePickers/FileOpenPicker.h @@ -8,6 +8,7 @@ #include #include "FileTypeChoicesMap.h" #include "FileTypeFilterVector.h" +#include namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { @@ -24,9 +25,18 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation winrt::hstring CommitButtonText(); void CommitButtonText(winrt::hstring const& value); + winrt::hstring Title(); + void Title(winrt::hstring const& value); + + winrt::hstring SettingsIdentifier(); + void SettingsIdentifier(winrt::hstring const& value); + winrt::Windows::Foundation::Collections::IVector FileTypeFilter(); winrt::Windows::Foundation::Collections::IMap> FileTypeChoices(); + winrt::Windows::Foundation::IReference DefaultFileTypeIndex(); + void DefaultFileTypeIndex(winrt::Windows::Foundation::IReference const& value); + winrt::hstring SuggestedFolder(); void SuggestedFolder(winrt::hstring const& value); @@ -41,6 +51,8 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation PickerViewMode m_viewMode{ PickerViewMode::List }; PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; winrt::hstring m_commitButtonText{}; + winrt::hstring m_title{}; + winrt::hstring m_settingsIdentifier{}; winrt::Windows::Foundation::Collections::IVector m_fileTypeFilter{ make() }; winrt::Windows::Foundation::Collections::IMap> m_fileTypeChoices{ @@ -52,6 +64,8 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation }() }; + std::optional m_defaultFileTypeIndex{}; + winrt::hstring m_suggestedFolder{}; winrt::hstring m_suggestedStartFolder{}; diff --git a/dev/Interop/StoragePickers/FileSavePicker.cpp b/dev/Interop/StoragePickers/FileSavePicker.cpp index 7ffe72f818..a9b4f922aa 100644 --- a/dev/Interop/StoragePickers/FileSavePicker.cpp +++ b/dev/Interop/StoragePickers/FileSavePicker.cpp @@ -46,14 +46,78 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation PickerCommon::ValidateStringNoEmbeddedNulls(value); m_commitButtonText = value; } + winrt::hstring FileSavePicker::Title() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_title; + } + void FileSavePicker::Title(winrt::hstring const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + PickerCommon::ValidateStringNoEmbeddedNulls(value); + m_title = value; + } + winrt::hstring FileSavePicker::SettingsIdentifier() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_settingsIdentifier; + } + void FileSavePicker::SettingsIdentifier(winrt::hstring const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + PickerCommon::ValidateStringNoEmbeddedNulls(value); + m_settingsIdentifier = value; + } winrt::Windows::Foundation::Collections::IMap> FileSavePicker::FileTypeChoices() { return m_fileTypeChoices; } + winrt::Windows::Foundation::IReference FileSavePicker::DefaultFileTypeIndex() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + if (!m_defaultFileTypeIndex.has_value()) + { + return nullptr; + } + + return winrt::box_value(*m_defaultFileTypeIndex).as>(); + } + void FileSavePicker::DefaultFileTypeIndex(winrt::Windows::Foundation::IReference const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + if (value) + { + m_defaultFileTypeIndex = value.Value(); + } + else + { + m_defaultFileTypeIndex.reset(); + } + } hstring FileSavePicker::DefaultFileExtension() { return m_defaultFileExtension; } + bool FileSavePicker::ShowOverwritePrompt() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_showOverwritePrompt; + } + void FileSavePicker::ShowOverwritePrompt(bool value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + m_showOverwritePrompt = value; + } + bool FileSavePicker::CreateNewFileIfNotExists() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_createNewFileIfNotExists; + } + void FileSavePicker::CreateNewFileIfNotExists(bool value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + m_createNewFileIfNotExists = value; + } void FileSavePicker::DefaultFileExtension(hstring const& value) { m_defaultFileExtension = value; @@ -95,13 +159,18 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { parameters.HWnd = winrt::Microsoft::UI::GetWindowFromWindowId(m_windowId); parameters.CommitButtonText = m_commitButtonText; + parameters.Title = m_title; + parameters.SettingsIdentifier = m_settingsIdentifier; parameters.SuggestedFileName = m_suggestedFileName; parameters.SuggestedFolder = m_suggestedFolder; parameters.SuggestedStartLocation = m_suggestedStartLocation; parameters.SuggestedStartFolder = m_suggestedStartFolder; + + parameters.DefaultFileTypeIndex = m_defaultFileTypeIndex; parameters.CaptureFilterSpecData( winrt::Windows::Foundation::Collections::IVectorView{}, m_fileTypeChoices.GetView()); + parameters.ShowOverwritePrompt = m_showOverwritePrompt; } winrt::Windows::Foundation::IAsyncOperation FileSavePicker::PickSaveFileAsync() @@ -171,9 +240,13 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation check_hresult(shellItem->GetDisplayName(SIGDN_NORMALDISPLAY, fileName.put())); std::wstring fileNameStr(fileName.get()); - // Create an empty file if the file doesn't exist, - // If the file already exists, do nothing. - auto [handle, _] = wil::try_open_or_create_file(pathStr.c_str(), GENERIC_WRITE); + // Feature_StoragePickers2 gives the option to skip creating the picked file when it doesn't exist. + if (m_createNewFileIfNotExists || !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()) + { + // Create an empty file if the file doesn't exist, + // If the file already exists, do nothing. + auto [handle, _] = wil::try_open_or_create_file(pathStr.c_str(), GENERIC_WRITE); + } if (cancellationToken()) { diff --git a/dev/Interop/StoragePickers/FileSavePicker.h b/dev/Interop/StoragePickers/FileSavePicker.h index 849c819819..77a5b2d878 100644 --- a/dev/Interop/StoragePickers/FileSavePicker.h +++ b/dev/Interop/StoragePickers/FileSavePicker.h @@ -6,6 +6,7 @@ #include "PickerCommon.h" #include "StoragePickersTelemetryHelper.h" #include "FileTypeChoicesMap.h" +#include namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { @@ -21,11 +22,26 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation hstring CommitButtonText(); void CommitButtonText(hstring const& value); + hstring Title(); + void Title(hstring const& value); + + hstring SettingsIdentifier(); + void SettingsIdentifier(hstring const& value); + winrt::Windows::Foundation::Collections::IMap> FileTypeChoices(); + winrt::Windows::Foundation::IReference DefaultFileTypeIndex(); + void DefaultFileTypeIndex(winrt::Windows::Foundation::IReference const& value); + hstring DefaultFileExtension(); void DefaultFileExtension(hstring const& value); + bool ShowOverwritePrompt(); + void ShowOverwritePrompt(bool value); + + bool CreateNewFileIfNotExists(); + void CreateNewFileIfNotExists(bool value); + hstring SuggestedFolder(); void SuggestedFolder(hstring const& value); @@ -41,8 +57,13 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation winrt::Microsoft::UI::WindowId m_windowId{}; PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; hstring m_commitButtonText{}; + hstring m_title{}; + hstring m_settingsIdentifier{}; winrt::Windows::Foundation::Collections::IMap> m_fileTypeChoices{ make() }; + std::optional m_defaultFileTypeIndex{}; hstring m_defaultFileExtension{}; + bool m_showOverwritePrompt{ true }; + bool m_createNewFileIfNotExists{ true }; hstring m_suggestedFolder{}; hstring m_suggestedStartFolder{}; hstring m_suggestedFileName{}; diff --git a/dev/Interop/StoragePickers/FolderPicker.cpp b/dev/Interop/StoragePickers/FolderPicker.cpp index 5da7ab331e..c53a3b79de 100644 --- a/dev/Interop/StoragePickers/FolderPicker.cpp +++ b/dev/Interop/StoragePickers/FolderPicker.cpp @@ -48,6 +48,28 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation PickerCommon::ValidateStringNoEmbeddedNulls(value); m_commitButtonText = value; } + winrt::hstring FolderPicker::Title() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_title; + } + void FolderPicker::Title(winrt::hstring const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + PickerCommon::ValidateStringNoEmbeddedNulls(value); + m_title = value; + } + winrt::hstring FolderPicker::SettingsIdentifier() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + return m_settingsIdentifier; + } + void FolderPicker::SettingsIdentifier(winrt::hstring const& value) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); + PickerCommon::ValidateStringNoEmbeddedNulls(value); + m_settingsIdentifier = value; + } hstring FolderPicker::SuggestedFolder() { THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers2::IsEnabled()); @@ -75,6 +97,8 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation { parameters.HWnd = winrt::Microsoft::UI::GetWindowFromWindowId(m_windowId); parameters.CommitButtonText = m_commitButtonText; + parameters.Title = m_title; + parameters.SettingsIdentifier = m_settingsIdentifier; parameters.SuggestedFolder = m_suggestedFolder; parameters.SuggestedStartLocation = m_suggestedStartLocation; parameters.SuggestedStartFolder = m_suggestedStartFolder; @@ -88,8 +112,6 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation auto logTelemetry{ StoragePickersTelemetry::FolderPickerPickSingleFolder::Start(m_telemetryHelper) }; PickerCommon::PickerParameters parameters{}; - parameters.AllFilesText = PickerLocalization::GetStoragePickersLocalizationText(PickerCommon::AllFilesLocalizationKey); - CaptureParameters(parameters); auto cancellationToken = co_await winrt::get_cancellation_token(); @@ -131,4 +153,75 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation logTelemetry.Stop(m_telemetryHelper, true); co_return result; } + + winrt::Windows::Foundation::IAsyncOperation> FolderPicker::PickMultipleFoldersAsync() + { + // TODO: remove get strong reference when telementry is safe stop + auto lifetime{ get_strong() }; + + auto logTelemetry{ StoragePickersTelemetry::FolderPickerPickMultipleFolder::Start(m_telemetryHelper) }; + + // capture parameters to avoid using get strong referece of picker + PickerCommon::PickerParameters parameters{}; + CaptureParameters(parameters); + + auto cancellationToken = co_await winrt::get_cancellation_token(); + cancellationToken.enable_propagation(true); + co_await winrt::resume_background(); + + winrt::Windows::Foundation::Collections::IVector results{ winrt::single_threaded_vector() }; + if (cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, true, false); + co_return results.GetView(); + } + + auto dialog = create_instance(CLSID_FileOpenDialog, CLSCTX_INPROC_SERVER); + + FILEOPENDIALOGOPTIONS dialogOptions; + check_hresult(dialog->GetOptions(&dialogOptions)); + check_hresult(dialog->SetOptions(dialogOptions | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_ALLOWMULTISELECT)); + + { + auto hr = dialog->Show(parameters.HWnd); + if (FAILED(hr) || cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, true, false); + co_return results.GetView(); + } + } + + winrt::com_ptr shellItems{}; + check_hresult(dialog->GetResults(shellItems.put())); + + DWORD itemCount = 0; + check_hresult(shellItems->GetCount(&itemCount)); + + winrt::com_ptr shellItem{}; + for (DWORD i = 0; i < itemCount; i++) + { + check_hresult(shellItems->GetItemAt(i, shellItem.put())); + auto path = PickerCommon::GetPathFromShellItem(shellItem); + auto result{ make(path) }; + results.Append(result); + } + + bool isCancelled = false; + if (cancellationToken()) + { + results.Clear(); + isCancelled = true; + } + auto resultView = results.GetView(); + + if (results.Size() > 0) + { + logTelemetry.Stop(m_telemetryHelper, isCancelled, true); + } + else + { + logTelemetry.Stop(m_telemetryHelper, isCancelled, false); + } + co_return resultView; + } } diff --git a/dev/Interop/StoragePickers/FolderPicker.h b/dev/Interop/StoragePickers/FolderPicker.h index 49e7c4b1f7..abc82e51f1 100644 --- a/dev/Interop/StoragePickers/FolderPicker.h +++ b/dev/Interop/StoragePickers/FolderPicker.h @@ -21,6 +21,12 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation hstring CommitButtonText(); void CommitButtonText(hstring const& value); + hstring Title(); + void Title(hstring const& value); + + hstring SettingsIdentifier(); + void SettingsIdentifier(hstring const& value); + hstring SuggestedFolder(); void SuggestedFolder(hstring const& value); @@ -28,6 +34,7 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation void SuggestedStartFolder(hstring const& value); winrt::Windows::Foundation::IAsyncOperation PickSingleFolderAsync(); + winrt::Windows::Foundation::IAsyncOperation> PickMultipleFoldersAsync(); private: winrt::Microsoft::UI::WindowId m_windowId{}; @@ -35,6 +42,8 @@ namespace winrt::Microsoft::Windows::Storage::Pickers::implementation PickerViewMode m_viewMode{ PickerViewMode::List }; PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; hstring m_commitButtonText{}; + hstring m_title{}; + hstring m_settingsIdentifier{}; hstring m_suggestedFolder{}; hstring m_suggestedStartFolder{}; StoragePickersTelemetryHelper m_telemetryHelper{}; diff --git a/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl b/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl index c71d622193..542ee3da23 100644 --- a/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl +++ b/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl @@ -44,12 +44,24 @@ namespace Microsoft.Windows.Storage.Pickers Microsoft.Windows.Storage.Pickers.PickerLocationId SuggestedStartLocation; String CommitButtonText; + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + String Title; + + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + String SettingsIdentifier; + [contract(StoragePickersContract, 2.0)] [feature(Feature_StoragePickers2)] Windows.Foundation.Collections.IMap > FileTypeChoices{ get; }; Windows.Foundation.Collections.IVector FileTypeFilter{ get; }; + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + Windows.Foundation.IReference DefaultFileTypeIndex; + [contract(StoragePickersContract, 2.0)] [feature(Feature_StoragePickers2)] String SuggestedFolder; @@ -69,7 +81,21 @@ namespace Microsoft.Windows.Storage.Pickers Microsoft.Windows.Storage.Pickers.PickerLocationId SuggestedStartLocation; String CommitButtonText; + + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + String Title; + + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + String SettingsIdentifier; + Windows.Foundation.Collections.IMap > FileTypeChoices{ get; }; + + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + Windows.Foundation.IReference DefaultFileTypeIndex; + String DefaultFileExtension; String SuggestedFileName; String SuggestedFolder; @@ -78,6 +104,14 @@ namespace Microsoft.Windows.Storage.Pickers [feature(Feature_StoragePickers2)] String SuggestedStartFolder; + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + Boolean ShowOverwritePrompt; + + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + Boolean CreateNewFileIfNotExists; + [remote_sync] Windows.Foundation.IAsyncOperation PickSaveFileAsync(); } @@ -96,6 +130,14 @@ namespace Microsoft.Windows.Storage.Pickers Microsoft.Windows.Storage.Pickers.PickerLocationId SuggestedStartLocation; String CommitButtonText; + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + String Title; + + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + String SettingsIdentifier; + [contract(StoragePickersContract, 2.0)] [feature(Feature_StoragePickers2)] String SuggestedFolder; @@ -105,5 +147,9 @@ namespace Microsoft.Windows.Storage.Pickers String SuggestedStartFolder; [remote_sync] Windows.Foundation.IAsyncOperation PickSingleFolderAsync(); + + [contract(StoragePickersContract, 2.0)] + [feature(Feature_StoragePickers2)] + [remote_sync] Windows.Foundation.IAsyncOperation > PickMultipleFoldersAsync(); } } diff --git a/dev/Interop/StoragePickers/PickerCommon.cpp b/dev/Interop/StoragePickers/PickerCommon.cpp index 3ba9effe05..0874861808 100644 --- a/dev/Interop/StoragePickers/PickerCommon.cpp +++ b/dev/Interop/StoragePickers/PickerCommon.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "shellapi.h" +#include #include "PickerCommon.h" #include "PickerLocalization.h" #include @@ -14,7 +15,7 @@ #include #include #include - +#include namespace { @@ -356,7 +357,11 @@ namespace PickerCommon { FileTypeFilterPara.push_back({ FileTypeFilterData.at(i * 2).c_str(), FileTypeFilterData.at(i * 2 + 1).c_str() }); } - FocusLastFilter = true; + if (!DefaultFileTypeIndex.has_value()) + { + // If the DefaultFileTypeIndex is not specified by the user, set to focuse the last one ("All Files") + DefaultFileTypeIndex = static_cast(resultSize - 1); + } } /// @@ -392,6 +397,34 @@ namespace PickerCommon { } } + winrt::hstring PickerParameters::TryGetAppUserModelId() + { + wchar_t appUserModelId[APPLICATION_USER_MODEL_ID_MAX_LENGTH] = {}; + UINT32 appUserModelIdSize{ APPLICATION_USER_MODEL_ID_MAX_LENGTH }; + + auto hr{GetCurrentApplicationUserModelId(&appUserModelIdSize, appUserModelId) }; + if (SUCCEEDED(hr)) + { + return winrt::hstring{ appUserModelId }; + } + + return winrt::hstring{}; + } + + winrt::hstring PickerParameters::TryGetProcessFullPath() + { + PCWSTR exeFullPath{}; + wil::unique_cotaskmem_string module; + auto hr{ LOG_IF_FAILED(wil::GetModuleFileNameW(nullptr, module)) }; + if (SUCCEEDED(hr)) + { + exeFullPath = module.get(); + return winrt::hstring{ exeFullPath }; + } + + return winrt::hstring{}; + } + void PickerParameters::ConfigureDialog(winrt::com_ptr dialog) { if (!IsHStringNullOrEmpty(CommitButtonText)) @@ -399,6 +432,11 @@ namespace PickerCommon { check_hresult(dialog->SetOkButtonLabel(CommitButtonText.c_str())); } + if (!IsHStringNullOrEmpty(Title)) + { + check_hresult(dialog->SetTitle(Title.c_str())); + } + winrt::com_ptr defaultFolder{}; // The SuggestedStartFolder takes precedence over SuggestedStartLocation if both are provided. @@ -430,11 +468,26 @@ namespace PickerCommon { { check_hresult(dialog->SetFileTypes((UINT)FileTypeFilterPara.size(), FileTypeFilterPara.data())); - if (FocusLastFilter) + if (DefaultFileTypeIndex.has_value() && *DefaultFileTypeIndex < static_cast(FileTypeFilterPara.size())) + { + check_hresult(dialog->SetFileTypeIndex(static_cast(*DefaultFileTypeIndex + 1))); // COMDLG file type index is 1-based + } + } + + if (!IsHStringNullOrEmpty(SettingsIdentifier)) + { + auto appDistinctString = TryGetAppUserModelId(); + if (appDistinctString.empty()) + { + appDistinctString = TryGetProcessFullPath(); + } + if (!appDistinctString.empty()) { - check_hresult(dialog->SetFileTypeIndex(FileTypeFilterPara.size())); + auto clientId = HashHStringToGuid(appDistinctString + L"|" + SettingsIdentifier); + check_hresult(dialog->SetClientGuid(clientId)); } } + } /// @@ -448,5 +501,13 @@ namespace PickerCommon { check_hresult(dialog->SetFileName(SuggestedFileName.c_str())); } + if (!ShowOverwritePrompt) + { + FILEOPENDIALOGOPTIONS options{}; + check_hresult(dialog->GetOptions(&options)); + options = static_cast(options & ~FOS_OVERWRITEPROMPT); + check_hresult(dialog->SetOptions(options)); + } + } } diff --git a/dev/Interop/StoragePickers/PickerCommon.h b/dev/Interop/StoragePickers/PickerCommon.h index f2bc2045ab..a682017af2 100644 --- a/dev/Interop/StoragePickers/PickerCommon.h +++ b/dev/Interop/StoragePickers/PickerCommon.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace PickerCommon { winrt::hstring GetPathFromShellItem(winrt::com_ptr shellItem); @@ -31,10 +32,12 @@ namespace PickerCommon { struct PickerParameters { HWND HWnd{}; winrt::hstring CommitButtonText; + winrt::hstring Title; + winrt::hstring SettingsIdentifier; winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId SuggestedStartLocation; std::vector FileTypeFilterData{}; std::vector FileTypeFilterPara{}; - bool FocusLastFilter{ false }; + std::optional DefaultFileTypeIndex{}; winrt::hstring AllFilesText{ L"All Files" }; // initialize to All Files as a default value, will be updated by localization winrt::hstring SuggestedFileName; @@ -51,6 +54,11 @@ namespace PickerCommon { void ConfigureDialog(winrt::com_ptr dialog); void ConfigureFileSaveDialog(winrt::com_ptr dialog); + winrt::hstring TryGetAppUserModelId(); + winrt::hstring TryGetProcessFullPath(); + + bool ShowOverwritePrompt; + private: void CaptureFilterSpec(winrt::Windows::Foundation::Collections::IVectorView filters); void CaptureFilterSpec(winrt::Windows::Foundation::Collections::IMapView> filters); diff --git a/dev/Interop/StoragePickers/StoragePickersTelemetry.h b/dev/Interop/StoragePickers/StoragePickersTelemetry.h index cf116c778f..2bb72d6cd8 100644 --- a/dev/Interop/StoragePickers/StoragePickersTelemetry.h +++ b/dev/Interop/StoragePickers/StoragePickersTelemetry.h @@ -124,4 +124,32 @@ class StoragePickersTelemetry : public wil::TraceLoggingProvider } CATCH_LOG() END_ACTIVITY_CLASS(); + + BEGIN_COMPLIANT_CRITICAL_DATA_ACTIVITY_CLASS(FolderPickerPickMultipleFolder, PDT_ProductAndServicePerformance); + DEFINE_ACTIVITY_START(StoragePickersTelemetryHelper& telemetryHelper) noexcept try + { + TraceLoggingClassWriteStart( + FolderPickerPickMultipleFolder, + _GENERIC_PARTB_FIELDS_ENABLED, + TraceLoggingBool(telemetryHelper.IsPackagedApp(), "IsAppPackaged"), + TraceLoggingBool(telemetryHelper.IsRunningAsAdmin(), "IsRunningAsAdmin"), + TraceLoggingBool(telemetryHelper.IsRunningInContainer(), "IsRunningInContainer"), + TraceLoggingWideString(telemetryHelper.GetAppName().c_str(), "AppName")); + } + CATCH_LOG() + + DEFINE_ACTIVITY_STOP(StoragePickersTelemetryHelper& telemetryHelper, bool const resultCancelled, bool const resultNotEmpty) noexcept try + { + TraceLoggingClassWriteStop( + FolderPickerPickMultipleFolder, + _GENERIC_PARTB_FIELDS_ENABLED, + TraceLoggingBool(resultCancelled, "resultCancelled"), + TraceLoggingBool(resultNotEmpty, "resultNotEmpty"), + TraceLoggingBool(telemetryHelper.IsPackagedApp(), "IsAppPackaged"), + TraceLoggingBool(telemetryHelper.IsRunningAsAdmin(), "IsRunningAsAdmin"), + TraceLoggingBool(telemetryHelper.IsRunningInContainer(), "IsRunningInContainer"), + TraceLoggingWideString(telemetryHelper.GetAppName().c_str(), "AppName")); + } + CATCH_LOG() + END_ACTIVITY_CLASS(); };