diff --git a/WindowsAppRuntime.sln b/WindowsAppRuntime.sln index b3759be95e..e79cd9d891 100644 --- a/WindowsAppRuntime.sln +++ b/WindowsAppRuntime.sln @@ -678,6 +678,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorldAdvancedCS_NoThro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BadgeNotificationTest", "test\BadgeNotificationTest\BadgeNotificationTest.vcxproj", "{7C8BEED1-7B27-41C5-A22F-9D6B4468F52A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Windows.Storage.Pickers.Projection", "dev\Projections\CS\Microsoft.Windows.Storage.Pickers.Projection\Microsoft.Windows.Storage.Pickers.Projection.csproj", "{8E01AA4F-A16A-4E3F-A59F-6D49422B4410}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StoragePickers", "dev\Interop\StoragePickers\StoragePickers.vcxitems", "{A39E7B2F-5F67-47DD-8443-531D095CA7F3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StoragePickersTests", "test\StoragePickersTests\StoragePickersTests.vcxproj", "{85C86306-46D1-4563-8303-0A79DF923586}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -2348,6 +2354,38 @@ Global {7C8BEED1-7B27-41C5-A22F-9D6B4468F52A}.Release|x64.Build.0 = Release|x64 {7C8BEED1-7B27-41C5-A22F-9D6B4468F52A}.Release|x86.ActiveCfg = Release|Win32 {7C8BEED1-7B27-41C5-A22F-9D6B4468F52A}.Release|x86.Build.0 = Release|Win32 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|Any CPU.ActiveCfg = Debug|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|Any CPU.Build.0 = Debug|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|ARM64.ActiveCfg = Debug|arm64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|ARM64.Build.0 = Debug|arm64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|x64.ActiveCfg = Debug|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|x64.Build.0 = Debug|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|x86.ActiveCfg = Debug|x86 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Debug|x86.Build.0 = Debug|x86 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|Any CPU.ActiveCfg = Release|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|Any CPU.Build.0 = Release|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|ARM64.ActiveCfg = Release|arm64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|ARM64.Build.0 = Release|arm64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|x64.ActiveCfg = Release|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|x64.Build.0 = Release|x64 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|x86.ActiveCfg = Release|x86 + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410}.Release|x86.Build.0 = Release|x86 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|Any CPU.ActiveCfg = Debug|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|Any CPU.Build.0 = Debug|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|ARM64.Build.0 = Debug|ARM64 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|x64.ActiveCfg = Debug|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|x64.Build.0 = Debug|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|x86.ActiveCfg = Debug|Win32 + {85C86306-46D1-4563-8303-0A79DF923586}.Debug|x86.Build.0 = Debug|Win32 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|Any CPU.ActiveCfg = Release|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|Any CPU.Build.0 = Release|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|ARM64.ActiveCfg = Release|ARM64 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|ARM64.Build.0 = Release|ARM64 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|x64.ActiveCfg = Release|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|x64.Build.0 = Release|x64 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|x86.ActiveCfg = Release|Win32 + {85C86306-46D1-4563-8303-0A79DF923586}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2553,6 +2591,9 @@ Global {9369C7DD-430B-4B52-822A-231AA2791D23} = {63196919-AEA8-4EA8-BFB4-08CE824249A3} {9D26A965-065A-4897-B476-4F15B0F5A19F} = {63196919-AEA8-4EA8-BFB4-08CE824249A3} {7C8BEED1-7B27-41C5-A22F-9D6B4468F52A} = {1FDC307C-2DB7-4B40-8F18-F1057E9E0969} + {8E01AA4F-A16A-4E3F-A59F-6D49422B4410} = {716C26A0-E6B0-4981-8412-D14A4D410531} + {A39E7B2F-5F67-47DD-8443-531D095CA7F3} = {3B706C5C-55E0-4B76-BF59-89E20FE46795} + {85C86306-46D1-4563-8303-0A79DF923586} = {8630F7AA-2969-4DC9-8700-9B468C1DC21D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4B3D7591-CFEC-4762-9A07-ABE99938FB77} @@ -2579,6 +2620,7 @@ Global dev\PackageManager\API\PackageManager.vcxitems*{8a9a0c85-65a8-4bca-a49e-45fc4fdbc7d2}*SharedItemsImports = 9 test\inc\inc.vcxitems*{8e52d7ea-a200-4a6b-ba74-8efb49468caf}*SharedItemsImports = 4 dev\Interop\CameraCaptureUI\CameraCaptureUI\CameraCaptureUI.vcxitems*{95409d1e-843f-4316-8d8e-471b3e203f94}*SharedItemsImports = 9 + dev\Interop\StoragePickers\StoragePickers.vcxitems*{a39e7b2f-5f67-47dd-8443-531d095ca7f3}*SharedItemsImports = 9 dev\AppNotifications\AppNotifications.vcxitems*{b4824897-88e0-4927-8fb9-e60106f01ed9}*SharedItemsImports = 9 test\inc\inc.vcxitems*{b567fe2e-3a03-48d0-b2b5-760cdec35891}*SharedItemsImports = 9 dev\ApplicationData\ApplicationData.vcxitems*{b73ad907-6164-4294-88fb-f3c9c10da1f1}*SharedItemsImports = 4 diff --git a/build/AzurePipelinesTemplates/WindowsAppSDK-SetupBuildEnvironment-Steps.yml b/build/AzurePipelinesTemplates/WindowsAppSDK-SetupBuildEnvironment-Steps.yml index 46ebe1f9df..a8d8deb6f8 100644 --- a/build/AzurePipelinesTemplates/WindowsAppSDK-SetupBuildEnvironment-Steps.yml +++ b/build/AzurePipelinesTemplates/WindowsAppSDK-SetupBuildEnvironment-Steps.yml @@ -187,6 +187,14 @@ steps: arguments: -Path $(Build.SourcesDirectory)\dev\common\TerminalVelocityFeatures-CameraCaptureUI.xml -Channel $(channel) -Language C++ -Namespace Microsoft.Windows.Media.Capture -Output $(Build.SourcesDirectory)\dev\common\TerminalVelocityFeatures-CameraCaptureUI.h workingDirectory: '$(Build.SourcesDirectory)' +- task: powershell@2 + displayName: 'Create OAuth2Manager TerminalVelocity features' + inputs: + targetType: filePath + filePath: tools\TerminalVelocity\Generate-TerminalVelocityFeatures.ps1 + arguments: -Path $(Build.SourcesDirectory)\dev\common\TerminalVelocityFeatures-StoragePickers.xml -Channel $(channel) -Language C++ -Namespace Microsoft.Windows.Storage.Pickers -Output $(Build.SourcesDirectory)\dev\common\TerminalVelocityFeatures-StoragePickers.h + workingDirectory: '$(Build.SourcesDirectory)' + - task: powershell@2 displayName: 'Create OAuth2Manager TerminalVelocity features' inputs: diff --git a/build/CopyFilesToStagingDir.ps1 b/build/CopyFilesToStagingDir.ps1 index 3c280a1ad9..90ec0cf422 100644 --- a/build/CopyFilesToStagingDir.ps1 +++ b/build/CopyFilesToStagingDir.ps1 @@ -56,6 +56,7 @@ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windo PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.BadgeNotifications.winmd $FullPublishDir\Microsoft.WindowsAppRuntime\ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Management.Deployment.winmd $FullPublishDir\Microsoft.WindowsAppRuntime\ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Media.Capture.winmd $FullPublishDir\Microsoft.WindowsAppRuntime\ +PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Storage.Pickers.winmd $FullPublishDir\Microsoft.WindowsAppRuntime\ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.PushNotifications.winmd $FullPublishDir\Microsoft.WindowsAppRuntime\ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Security.AccessControl.winmd $FullPublishDir\Microsoft.WindowsAppRuntime\ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Security.Authentication.OAuth.winmd $FullPublishDir\Microsoft.WindowsAppRuntime\ @@ -129,6 +130,8 @@ PublishFile $FullBuildOutput\Microsoft.Windows.Management.Deployment.Projection\ PublishFile $FullBuildOutput\Microsoft.Windows.Management.Deployment.Projection\Microsoft.Windows.Management.Deployment.Projection.pdb $NugetDir\lib\net6.0-windows10.0.17763.0 PublishFile $FullBuildOutput\Microsoft.Windows.Media.Capture.Projection\Microsoft.Windows.Media.Capture.Projection.dll $NugetDir\lib\net6.0-windows10.0.17763.0 PublishFile $FullBuildOutput\Microsoft.Windows.Media.Capture.Projection\Microsoft.Windows.Media.Capture.Projection.pdb $NugetDir\lib\net6.0-windows10.0.17763.0 +PublishFile $FullBuildOutput\Microsoft.Windows.Storage.Pickers.Projection\Microsoft.Windows.Storage.Pickers.Projection.dll $NugetDir\lib\net6.0-windows10.0.17763.0 +PublishFile $FullBuildOutput\Microsoft.Windows.Storage.Pickers.Projection\Microsoft.Windows.Storage.Pickers.Projection.pdb $NugetDir\lib\net6.0-windows10.0.17763.0 PublishFile $FullBuildOutput\Microsoft.Windows.PushNotifications.Projection\Microsoft.Windows.PushNotifications.Projection.dll $NugetDir\lib\net6.0-windows10.0.17763.0 PublishFile $FullBuildOutput\Microsoft.Windows.PushNotifications.Projection\Microsoft.Windows.PushNotifications.Projection.pdb $NugetDir\lib\net6.0-windows10.0.17763.0 PublishFile $FullBuildOutput\Microsoft.Windows.Security.AccessControl.Projection\Microsoft.Windows.Security.AccessControl.Projection.dll $NugetDir\lib\net6.0-windows10.0.17763.0 @@ -206,6 +209,7 @@ PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windo PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.BadgeNotifications.winmd $NugetDir\lib\uap10.0 PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Management.Deployment.winmd $NugetDir\lib\uap10.0 PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Media.Capture.winmd $NugetDir\lib\uap10.0 +PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Storage.Pickers.winmd $NugetDir\lib\uap10.0 PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.PushNotifications.winmd $NugetDir\lib\uap10.0 PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Security.AccessControl.winmd $NugetDir\lib\uap10.0 PublishFile $FullBuildOutput\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Security.Authentication.OAuth.winmd $NugetDir\lib\uap10.0 diff --git a/build/NuSpecs/AppxManifest.xml b/build/NuSpecs/AppxManifest.xml index 23bafeec28..12fe8e6c5f 100644 --- a/build/NuSpecs/AppxManifest.xml +++ b/build/NuSpecs/AppxManifest.xml @@ -116,6 +116,11 @@ + + + + + diff --git a/build/NuSpecs/WindowsAppSDK-Nuget-Native.WinRt.props b/build/NuSpecs/WindowsAppSDK-Nuget-Native.WinRt.props index 45fe60f31a..e99257f152 100644 --- a/build/NuSpecs/WindowsAppSDK-Nuget-Native.WinRt.props +++ b/build/NuSpecs/WindowsAppSDK-Nuget-Native.WinRt.props @@ -81,6 +81,12 @@ $(MSBuildThisFileDirectory)..\..\runtimes\win10-$(_WindowsAppSDKFoundationPlatform)\native\Microsoft.WindowsAppRuntime.dll true + + $(MSBuildThisFileDirectory)..\..\lib\uap10.0\Microsoft.Windows.Storage.Pickers.winmd + $(MSBuildThisFileDirectory)..\..\runtimes\win10-$(_WindowsAppSDKFoundationPlatform)\native\Microsoft.WindowsAppRuntime.dll + true + diff --git a/build/NuSpecs/WindowsAppSDK-Nuget-Native.targets b/build/NuSpecs/WindowsAppSDK-Nuget-Native.targets index a51d55d97c..ae295ae46f 100644 --- a/build/NuSpecs/WindowsAppSDK-Nuget-Native.targets +++ b/build/NuSpecs/WindowsAppSDK-Nuget-Native.targets @@ -143,6 +143,14 @@ + + + false + Microsoft.WindowsAppRuntime.dll + + + diff --git a/dev/Common/TerminalVelocityFeatures-StoragePickers.h b/dev/Common/TerminalVelocityFeatures-StoragePickers.h new file mode 100644 index 0000000000..85d77e743d --- /dev/null +++ b/dev/Common/TerminalVelocityFeatures-StoragePickers.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT IT + +// INPUT FILE: dev\Common\TerminalVelocityFeatures-StoragePickers.xml +// OPTIONS: -Channel Experimental -Language C++ -Namespace Microsoft.Windows.Storage.Pickers -Path dev\Common\TerminalVelocityFeatures-StoragePickers.xml -Output dev\Common\TerminalVelocityFeatures-StoragePickers.h + +#if defined(__midlrt) +namespace features +{ + feature_name Feature_StoragePickers = { DisabledByDefault, FALSE }; +} +#endif // defined(__midlrt) + +// Feature constants +#define WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_STORAGE_PICKERS_FEATURE_STORAGEPICKERS_ENABLED 1 + +#if defined(__cplusplus) + +namespace Microsoft::Windows::Storage::Pickers +{ + +__pragma(detect_mismatch("ODR_violation_WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_STORAGE_PICKERS_FEATURE_STORAGEPICKERS_ENABLED_mismatch", "AlwaysEnabled")) +struct Feature_StoragePickers +{ + static constexpr bool IsEnabled() { return WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_STORAGE_PICKERS_FEATURE_STORAGEPICKERS_ENABLED == 1; } +}; + +} // namespace Microsoft.Windows.Storage.Pickers + +#endif // defined(__cplusplus) diff --git a/dev/Common/TerminalVelocityFeatures-StoragePickers.xml b/dev/Common/TerminalVelocityFeatures-StoragePickers.xml new file mode 100644 index 0000000000..123cf78ecb --- /dev/null +++ b/dev/Common/TerminalVelocityFeatures-StoragePickers.xml @@ -0,0 +1,20 @@ + + + + + + + + + + Feature_StoragePickers + StoragePickers for the WindowsAppRuntime + AlwaysEnabled + + Preview + Stable + + + diff --git a/dev/Interop/StoragePickers/FileOpenPicker.cpp b/dev/Interop/StoragePickers/FileOpenPicker.cpp new file mode 100644 index 0000000000..7f6181c946 --- /dev/null +++ b/dev/Interop/StoragePickers/FileOpenPicker.cpp @@ -0,0 +1,190 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "FileOpenPicker.h" +#include "Microsoft.Windows.Storage.Pickers.FileOpenPicker.g.cpp" +#include "StoragePickersTelemetry.h" +#include +#include +#include +#include +#include "PickerCommon.h" +#include "PickFileResult.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + FileOpenPicker::FileOpenPicker(winrt::Microsoft::UI::WindowId const& windowId) + : m_windowId(windowId) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers::IsEnabled()); + } + + winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode FileOpenPicker::ViewMode() + { + return m_viewMode; + } + void FileOpenPicker::ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value) + { + m_viewMode = value; + } + hstring FileOpenPicker::SettingsIdentifier() + { + return m_settingsIdentifier; + } + void FileOpenPicker::SettingsIdentifier(hstring const& value) + { + m_settingsIdentifier = value; + } + winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId FileOpenPicker::SuggestedStartLocation() + { + return m_suggestedStartLocation; + } + void FileOpenPicker::SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value) + { + m_suggestedStartLocation = value; + } + winrt::hstring FileOpenPicker::CommitButtonText() + { + return m_commitButtonText; + } + void FileOpenPicker::CommitButtonText(winrt::hstring const& value) + { + m_commitButtonText = value; + } + winrt::Windows::Foundation::Collections::IVector FileOpenPicker::FileTypeFilter() + { + return m_fileTypeFilter; + } + + void FileOpenPicker::CaptureParameters(PickerCommon::PickerParameters& parameters) + { + parameters.HWnd = winrt::Microsoft::UI::GetWindowFromWindowId(m_windowId); + parameters.CommitButtonText = m_commitButtonText; + parameters.SettingsIdentifierId = m_settingsIdentifier; + parameters.PickerLocationId = m_suggestedStartLocation; + parameters.FileTypeFilterPara = PickerCommon::CaptureFilterSpec(parameters.FileTypeFilterData, m_fileTypeFilter.GetView()); + } + + winrt::Windows::Foundation::IAsyncOperation FileOpenPicker::PickSingleFileAsync() + { + // TODO: remove get strong reference when telementry is safe stop + auto lifetime { get_strong() }; + + auto logTelemetry{ StoragePickersTelemetry::FileOpenPickerPickSingleFile::Start(m_telemetryHelper) }; + + PickerCommon::PickerParameters parameters{}; + + CaptureParameters(parameters); + + auto cancellationToken = co_await winrt::get_cancellation_token(); + cancellationToken.enable_propagation(true); + co_await winrt::resume_background(); + + if (cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + + auto dialog = create_instance(CLSID_FileOpenDialog, CONTEXT_ALL); + + parameters.ConfigureDialog(dialog); + + { + auto hr = dialog->Show(parameters.HWnd); + if (FAILED(hr) || cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + } + + winrt::com_ptr shellItem{}; + check_hresult(dialog->GetResult(shellItem.put())); + auto path = PickerCommon::GetPathFromShellItem(shellItem); + + if (cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + + auto result = make(path); + + logTelemetry.Stop(m_telemetryHelper, true); + co_return result; + } + + winrt::Windows::Foundation::IAsyncOperation > FileOpenPicker::PickMultipleFilesAsync() + { + // TODO: remove get strong reference when telementry is safe stop + auto lifetime { get_strong() }; + + auto logTelemetry{ StoragePickersTelemetry::FileOpenPickerPickMultipleFile::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, CONTEXT_ALL); + + parameters.ConfigureDialog(dialog); + + check_hresult(dialog->SetOptions(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/FileOpenPicker.h b/dev/Interop/StoragePickers/FileOpenPicker.h new file mode 100644 index 0000000000..208025488d --- /dev/null +++ b/dev/Interop/StoragePickers/FileOpenPicker.h @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include "Microsoft.Windows.Storage.Pickers.FileOpenPicker.g.h" +#include "PickerCommon.h" +#include "StoragePickersTelemetryHelper.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + struct FileOpenPicker : FileOpenPickerT + { + FileOpenPicker(winrt::Microsoft::UI::WindowId const& windowId); + + winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode ViewMode(); + void ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value); + + hstring SettingsIdentifier(); + void SettingsIdentifier(hstring const& value); + + winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId SuggestedStartLocation(); + void SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value); + + winrt::hstring CommitButtonText(); + void CommitButtonText(winrt::hstring const& value); + + winrt::Windows::Foundation::Collections::IVector FileTypeFilter(); + + winrt::Windows::Foundation::IAsyncOperation PickSingleFileAsync(); + winrt::Windows::Foundation::IAsyncOperation> PickMultipleFilesAsync(); + + private: + winrt::Microsoft::UI::WindowId m_windowId{}; + PickerViewMode m_viewMode{ PickerViewMode::List }; + winrt::hstring m_settingsIdentifier{}; + PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; + winrt::hstring m_commitButtonText{}; + winrt::Windows::Foundation::Collections::IVector m_fileTypeFilter{ winrt::single_threaded_vector() }; + StoragePickersTelemetryHelper m_telemetryHelper{}; + + void CaptureParameters(PickerCommon::PickerParameters& parameters); + }; +} +namespace winrt::Microsoft::Windows::Storage::Pickers::factory_implementation +{ + struct FileOpenPicker : FileOpenPickerT + { + }; +} diff --git a/dev/Interop/StoragePickers/FileSavePicker.cpp b/dev/Interop/StoragePickers/FileSavePicker.cpp new file mode 100644 index 0000000000..339cfaea9a --- /dev/null +++ b/dev/Interop/StoragePickers/FileSavePicker.cpp @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "FileSavePicker.h" +#include "Microsoft.Windows.Storage.Pickers.FileSavePicker.g.cpp" +#include "StoragePickersTelemetry.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "PickerCommon.h" +#include "PickFileResult.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + + FileSavePicker::FileSavePicker(winrt::Microsoft::UI::WindowId const& windowId) + : m_windowId(windowId) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers::IsEnabled()); + } + hstring FileSavePicker::SettingsIdentifier() + { + return m_settingsIdentifier; + } + void FileSavePicker::SettingsIdentifier(hstring const& value) + { + m_settingsIdentifier = value; + } + winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId FileSavePicker::SuggestedStartLocation() + { + return m_suggestedStartLocation; + } + void FileSavePicker::SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value) + { + m_suggestedStartLocation = value; + } + hstring FileSavePicker::CommitButtonText() + { + return m_commitButtonText; + } + void FileSavePicker::CommitButtonText(hstring const& value) + { + m_commitButtonText = value; + } + winrt::Windows::Foundation::Collections::IMap> FileSavePicker::FileTypeChoices() + { + return m_fileTypeChoices; + } + hstring FileSavePicker::DefaultFileExtension() + { + return m_defaultFileExtension; + } + void FileSavePicker::DefaultFileExtension(hstring const& value) + { + m_defaultFileExtension = value; + } + winrt::Windows::Storage::StorageFile FileSavePicker::SuggestedSaveFile() + { + return m_suggestedSaveFile; + } + void FileSavePicker::SuggestedSaveFile(winrt::Windows::Storage::StorageFile const& value) + { + m_suggestedSaveFile = value; + } + hstring FileSavePicker::SuggestedFileName() + { + return m_suggestedFileName; + } + void FileSavePicker::SuggestedFileName(hstring const& value) + { + m_suggestedFileName = value; + } + + + void FileSavePicker::CaptureParameters(PickerCommon::PickerParameters& parameters) + { + parameters.HWnd = winrt::Microsoft::UI::GetWindowFromWindowId(m_windowId); + parameters.CommitButtonText = m_commitButtonText; + parameters.SettingsIdentifierId = m_settingsIdentifier; + parameters.PickerLocationId = m_suggestedStartLocation; + parameters.FileTypeFilterPara = PickerCommon::CaptureFilterSpec(parameters.FileTypeFilterData, m_fileTypeChoices.GetView()); + + } + + winrt::Windows::Foundation::IAsyncOperation FileSavePicker::PickSaveFileAsync() + { + // TODO: remove get strong reference when telementry is safe stop + auto lifetime{ get_strong() }; + + auto logTelemetry{ StoragePickersTelemetry::FileSavePickerPickSingleFile::Start(m_telemetryHelper) }; + + PickerCommon::PickerParameters parameters{}; + CaptureParameters(parameters); + auto defaultFileExtension = m_defaultFileExtension; + auto suggestedSaveFile = m_suggestedSaveFile; + auto suggestedFileName = m_suggestedFileName; + auto fileTypeChoices = m_fileTypeChoices; + + auto cancellationToken = co_await winrt::get_cancellation_token(); + cancellationToken.enable_propagation(true); + co_await winrt::resume_background(); + + if (cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + + auto dialog = create_instance(CLSID_FileSaveDialog, CONTEXT_ALL); + parameters.ConfigureDialog(dialog); + + if (!PickerCommon::IsHStringNullOrEmpty(defaultFileExtension)) + { + check_hresult(dialog->SetDefaultExtension(defaultFileExtension.c_str())); + } + + if (!PickerCommon::IsHStringNullOrEmpty(suggestedFileName)) + { + check_hresult(dialog->SetFileName(suggestedFileName.c_str())); + } + + if (suggestedSaveFile != nullptr) + { + winrt::com_ptr shellItem; + check_hresult(SHCreateItemFromParsingName(suggestedSaveFile.Path().c_str(), nullptr, IID_PPV_ARGS(shellItem.put()))); + check_hresult(dialog->SetSaveAsItem(shellItem.get())); + } + + { + auto hr = dialog->Show(parameters.HWnd); + if (FAILED(hr)) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + } + + winrt::com_ptr shellItem{}; + check_hresult(dialog->GetResult(shellItem.put())); + + // Get the file path from the dialog + wil::unique_cotaskmem_string filePath{}; + check_hresult(shellItem->GetDisplayName(SIGDN_FILESYSPATH, filePath.put())); + winrt::hstring pathStr(filePath.get()); + + wil::unique_cotaskmem_string fileName; + check_hresult(shellItem->GetDisplayName(SIGDN_NORMALDISPLAY, fileName.put())); + std::wstring fileNameStr(fileName.get()); + + // Check if the file name has an extension + if ((fileNameStr.find_last_of(L".") == std::wstring::npos) && (fileTypeChoices.Size() > 0)) + { + // If the user defined file name doesn't have an extension, + // automatically use the first extension from the first category in fileTypeChoices. + auto firstCategory = fileTypeChoices.First().Current(); + auto value = firstCategory.Value(); + if (value.Size() > 0) + { + auto firstExtension = value.GetAt(0); + pathStr = pathStr + firstExtension; + } + } + + // Create a file. If the file already exists, + // since common item dialog prompts to let user select cancel or override, thus we can safely truncate here. + // Due to our design spec to align with UWP pickers, we need ensure existance of picked file. + auto [handle, _] = wil::try_open_or_truncate_existing_file(pathStr.c_str(), GENERIC_WRITE); + + if (cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + + auto result = make(pathStr); + + logTelemetry.Stop(m_telemetryHelper, true); + co_return result; + } +} diff --git a/dev/Interop/StoragePickers/FileSavePicker.h b/dev/Interop/StoragePickers/FileSavePicker.h new file mode 100644 index 0000000000..92825d661f --- /dev/null +++ b/dev/Interop/StoragePickers/FileSavePicker.h @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include "Microsoft.Windows.Storage.Pickers.FileSavePicker.g.h" +#include "PickerCommon.h" +#include "StoragePickersTelemetryHelper.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + struct FileSavePickerParameters; + + struct FileSavePicker : FileSavePickerT + { + FileSavePicker(winrt::Microsoft::UI::WindowId const& windowId); + + hstring SettingsIdentifier(); + void SettingsIdentifier(hstring const& value); + + winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId SuggestedStartLocation(); + void SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value); + + hstring CommitButtonText(); + void CommitButtonText(hstring const& value); + + winrt::Windows::Foundation::Collections::IMap> FileTypeChoices(); + + hstring DefaultFileExtension(); + void DefaultFileExtension(hstring const& value); + + winrt::Windows::Storage::StorageFile SuggestedSaveFile(); + void SuggestedSaveFile(winrt::Windows::Storage::StorageFile const& value); + + hstring SuggestedFileName(); + void SuggestedFileName(hstring const& value); + + winrt::Windows::Foundation::IAsyncOperation PickSaveFileAsync(); + + private: + winrt::Microsoft::UI::WindowId m_windowId{}; + hstring m_settingsIdentifier{}; + PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; + hstring m_commitButtonText{}; + winrt::Windows::Foundation::Collections::IMap> m_fileTypeChoices{ winrt::single_threaded_map>() }; + hstring m_defaultFileExtension{}; + winrt::Windows::Storage::StorageFile m_suggestedSaveFile{ nullptr }; + hstring m_suggestedFileName{}; + StoragePickersTelemetryHelper m_telemetryHelper{}; + + void CaptureParameters(PickerCommon::PickerParameters& parameters); + }; +} +namespace winrt::Microsoft::Windows::Storage::Pickers::factory_implementation +{ + struct FileSavePicker : FileSavePickerT + { + }; +} diff --git a/dev/Interop/StoragePickers/FolderPicker.cpp b/dev/Interop/StoragePickers/FolderPicker.cpp new file mode 100644 index 0000000000..42b8aaa5f4 --- /dev/null +++ b/dev/Interop/StoragePickers/FolderPicker.cpp @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "FolderPicker.h" +#include "Microsoft.Windows.Storage.Pickers.FolderPicker.g.cpp" +#include "StoragePickersTelemetry.h" +#include +#include +#include +#include "PickerCommon.h" +#include "PickFolderResult.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + FolderPicker::FolderPicker(winrt::Microsoft::UI::WindowId const& windowId) + : m_windowId(windowId) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Storage::Pickers::Feature_StoragePickers::IsEnabled()); + } + winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode FolderPicker::ViewMode() + { + return m_viewMode; + } + void FolderPicker::ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value) + { + m_viewMode = value; + } + hstring FolderPicker::SettingsIdentifier() + { + return m_settingsIdentifier; + } + void FolderPicker::SettingsIdentifier(hstring const& value) + { + m_settingsIdentifier = value; + } + winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId FolderPicker::SuggestedStartLocation() + { + return m_suggestedStartLocation; + } + void FolderPicker::SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value) + { + m_suggestedStartLocation = value; + } + hstring FolderPicker::CommitButtonText() + { + return m_commitButtonText; + } + void FolderPicker::CommitButtonText(hstring const& value) + { + m_commitButtonText = value; + } + winrt::Windows::Foundation::Collections::IVector FolderPicker::FileTypeFilter() + { + return m_fileTypeFilter; + } + + void FolderPicker::CaptureParameters(PickerCommon::PickerParameters& parameters) + { + parameters.HWnd = winrt::Microsoft::UI::GetWindowFromWindowId(m_windowId); + parameters.CommitButtonText = m_commitButtonText; + parameters.SettingsIdentifierId = m_settingsIdentifier; + parameters.PickerLocationId = m_suggestedStartLocation; + parameters.FileTypeFilterPara = PickerCommon::CaptureFilterSpec(parameters.FileTypeFilterData, m_fileTypeFilter.GetView()); + } + + + winrt::Windows::Foundation::IAsyncOperation FolderPicker::PickSingleFolderAsync() + { + // TODO: remove get strong reference when telementry is safe stop + auto lifetime{ get_strong() }; + + auto logTelemetry{ StoragePickersTelemetry::FolderPickerPickSingleFolder::Start(m_telemetryHelper) }; + + PickerCommon::PickerParameters parameters{}; + CaptureParameters(parameters); + + auto cancellationToken = co_await winrt::get_cancellation_token(); + cancellationToken.enable_propagation(true); + co_await winrt::resume_background(); + + if (cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + + auto dialog = create_instance(CLSID_FileOpenDialog, CONTEXT_ALL); + + parameters.ConfigureDialog(dialog); + dialog->SetOptions(FOS_PICKFOLDERS); + + { + auto hr = dialog->Show(parameters.HWnd); + if (FAILED(hr) || cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + } + + winrt::com_ptr shellItem{}; + check_hresult(dialog->GetResult(shellItem.put())); + auto path = PickerCommon::GetPathFromShellItem(shellItem); + + if (cancellationToken()) + { + logTelemetry.Stop(m_telemetryHelper, false); + co_return nullptr; + } + + auto result = make(path); + + logTelemetry.Stop(m_telemetryHelper, true); + co_return result; + } +} diff --git a/dev/Interop/StoragePickers/FolderPicker.h b/dev/Interop/StoragePickers/FolderPicker.h new file mode 100644 index 0000000000..6bd8f2e718 --- /dev/null +++ b/dev/Interop/StoragePickers/FolderPicker.h @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include "Microsoft.Windows.Storage.Pickers.FolderPicker.g.h" +#include "PickerCommon.h" +#include "StoragePickersTelemetryHelper.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + struct FolderPicker : FolderPickerT + { + FolderPicker(winrt::Microsoft::UI::WindowId const& windowId); + + winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode ViewMode(); + void ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode const& value); + + hstring SettingsIdentifier(); + void SettingsIdentifier(hstring const& value); + + winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId SuggestedStartLocation(); + void SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId const& value); + + hstring CommitButtonText(); + void CommitButtonText(hstring const& value); + + winrt::Windows::Foundation::Collections::IVector FileTypeFilter(); + + winrt::Windows::Foundation::IAsyncOperation PickSingleFolderAsync(); + + private: + winrt::Microsoft::UI::WindowId m_windowId{}; + + PickerViewMode m_viewMode{ PickerViewMode::List }; + hstring m_settingsIdentifier{}; + PickerLocationId m_suggestedStartLocation{ PickerLocationId::Unspecified }; + hstring m_commitButtonText{}; + StoragePickersTelemetryHelper m_telemetryHelper{}; + + winrt::Windows::Foundation::Collections::IVector m_fileTypeFilter{ winrt::single_threaded_vector() }; + + void CaptureParameters(PickerCommon::PickerParameters& parameters); + }; +} +namespace winrt::Microsoft::Windows::Storage::Pickers::factory_implementation +{ + struct FolderPicker : FolderPickerT + { + }; +} diff --git a/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl b/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl new file mode 100644 index 0000000000..55a64b4fe8 --- /dev/null +++ b/dev/Interop/StoragePickers/Microsoft.Windows.Storage.Pickers.idl @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include +namespace Microsoft.Windows.Storage.Pickers +{ + [feature(Feature_StoragePickers)] + enum PickerViewMode + { + List, + Thumbnail, + }; + + [feature(Feature_StoragePickers)] + enum PickerLocationId + { + DocumentsLibrary, + ComputerFolder, + Desktop, + Downloads, + HomeGroup, + MusicLibrary, + PicturesLibrary, + VideosLibrary, + Objects3D, + Unspecified, + }; + + [feature(Feature_StoragePickers)] + runtimeclass PickFileResult + { + String Path { get; }; + } + + [feature(Feature_StoragePickers)] + runtimeclass FileOpenPicker + { + FileOpenPicker(Microsoft.UI.WindowId windowId); + + Microsoft.Windows.Storage.Pickers.PickerViewMode ViewMode; + String SettingsIdentifier; + Microsoft.Windows.Storage.Pickers.PickerLocationId SuggestedStartLocation; + String CommitButtonText; + Windows.Foundation.Collections.IVector FileTypeFilter{ get; }; + + [remote_sync] Windows.Foundation.IAsyncOperation PickSingleFileAsync(); + [remote_sync] Windows.Foundation.IAsyncOperation > PickMultipleFilesAsync(); + } + + [feature(Feature_StoragePickers)] + runtimeclass FileSavePicker + { + FileSavePicker(Microsoft.UI.WindowId windowId); + + String SettingsIdentifier; + Microsoft.Windows.Storage.Pickers.PickerLocationId SuggestedStartLocation; + String CommitButtonText; + Windows.Foundation.Collections.IMap > FileTypeChoices{ get; }; + String DefaultFileExtension; + Windows.Storage.StorageFile SuggestedSaveFile; + String SuggestedFileName; + + [remote_sync] Windows.Foundation.IAsyncOperation PickSaveFileAsync(); + } + + [feature(Feature_StoragePickers)] + runtimeclass PickFolderResult + { + String Path { get; }; + } + + [feature(Feature_StoragePickers)] + runtimeclass FolderPicker + { + FolderPicker(Microsoft.UI.WindowId windowId); + + Microsoft.Windows.Storage.Pickers.PickerViewMode ViewMode; + String SettingsIdentifier; + Microsoft.Windows.Storage.Pickers.PickerLocationId SuggestedStartLocation; + String CommitButtonText; + Windows.Foundation.Collections.IVector FileTypeFilter{ get; }; + + [remote_sync] Windows.Foundation.IAsyncOperation PickSingleFolderAsync(); + } +} diff --git a/dev/Interop/StoragePickers/PickFileResult.cpp b/dev/Interop/StoragePickers/PickFileResult.cpp new file mode 100644 index 0000000000..c2029e3757 --- /dev/null +++ b/dev/Interop/StoragePickers/PickFileResult.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "PickFileResult.h" +#include "Microsoft.Windows.Storage.Pickers.PickFileResult.g.cpp" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + PickFileResult::PickFileResult(hstring const& path) : + m_path(path) + { + } + + hstring PickFileResult::Path() + { + return m_path; + } +} diff --git a/dev/Interop/StoragePickers/PickFileResult.h b/dev/Interop/StoragePickers/PickFileResult.h new file mode 100644 index 0000000000..36e6dee336 --- /dev/null +++ b/dev/Interop/StoragePickers/PickFileResult.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include "Microsoft.Windows.Storage.Pickers.PickFileResult.g.h" +#include "PickerCommon.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + struct PickFileResult : PickFileResultT + { + PickFileResult() = default; + + PickFileResult(winrt::hstring const& path); + + hstring Path(); + + private: + hstring m_path{}; + }; +} diff --git a/dev/Interop/StoragePickers/PickFolderResult.cpp b/dev/Interop/StoragePickers/PickFolderResult.cpp new file mode 100644 index 0000000000..2e61733811 --- /dev/null +++ b/dev/Interop/StoragePickers/PickFolderResult.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "PickFolderResult.h" +#include "Microsoft.Windows.Storage.Pickers.PickFolderResult.g.cpp" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + PickFolderResult::PickFolderResult(winrt::hstring const& path) + : m_path(path) + { + } + + hstring PickFolderResult::Path() + { + return m_path; + } +} diff --git a/dev/Interop/StoragePickers/PickFolderResult.h b/dev/Interop/StoragePickers/PickFolderResult.h new file mode 100644 index 0000000000..3fa4c827ed --- /dev/null +++ b/dev/Interop/StoragePickers/PickFolderResult.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include "Microsoft.Windows.Storage.Pickers.PickFolderResult.g.h" +#include "PickerCommon.h" + +namespace winrt::Microsoft::Windows::Storage::Pickers::implementation +{ + struct PickFolderResult : PickFolderResultT + { + PickFolderResult() = default; + + PickFolderResult(winrt::hstring const& path); + + hstring Path(); + + private: + hstring m_path{}; + }; +} diff --git a/dev/Interop/StoragePickers/PickerCommon.cpp b/dev/Interop/StoragePickers/PickerCommon.cpp new file mode 100644 index 0000000000..13e456e3cf --- /dev/null +++ b/dev/Interop/StoragePickers/PickerCommon.cpp @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "PickerCommon.h" +#include +#include "ShObjIdl.h" +#include + +namespace { + + GUID HashHStringToGuid(winrt::hstring const& input) + { + auto algorithm = winrt::Windows::Security::Cryptography::Core::HashAlgorithmProvider::OpenAlgorithm(winrt::Windows::Security::Cryptography::Core::HashAlgorithmNames::Md5()); + + auto buffer = winrt::Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary(input, winrt::Windows::Security::Cryptography::BinaryStringEncoding::Utf16LE); + + auto hash = algorithm.HashData(buffer); + + if (hash.Length() != sizeof(GUID)) + { + throw winrt::hresult_error(E_FAIL, L"Invalid hash length"); + } + + winrt::com_array resultBuffer(sizeof(GUID)); + winrt::Windows::Security::Cryptography::CryptographicBuffer::CopyToByteArray(hash, resultBuffer); + GUID guid = *(reinterpret_cast(resultBuffer.data())); + + // Adjust the GUID to conform to version 3 UUID (MD5-based) + guid.Data3 = (guid.Data3 & 0x0FFF) | 0x3000; // Set the version to 3 + guid.Data4[0] = (guid.Data4[0] & 0x3F) | 0x80; // Set variant to RFC 4122 + + return guid; + } + + winrt::com_ptr GetKnownFolderFromId(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId pickerLocationId) + { + KNOWNFOLDERID knownFolderId; + switch (pickerLocationId) + { + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary: + knownFolderId = FOLDERID_Documents; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::ComputerFolder: + knownFolderId = FOLDERID_ComputerFolder; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Desktop: + knownFolderId = FOLDERID_Desktop; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Downloads: + knownFolderId = FOLDERID_Downloads; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::HomeGroup: + knownFolderId = FOLDERID_HomeGroup; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::MusicLibrary: + knownFolderId = FOLDERID_MusicLibrary; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::PicturesLibrary: + knownFolderId = FOLDERID_PicturesLibrary; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::VideosLibrary: + knownFolderId = FOLDERID_VideosLibrary; + break; + case winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::Objects3D: + knownFolderId = FOLDERID_Objects3D; + break; + default: + return nullptr; + } + + auto knownFolderManager = winrt::create_instance(CLSID_KnownFolderManager); + + winrt::com_ptr knownFolder{}; + winrt::hresult hr = knownFolderManager->GetFolder(knownFolderId, knownFolder.put()); + if (!knownFolder) + { + knownFolderManager->GetFolder(FOLDERID_Documents, knownFolder.put()); + } + if (!knownFolder) + { + return nullptr; + } + + winrt::com_ptr defaultFolder{}; + hr = knownFolder->GetShellItem(0, IID_PPV_ARGS(defaultFolder.put())); + if (FAILED(hr)) + { + return nullptr; + } + + return defaultFolder; + } + + + winrt::hstring FormatExtensionWithWildcard(winrt::hstring extension) + { + if (!extension.empty() && extension[0] == L'*') + { + return extension; + } + else + { + return L"*" + extension; + } + } + + winrt::hstring JoinExtensions(winrt::Windows::Foundation::Collections::IVectorView extensions) + { + winrt::hstring result; + bool first = true; + for (const auto& ext : extensions) + { + if (first) + { + result = FormatExtensionWithWildcard(ext); + first = false; + } + else + { + result = result + L";" + FormatExtensionWithWildcard(ext); + } + } + return result; + } +} + + +namespace PickerCommon { + + using namespace winrt; + + bool IsHStringNullOrEmpty(winrt::hstring value) + { + return value.empty(); + } + + winrt::hstring GetPathFromShellItem(winrt::com_ptr shellItem) + { + wil::unique_cotaskmem_string filePath; + check_hresult(shellItem->GetDisplayName(SIGDN_FILESYSPATH, filePath.put())); + return winrt::hstring{ filePath.get() }; + } + + /// + /// Capture and processing pickers filter inputs and convert them into Common Item Dialog's accepting type + /// + /// temp buffer to hold dynamically transformed strings + /// winrt style filters + /// result Coomon Item Dialog style filters, note only raw pointers here, + /// they are valid up to lifetime of buffer + /// + std::vector CaptureFilterSpec(std::vector& buffer, winrt::Windows::Foundation::Collections::IVectorView filters) + { + std::vector result(filters.Size()); + buffer.clear(); + buffer.reserve(filters.Size() * (size_t)2); + for (const auto& filter : filters) + { + auto ext = FormatExtensionWithWildcard(filter); + buffer.push_back(filter); + buffer.push_back(ext); + } + for (size_t i = 0; i < filters.Size(); i++) + { + result.at(i) = { buffer.at(i * 2).c_str(), buffer.at(i * 2 + 1).c_str() }; + } + + if (result.size() == 0) + { + result.push_back({ L"All Files", L"*.*" }); + } + return result; + } + + /// + /// Capture and processing pickers filter inputs and convert them into Common Item Dialog's accepting type + /// + /// temp buffer to hold dynamically transformed strings + /// winrt style filters + /// result Coomon Item Dialog style filters, note only raw pointers here, + /// they are valid up to lifetime of buffer + /// + std::vector CaptureFilterSpec(std::vector& buffer, winrt::Windows::Foundation::Collections::IMapView> filters) + { + std::vector result(filters.Size()); + buffer.clear(); + buffer.reserve(filters.Size() * (size_t)2); + + for (const auto& filter : filters) + { + buffer.push_back(filter.Key()); + auto extensionList = JoinExtensions(filter.Value().GetView()); + buffer.push_back(extensionList); + } + for (size_t i = 0; i < filters.Size(); i++) + { + result.at(i) = { buffer.at(i * 2).c_str(), buffer.at(i * 2 + 1).c_str() }; + } + + if (result.empty()) + { + result.push_back({ L"All Files", L"*.*" }); + } + return result; + } + + void PickerParameters::ConfigureDialog(winrt::com_ptr dialog) + { + if (!IsHStringNullOrEmpty(CommitButtonText)) + { + check_hresult(dialog->SetOkButtonLabel(CommitButtonText.c_str())); + } + + if (!IsHStringNullOrEmpty(SettingsIdentifierId)) + { + auto guid = HashHStringToGuid(SettingsIdentifierId); + check_hresult(dialog->SetClientGuid(guid)); + } + + auto defaultFolder = GetKnownFolderFromId(PickerLocationId); + if (defaultFolder != nullptr) + { + check_hresult(dialog->SetDefaultFolder(defaultFolder.get())); + } + + check_hresult(dialog->SetFileTypes((UINT)FileTypeFilterPara.size(), FileTypeFilterPara.data())); + } +} diff --git a/dev/Interop/StoragePickers/PickerCommon.h b/dev/Interop/StoragePickers/PickerCommon.h new file mode 100644 index 0000000000..ad2e314b51 --- /dev/null +++ b/dev/Interop/StoragePickers/PickerCommon.h @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include "pch.h" +#include "ShObjIdl.h" +#include "wil/cppwinrt.h" +#include "winrt/base.h" +#include "winrt/Microsoft.Windows.Storage.Pickers.h" +#include "TerminalVelocityFeatures-StoragePickers.h" +#include +#include +#include + +namespace PickerCommon { + winrt::hstring GetPathFromShellItem(winrt::com_ptr shellItem); + + std::vector CaptureFilterSpec(std::vector& buffer, winrt::Windows::Foundation::Collections::IVectorView filters); + std::vector CaptureFilterSpec(std::vector& buffer, winrt::Windows::Foundation::Collections::IMapView> filters); + + bool IsHStringNullOrEmpty(winrt::hstring value); + + struct PickerParameters { + HWND HWnd{}; + winrt::hstring CommitButtonText; + winrt::hstring SettingsIdentifierId; + winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId PickerLocationId; + std::vector FileTypeFilterData{}; + std::vector FileTypeFilterPara{}; + + void ConfigureDialog(winrt::com_ptr dialog); + }; +} diff --git a/dev/Interop/StoragePickers/StoragePickers.vcxitems b/dev/Interop/StoragePickers/StoragePickers.vcxitems new file mode 100644 index 0000000000..f733b72cb1 --- /dev/null +++ b/dev/Interop/StoragePickers/StoragePickers.vcxitems @@ -0,0 +1,49 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {a39e7b2f-5f67-47dd-8443-531d095ca7f3} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dev/Interop/StoragePickers/StoragePickersTelemetry.h b/dev/Interop/StoragePickers/StoragePickersTelemetry.h new file mode 100644 index 0000000000..2811d82a1b --- /dev/null +++ b/dev/Interop/StoragePickers/StoragePickersTelemetry.h @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "..\WindowsAppRuntime_Insights\WindowsAppRuntimeInsights.h" +#include + +DECLARE_TRACELOGGING_CLASS(StoragePickersTelemetryProvider, + "Microsoft.WindowsAppSDK.StoragePickersTelemetry", + // {6ddc5826-bf0a-522e-cc84-0e70eda439ed} + (0x6ddc5826,0xbf0a,0x522e,0xcc,0x84,0x0e,0x70,0xed,0xa4,0x39,0xed)); + +class StoragePickersTelemetry : public wil::TraceLoggingProvider +{ + IMPLEMENT_TELEMETRY_CLASS(StoragePickersTelemetry, StoragePickersTelemetryProvider); + +public: + BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(FileOpenPickerPickSingleFile, PDT_ProductAndServicePerformance); + DEFINE_ACTIVITY_START(StoragePickersTelemetryHelper& telemetryHelper) noexcept try + { + TraceLoggingClassWriteStart( + FileOpenPickerPickSingleFile, + _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 resultNotNull) noexcept try + { + TraceLoggingClassWriteStop( + FileOpenPickerPickSingleFile, + _GENERIC_PARTB_FIELDS_ENABLED, + TraceLoggingBool(resultNotNull, "resultNotNull"), + TraceLoggingBool(telemetryHelper.IsPackagedApp(), "IsAppPackaged"), + TraceLoggingBool(telemetryHelper.IsRunningAsAdmin(), "IsRunningAsAdmin"), + TraceLoggingBool(telemetryHelper.IsRunningInContainer(), "IsRunningInContainer"), + TraceLoggingWideString(telemetryHelper.GetAppName().c_str(), "AppName")); + } + CATCH_LOG() + END_ACTIVITY_CLASS(); + + BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(FileOpenPickerPickMultipleFile, PDT_ProductAndServicePerformance); + DEFINE_ACTIVITY_START(StoragePickersTelemetryHelper& telemetryHelper) noexcept try + { + TraceLoggingClassWriteStart( + FileOpenPickerPickMultipleFile, + _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( + FileOpenPickerPickMultipleFile, + _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(); + + BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(FileSavePickerPickSingleFile, PDT_ProductAndServicePerformance); + DEFINE_ACTIVITY_START(StoragePickersTelemetryHelper& telemetryHelper) noexcept try + { + TraceLoggingClassWriteStart( + FileSavePickerPickSingleFile, + _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 resultNotNull) noexcept try + { + TraceLoggingClassWriteStop( + FileSavePickerPickSingleFile, + _GENERIC_PARTB_FIELDS_ENABLED, + TraceLoggingBool(resultNotNull, "resultNotNull"), + TraceLoggingBool(telemetryHelper.IsPackagedApp(), "IsAppPackaged"), + TraceLoggingBool(telemetryHelper.IsRunningAsAdmin(), "IsRunningAsAdmin"), + TraceLoggingBool(telemetryHelper.IsRunningInContainer(), "IsRunningInContainer"), + TraceLoggingWideString(telemetryHelper.GetAppName().c_str(), "AppName")); + } + CATCH_LOG() + END_ACTIVITY_CLASS(); + + BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(FolderPickerPickSingleFolder, PDT_ProductAndServicePerformance); + DEFINE_ACTIVITY_START(StoragePickersTelemetryHelper& telemetryHelper) noexcept try + { + TraceLoggingClassWriteStart( + FolderPickerPickSingleFolder, + _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 resultNotNull) noexcept try + { + TraceLoggingClassWriteStop( + FolderPickerPickSingleFolder, + _GENERIC_PARTB_FIELDS_ENABLED, + TraceLoggingBool(resultNotNull, "resultNotNull"), + TraceLoggingBool(telemetryHelper.IsPackagedApp(), "IsAppPackaged"), + TraceLoggingBool(telemetryHelper.IsRunningAsAdmin(), "IsRunningAsAdmin"), + TraceLoggingBool(telemetryHelper.IsRunningInContainer(), "IsRunningInContainer"), + TraceLoggingWideString(telemetryHelper.GetAppName().c_str(), "AppName")); + } + CATCH_LOG() + END_ACTIVITY_CLASS(); +}; diff --git a/dev/Interop/StoragePickers/StoragePickersTelemetryHelper.h b/dev/Interop/StoragePickers/StoragePickersTelemetryHelper.h new file mode 100644 index 0000000000..fae7f391d6 --- /dev/null +++ b/dev/Interop/StoragePickers/StoragePickersTelemetryHelper.h @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. +#pragma once + +#include "TelemetryHelper.h" +#include // For IsUserAnAdmin +#include // For process APIs +#include + +class StoragePickersTelemetryHelper : public TelemetryHelper +{ +public: + StoragePickersTelemetryHelper() : TelemetryHelper() + { + m_asAdmin = IsAsAdmin(); + m_inContainer = IsInContainer(); + } + + inline bool IsRunningAsAdmin() const + { + return m_asAdmin; + } + + inline bool IsRunningInContainer() const + { + return m_inContainer; + } + +private: + bool m_asAdmin; + bool m_inContainer; + + BOOL IsAsAdmin() + { + BOOL fIsRunAsAdmin = FALSE; + DWORD dwError = ERROR_SUCCESS; + PSID pAdministratorsGroup = NULL; + + // Allocate and initialize a SID of the administrators group. + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &pAdministratorsGroup)) + { + dwError = GetLastError(); + } + else + { + // Determine whether the SID of administrators group is enabled in + // the primary access token of the process. + if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin)) + { + dwError = GetLastError(); + } + + FreeSid(pAdministratorsGroup); + } + + return fIsRunAsAdmin; + } + + BOOL IsInContainer() + { + BOOL fIsInSandbox = FALSE; + DWORD dwError = ERROR_SUCCESS; + + // Get the process token + HANDLE hToken = NULL; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + dwError = GetLastError(); + return FALSE; + } + + // Check if the process is running in an AppContainer + DWORD dwIsAppContainer = 0; + DWORD dwSize = sizeof(dwIsAppContainer); + BOOL ans = GetTokenInformation(hToken, TokenIsAppContainer, &dwIsAppContainer, dwSize, &dwSize); + if (!ans) + { + dwError = GetLastError(); + CloseHandle(hToken); + return FALSE; + } + + fIsInSandbox = (dwIsAppContainer != 0); + + CloseHandle(hToken); + return fIsInSandbox; + } +}; diff --git a/dev/Projections/CS/Microsoft.Windows.Storage.Pickers.Projection/Microsoft.Windows.Storage.Pickers.Projection.csproj b/dev/Projections/CS/Microsoft.Windows.Storage.Pickers.Projection/Microsoft.Windows.Storage.Pickers.Projection.csproj new file mode 100644 index 0000000000..46e216e9bc --- /dev/null +++ b/dev/Projections/CS/Microsoft.Windows.Storage.Pickers.Projection/Microsoft.Windows.Storage.Pickers.Projection.csproj @@ -0,0 +1,59 @@ + + + net6.0-windows10.0.17763.0 + 10.0.17763.0 + x64;x86;arm64 + AnyCPU + false + + + + true + true + + + + + 8305 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + Microsoft.Windows.Storage.Pickers + 10.0.17763.0 + false + + + + + pdbonly + true + + + + + + + + + + + + + $(OutDir)..\WindowsAppRuntime_DLL\StrippedWinMD\Microsoft.Windows.Storage.Pickers.winmd + true + + + diff --git a/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj b/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj index 455d992f8a..c5ac889f59 100644 --- a/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj +++ b/dev/WindowsAppRuntime_DLL/WindowsAppRuntime_DLL.vcxproj @@ -105,6 +105,7 @@ + @@ -153,9 +154,9 @@ Windows false - onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + onecore.lib;onecoreuap.lib;shell32.lib;%(AdditionalDependencies) WindowsAppRuntime.def - Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) + Microsoft.Internal.FrameworkUdk.dll;shell32.dll;%(DelayLoadDLLs) $(RepoRoot)\dev\common @@ -171,9 +172,9 @@ Windows false - onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + onecore.lib;onecoreuap.lib;shell32.lib;%(AdditionalDependencies) WindowsAppRuntime.def - Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) + Microsoft.Internal.FrameworkUdk.dll;shell32.dll;%(DelayLoadDLLs) $(RepoRoot)\dev\common @@ -189,9 +190,9 @@ Windows false - onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + onecore.lib;onecoreuap.lib;shell32.lib;%(AdditionalDependencies) WindowsAppRuntime.def - Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) + Microsoft.Internal.FrameworkUdk.dll;shell32.dll;%(DelayLoadDLLs) $(RepoRoot)\dev\common @@ -207,9 +208,9 @@ Windows false - onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + onecore.lib;onecoreuap.lib;shell32.lib;%(AdditionalDependencies) WindowsAppRuntime.def - Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) + Microsoft.Internal.FrameworkUdk.dll;shell32.dll;%(DelayLoadDLLs) $(RepoRoot)\dev\common @@ -225,9 +226,9 @@ Windows false - onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + onecore.lib;onecoreuap.lib;shell32.lib;%(AdditionalDependencies) WindowsAppRuntime.def - Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) + Microsoft.Internal.FrameworkUdk.dll;shell32.dll;%(DelayLoadDLLs) $(RepoRoot)\dev\common @@ -243,9 +244,9 @@ Windows false - onecore.lib;onecoreuap.lib;%(AdditionalDependencies) + onecore.lib;onecoreuap.lib;shell32.lib;%(AdditionalDependencies) WindowsAppRuntime.def - Microsoft.Internal.FrameworkUdk.dll;%(DelayLoadDLLs) + Microsoft.Internal.FrameworkUdk.dll;shell32.dll;%(DelayLoadDLLs) $(RepoRoot)\dev\common @@ -329,4 +330,4 @@ - + \ No newline at end of file diff --git a/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml b/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml index 81bbf799f8..7666d7f656 100644 --- a/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml +++ b/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml @@ -141,6 +141,14 @@ + + + Microsoft.WindowsAppRuntime.dll + + + + + Microsoft.WindowsAppRuntime.dll diff --git a/test/StoragePickersTests/StoragePickersTests.cpp b/test/StoragePickersTests/StoragePickersTests.cpp new file mode 100644 index 0000000000..95369d5151 --- /dev/null +++ b/test/StoragePickersTests/StoragePickersTests.cpp @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "AssemblyInfo.h" + +#include +#include +#include + +namespace TB = ::Test::Bootstrap; +namespace TP = ::Test::Packages; + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +namespace Test::StoragePickersTests +{ + class StoragePickersTests + { + public: + BEGIN_TEST_CLASS(StoragePickersTests) + TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA") // MTA is required for ::Test::Bootstrap::SetupPackages() + TEST_CLASS_PROPERTY(L"RunFixtureAs:Class", L"RestrictedUser") + //TEST_CLASS_PROPERTY(L"RunFixtureAs:Class", L"UAP") + //TEST_CLASS_PROPERTY(L"RunAs", L"UAP") + + END_TEST_CLASS() + + TEST_CLASS_SETUP(ClassSetup) + { + ::Test::Bootstrap::SetupPackages(); + return true; + } + + TEST_CLASS_CLEANUP(ClassCleanup) + { + ::Test::Bootstrap::CleanupPackages(); + return true; + } + + TEST_METHOD_SETUP(MethodInit) + { + VERIFY_IS_TRUE(TP::IsPackageRegistered_WindowsAppRuntimeFramework()); + + // The test method setup and execution is on a different thread than the class setup. + // Initialize the framework for the test thread. + ::Test::Bootstrap::SetupBootstrap(); + return true; + } + + TEST_METHOD_CLEANUP(MethodUninit) + { + VERIFY_IS_TRUE(TP::IsPackageRegistered_WindowsAppRuntimeFramework()); + ::Test::Bootstrap::CleanupBootstrap(); + return true; + } + // The unit tests will be updated,first test might is there for testing purpose locally. + // Focusing solely on functional tests for now. + + // Commenting out this test as it is an E2E scenario test that requires UI automation for pipeline execution. + /* + + TEST_METHOD(FileOpenPicker_ShouldPickFile) + { + try + { + auto parentWindow = ::GetForegroundWindow(); + winrt::Microsoft::UI::WindowId windowId{ reinterpret_cast(parentWindow) }; + winrt::Microsoft::Windows::Storage::Pickers::FileOpenPicker picker{ windowId }; + picker.FileTypeFilter().Append(L"*"); + // Act + auto operation = picker.PickSingleFileAsync(); + auto file = operation.get(); + auto path = file.Path(); + // Assert + if (file != nullptr) + { + Log::Comment(L"File open was successful"); + } + else + { + Log::Error(L"File open canceled."); + } + } + catch (const winrt::hresult_error& ex) + { + Log::Error((std::wstring(L"Exception thrown: ") + ex.message().c_str()).c_str()); + VERIFY_FAIL(L"Exception occurred during file open picker."); + } + catch (const std::exception& ex) + { + Log::Error((std::wstring(L"Standard exception thrown: ") + winrt::to_hstring(ex.what()).c_str()).c_str()); + VERIFY_FAIL(L"Standard exception occurred during file open picker."); + } + } + + TEST_METHOD(FileSavePicker_ShouldCreateNewFile) + { + try + { + auto parentWindow = ::GetForegroundWindow(); + winrt::Microsoft::UI::WindowId windowId{ reinterpret_cast(parentWindow) }; + winrt::Microsoft::Windows::Storage::Pickers::FileSavePicker savePicker(windowId); + //savePicker.SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); + savePicker.FileTypeChoices().Insert(L"Plain Text", winrt::single_threaded_vector({ L".txt" })); + savePicker.SuggestedFileName(L"test.txt"); + // Act + auto fileOperation = savePicker.PickSaveFileAsync(); + auto file = fileOperation.get(); + auto path = file.Path(); + + // Assert + if (file != nullptr) + { + Log::Comment(L"File save was successful."); + } + else + { + Log::Error(L"File save failed or was canceled."); + } + } + catch (const winrt::hresult_error& ex) + { + Log::Error((std::wstring(L"Exception thrown: ") + ex.message().c_str()).c_str()); + VERIFY_FAIL(L"Exception occurred during file save picker."); + } + catch (const std::exception& ex) + { + Log::Error((std::wstring(L"Standard exception thrown: ") + winrt::to_hstring(ex.what()).c_str()).c_str()); + VERIFY_FAIL(L"Standard exception occurred during file save picker."); + } + } + + */ + + + TEST_METHOD(VerifyFileOpenPickerOptionsAreReadCorrectly) + { + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileOpenPicker picker(windowId); + + picker.ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::List); + VERIFY_ARE_EQUAL(picker.ViewMode(), winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::List); + + picker.ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::Thumbnail); + VERIFY_ARE_EQUAL(picker.ViewMode(), winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::Thumbnail); + + picker.SettingsIdentifier(L"id"); + VERIFY_ARE_EQUAL(picker.SettingsIdentifier(), L"id"); + + picker.SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); + VERIFY_ARE_EQUAL(picker.SuggestedStartLocation(), winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); + + picker.CommitButtonText(L"commit"); + VERIFY_ARE_EQUAL(picker.CommitButtonText(), L"commit"); + + picker.FileTypeFilter().Append(L"*"); + VERIFY_ARE_EQUAL(picker.FileTypeFilter().GetAt(0), L"*"); + } + + TEST_METHOD(VerifyFileSavePickerOptionsAreReadCorrectly) + { + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FileSavePicker picker(windowId); + + picker.SettingsIdentifier(L"id"); + VERIFY_ARE_EQUAL(picker.SettingsIdentifier(), L"id"); + + picker.SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); + VERIFY_ARE_EQUAL(picker.SuggestedStartLocation(), winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); + + picker.CommitButtonText(L"commit"); + VERIFY_ARE_EQUAL(picker.CommitButtonText(), L"commit"); + + auto filters = winrt::single_threaded_vector(); + filters.Append(L"*"); + picker.FileTypeChoices().Insert(L"All Files", filters); + VERIFY_ARE_EQUAL(picker.FileTypeChoices().Lookup(L"All Files").GetAt(0), L"*"); + } + + TEST_METHOD(VerifyFolderPickerOptionsAreReadCorrectly) + { + auto parentWindow = ::GetForegroundWindow(); + winrt::Microsoft::UI::WindowId windowId{}; + winrt::Microsoft::Windows::Storage::Pickers::FolderPicker picker(windowId); + + picker.ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::List); + VERIFY_ARE_EQUAL(picker.ViewMode(), winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::List); + + picker.ViewMode(winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::Thumbnail); + VERIFY_ARE_EQUAL(picker.ViewMode(), winrt::Microsoft::Windows::Storage::Pickers::PickerViewMode::Thumbnail); + + picker.SettingsIdentifier(L"id"); + VERIFY_ARE_EQUAL(picker.SettingsIdentifier(), L"id"); + + picker.SuggestedStartLocation(winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); + VERIFY_ARE_EQUAL(picker.SuggestedStartLocation(), winrt::Microsoft::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary); + + picker.CommitButtonText(L"commit"); + VERIFY_ARE_EQUAL(picker.CommitButtonText(), L"commit"); + + picker.FileTypeFilter().Append(L"*"); + VERIFY_ARE_EQUAL(picker.FileTypeFilter().GetAt(0), L"*"); + } + }; +} diff --git a/test/StoragePickersTests/StoragePickersTests.vcxproj b/test/StoragePickersTests/StoragePickersTests.vcxproj new file mode 100644 index 0000000000..ebe73a74e7 --- /dev/null +++ b/test/StoragePickersTests/StoragePickersTests.vcxproj @@ -0,0 +1,153 @@ + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + 17.0 + Win32Proj + {85C86306-46D1-4563-8303-0A79DF923586} + StoragePickersTests + 10.0 + StoragePickersTests + + + DynamicLibrary + v143 + Unicode + + + false + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + Use + true + pch.h + $(RepoRoot)\test\inc;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(OutDir)\..\WindowsAppRuntime_DLL;$(OutDir)\..\WindowsAppRuntime_BootstrapDLL;$(RepoRoot)\dev\common + $(RepoRoot);%(AdditionalIncludeDirectories) + + + Windows + onecore.lib;onecoreuap.lib;Microsoft.WindowsAppRuntime.lib;wex.common.lib;wex.logger.lib;te.common.lib;%(AdditionalDependencies) + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories);$(OutDir)\..\WindowsAppRuntime_DLL + + + + + NDEBUG;%(PreprocessorDefinitions) + + + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + + + WIN32;%(PreprocessorDefinitions) + + + + + Create + + + $(RepoRoot)\build\VersionInfo;%(AdditionalIncludeDirectories) + + + + + + + + + + + $(OutDir)\..\WindowsAppRuntime_DLL\Microsoft.Windows.Storage.Pickers.winmd + true + + + + + {f76b776e-86f5-48c5-8fc7-d2795ecc9746} + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/test/StoragePickersTests/StoragePickersTests.vcxproj.filters b/test/StoragePickersTests/StoragePickersTests.vcxproj.filters new file mode 100644 index 0000000000..404acd038b --- /dev/null +++ b/test/StoragePickersTests/StoragePickersTests.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/test/StoragePickersTests/packages.config b/test/StoragePickersTests/packages.config new file mode 100644 index 0000000000..1a51bd1772 --- /dev/null +++ b/test/StoragePickersTests/packages.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/StoragePickersTests/pch.cpp b/test/StoragePickersTests/pch.cpp new file mode 100644 index 0000000000..a77728ba07 --- /dev/null +++ b/test/StoragePickersTests/pch.cpp @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/test/StoragePickersTests/pch.h b/test/StoragePickersTests/pch.h new file mode 100644 index 0000000000..0c38a1ba77 --- /dev/null +++ b/test/StoragePickersTests/pch.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#ifndef PCH_H +#define PCH_H + +#include + +#include + +#include +#include + +#include "winrt/Microsoft.Windows.Storage.Pickers.h" + +#include + +#include +#include +#include + +#endif //PCH_H