diff --git a/dev/ApplicationData/ApplicationData.idl b/dev/ApplicationData/ApplicationData.idl
index ebbbcedd76..1e421111c3 100644
--- a/dev/ApplicationData/ApplicationData.idl
+++ b/dev/ApplicationData/ApplicationData.idl
@@ -86,7 +86,8 @@ namespace Microsoft.Windows.Storage
static ApplicationData GetForPackageFamily(String packageFamilyName);
/// Get an instance of ApplicationData for the specified unpackaged app for the current user.
- [feature(Feature_ApplicationData)]
+ /// @note This method respects impersonation.
+ /// @warning The returned instance of ApplicationData does not support LocalCache, PublisherCache or SharedLocal features.
[contract(ApplicationDataContract, 2)]
static ApplicationData GetForUnpackaged(String publisher, String product);
@@ -95,6 +96,7 @@ namespace Microsoft.Windows.Storage
/// Return the path for the local cache data store not included in backup and restore operations.
/// @note This is equivalent to Windows.Storage.ApplicationData.LocalCacheFolder().Path()
+ /// @warning This method is not supported if the ApplicationData instance was acquired via GetForUnpackaged().
/// @see https://learn.microsoft.com/uwp/api/windows.storage.applicationdata.localcachefolder
String LocalCachePath { get; };
@@ -111,6 +113,7 @@ namespace Microsoft.Windows.Storage
/// Return the path for the shared data store.
/// @note This is equivalent to Windows.Storage.ApplicationData.SharedLocalFolder().Path()
+ /// @warning This method is not supported if the ApplicationData instance was acquired via GetForUnpackaged().
/// @see https://learn.microsoft.com/uwp/api/windows.storage.applicationdata.sharedlocalfolder
String SharedLocalPath { get; };
@@ -120,6 +123,7 @@ namespace Microsoft.Windows.Storage
String TemporaryPath { get; };
/// Return a StorageFolder for the local cache data store not included in backup and restore operations.
+ /// @warning This method is not supported if the ApplicationData instance was acquired via GetForUnpackaged().
/// @see https://learn.microsoft.com/uwp/api/windows.storage.applicationdata.localcachefolder
Windows.Storage.StorageFolder LocalCacheFolder { get; };
@@ -134,6 +138,7 @@ namespace Microsoft.Windows.Storage
Windows.Storage.StorageFolder MachineFolder { get; };
/// Return a StorageFolder for the shared data store.
+ /// @warning This method is not supported if the ApplicationData instance was acquired via GetForUnpackaged().
/// @see https://learn.microsoft.com/uwp/api/windows.storage.applicationdata.sharedlocalfolder
Windows.Storage.StorageFolder SharedLocalFolder { get; };
@@ -157,10 +162,12 @@ namespace Microsoft.Windows.Storage
/// Return the specified path of the shared data store for the publisher of the app.
/// @note This is equivalent to Windows.Storage.ApplicationData.GetPublisherCacheFolder(folderName).Path()
+ /// @warning This method is not supported if the ApplicationData instance was acquired via GetForUnpackaged().
/// @see https://learn.microsoft.com/uwp/api/windows.storage.applicationdata.getpublishercachefolder
String GetPublisherCachePath(String folderName);
/// Return the specified subfolder of the shared data store for the publisher of the app.
+ /// @warning This method is not supported if the ApplicationData instance was acquired via GetForUnpackaged().
/// @see https://learn.microsoft.com/uwp/api/windows.storage.applicationdata.getpublishercachefolder
Windows.Storage.StorageFolder GetPublisherCacheFolder(String folderName);
}
diff --git a/dev/ApplicationData/ApplicationData.vcxitems b/dev/ApplicationData/ApplicationData.vcxitems
index 32e3851ac8..3655db8ddf 100644
--- a/dev/ApplicationData/ApplicationData.vcxitems
+++ b/dev/ApplicationData/ApplicationData.vcxitems
@@ -7,7 +7,7 @@
- %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
+ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory);$(RepoRoot)\dev\common
@@ -16,12 +16,17 @@
+
+
+
+
+
diff --git a/dev/ApplicationData/ApplicationData.vcxitems.filters b/dev/ApplicationData/ApplicationData.vcxitems.filters
index 215fe7b0dc..b0a21278ad 100644
--- a/dev/ApplicationData/ApplicationData.vcxitems.filters
+++ b/dev/ApplicationData/ApplicationData.vcxitems.filters
@@ -17,6 +17,12 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
@@ -31,6 +37,15 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
diff --git a/dev/ApplicationData/ApplicationDataTelemetry.h b/dev/ApplicationData/ApplicationDataTelemetry.h
index 9ce1d58fc5..3b00ac5566 100644
--- a/dev/ApplicationData/ApplicationDataTelemetry.h
+++ b/dev/ApplicationData/ApplicationDataTelemetry.h
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation and Contributors.
+// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT license.
#pragma once
@@ -26,4 +26,16 @@ class ApplicationDataTelemetry : public wil::TraceLoggingProvider
}
CATCH_LOG()
END_ACTIVITY_CLASS();
+ BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(UnpackagedClearAsync, PDT_ProductAndServicePerformance);
+ DEFINE_ACTIVITY_START(winrt::Microsoft::Windows::Storage::ApplicationDataLocality locality, winrt::hstring const& publisher, winrt::hstring const& product) noexcept try
+ {
+ TraceLoggingClassWriteStart(
+ UnpackagedClearAsync,
+ _GENERIC_PARTB_FIELDS_ENABLED,
+ TraceLoggingInt32(static_cast(locality), "Locality"),
+ TraceLoggingWideString(publisher.c_str(), "Publisher"),
+ TraceLoggingWideString(product.c_str(), "Product"));
+ }
+ CATCH_LOG()
+ END_ACTIVITY_CLASS();
};
diff --git a/dev/ApplicationData/M.W.S.ApplicationData.cpp b/dev/ApplicationData/M.W.S.ApplicationData.cpp
index 89ecdecb0d..3fd36a45d3 100644
--- a/dev/ApplicationData/M.W.S.ApplicationData.cpp
+++ b/dev/ApplicationData/M.W.S.ApplicationData.cpp
@@ -14,6 +14,8 @@
#include "ApplicationDataTelemetry.h"
+#include "Validate.h"
+
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local) == static_cast(winrt::Windows::Storage::ApplicationDataLocality::Local));
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::LocalCache) == static_cast(winrt::Windows::Storage::ApplicationDataLocality::LocalCache));
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::SharedLocal) == static_cast(winrt::Windows::Storage::ApplicationDataLocality::SharedLocal));
@@ -21,11 +23,26 @@ static_assert(static_cast(winrt::Microsoft::Windows::Storage::Applicati
namespace winrt::Microsoft::Windows::Storage::implementation
{
- ApplicationData::ApplicationData(winrt::Windows::Storage::ApplicationData const& value, hstring const& packageFamilyName) :
+ ApplicationData::ApplicationData(hstring const& packageFamilyName) :
+ m_unpackagedApplicationData(),
+ m_applicationData(nullptr),
+ m_packageFamilyName(packageFamilyName)
+ {
+ }
+ ApplicationData::ApplicationData(winrt::Windows::Storage::ApplicationData& value, hstring const& packageFamilyName) :
+ m_unpackagedApplicationData(),
m_applicationData(value),
m_packageFamilyName(packageFamilyName)
{
}
+ ApplicationData::ApplicationData(hstring const& publisher, hstring const& product) :
+ m_unpackagedApplicationData(),
+ m_applicationData(nullptr),
+ m_packageFamilyName()
+ {
+ auto unpackagedApplicationData{ new ::Microsoft::Windows::Storage::UnpackagedApplicationData(publisher, product) };
+ m_unpackagedApplicationData.reset(unpackagedApplicationData);
+ }
winrt::Microsoft::Windows::Storage::ApplicationData ApplicationData::GetDefault()
{
const auto packageFamilyName{ ::AppModel::Identity::GetCurrentPackageFamilyName() };
@@ -84,7 +101,7 @@ namespace winrt::Microsoft::Windows::Storage::implementation
{
// The package family has package(s) registered for the user with at least 1 Framework package
// (and a package family can't have Framework and not-Framework packages in the same package family)
- return winrt::make(nullptr, packageFamilyName);
+ return winrt::make(packageFamilyName);
}
}
@@ -92,18 +109,30 @@ namespace winrt::Microsoft::Windows::Storage::implementation
throw;
}
}
- winrt::Microsoft::Windows::Storage::ApplicationData ApplicationData::GetForUnpackaged(hstring const& /*publisher*/, hstring const& /*product*/)
+ winrt::Microsoft::Windows::Storage::ApplicationData ApplicationData::GetForUnpackaged(hstring const& publisher, hstring const& product)
{
- // TODO implement GetForUnpackaged
- throw hresult_not_implemented();
+ _VerifyPublisher(publisher);
+ _VerifyProduct(product);
+
+ return winrt::make(publisher, product);
}
bool ApplicationData::IsMachinePathSupported()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->IsMachinePathSupported();
+ }
+
const auto path{ _MachinePath(m_packageFamilyName) };
return !path.empty();
}
hstring ApplicationData::LocalCachePath()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->LocalCachePath();
+ }
+
if (!m_applicationData)
{
return winrt::hstring{};
@@ -112,6 +141,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
hstring ApplicationData::LocalPath()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->LocalPath();
+ }
+
if (!m_applicationData)
{
return winrt::hstring{};
@@ -120,11 +154,21 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
hstring ApplicationData::MachinePath()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->MachinePath();
+ }
+
const auto path{ _MachinePath(m_packageFamilyName) };
return winrt::hstring{ path.c_str() };
}
hstring ApplicationData::SharedLocalPath()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->SharedLocalPath();
+ }
+
if (!m_applicationData)
{
return winrt::hstring{};
@@ -143,6 +187,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
hstring ApplicationData::TemporaryPath()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->TemporaryPath();
+ }
+
if (!m_applicationData)
{
return winrt::hstring{};
@@ -151,6 +200,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Storage::StorageFolder ApplicationData::LocalCacheFolder()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->LocalCacheFolder();
+ }
+
if (!m_applicationData)
{
return nullptr;
@@ -159,6 +213,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Storage::StorageFolder ApplicationData::LocalFolder()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->LocalFolder();
+ }
+
if (!m_applicationData)
{
return nullptr;
@@ -167,6 +226,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Storage::StorageFolder ApplicationData::MachineFolder()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->MachineFolder();
+ }
+
const auto path{ MachinePath() };
if (path.empty())
{
@@ -176,6 +240,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Storage::StorageFolder ApplicationData::SharedLocalFolder()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->SharedLocalFolder();
+ }
+
if (!m_applicationData)
{
return nullptr;
@@ -184,6 +253,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Storage::StorageFolder ApplicationData::TemporaryFolder()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->TemporaryFolder();
+ }
+
if (!m_applicationData)
{
return nullptr;
@@ -192,6 +266,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Microsoft::Windows::Storage::ApplicationDataContainer ApplicationData::LocalSettings()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->LocalSettings();
+ }
+
if (!m_applicationData)
{
return nullptr;
@@ -201,6 +280,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Foundation::IAsyncAction ApplicationData::ClearAsync(winrt::Microsoft::Windows::Storage::ApplicationDataLocality locality)
{
+ if (m_unpackagedApplicationData)
+ {
+ co_await m_unpackagedApplicationData->ClearAsync(locality);
+ }
+
if (!m_applicationData)
{
co_return;
@@ -216,6 +300,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Foundation::IAsyncAction ApplicationData::ClearPublisherCacheFolderAsync(hstring folderName)
{
+ if (m_unpackagedApplicationData)
+ {
+ co_await m_unpackagedApplicationData->ClearPublisherCacheFolderAsync(folderName);
+ }
+
if (!m_applicationData)
{
co_return;
@@ -224,6 +313,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
void ApplicationData::Close()
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->Close();
+ }
+
if (m_applicationData)
{
m_applicationData.Close();
@@ -231,6 +325,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
hstring ApplicationData::GetPublisherCachePath(hstring const& folderName)
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->GetPublisherCachePath(folderName);
+ }
+
auto folder{ GetPublisherCacheFolder(folderName) };
winrt::hstring path;
if (folder)
@@ -239,25 +338,13 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
return path;
}
- winrt::Windows::Foundation::IAsyncAction ApplicationData::ClearMachineFolderAsync()
- {
- const auto path{ MachinePath() };
-
- auto logTelemetry{ ApplicationDataTelemetry::ClearMachineFolderAsync::Start(path) };
-
- auto strong{ get_strong() };
-
- logTelemetry.IgnoreCurrentThread();
- co_await winrt::resume_background();
- auto logTelemetryContinuation{ logTelemetry.ContinueOnCurrentThread() };
-
- const auto options{ wil::RemoveDirectoryOptions::KeepRootDirectory | wil::RemoveDirectoryOptions::RemoveReadOnly };
- wil::RemoveDirectoryRecursive(path.c_str(), options);
-
- logTelemetry.Stop();
- }
winrt::Windows::Storage::StorageFolder ApplicationData::GetPublisherCacheFolder(hstring const& folderName)
{
+ if (m_unpackagedApplicationData)
+ {
+ return m_unpackagedApplicationData->GetPublisherCacheFolder(folderName);
+ }
+
if (!m_applicationData)
{
return nullptr;
@@ -279,6 +366,23 @@ namespace winrt::Microsoft::Windows::Storage::implementation
throw;
}
}
+ winrt::Windows::Foundation::IAsyncAction ApplicationData::ClearMachineFolderAsync()
+ {
+ const auto path{ MachinePath() };
+
+ auto logTelemetry{ ApplicationDataTelemetry::ClearMachineFolderAsync::Start(path) };
+
+ auto strong{ get_strong() };
+
+ logTelemetry.IgnoreCurrentThread();
+ co_await winrt::resume_background();
+ auto logTelemetryContinuation{ logTelemetry.ContinueOnCurrentThread() };
+
+ const auto options{ wil::RemoveDirectoryOptions::KeepRootDirectory | wil::RemoveDirectoryOptions::RemoveReadOnly };
+ wil::RemoveDirectoryRecursive(path.c_str(), options);
+
+ logTelemetry.Stop();
+ }
std::filesystem::path ApplicationData::_MachinePath(hstring const& packageFamilyName)
{
// Path = HKLM\...apprepository...\Families\ApplicationData\...pkgfamilyname...\Machine
@@ -297,7 +401,6 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
return std::filesystem::path{};
}
-
bool ApplicationData::_PathExists(std::filesystem::path const& path)
{
const std::filesystem::directory_entry directoryEntry{ path };
@@ -312,4 +415,14 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
return path;
}
+ void ApplicationData::_VerifyPublisher(winrt::hstring const& string)
+ {
+ THROW_HR_IF_MSG(E_INVALIDARG, ::Microsoft::Foundation::String::IsNullOrEmpty(string.c_str()), "Publisher not valid (%ls)", string.c_str());
+ THROW_HR_IF_MSG(E_INVALIDARG, ::Microsoft::Windows::Storage::is_prohibited_string(string.c_str()), "Publisher not valid (%ls)", string.c_str());
+ }
+ void ApplicationData::_VerifyProduct(winrt::hstring const& string)
+ {
+ THROW_HR_IF_MSG(E_INVALIDARG, ::Microsoft::Foundation::String::IsNullOrEmpty(string.c_str()), "Product not valid (%ls)", string.c_str());
+ THROW_HR_IF_MSG(E_INVALIDARG, ::Microsoft::Windows::Storage::is_prohibited_string(string.c_str()), "Product not valid (%ls)", string.c_str());
+ }
}
diff --git a/dev/ApplicationData/M.W.S.ApplicationData.h b/dev/ApplicationData/M.W.S.ApplicationData.h
index 8256e50701..11bb311a84 100644
--- a/dev/ApplicationData/M.W.S.ApplicationData.h
+++ b/dev/ApplicationData/M.W.S.ApplicationData.h
@@ -5,12 +5,16 @@
#include "Microsoft.Windows.Storage.ApplicationData.g.h"
+#include "UnpackagedApplicationData.h"
+
namespace winrt::Microsoft::Windows::Storage::implementation
{
struct ApplicationData : ApplicationDataT
{
ApplicationData() = default;
- ApplicationData(winrt::Windows::Storage::ApplicationData const& value, hstring const& packageFamilyName);
+ ApplicationData(hstring const& packageFamilyName);
+ ApplicationData(winrt::Windows::Storage::ApplicationData& value, hstring const& packageFamilyName);
+ ApplicationData(hstring const& publisher, hstring const& product);
static winrt::Microsoft::Windows::Storage::ApplicationData GetDefault();
static winrt::Microsoft::Windows::Storage::ApplicationData GetForUser(winrt::Windows::System::User user);
@@ -39,8 +43,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
static std::filesystem::path _MachinePath(hstring const& packageFamilyName);
static bool _PathExists(std::filesystem::path const& path);
static hstring StorageFolderToPath(winrt::Windows::Storage::StorageFolder storageFolder);
+ static void _VerifyPublisher(winrt::hstring const& string);
+ static void _VerifyProduct(winrt::hstring const& string);
private:
+ std::unique_ptr<::Microsoft::Windows::Storage::UnpackagedApplicationData> m_unpackagedApplicationData;
winrt::Windows::Storage::ApplicationData m_applicationData;
winrt::hstring m_packageFamilyName;
};
diff --git a/dev/ApplicationData/M.W.S.ApplicationDataContainer.cpp b/dev/ApplicationData/M.W.S.ApplicationDataContainer.cpp
index b5181a619f..048f144cc3 100644
--- a/dev/ApplicationData/M.W.S.ApplicationDataContainer.cpp
+++ b/dev/ApplicationData/M.W.S.ApplicationDataContainer.cpp
@@ -6,14 +6,26 @@
#include "M.W.S.ApplicationDataContainer.h"
#include "Microsoft.Windows.Storage.ApplicationDataContainer.g.cpp"
+#include "UnpackagedApplicationDataContainer.h"
+
namespace winrt::Microsoft::Windows::Storage::implementation
{
ApplicationDataContainer::ApplicationDataContainer(winrt::Windows::Storage::ApplicationDataContainer const& value) :
m_applicationDataContainer(value)
{
}
+ ApplicationDataContainer::ApplicationDataContainer(std::shared_ptr<::Microsoft::Windows::Storage::UnpackagedApplicationDataContainer> const& value) :
+ m_unpackagedApplicationDataContainer(value),
+ m_applicationDataContainer(nullptr)
+ {
+ }
winrt::Windows::Foundation::Collections::IMap ApplicationDataContainer::Containers()
{
+ if (m_unpackagedApplicationDataContainer)
+ {
+ return m_unpackagedApplicationDataContainer->Containers();
+ }
+
winrt::Windows::Foundation::Collections::IMap map{ winrt::single_threaded_map() };
auto containers{ m_applicationDataContainer.Containers() };
for (auto container : containers)
@@ -25,10 +37,20 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
hstring ApplicationDataContainer::Name()
{
+ if (m_unpackagedApplicationDataContainer)
+ {
+ return m_unpackagedApplicationDataContainer->Name();
+ }
+
return m_applicationDataContainer.Name();
}
winrt::Microsoft::Windows::Storage::ApplicationDataLocality ApplicationDataContainer::Locality()
{
+ if (m_unpackagedApplicationDataContainer)
+ {
+ return m_unpackagedApplicationDataContainer->Locality();
+ }
+
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local) == static_cast(winrt::Windows::Storage::ApplicationDataLocality::Local));
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::LocalCache) == static_cast(winrt::Windows::Storage::ApplicationDataLocality::LocalCache));
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Temporary) == static_cast(winrt::Windows::Storage::ApplicationDataLocality::Temporary));
@@ -37,14 +59,29 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
winrt::Windows::Foundation::Collections::IPropertySet ApplicationDataContainer::Values()
{
+ if (m_unpackagedApplicationDataContainer)
+ {
+ return m_unpackagedApplicationDataContainer->Values();
+ }
+
return m_applicationDataContainer.Values();
}
void ApplicationDataContainer::Close()
{
+ if (m_unpackagedApplicationDataContainer)
+ {
+ return m_unpackagedApplicationDataContainer->Close();
+ }
+
return m_applicationDataContainer.Close();
}
winrt::Microsoft::Windows::Storage::ApplicationDataContainer ApplicationDataContainer::CreateContainer(hstring const& name, winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition const& disposition)
{
+ if (m_unpackagedApplicationDataContainer)
+ {
+ return m_unpackagedApplicationDataContainer->CreateContainer(name, disposition);
+ }
+
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always) == static_cast(winrt::Windows::Storage::ApplicationDataCreateDisposition::Always));
static_assert(static_cast(winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Existing) == static_cast(winrt::Windows::Storage::ApplicationDataCreateDisposition::Existing));
@@ -53,6 +90,11 @@ namespace winrt::Microsoft::Windows::Storage::implementation
}
void ApplicationDataContainer::DeleteContainer(hstring const& name)
{
+ if (m_unpackagedApplicationDataContainer)
+ {
+ return m_unpackagedApplicationDataContainer->DeleteContainer(name);
+ }
+
m_applicationDataContainer.DeleteContainer(name);
}
}
diff --git a/dev/ApplicationData/M.W.S.ApplicationDataContainer.h b/dev/ApplicationData/M.W.S.ApplicationDataContainer.h
index cb96cb97af..c22bcce957 100644
--- a/dev/ApplicationData/M.W.S.ApplicationDataContainer.h
+++ b/dev/ApplicationData/M.W.S.ApplicationDataContainer.h
@@ -5,12 +5,18 @@
#include "Microsoft.Windows.Storage.ApplicationDataContainer.g.h"
+namespace Microsoft::Windows::Storage
+{
+ struct UnpackagedApplicationDataContainer;
+}
+
namespace winrt::Microsoft::Windows::Storage::implementation
{
struct ApplicationDataContainer : ApplicationDataContainerT
{
ApplicationDataContainer() = default;
ApplicationDataContainer(winrt::Windows::Storage::ApplicationDataContainer const& value);
+ ApplicationDataContainer(std::shared_ptr<::Microsoft::Windows::Storage::UnpackagedApplicationDataContainer> const& value);
winrt::Windows::Foundation::Collections::IMap Containers();
hstring Name();
@@ -21,6 +27,7 @@ namespace winrt::Microsoft::Windows::Storage::implementation
void DeleteContainer(hstring const& name);
private:
+ std::shared_ptr<::Microsoft::Windows::Storage::UnpackagedApplicationDataContainer> m_unpackagedApplicationDataContainer;
winrt::Windows::Storage::ApplicationDataContainer m_applicationDataContainer;
};
}
diff --git a/dev/ApplicationData/UnpackagedApplicationData.cpp b/dev/ApplicationData/UnpackagedApplicationData.cpp
new file mode 100644
index 0000000000..e1d3ba000f
--- /dev/null
+++ b/dev/ApplicationData/UnpackagedApplicationData.cpp
@@ -0,0 +1,275 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+#include "pch.h"
+
+#include
+
+#include
+
+#include "UnpackagedApplicationData.h"
+#include "UnpackagedApplicationDataContainer.h"
+
+#include "ApplicationDataTelemetry.h"
+
+namespace Microsoft::Windows::Storage
+{
+ UnpackagedApplicationData::UnpackagedApplicationData(winrt::hstring const& publisher, winrt::hstring const& product) :
+ m_publisher(publisher),
+ m_product(product),
+ m_localPath(),
+ m_temporaryPath()
+ {
+ }
+ bool UnpackagedApplicationData::IsMachinePathSupported()
+ {
+ _VerifyNotClosed();
+
+ const auto path{ _MachinePath(m_publisher, m_product) };
+ return !path.empty();
+ }
+ winrt::hstring UnpackagedApplicationData::LocalCachePath()
+ {
+ _VerifyNotClosed();
+
+ // ApplicationData.LocalCacheFolder has no unpackaged equivalent
+ throw winrt::hresult_not_implemented();
+ }
+ winrt::hstring UnpackagedApplicationData::LocalPath()
+ {
+ _VerifyNotClosed();
+
+ if (m_localPath.empty())
+ {
+ // Caller is LocalSystem : %PROGRAMDATA%\...publisher...\...product...
+ // Caller is =MediumIL : %USERPROFILE%\AppData\Local\...publisher...\...product...
+ auto folderId{ FOLDERID_LocalAppData };
+ if (::Security::User::IsLocalSystem())
+ {
+ folderId = FOLDERID_ProgramData;
+ }
+ else if (::Security::IntegrityLevel::GetIntegrityLevel() < SECURITY_MANDATORY_MEDIUM_RID)
+ {
+ folderId = FOLDERID_LocalAppDataLow;
+ }
+ wil::unique_cotaskmem_string rootPath;
+ THROW_IF_FAILED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT/*TODO KF_FLAG_CREATE | KF_FLAG_DONT_VERIFY*/, nullptr, wil::out_param(rootPath)));
+ std::filesystem::path path{ rootPath.get() };
+ path /= m_publisher.c_str();
+ path /= m_product.c_str();
+ m_localPath = path.c_str();
+ }
+ return m_localPath;
+ }
+ winrt::hstring UnpackagedApplicationData::MachinePath()
+ {
+ _VerifyNotClosed();
+
+ const auto path{ _MachinePath(m_publisher, m_product) };
+ return winrt::hstring{ path.c_str() };
+ }
+ winrt::hstring UnpackagedApplicationData::SharedLocalPath()
+ {
+ _VerifyNotClosed();
+
+ // ApplicationData.SharedLocalFolder has no unpackaged equivalent
+ throw winrt::hresult_not_implemented();
+ }
+ winrt::hstring UnpackagedApplicationData::TemporaryPath()
+ {
+ _VerifyNotClosed();
+
+ if (m_temporaryPath.empty())
+ {
+ // GetTempPath[2]() + \...publisher...\...product...
+ auto path{ ::Microsoft::FileSystem::Path::GetTempDirectory() };
+ path /= m_publisher.c_str();
+ path /= m_product.c_str();
+ m_temporaryPath = path.c_str();
+ }
+ return m_temporaryPath;
+ }
+ winrt::Windows::Storage::StorageFolder UnpackagedApplicationData::LocalCacheFolder()
+ {
+ _VerifyNotClosed();
+
+ const auto path{ LocalCachePath() };
+ if (path.empty())
+ {
+ return nullptr;
+ }
+ return winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(path).get();
+ }
+ winrt::Windows::Storage::StorageFolder UnpackagedApplicationData::LocalFolder()
+ {
+ _VerifyNotClosed();
+
+ const auto path{ LocalPath() };
+ if (path.empty())
+ {
+ return nullptr;
+ }
+ return winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(path).get();
+ }
+ winrt::Windows::Storage::StorageFolder UnpackagedApplicationData::MachineFolder()
+ {
+ _VerifyNotClosed();
+
+ const auto path{ MachinePath() };
+ if (path.empty())
+ {
+ return nullptr;
+ }
+ return winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(path).get();
+ }
+ winrt::Windows::Storage::StorageFolder UnpackagedApplicationData::SharedLocalFolder()
+ {
+ _VerifyNotClosed();
+
+ const auto path{ SharedLocalPath() };
+ if (path.empty())
+ {
+ return nullptr;
+ }
+ return winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(path).get();
+ }
+ winrt::Windows::Storage::StorageFolder UnpackagedApplicationData::TemporaryFolder()
+ {
+ _VerifyNotClosed();
+
+ const auto path{ TemporaryPath() };
+ if (path.empty())
+ {
+ return nullptr;
+ }
+ return winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(path).get();
+ }
+ winrt::Microsoft::Windows::Storage::ApplicationDataContainer UnpackagedApplicationData::LocalSettings()
+ {
+ _VerifyNotClosed();
+
+ wil::unique_hkey currentUserKey;
+ THROW_IF_WIN32_ERROR(::RegOpenCurrentUser(KEY_READ | KEY_WRITE, currentUserKey.put()));
+ auto subKey{ std::format(L"SOFTWARE\\{}\\{}", m_publisher, m_product) };
+ auto key{ wil::reg::create_shared_key(currentUserKey.get(), subKey.c_str(), wil::reg::key_access::readwrite) };
+ auto container{ std::make_shared(
+ std::move(key), winrt::hstring{}, winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local) };
+ return winrt::make(container);
+ }
+ winrt::Windows::Foundation::IAsyncAction UnpackagedApplicationData::ClearAsync(winrt::Microsoft::Windows::Storage::ApplicationDataLocality locality)
+ {
+ _VerifyNotClosed();
+
+ const winrt::hstring publisher{ m_publisher };
+ const winrt::hstring product{ m_product };
+ winrt::hstring localityPath;
+ switch (locality)
+ {
+ case winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local:
+ {
+ localityPath = LocalPath();
+ break;
+ }
+ case winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Temporary:
+ {
+ localityPath = TemporaryPath();
+ break;
+ }
+ case winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Machine:
+ {
+ localityPath = MachinePath();
+ break;
+ }
+ default:
+ THROW_HR_MSG(E_NOTIMPL, "%d", static_cast(locality));
+ }
+
+ auto logTelemetry{ ApplicationDataTelemetry::UnpackagedClearAsync::Start(locality, publisher, product) };
+
+ logTelemetry.IgnoreCurrentThread();
+ co_await winrt::resume_background();
+ auto logTelemetryContinuation{ logTelemetry.ContinueOnCurrentThread() };
+
+ // Clear the path
+ if (!localityPath.empty())
+ {
+ std::filesystem::path path{ localityPath.c_str() };
+ if (_PathExists(path))
+ {
+ const auto options{ wil::RemoveDirectoryOptions::KeepRootDirectory | wil::RemoveDirectoryOptions::RemoveReadOnly };
+ THROW_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(path.c_str(), options));
+ }
+ }
+
+ // Clear the settings (if necessary)
+ if (locality == winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local)
+ {
+ wil::unique_hkey currentUserKey;
+ THROW_IF_WIN32_ERROR(::RegOpenCurrentUser(KEY_READ | KEY_WRITE, currentUserKey.put()));
+ auto subKey{ std::format(L"SOFTWARE\\{}\\{}", publisher, product) };
+ const auto hr{ HRESULT_FROM_WIN32(::RegDeleteTreeW(currentUserKey.get(), subKey.c_str())) };
+ if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && hr != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
+ {
+ THROW_IF_FAILED(hr);
+ }
+ }
+
+ logTelemetry.Stop();
+
+ co_return;
+ }
+ winrt::Windows::Foundation::IAsyncAction UnpackagedApplicationData::ClearPublisherCacheFolderAsync(winrt::hstring folderName)
+ {
+ _VerifyNotClosed();
+
+ // ApplicationData.PublisherCacheFolder has no unpackaged equivalent
+ throw winrt::hresult_not_implemented();
+ }
+ void UnpackagedApplicationData::Close()
+ {
+ m_publisher.clear();
+ m_product.clear();
+ m_localPath.clear();
+ m_temporaryPath.clear();
+ }
+ winrt::hstring UnpackagedApplicationData::GetPublisherCachePath(winrt::hstring const& folderName)
+ {
+ _VerifyNotClosed();
+
+ // ApplicationData.PublisherCacheFolder has no unpackaged equivalent
+ throw winrt::hresult_not_implemented();
+ }
+ winrt::Windows::Storage::StorageFolder UnpackagedApplicationData::GetPublisherCacheFolder(winrt::hstring const& folderName)
+ {
+ _VerifyNotClosed();
+
+ // ApplicationData.PublisherCacheFolder has no unpackaged equivalent
+ throw winrt::hresult_not_implemented();
+ }
+ void UnpackagedApplicationData::_VerifyNotClosed()
+ {
+ THROW_HR_IF(RO_E_CLOSED, m_publisher.empty());
+ }
+ std::filesystem::path UnpackagedApplicationData::_MachinePath(winrt::hstring const& publisher, winrt::hstring const& product)
+ {
+ wil::unique_cotaskmem_string rootPath;
+ THROW_IF_FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_DEFAULT/*TODO KF_FLAG_CREATE | KF_FLAG_DONT_VERIFY*/, nullptr, wil::out_param(rootPath)));
+ std::filesystem::path path{ rootPath.get() };
+ path /= publisher.c_str();
+ path /= product.c_str();
+
+ // Does it exist?
+ if (_PathExists(path))
+ {
+ return path;
+ }
+ return std::filesystem::path{};
+ }
+
+ bool UnpackagedApplicationData::_PathExists(std::filesystem::path const& path)
+ {
+ const std::filesystem::directory_entry directoryEntry{ path };
+ return directoryEntry.is_directory();
+ }
+}
diff --git a/dev/ApplicationData/UnpackagedApplicationData.h b/dev/ApplicationData/UnpackagedApplicationData.h
new file mode 100644
index 0000000000..4424cde99d
--- /dev/null
+++ b/dev/ApplicationData/UnpackagedApplicationData.h
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+#pragma once
+
+#include "M.W.S.ApplicationDataContainer.h"
+
+namespace Microsoft::Windows::Storage
+{
+ struct UnpackagedApplicationData
+ {
+ UnpackagedApplicationData() = default;
+ UnpackagedApplicationData(winrt::hstring const& publisher, winrt::hstring const& product);
+
+ bool IsMachinePathSupported();
+ winrt::hstring LocalCachePath();
+ winrt::hstring LocalPath();
+ winrt::hstring MachinePath();
+ winrt::hstring SharedLocalPath();
+ winrt::hstring TemporaryPath();
+ winrt::Windows::Storage::StorageFolder LocalCacheFolder();
+ winrt::Windows::Storage::StorageFolder LocalFolder();
+ winrt::Windows::Storage::StorageFolder MachineFolder();
+ winrt::Windows::Storage::StorageFolder SharedLocalFolder();
+ winrt::Windows::Storage::StorageFolder TemporaryFolder();
+ winrt::Microsoft::Windows::Storage::ApplicationDataContainer LocalSettings();
+ winrt::Windows::Foundation::IAsyncAction ClearAsync(winrt::Microsoft::Windows::Storage::ApplicationDataLocality locality);
+ winrt::Windows::Foundation::IAsyncAction ClearPublisherCacheFolderAsync(winrt::hstring folderName);
+ void Close();
+ winrt::hstring GetPublisherCachePath(winrt::hstring const& folderName);
+ winrt::Windows::Storage::StorageFolder GetPublisherCacheFolder(winrt::hstring const& folderName);
+
+ private:
+ void _VerifyNotClosed();
+ static std::filesystem::path _MachinePath(winrt::hstring const& publisher, winrt::hstring const& product);
+ static bool _PathExists(std::filesystem::path const& path);
+
+ private:
+ winrt::hstring m_publisher;
+ winrt::hstring m_product;
+ winrt::hstring m_localPath;
+ winrt::hstring m_temporaryPath;
+ };
+}
diff --git a/dev/ApplicationData/UnpackagedApplicationDataContainer.cpp b/dev/ApplicationData/UnpackagedApplicationDataContainer.cpp
new file mode 100644
index 0000000000..ce81b681eb
--- /dev/null
+++ b/dev/ApplicationData/UnpackagedApplicationDataContainer.cpp
@@ -0,0 +1,931 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+#include "pch.h"
+
+#include "UnpackagedApplicationDataContainer.h"
+
+#include "Validate.h"
+
+namespace Microsoft::Windows::Storage
+{
+ namespace
+ {
+ // Custom registry types for WinRT PropertyType values that don't have native
+ // registry equivalents. Modeled after Windows' STATE_REG_TYPE used in
+ // onecore\base\appmodel\statemanager. Standard registry types (REG_SZ,
+ // REG_DWORD, REG_QWORD) are used for String, UInt32, and UInt64. Custom
+ // types start at PropertyType value + 0x20000 to avoid collisions with standard registry types.
+ enum ApplicationDataRegistryType : DWORD
+ {
+ // Scalar types: PropertyType enum value + 0x20000
+ ApplicationDataRegistryType_UInt8 = 0x20001, // PropertyType::UInt8 (1)
+ ApplicationDataRegistryType_Int16 = 0x20002, // PropertyType::Int16 (2)
+ ApplicationDataRegistryType_UInt16 = 0x20003, // PropertyType::UInt16 (3)
+ ApplicationDataRegistryType_Int32 = 0x20004, // PropertyType::Int32 (4)
+ // UInt32 (5) -> REG_DWORD
+ ApplicationDataRegistryType_Int64 = 0x20006, // PropertyType::Int64 (6)
+ // UInt64 (7) -> REG_QWORD
+ ApplicationDataRegistryType_Single = 0x20008, // PropertyType::Single (8)
+ ApplicationDataRegistryType_Double = 0x20009, // PropertyType::Double (9)
+ ApplicationDataRegistryType_Char16 = 0x2000A, // PropertyType::Char16 (10)
+ ApplicationDataRegistryType_Boolean = 0x2000B, // PropertyType::Boolean (11)
+ // String (12) -> REG_SZ
+ ApplicationDataRegistryType_DateTime = 0x2000E, // PropertyType::DateTime (14)
+ ApplicationDataRegistryType_TimeSpan = 0x2000F, // PropertyType::TimeSpan (15)
+ ApplicationDataRegistryType_Guid = 0x20010, // PropertyType::Guid (16)
+ ApplicationDataRegistryType_Point = 0x20011, // PropertyType::Point (17)
+ ApplicationDataRegistryType_Size = 0x20012, // PropertyType::Size (18)
+ ApplicationDataRegistryType_Rect = 0x20013, // PropertyType::Rect (19)
+ // Array types: PropertyType enum value + 0x20000
+ ApplicationDataRegistryType_UInt8Array = 0x20401, // PropertyType::UInt8Array (1025)
+ ApplicationDataRegistryType_Int16Array = 0x20402, // PropertyType::Int16Array (1026)
+ ApplicationDataRegistryType_UInt16Array = 0x20403, // PropertyType::UInt16Array (1027)
+ ApplicationDataRegistryType_Int32Array = 0x20404, // PropertyType::Int32Array (1028)
+ ApplicationDataRegistryType_UInt32Array = 0x20405, // PropertyType::UInt32Array (1029)
+ ApplicationDataRegistryType_Int64Array = 0x20406, // PropertyType::Int64Array (1030)
+ ApplicationDataRegistryType_UInt64Array = 0x20407, // PropertyType::UInt64Array (1031)
+ ApplicationDataRegistryType_SingleArray = 0x20408, // PropertyType::SingleArray (1032)
+ ApplicationDataRegistryType_DoubleArray = 0x20409, // PropertyType::DoubleArray (1033)
+ ApplicationDataRegistryType_Char16Array = 0x2040A, // PropertyType::Char16Array (1034)
+ ApplicationDataRegistryType_BooleanArray = 0x2040B, // PropertyType::BooleanArray (1035)
+ // StringArray (1036) -> REG_MULTI_SZ
+ ApplicationDataRegistryType_DateTimeArray = 0x2040E, // PropertyType::DateTimeArray (1038)
+ ApplicationDataRegistryType_TimeSpanArray = 0x2040F, // PropertyType::TimeSpanArray (1039)
+ ApplicationDataRegistryType_GuidArray = 0x20410, // PropertyType::GuidArray (1040)
+ ApplicationDataRegistryType_PointArray = 0x20411, // PropertyType::PointArray (1041)
+ ApplicationDataRegistryType_SizeArray = 0x20412, // PropertyType::SizeArray (1042)
+ ApplicationDataRegistryType_RectArray = 0x20413, // PropertyType::RectArray (1043)
+ };
+
+ template
+ void SetCustomTypeValue(HKEY key, PCWSTR valueName, DWORD regType, const T& val)
+ {
+ THROW_IF_WIN32_ERROR(::RegSetValueExW(
+ key, valueName, 0, regType,
+ reinterpret_cast(&val), sizeof(T)));
+ }
+
+ template
+ T ReadCustomTypeValue(HKEY key, PCWSTR valueName, DWORD dataSize)
+ {
+ THROW_HR_IF(E_UNEXPECTED, dataSize != sizeof(T));
+ T val{};
+ THROW_IF_WIN32_ERROR(::RegQueryValueExW(
+ key, valueName, nullptr, nullptr,
+ reinterpret_cast(&val), &dataSize));
+ return val;
+ }
+
+ template
+ void SetCustomTypeArrayValue(HKEY key, PCWSTR valueName, DWORD regType, const T* data, uint32_t count)
+ {
+ THROW_IF_WIN32_ERROR(::RegSetValueExW(
+ key, valueName, 0, regType,
+ reinterpret_cast(data),
+ static_cast(count * sizeof(T))));
+ }
+
+ template
+ winrt::com_array ReadCustomTypeArrayValue(HKEY key, PCWSTR valueName, DWORD dataSize)
+ {
+ THROW_HR_IF(E_UNEXPECTED, dataSize % sizeof(T) != 0);
+ const uint32_t count{ dataSize / static_cast(sizeof(T)) };
+ winrt::com_array arr(count);
+ THROW_IF_WIN32_ERROR(::RegQueryValueExW(
+ key, valueName, nullptr, nullptr,
+ reinterpret_cast(arr.data()), &dataSize));
+ return arr;
+ }
+
+ winrt::Windows::Foundation::IInspectable ReadRegistryValue(HKEY key, PCWSTR valueName)
+ {
+ DWORD type{};
+ DWORD dataSize{};
+ const auto status{ ::RegQueryValueExW(key, valueName, nullptr, &type, nullptr, &dataSize) };
+ THROW_IF_WIN32_ERROR(status);
+
+ switch (type)
+ {
+ case REG_NONE:
+ return nullptr;
+ case REG_SZ:
+ {
+ auto value{ wil::reg::get_value_string(key, valueName) };
+ return winrt::box_value(winrt::hstring{ value });
+ }
+ case REG_DWORD:
+ {
+ auto value{ wil::reg::get_value_dword(key, valueName) };
+ return winrt::box_value(value);
+ }
+ case REG_QWORD:
+ {
+ auto value{ wil::reg::get_value_qword(key, valueName) };
+ return winrt::box_value(value);
+ }
+ case REG_MULTI_SZ:
+ {
+ std::vector buffer(dataSize);
+ THROW_IF_WIN32_ERROR(::RegQueryValueExW(key, valueName, nullptr, nullptr, buffer.data(), &dataSize));
+ THROW_HR_IF(E_UNEXPECTED, dataSize % sizeof(wchar_t) != 0);
+ auto* p{ reinterpret_cast(buffer.data()) };
+ auto* end{ reinterpret_cast(buffer.data() + dataSize) };
+ std::vector strings;
+ while (p < end && *p != L'\0')
+ {
+ auto len{ wcsnlen(p, static_cast(end - p)) };
+ winrt::hstring str{ p, static_cast(len) };
+ strings.push_back(str);
+ p += str.size() + 1;
+ }
+ winrt::com_array arr(strings.begin(), strings.end());
+ return winrt::Windows::Foundation::PropertyValue::CreateStringArray(arr);
+ }
+ case ApplicationDataRegistryType_UInt8:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Int16:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_UInt16:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Int32:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Int64:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Single:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Double:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Char16:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Boolean:
+ return winrt::box_value(static_cast(ReadCustomTypeValue(key, valueName, dataSize)));
+ case ApplicationDataRegistryType_DateTime:
+ {
+ auto ticks{ ReadCustomTypeValue(key, valueName, dataSize) };
+ return winrt::box_value(winrt::Windows::Foundation::DateTime{ winrt::Windows::Foundation::TimeSpan{ ticks } });
+ }
+ case ApplicationDataRegistryType_TimeSpan:
+ {
+ auto duration{ ReadCustomTypeValue(key, valueName, dataSize) };
+ return winrt::box_value(winrt::Windows::Foundation::TimeSpan{ duration });
+ }
+ case ApplicationDataRegistryType_Guid:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Point:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Size:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_Rect:
+ return winrt::box_value(ReadCustomTypeValue(key, valueName, dataSize));
+ case ApplicationDataRegistryType_UInt8Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateUInt8Array(arr);
+ }
+ case ApplicationDataRegistryType_Int16Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateInt16Array(arr);
+ }
+ case ApplicationDataRegistryType_UInt16Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateUInt16Array(arr);
+ }
+ case ApplicationDataRegistryType_Int32Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateInt32Array(arr);
+ }
+ case ApplicationDataRegistryType_UInt32Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateUInt32Array(arr);
+ }
+ case ApplicationDataRegistryType_Int64Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateInt64Array(arr);
+ }
+ case ApplicationDataRegistryType_UInt64Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateUInt64Array(arr);
+ }
+ case ApplicationDataRegistryType_SingleArray:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateSingleArray(arr);
+ }
+ case ApplicationDataRegistryType_DoubleArray:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateDoubleArray(arr);
+ }
+ case ApplicationDataRegistryType_Char16Array:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateChar16Array(arr);
+ }
+ case ApplicationDataRegistryType_BooleanArray:
+ {
+ auto bytes{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ winrt::com_array arr(bytes.size());
+ for (uint32_t i = 0; i < bytes.size(); ++i)
+ {
+ arr[i] = static_cast(bytes[i]);
+ }
+ return winrt::Windows::Foundation::PropertyValue::CreateBooleanArray(arr);
+ }
+ case ApplicationDataRegistryType_DateTimeArray:
+ {
+ auto ticks{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ winrt::com_array arr(ticks.size());
+ for (uint32_t i = 0; i < ticks.size(); ++i)
+ {
+ arr[i] = winrt::Windows::Foundation::DateTime{ winrt::Windows::Foundation::TimeSpan{ ticks[i] } };
+ }
+ return winrt::Windows::Foundation::PropertyValue::CreateDateTimeArray(arr);
+ }
+ case ApplicationDataRegistryType_TimeSpanArray:
+ {
+ auto durations{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ winrt::com_array arr(durations.size());
+ for (uint32_t i = 0; i < durations.size(); ++i)
+ {
+ arr[i] = winrt::Windows::Foundation::TimeSpan{ durations[i] };
+ }
+ return winrt::Windows::Foundation::PropertyValue::CreateTimeSpanArray(arr);
+ }
+ case ApplicationDataRegistryType_GuidArray:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateGuidArray(arr);
+ }
+ case ApplicationDataRegistryType_PointArray:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreatePointArray(arr);
+ }
+ case ApplicationDataRegistryType_SizeArray:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateSizeArray(arr);
+ }
+ case ApplicationDataRegistryType_RectArray:
+ {
+ auto arr{ ReadCustomTypeArrayValue(key, valueName, dataSize) };
+ return winrt::Windows::Foundation::PropertyValue::CreateRectArray(arr);
+ }
+ default:
+ THROW_HR(E_UNEXPECTED);
+ }
+ }
+
+ void WriteRegistryValue(HKEY key, PCWSTR valueName, winrt::Windows::Foundation::IInspectable const& value)
+ {
+ if (!value)
+ {
+ THROW_IF_WIN32_ERROR(::RegSetValueExW(
+ key, valueName, 0, REG_NONE, nullptr, 0));
+ return;
+ }
+ auto propertyValue{ value.as() };
+ switch (propertyValue.Type())
+ {
+ case winrt::Windows::Foundation::PropertyType::Empty:
+ {
+ THROW_IF_WIN32_ERROR(::RegSetValueExW(
+ key, valueName, 0, REG_NONE, nullptr, 0));
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::String:
+ {
+ auto str{ winrt::unbox_value(value) };
+ wil::reg::set_value_string(key, valueName, str.c_str());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt32:
+ {
+ auto val{ winrt::unbox_value(value) };
+ wil::reg::set_value_dword(key, valueName, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt64:
+ {
+ auto val{ winrt::unbox_value(value) };
+ wil::reg::set_value_qword(key, valueName, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt8:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_UInt8, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Int16:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Int16, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt16:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_UInt16, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Int32:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Int32, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Int64:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Int64, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Single:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Single, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Double:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Double, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Char16:
+ {
+ auto val{ winrt::unbox_value(value) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Char16, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Boolean:
+ {
+ auto val{ winrt::unbox_value(value) };
+ uint8_t byte{ val ? static_cast(1) : static_cast(0) };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Boolean, byte);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::DateTime:
+ {
+ auto val{ propertyValue.GetDateTime() };
+ auto ticks{ val.time_since_epoch().count() };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_DateTime, ticks);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::TimeSpan:
+ {
+ auto val{ propertyValue.GetTimeSpan() };
+ auto duration{ val.count() };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_TimeSpan, duration);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Guid:
+ {
+ auto val{ propertyValue.GetGuid() };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Guid, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Point:
+ {
+ auto val{ propertyValue.GetPoint() };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Point, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Size:
+ {
+ auto val{ propertyValue.GetSize() };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Size, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Rect:
+ {
+ auto val{ propertyValue.GetRect() };
+ SetCustomTypeValue(key, valueName, ApplicationDataRegistryType_Rect, val);
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt8Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetUInt8Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_UInt8Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Int16Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetInt16Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_Int16Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt16Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetUInt16Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_UInt16Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Int32Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetInt32Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_Int32Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt32Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetUInt32Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_UInt32Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Int64Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetInt64Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_Int64Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::UInt64Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetUInt64Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_UInt64Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::SingleArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetSingleArray(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_SingleArray, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::DoubleArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetDoubleArray(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_DoubleArray, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::Char16Array:
+ {
+ winrt::com_array arr;
+ propertyValue.GetChar16Array(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_Char16Array, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::BooleanArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetBooleanArray(arr);
+ std::vector bytes(arr.size());
+ for (uint32_t i = 0; i < arr.size(); ++i)
+ {
+ bytes[i] = arr[i] ? static_cast(1) : static_cast(0);
+ }
+ THROW_IF_WIN32_ERROR(::RegSetValueExW(
+ key, valueName, 0, ApplicationDataRegistryType_BooleanArray,
+ bytes.data(), static_cast(bytes.size())));
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::StringArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetStringArray(arr);
+ std::wstring multiSz;
+ for (const auto& s : arr)
+ {
+ multiSz.append(s.c_str(), s.size());
+ multiSz.push_back(L'\0');
+ }
+ multiSz.push_back(L'\0');
+ THROW_IF_WIN32_ERROR(::RegSetValueExW(
+ key, valueName, 0, REG_MULTI_SZ,
+ reinterpret_cast(multiSz.c_str()),
+ static_cast(multiSz.size() * sizeof(wchar_t))));
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::DateTimeArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetDateTimeArray(arr);
+ std::vector ticks(arr.size());
+ for (uint32_t i = 0; i < arr.size(); ++i)
+ {
+ ticks[i] = arr[i].time_since_epoch().count();
+ }
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_DateTimeArray, ticks.data(), static_cast(ticks.size()));
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::TimeSpanArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetTimeSpanArray(arr);
+ std::vector durations(arr.size());
+ for (uint32_t i = 0; i < arr.size(); ++i)
+ {
+ durations[i] = arr[i].count();
+ }
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_TimeSpanArray, durations.data(), static_cast(durations.size()));
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::GuidArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetGuidArray(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_GuidArray, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::PointArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetPointArray(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_PointArray, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::SizeArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetSizeArray(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_SizeArray, arr.data(), arr.size());
+ break;
+ }
+ case winrt::Windows::Foundation::PropertyType::RectArray:
+ {
+ winrt::com_array arr;
+ propertyValue.GetRectArray(arr);
+ SetCustomTypeArrayValue(key, valueName, ApplicationDataRegistryType_RectArray, arr.data(), arr.size());
+ break;
+ }
+ default:
+ THROW_HR(E_NOTIMPL);
+ }
+ }
+
+ struct RegistryPropertySetIterator : winrt::implements>>
+ {
+ RegistryPropertySetIterator(std::vector>&& items) :
+ m_items(std::move(items)),
+ m_index(0)
+ {
+ }
+
+ winrt::Windows::Foundation::Collections::IKeyValuePair Current() const
+ {
+ if (m_index >= m_items.size())
+ {
+ throw winrt::hresult_out_of_bounds();
+ }
+ return m_items[m_index];
+ }
+
+ bool HasCurrent() const
+ {
+ return m_index < m_items.size();
+ }
+
+ bool MoveNext()
+ {
+ if (m_index < m_items.size())
+ {
+ ++m_index;
+ }
+ return m_index < m_items.size();
+ }
+
+ uint32_t GetMany(winrt::array_view> items)
+ {
+ uint32_t count{};
+ for (auto& item : items)
+ {
+ if (m_index >= m_items.size())
+ {
+ break;
+ }
+ item = m_items[m_index];
+ ++m_index;
+ ++count;
+ }
+ return count;
+ }
+
+ private:
+ std::vector> m_items;
+ uint32_t m_index{};
+ };
+
+ struct RegistryPropertySet : winrt::implements,
+ winrt::Windows::Foundation::Collections::IMap,
+ winrt::Windows::Foundation::Collections::IIterable>>
+ {
+ RegistryPropertySet(wil::shared_hkey key) :
+ m_key(std::move(key))
+ {
+ }
+
+ // IMap
+ winrt::Windows::Foundation::IInspectable Lookup(winrt::hstring const& key) const
+ {
+ return ReadRegistryValue(m_key.get(), key.c_str());
+ }
+
+ uint32_t Size() const
+ {
+ DWORD valueCount{};
+ THROW_IF_WIN32_ERROR(::RegQueryInfoKeyW(m_key.get(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &valueCount, nullptr, nullptr, nullptr, nullptr));
+ return valueCount;
+ }
+
+ bool HasKey(winrt::hstring const& key) const
+ {
+ DWORD type{};
+ const auto hr{ HRESULT_FROM_WIN32(::RegQueryValueExW(m_key.get(), key.c_str(), nullptr, &type, nullptr, nullptr)) };
+ if (SUCCEEDED(hr))
+ {
+ return true;
+ }
+ else if (wil::reg::is_registry_not_found(hr))
+ {
+ return false;
+ }
+ else
+ {
+ THROW_HR_MSG(hr, "%ls", key.c_str());
+ }
+ }
+
+ winrt::Windows::Foundation::Collections::IMapView GetView() const
+ {
+ auto snapshot{ winrt::single_threaded_map() };
+ for (const auto& valueData : wil::make_range(wil::reg::value_iterator{ m_key.get() }, wil::reg::value_iterator{}))
+ {
+ try
+ {
+ auto value{ ReadRegistryValue(m_key.get(), valueData.name.c_str()) };
+ snapshot.Insert(winrt::hstring{ valueData.name }, value);
+ }
+ catch (...)
+ {
+ // Skip values that cannot be read
+ LOG_CAUGHT_EXCEPTION();
+ }
+ }
+ return snapshot.GetView();
+ }
+
+ bool Insert(winrt::hstring const& key, winrt::Windows::Foundation::IInspectable const& value)
+ {
+ bool existed{ HasKey(key) };
+ WriteRegistryValue(m_key.get(), key.c_str(), value);
+ m_mapChanged(*this, winrt::make(
+ existed ? winrt::Windows::Foundation::Collections::CollectionChange::ItemChanged : winrt::Windows::Foundation::Collections::CollectionChange::ItemInserted,
+ key));
+ return existed;
+ }
+
+ void Remove(winrt::hstring const& key)
+ {
+ THROW_IF_WIN32_ERROR(::RegDeleteValueW(m_key.get(), key.c_str()));
+ m_mapChanged(*this, winrt::make(
+ winrt::Windows::Foundation::Collections::CollectionChange::ItemRemoved, key));
+ }
+
+ void Clear()
+ {
+ // Best-effort but on failure report the first
+ HRESULT hrFirst{};
+
+ // Collect all value names first, then delete
+ std::vector names;
+ for (const auto& valueData : wil::make_range(wil::reg::value_iterator{ m_key.get() }, wil::reg::value_iterator{}))
+ {
+ names.push_back(valueData.name);
+ }
+ for (const auto& name : names)
+ {
+ const HRESULT hrDeleteValue{ HRESULT_FROM_WIN32(::RegDeleteValueW(m_key.get(), name.c_str())) };
+ if (FAILED(hrDeleteValue) && !wil::reg::is_registry_not_found(hrDeleteValue))
+ {
+ std::ignore = LOG_HR_MSG(hrDeleteValue, "%ls", name.c_str());
+ if (SUCCEEDED(hrFirst))
+ {
+ hrFirst = hrDeleteValue;
+ }
+ }
+ }
+ m_mapChanged(*this, winrt::make(
+ FAILED(hrFirst) ?
+ winrt::Windows::Foundation::Collections::CollectionChange::ItemRemoved :
+ winrt::Windows::Foundation::Collections::CollectionChange::Reset,
+ winrt::hstring{}));
+ THROW_IF_FAILED(hrFirst);
+ }
+
+ // IIterable
+ winrt::Windows::Foundation::Collections::IIterator> First() const
+ {
+ std::vector> items;
+ for (const auto& valueData : wil::make_range(wil::reg::value_iterator{ m_key.get() }, wil::reg::value_iterator{}))
+ {
+ try
+ {
+ auto value{ ReadRegistryValue(m_key.get(), valueData.name.c_str()) };
+ items.push_back(winrt::Windows::Foundation::Collections::IKeyValuePair{
+ winrt::make(winrt::hstring{ valueData.name }, value) });
+ }
+ catch (...)
+ {
+ // Skip values that cannot be read
+ LOG_CAUGHT_EXCEPTION();
+ }
+ }
+ return winrt::make(std::move(items));
+ }
+
+ // IObservableMap
+ winrt::event_token MapChanged(winrt::Windows::Foundation::Collections::MapChangedEventHandler const& handler)
+ {
+ return m_mapChanged.add(handler);
+ }
+
+ void MapChanged(winrt::event_token const& token) noexcept
+ {
+ m_mapChanged.remove(token);
+ }
+
+ private:
+ struct MapChangedEventArgs : winrt::implements>
+ {
+ MapChangedEventArgs(winrt::Windows::Foundation::Collections::CollectionChange change, winrt::hstring const& key) :
+ m_change(change),
+ m_key(key)
+ {
+ }
+
+ winrt::Windows::Foundation::Collections::CollectionChange CollectionChange() const
+ {
+ return m_change;
+ }
+
+ winrt::hstring Key() const
+ {
+ return m_key;
+ }
+
+ private:
+ winrt::Windows::Foundation::Collections::CollectionChange m_change;
+ winrt::hstring m_key;
+ };
+
+ struct KeyValuePair : winrt::implements>
+ {
+ KeyValuePair(winrt::hstring const& key, winrt::Windows::Foundation::IInspectable const& value) :
+ m_key(key),
+ m_value(value)
+ {
+ }
+
+ winrt::hstring Key() const
+ {
+ return m_key;
+ }
+
+ winrt::Windows::Foundation::IInspectable Value() const
+ {
+ return m_value;
+ }
+
+ private:
+ winrt::hstring m_key;
+ winrt::Windows::Foundation::IInspectable m_value;
+ };
+
+ wil::shared_hkey m_key;
+ winrt::event> m_mapChanged;
+ };
+ }
+
+ UnpackagedApplicationDataContainer::UnpackagedApplicationDataContainer(
+ wil::shared_hkey key,
+ winrt::hstring const& name,
+ winrt::Microsoft::Windows::Storage::ApplicationDataLocality locality) :
+ m_key(std::move(key)),
+ m_name(name),
+ m_locality(locality)
+ {
+ }
+
+ winrt::Windows::Foundation::Collections::IMap UnpackagedApplicationDataContainer::Containers()
+ {
+ _VerifyNotClosed();
+
+ auto map{ winrt::single_threaded_map() };
+ for (const auto& keyData : wil::make_range(wil::reg::key_iterator{ m_key.get() }, wil::reg::key_iterator{}))
+ {
+ auto subKey{ wil::reg::open_shared_key(m_key.get(), keyData.name.c_str(), wil::reg::key_access::readwrite) };
+ auto subContainer{ std::make_shared(std::move(subKey), winrt::hstring{ keyData.name }, m_locality) };
+ auto wrappedContainer{ winrt::make(subContainer) };
+ map.Insert(winrt::hstring{ keyData.name }, wrappedContainer);
+ }
+ return map;
+ }
+
+ winrt::hstring UnpackagedApplicationDataContainer::Name()
+ {
+ _VerifyNotClosed();
+
+ return m_name;
+ }
+
+ winrt::Microsoft::Windows::Storage::ApplicationDataLocality UnpackagedApplicationDataContainer::Locality()
+ {
+ _VerifyNotClosed();
+
+ return m_locality;
+ }
+
+ winrt::Windows::Foundation::Collections::IPropertySet UnpackagedApplicationDataContainer::Values()
+ {
+ _VerifyNotClosed();
+
+ return winrt::make(m_key);
+ }
+
+ void UnpackagedApplicationDataContainer::Close()
+ {
+ m_key.reset();
+ }
+
+ winrt::Microsoft::Windows::Storage::ApplicationDataContainer UnpackagedApplicationDataContainer::CreateContainer(
+ winrt::hstring const& name,
+ winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition const& disposition)
+ {
+ _VerifyNotClosed();
+
+ _VerifyContainerName(name);
+
+ if (disposition == winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Existing)
+ {
+ auto subKey{ wil::reg::open_shared_key(m_key.get(), name.c_str(), wil::reg::key_access::readwrite) };
+ auto subContainer{ std::make_shared(std::move(subKey), name, m_locality) };
+ return winrt::make(subContainer);
+ }
+ else
+ {
+ auto subKey{ wil::reg::create_shared_key(m_key.get(), name.c_str(), wil::reg::key_access::readwrite) };
+ auto subContainer{ std::make_shared(std::move(subKey), name, m_locality) };
+ return winrt::make(subContainer);
+ }
+ }
+
+ void UnpackagedApplicationDataContainer::DeleteContainer(winrt::hstring const& name)
+ {
+ _VerifyNotClosed();
+
+ _VerifyContainerName(name);
+
+ const auto hr{ HRESULT_FROM_WIN32(::RegDeleteTreeW(m_key.get(), name.c_str())) };
+ if (SUCCEEDED(hr))
+ {
+ return;
+ }
+ else if (!wil::reg::is_registry_not_found(hr))
+ {
+ THROW_HR_MSG(hr, "%ls", name.c_str());
+ }
+ }
+
+ void UnpackagedApplicationDataContainer::_VerifyNotClosed()
+ {
+ THROW_HR_IF_NULL(RO_E_CLOSED, m_key);
+ }
+
+ void UnpackagedApplicationDataContainer::_VerifyContainerName(winrt::hstring const& name)
+ {
+ // Container name max length must be <= 255
+ const size_t c_registryKeyNameMaxLength{ 255 };
+ THROW_HR_IF_MSG(E_INVALIDARG, name.size() > c_registryKeyNameMaxLength, "Container name not valid (%ls)", name.c_str());
+
+ // Container name cannot contain a backslash
+ for (PCWSTR s = name.c_str(); *s != L'\0'; ++s)
+ {
+ THROW_HR_IF_MSG(E_INVALIDARG, *s == L'\\', "Container name not valid (%ls)", name.c_str());
+ }
+
+ // Container name cannot be "." or ".."
+ THROW_HR_IF_MSG(E_INVALIDARG, name == L".", "Container name not valid (%ls)", name.c_str());
+ THROW_HR_IF_MSG(E_INVALIDARG, name == L"..", "Container name not valid (%ls)", name.c_str());
+ }
+}
diff --git a/dev/ApplicationData/UnpackagedApplicationDataContainer.h b/dev/ApplicationData/UnpackagedApplicationDataContainer.h
new file mode 100644
index 0000000000..b6e660da9a
--- /dev/null
+++ b/dev/ApplicationData/UnpackagedApplicationDataContainer.h
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+#pragma once
+
+#include "M.W.S.ApplicationDataContainer.h"
+
+namespace Microsoft::Windows::Storage
+{
+ struct UnpackagedApplicationDataContainer
+ {
+ UnpackagedApplicationDataContainer() = default;
+ UnpackagedApplicationDataContainer(wil::shared_hkey key, winrt::hstring const& name, winrt::Microsoft::Windows::Storage::ApplicationDataLocality locality);
+
+ winrt::Windows::Foundation::Collections::IMap Containers();
+ winrt::hstring Name();
+ winrt::Microsoft::Windows::Storage::ApplicationDataLocality Locality();
+ winrt::Windows::Foundation::Collections::IPropertySet Values();
+ void Close();
+ winrt::Microsoft::Windows::Storage::ApplicationDataContainer CreateContainer(winrt::hstring const& name, winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition const& disposition);
+ void DeleteContainer(winrt::hstring const& name);
+
+ private:
+ void _VerifyNotClosed();
+ void _VerifyContainerName(winrt::hstring const& name);
+
+ private:
+ wil::shared_hkey m_key;
+ winrt::hstring m_name;
+ winrt::Microsoft::Windows::Storage::ApplicationDataLocality m_locality{};
+ };
+}
diff --git a/dev/ApplicationData/Validate.h b/dev/ApplicationData/Validate.h
new file mode 100644
index 0000000000..cc80fb01e0
--- /dev/null
+++ b/dev/ApplicationData/Validate.h
@@ -0,0 +1,99 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+namespace Microsoft::Windows::Storage
+{
+/// Allowed characters = ASCII alphanumeric, SPACE, DASH, DOT, UNDERSCORE
+/// Avoid https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+inline bool is_valid_character(const wchar_t c) noexcept
+{
+ return (L'0' <= c && c <= L'9') ||
+ (L'A' <= c && c <= L'Z') ||
+ (L'a' <= c && c <= L'z') ||
+ (L' ' == c || L'-' == c || L'.' == c || L'_' == c);
+}
+
+inline PCWSTR c_prohibitedStringsEquals[24]{
+ L".", L"..", L"con", L"prn", L"aux", L"nul",
+ L"com1", L"com2", L"com3", L"com4", L"com5", L"com6", L"com7", L"com8", L"com9",
+ L"lpt1", L"lpt2", L"lpt3", L"lpt4", L"lpt5", L"lpt6", L"lpt7", L"lpt8", L"lpt9"
+};
+
+inline PCWSTR c_prohibitedStringsStartsWith[23]{
+ L"con.", L"prn.", L"aux.", L"nul.",
+ L"com1.", L"com2.", L"com3.", L"com4.", L"com5.", L"com6.", L"com7.", L"com8.", L"com9.",
+ L"lpt1.", L"lpt2.", L"lpt3.", L"lpt4.", L"lpt5.", L"lpt6.", L"lpt7.", L"lpt8.", L"lpt9.",
+ L"xn--"
+};
+
+inline wchar_t ascii_tolower_ordinal(const wchar_t c) noexcept
+{
+ return (c >= L'A' && c <= L'Z') ? (c | 0x20) : c;
+}
+
+inline bool startswith_ordinal_nocase(_In_ PCWSTR string, _In_ PCWSTR prefix)
+{
+ for (;;)
+ {
+ const wchar_t pc{ *prefix++ };
+ if (pc == L'\0')
+ {
+ // prefix fully matched
+ return true;
+ }
+
+ const wchar_t sc{ *string++ };
+ if (sc == 0)
+ {
+ // string ended before prefix so cannot match
+ return false;
+ }
+
+ if (ascii_tolower_ordinal(sc) != ascii_tolower_ordinal(pc))
+ {
+ return false;
+ }
+ }
+}
+
+inline bool contains_prohibited_character(_In_ PCWSTR string)
+{
+ for (PCWSTR s = string; *s != L'\0'; ++s)
+ {
+ if (!is_valid_character(*s))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+inline bool is_prohibited_string(_In_ PCWSTR string)
+{
+ // Contains prohibited character(s)
+ if (contains_prohibited_character(string))
+ {
+ return true;
+ }
+
+ // Equals a prohibited string
+ for (size_t index = 0; index < ARRAYSIZE(c_prohibitedStringsEquals); ++index)
+ {
+ if (::CompareStringOrdinal(string, -1, c_prohibitedStringsEquals[index], -1, TRUE) == CSTR_EQUAL)
+ {
+ return true;
+ }
+ }
+
+ // Starts with a prohibited string
+ for (size_t index = 0; index < ARRAYSIZE(c_prohibitedStringsStartsWith); ++index)
+ {
+ if (startswith_ordinal_nocase(string, c_prohibitedStringsStartsWith[index]))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+}
diff --git a/dev/ApplicationData/pch.h b/dev/ApplicationData/pch.h
index 786111d0d5..b9095c50cb 100644
--- a/dev/ApplicationData/pch.h
+++ b/dev/ApplicationData/pch.h
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include
#include
@@ -28,4 +29,9 @@
#include
#include
+#include
+#include
+#include
#include
+
+#include "Validate.h"
diff --git a/dev/Common/Common.vcxitems b/dev/Common/Common.vcxitems
index 0da4fd227a..8091baaca7 100644
--- a/dev/Common/Common.vcxitems
+++ b/dev/Common/Common.vcxitems
@@ -23,8 +23,13 @@
+
+
+
+
+
diff --git a/dev/Common/Common.vcxitems.filters b/dev/Common/Common.vcxitems.filters
index 987c2340a5..3dabc04cff 100644
--- a/dev/Common/Common.vcxitems.filters
+++ b/dev/Common/Common.vcxitems.filters
@@ -23,31 +23,46 @@
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
Header Files
-
+
Header Files
Header Files
-
+
+ Header Files
+
+
Header Files
diff --git a/dev/Common/Microsoft.FileSystem.Path.h b/dev/Common/Microsoft.FileSystem.Path.h
new file mode 100644
index 0000000000..08fc1dad0d
--- /dev/null
+++ b/dev/Common/Microsoft.FileSystem.Path.h
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+#ifndef __MICROSOFT_FILESYSTEM_PATH_H
+#define __MICROSOFT_FILESYSTEM_PATH_H
+
+#include
+
+#include
+
+#include
+
+namespace Microsoft::FileSystem::Path
+{
+inline std::filesystem::path GetTempDirectory()
+{
+ // GetTempPath2() returns ...windir...\SystemTemp if the caller is LocalSystem
+ // else it's the same as GetTempPath(). However, GetTempPath2() is only available
+ // on Windows 11 >=10.0.22000.0 (aka 21H2) and we support Windows 10. So handle it...
+ if (::Security::User::IsLocalSystem())
+ {
+ const auto windowsPath{ wil::GetWindowsDirectoryW() };
+ std::filesystem::path path{ windowsPath.get() };
+ return path / L"SystemTemp";
+ }
+ else
+ {
+ // The Standard C++ Library (on Windows) is equivalent to GetTempPath()
+ return std::filesystem::temp_directory_path();
+ }
+}
+}
+
+#endif // __MICROSOFT_FILESYSTEM_PATH_H
diff --git a/dev/WindowsAppRuntime_DLL/pch.h b/dev/WindowsAppRuntime_DLL/pch.h
index 8dcd8e87ea..f1ec626873 100644
--- a/dev/WindowsAppRuntime_DLL/pch.h
+++ b/dev/WindowsAppRuntime_DLL/pch.h
@@ -56,6 +56,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
diff --git a/test/ApplicationData/ApplicationDataTests.cpp b/test/ApplicationData/ApplicationDataTests.cpp
index e48649be5e..1682a58a72 100644
--- a/test/ApplicationData/ApplicationDataTests.cpp
+++ b/test/ApplicationData/ApplicationDataTests.cpp
@@ -22,7 +22,6 @@ namespace Test::ApplicationData::Tests
public:
BEGIN_TEST_CLASS(ApplicationDataTests)
TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
- TEST_CLASS_PROPERTY(L"IsolationLevel", L"Class")
TEST_CLASS_PROPERTY(L"RunAs", L"RestrictedUser")
END_TEST_CLASS()
@@ -49,12 +48,12 @@ namespace Test::ApplicationData::Tests
TEST_METHOD(GetDefault_Main)
{
- //TODO
+ //TODO GetDefault_Main
}
TEST_METHOD(GetDefault_Framework)
{
- //TODO
+ //TODO GetDefault_Framework
}
TEST_METHOD(GetForPackageFamily_Main)
@@ -86,12 +85,12 @@ namespace Test::ApplicationData::Tests
TEST_METHOD(GetForUser_Main)
{
- //TODO
+ //TODO GetForUser_Main
}
TEST_METHOD(GetForUser_Framework)
{
- //TODO
+ //TODO GetForUser_Framework
}
TEST_METHOD(FolderAndPath_Main)
@@ -426,32 +425,32 @@ namespace Test::ApplicationData::Tests
TEST_METHOD(ClearAsync_Main)
{
- //TODO
+ //TODO ClearAsync_Main
}
TEST_METHOD(ClearAsync_Framework)
{
- //TODO
+ //TODO ClearAsync_Framework
}
TEST_METHOD(ClearFolderAsync_Machine_Main)
{
- //TODO
+ //TODO ClearFolderAsync_Machine_Main
}
TEST_METHOD(ClearFolderAsync_Machine_Framework)
{
- //TODO
+ //TODO ClearFolderAsync_Machine_Framework
}
TEST_METHOD(ClearPublisherCacheFolderAsync_Main)
{
- //TODO
+ //TODO ClearPublisherCacheFolderAsync_Main
}
TEST_METHOD(ClearPublisherCacheFolderAsync_Framework)
{
- //TODO
+ //TODO ClearPublisherCacheFolderAsync_Framework
}
};
}
diff --git a/test/ApplicationData/ApplicationDataTests.vcxproj b/test/ApplicationData/ApplicationDataTests.vcxproj
index e511f44ecd..261cef4581 100644
--- a/test/ApplicationData/ApplicationDataTests.vcxproj
+++ b/test/ApplicationData/ApplicationDataTests.vcxproj
@@ -111,7 +111,7 @@
-
+
diff --git a/test/ApplicationData/ApplicationDataTests.vcxproj.filters b/test/ApplicationData/ApplicationDataTests.vcxproj.filters
index 393dcf3001..fc2384bff0 100644
--- a/test/ApplicationData/ApplicationDataTests.vcxproj.filters
+++ b/test/ApplicationData/ApplicationDataTests.vcxproj.filters
@@ -24,7 +24,7 @@
Source Files
-
+
Source Files
diff --git a/test/ApplicationData/ApplicationDataTests_Elevated.cpp b/test/ApplicationData/ApplicationDataTests_Elevated.cpp
index 5e69cc4db1..a15e533a39 100644
--- a/test/ApplicationData/ApplicationDataTests_Elevated.cpp
+++ b/test/ApplicationData/ApplicationDataTests_Elevated.cpp
@@ -34,7 +34,6 @@ namespace Test::ApplicationData::Tests
public:
BEGIN_TEST_CLASS(ApplicationDataTests_Elevated)
TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
- TEST_CLASS_PROPERTY(L"IsolationLevel", L"Class")
TEST_CLASS_PROPERTY(L"RunAs", L"ElevatedUser")
TEST_CLASS_PROPERTY(L"RunFixtureAs", L"System")
END_TEST_CLASS()
@@ -117,6 +116,9 @@ namespace Test::ApplicationData::Tests
TEST_METHOD(MachineFolderAndPath_Framework_Supported)
{
+#if !defined(TODO_MACHINEFOLDER_PATH_DISAPPEARING_FOR_FRAMEWORK_AFTER_SETUP_BEFORE_TEST)
+ WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped, L"ApplicationDataTests_Elevated::MachineFolderAndPath_Framework_Supported: MachineFolder created in Setup is missing when TestMethod starts. Skipping test");
+#else
::TB::Setup();
winrt::hstring packageFamilyName{ Framework_PackageFamilyName };
@@ -134,6 +136,7 @@ namespace Test::ApplicationData::Tests
VERIFY_ARE_EQUAL(machinePath, winrt::hstring(expectedMachinePath.c_str()));
::TB::Cleanup();
+#endif // !defined(TODO_MACHINEFOLDER_PATH_DISAPPEARING_FOR_FRAMEWORK_AFTER_SETUP_BEFORE_TEST)
}
};
}
diff --git a/test/ApplicationData/ApplicationDataTests_Module.cpp b/test/ApplicationData/ApplicationDataTests_Module.cpp
deleted file mode 100644
index d9df50685a..0000000000
--- a/test/ApplicationData/ApplicationDataTests_Module.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) Microsoft Corporation and Contributors.
-// Licensed under the MIT License.
-
-#include "pch.h"
-
-//BEGIN_MODULE()
-// MODULE_PROPERTY(L"IsolationLevel", L"Class")
-//END_MODULE()
diff --git a/test/ApplicationData/UnpackagedApplicationDataTests.cpp b/test/ApplicationData/UnpackagedApplicationDataTests.cpp
new file mode 100644
index 0000000000..6db73ee9c0
--- /dev/null
+++ b/test/ApplicationData/UnpackagedApplicationDataTests.cpp
@@ -0,0 +1,1249 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+#include "pch.h"
+
+#include
+
+namespace TD = ::Test::Diagnostics;
+namespace TB = ::Test::Bootstrap;
+namespace TP = ::Test::Packages;
+namespace TD = ::Test::Diagnostics;
+
+static const winrt::hstring null_hstring;
+
+namespace Test::ApplicationData::Tests
+{
+ const auto Main_PackageFamilyName{ ::TP::DynamicDependencyDataStore::c_PackageFamilyName };
+
+ const winrt::hstring Publisher{ L"ApplicationDataTests" };
+ const winrt::hstring Product{ L"UnpackagedApplicationDataTests" };
+
+ class UnpackagedApplicationDataTests
+ {
+ public:
+ BEGIN_TEST_CLASS(UnpackagedApplicationDataTests)
+ TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
+ TEST_CLASS_PROPERTY(L"RunAs", L"RestrictedUser")
+ END_TEST_CLASS()
+
+ TEST_CLASS_SETUP(ClassSetup)
+ {
+ ::TD::DumpExecutionContext();
+ if (!::WindowsVersion::IsWindows11_21H2OrGreater())
+ {
+ WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped, L"ApplicationData requires Win11 >= 21H2 (SV1). Skipping tests");
+ return true;
+ }
+
+ ::TD::DumpExecutionContext();
+
+ ::TB::Setup();
+ CreateResources();
+ return true;
+ }
+
+ TEST_CLASS_CLEANUP(ClassCleanup)
+ {
+ DeleteResources();
+ ::TB::Cleanup();
+ return true;
+ }
+
+ void CreateResources()
+ {
+ CreateResources_FileSystem();
+ // Registry resources are created by the code under test, so no need to create them here
+ // But delete any pre-existing in case a previous test crashed or otherwise didn't complete and clean up properly
+ DeleteResources_Registry();
+ }
+
+ void CreateResources_FileSystem()
+ {
+ const auto localPath{ UserLocalPath(Publisher, Product) };
+ VERIFY_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(localPath.c_str()),
+ WEX::Common::String().Format(L"MKDIR %s", localPath.c_str()));
+
+ const auto temporaryPath{ UserTemporaryPath(Publisher, Product) };
+ VERIFY_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(temporaryPath.c_str()),
+ WEX::Common::String().Format(L"MKDIR %s", temporaryPath.c_str()));
+ }
+
+ void DeleteResources()
+ {
+ DeleteResources_FileSystem();
+ DeleteResources_Registry();
+ }
+
+ void DeleteResources_FileSystem()
+ {
+ const std::filesystem::path temporaryPath{ UserTemporaryPath(Publisher).c_str() };
+ if (PathExists(temporaryPath))
+ {
+ const auto removeOptions{ wil::RemoveDirectoryOptions::RemoveReadOnly };
+ VERIFY_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(temporaryPath.c_str(), removeOptions),
+ WEX::Common::String().Format(L"RMDIR %s", temporaryPath.c_str()));
+ }
+
+ const std::filesystem::path localPath{ UserLocalPath(Publisher).c_str() };
+ if (PathExists(localPath))
+ {
+ const auto removeOptions{ wil::RemoveDirectoryOptions::RemoveReadOnly };
+ VERIFY_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(localPath.c_str(), removeOptions),
+ WEX::Common::String().Format(L"RMDIR %s", localPath.c_str()));
+ }
+ }
+
+ void DeleteResources_Registry()
+ {
+ const auto regkey{ std::filesystem::path{ L"SOFTWARE" } / Publisher.c_str() };
+ const auto hr{ HRESULT_FROM_WIN32(::RegDeleteTreeW(HKEY_CURRENT_USER, regkey.c_str())) };
+ if (!wil::reg::is_registry_not_found(hr))
+ {
+ VERIFY_SUCCEEDED(hr, WEX::Common::String().Format(L"RegDeleteTreeW HKEY_CURRENT_USER\\%s", regkey.c_str()));
+ }
+ }
+
+ std::filesystem::path ToLongPath(const winrt::hstring& path)
+ {
+ DWORD size{ GetLongPathNameW(path.c_str(), nullptr, 0) };
+ VERIFY_ARE_NOT_EQUAL(0u, size);
+ std::wstring longPath(size, L'\0');
+ size = GetLongPathNameW(path.c_str(), longPath.data(), size);
+ VERIFY_ARE_NOT_EQUAL(0u, size);
+ longPath.resize(size); // Trim the trailing NUL that Windows kindly included
+ return std::filesystem::path{ longPath.c_str() };
+ }
+
+ TEST_METHOD(GetForUnpackaged_InvalidParameter)
+ {
+ constexpr static PCWSTR c_invalidIds[]{
+ L"",
+ L"foo/bar",
+ L"foo\\bar",
+ L"foo@bar",
+ L".",
+ L"..",
+ L"lpt1",
+ L"lpt1.invalid",
+ };
+ for (PCWSTR invalidId : c_invalidIds)
+ {
+ const winrt::hstring invalid{ invalidId };
+ try
+ {
+ [[maybe_unused]] auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(invalid, Product) };
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", invalid.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", invalid.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ [[maybe_unused]] auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, invalid) };
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", Publisher.c_str(), invalid.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", Publisher.c_str(), invalid.c_str(), e.code(), e.message().c_str()));
+ }
+ }
+ }
+ TEST_METHOD(GetForUnpackaged)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+ }
+
+ static bool PathExists(std::filesystem::path const& path)
+ {
+ const std::filesystem::directory_entry directoryEntry{ path };
+ return directoryEntry.is_directory();
+ }
+
+ std::filesystem::path UserLocalPath(winrt::hstring const& publisher)
+ {
+ // Caller is LocalSystem : %PROGRAMDATA%\...publisher...
+ // Caller is =MediumIL : %USERPROFILE%\AppData\Local\...publisher...
+ auto folderId{ FOLDERID_LocalAppData };
+ if (::Security::User::IsLocalSystem())
+ {
+ folderId = FOLDERID_ProgramData;
+ }
+ else if (::Security::IntegrityLevel::GetIntegrityLevel() < SECURITY_MANDATORY_MEDIUM_RID)
+ {
+ folderId = FOLDERID_LocalAppDataLow;
+ }
+ wil::unique_cotaskmem_string rootPath;
+ VERIFY_SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT/*TODO KF_FLAG_CREATE | KF_FLAG_DONT_VERIFY*/, nullptr, wil::out_param(rootPath)));
+ std::filesystem::path path{ rootPath.get() };
+ path /= publisher.c_str();
+ return path;
+ }
+
+ winrt::hstring UserLocalPath(winrt::hstring const& publisher, winrt::hstring const& product)
+ {
+ // Caller is LocalSystem : %PROGRAMDATA%\...publisher...\...product...
+ // Caller is =MediumIL : %USERPROFILE%\AppData\Local\...publisher...\...product...
+ auto path{ UserLocalPath(publisher) };
+ path /= product.c_str();
+ return winrt::hstring{ path.c_str() };
+ }
+
+ winrt::Windows::Storage::StorageFolder UserLocalFolder(winrt::hstring const& publisher, winrt::hstring const& product)
+ {
+ const auto path{ UserLocalPath(publisher, product) };
+ return winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(path).get();
+ }
+
+ std::filesystem::path UserTemporaryPath(winrt::hstring const& publisher)
+ {
+ // GetTempPath[2]() + \...publisher...
+ auto path{ ::Microsoft::FileSystem::Path::GetTempDirectory() };
+ path /= publisher.c_str();
+ return path;
+ }
+
+ winrt::hstring UserTemporaryPath(winrt::hstring const& publisher, winrt::hstring const& product)
+ {
+ // GetTempPath[2]() + \...publisher...\...product...
+ auto path{ UserTemporaryPath(publisher) };
+ path /= product.c_str();
+ return winrt::hstring{ path.c_str() };
+ }
+
+ winrt::Windows::Storage::StorageFolder UserTemporaryFolder(winrt::hstring const& publisher, winrt::hstring const& product)
+ {
+ const auto path{ UserTemporaryPath(publisher, product) };
+ return winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(path).get();
+ }
+
+ TEST_METHOD(LocalCacheFolderAndPath)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ try
+ {
+ [[maybe_unused]] auto path{ applicationData.LocalCachePath() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_NOTIMPL, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ [[maybe_unused]] auto folder{ applicationData.LocalCacheFolder() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_NOTIMPL, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+ }
+
+ TEST_METHOD(LocalFolderAndPath)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ const auto localFolder{ applicationData.LocalFolder() };
+ const auto localFolderPath{ ToLongPath(localFolder.Path()) };
+ const auto localPath{ ToLongPath(applicationData.LocalPath()) };
+ VERIFY_ARE_EQUAL(localFolderPath, localPath);
+ const auto expectedLocalFolder{ UserLocalFolder(Publisher, Product) };
+ const auto expectedLocalPath{ ToLongPath(UserLocalPath(Publisher, Product).c_str()) };
+ VERIFY_ARE_EQUAL(localPath, expectedLocalPath);
+ }
+
+ TEST_METHOD(SharedLocalFolderAndPath)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ try
+ {
+ [[maybe_unused]] auto path{ applicationData.SharedLocalPath() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_NOTIMPL, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ [[maybe_unused]] auto folder{ applicationData.SharedLocalFolder() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_NOTIMPL, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+ }
+
+ TEST_METHOD(TemporaryFolderAndPath)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ const auto temporaryFolder{ applicationData.TemporaryFolder() };
+ const auto temporaryFolderPath{ ToLongPath(temporaryFolder.Path()) };
+ const auto temporaryPath{ ToLongPath(applicationData.TemporaryPath()) };
+ VERIFY_ARE_EQUAL(temporaryFolderPath, temporaryPath, WEX::Common::String().Format(L"Expected:%ls Actual:%ls", temporaryFolderPath.c_str(), temporaryPath.c_str()));
+ const auto expectedTemporaryFolder{ UserTemporaryFolder(Publisher, Product) };
+ const auto expectedTemporaryPath{ ToLongPath(UserTemporaryPath(Publisher, Product)) };
+ VERIFY_ARE_EQUAL(temporaryPath, expectedTemporaryPath, WEX::Common::String().Format(L"Expected:%ls Actual:%ls", expectedTemporaryPath.c_str(), temporaryPath.c_str()));
+ }
+
+ TEST_METHOD(PublisherCacheFolderAndPath)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ winrt::hstring folderName{ L"Does.Not.Exist" };
+ try
+ {
+ [[maybe_unused]] auto path{ applicationData.GetPublisherCachePath(folderName) };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_NOTIMPL, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ [[maybe_unused]] auto folder{ applicationData.GetPublisherCacheFolder(folderName) };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_NOTIMPL, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+ }
+
+ TEST_METHOD(MachineFolderAndPath_NotSupported)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ VERIFY_IS_FALSE(applicationData.IsMachinePathSupported());
+
+ const auto machinePath{ applicationData.MachinePath() };
+ VERIFY_ARE_EQUAL(machinePath, null_hstring);
+ const auto machineFolder{ applicationData.MachineFolder() };
+ VERIFY_IS_NULL(machineFolder);
+ }
+
+ TEST_METHOD(LocalSettings)
+ {
+ winrt::hstring packageFamilyName{ Main_PackageFamilyName };
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ const auto localSettings{ applicationData.LocalSettings() };
+ VERIFY_ARE_EQUAL(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local, localSettings.Locality());
+
+ auto containers{ localSettings.Containers() };
+ VERIFY_ARE_EQUAL(0u, containers.Size());
+
+ const winrt::hstring foodAndStuff{ L"FoodAndStuff" };
+ VERIFY_IS_FALSE(containers.HasKey(foodAndStuff));
+
+ auto container{ localSettings.CreateContainer(foodAndStuff, winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always) };
+ VERIFY_ARE_EQUAL(foodAndStuff, container.Name());
+ //
+ VERIFY_ARE_EQUAL(0u, containers.Size());
+ VERIFY_IS_FALSE(containers.HasKey(foodAndStuff));
+ containers = localSettings.Containers();
+ VERIFY_ARE_EQUAL(1u, containers.Size());
+ VERIFY_IS_TRUE(containers.HasKey(foodAndStuff));
+ container = containers.Lookup(foodAndStuff);
+ VERIFY_IS_NOT_NULL(container);
+ VERIFY_ARE_EQUAL(foodAndStuff, container.Name());
+
+ const winrt::hstring keyMeat{ L"Meat" };
+ const winrt::hstring rawValueSteak{ L"Steak" };
+ auto valueSteak{ winrt::Windows::Foundation::PropertyValue::CreateString(rawValueSteak) };
+ auto values{ container.Values() };
+ VERIFY_ARE_EQUAL(0u, values.Size());
+ values.Insert(keyMeat, valueSteak);
+ VERIFY_ARE_EQUAL(1u, values.Size());
+ auto steak{ values.Lookup(keyMeat) };
+ VERIFY_IS_NOT_NULL(steak);
+ auto steakLookupAsReferenceString{ steak.try_as>() };
+ VERIFY_IS_NOT_NULL(steakLookupAsReferenceString);
+ auto steakString{ steakLookupAsReferenceString.GetString() };
+ VERIFY_ARE_EQUAL(rawValueSteak, steakString);
+
+ const winrt::hstring keyDrink{ L"Drink" };
+ const winrt::hstring rawValueWhiskey{ L"Whiskey" };
+ auto valueWhiskey{ winrt::Windows::Foundation::PropertyValue::CreateString(rawValueWhiskey) };
+ VERIFY_ARE_EQUAL(1u, values.Size());
+ values.Insert(keyDrink, valueWhiskey);
+ VERIFY_ARE_EQUAL(2u, values.Size());
+ auto whiskey{ values.Lookup(keyDrink) };
+ VERIFY_IS_NOT_NULL(whiskey);
+ auto whiskeyLookupAsReferenceString{ whiskey.try_as>() };
+ VERIFY_IS_NOT_NULL(whiskeyLookupAsReferenceString);
+ auto whiskeyString{ whiskeyLookupAsReferenceString.GetString() };
+ VERIFY_ARE_EQUAL(rawValueWhiskey, whiskeyString);
+
+ VERIFY_ARE_EQUAL(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local, container.Locality());
+ container.Close();
+ try
+ {
+ [[maybe_unused]] auto locality{ container.Locality() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ VERIFY_ARE_EQUAL(1u, localSettings.Containers().Size());
+ localSettings.DeleteContainer(foodAndStuff);
+ VERIFY_ARE_EQUAL(0u, localSettings.Containers().Size());
+
+ VERIFY_ARE_EQUAL(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local, localSettings.Locality());
+ localSettings.Close();
+ try
+ {
+ [[maybe_unused]] auto locality{ localSettings.Locality() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+ }
+
+ TEST_METHOD(ClearAsync_LocalPath)
+ {
+ //TODO ClearAsync_LocalPath
+ }
+
+ TEST_METHOD(ClearAsync_LocalSettings)
+ {
+ //TODO ClearAsync_LocalSettings
+ }
+
+ TEST_METHOD(ClearAsync_Local)
+ {
+ //TODO ClearAsync_Local
+ }
+
+ TEST_METHOD(ClearAsync_MachinePath)
+ {
+ //TODO ClearAsync_MachinePath
+ }
+
+ TEST_METHOD(ClearPublisherCacheFolderAsync)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ try
+ {
+ winrt::hstring folderName{ L"Does.Not.Exist" };
+ [[maybe_unused]] auto folder{ applicationData.GetPublisherCacheFolder(folderName) };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_NOTIMPL, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+ }
+
+ TEST_METHOD(CreateContainer_InvalidParameter)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+ auto localSettings{ applicationData.LocalSettings() };
+ VERIFY_IS_NOT_NULL(localSettings);
+
+ const winrt::hstring empty;
+ constexpr auto disposition{ winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always };
+ auto container{ localSettings.CreateContainer(empty, disposition) };
+ VERIFY_IS_NOT_NULL(container);
+
+ const winrt::hstring invalidBackslash{ L"foo\\bar" };
+ try
+ {
+ [[maybe_unused]] auto container{ localSettings.CreateContainer(invalidBackslash, disposition) };
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", invalidBackslash.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", invalidBackslash.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ constexpr size_t c_nameMaxLength{ 255 };
+ const std::wstring nameMaxLengthString(c_nameMaxLength, L'h');
+ const winrt::hstring nameMaxLength{ nameMaxLengthString.c_str() };
+ auto maxLengthContainer{ localSettings.CreateContainer(nameMaxLength, disposition) };
+ VERIFY_IS_NOT_NULL(maxLengthContainer);
+ localSettings.DeleteContainer(nameMaxLength);
+
+ const std::wstring nameTooLongString(c_nameMaxLength + 1, L'k');
+ const winrt::hstring nameTooLong{ nameTooLongString.c_str() };
+ VERIFY_IS_NOT_NULL(maxLengthContainer);
+ try
+ {
+ [[maybe_unused]] auto container{ localSettings.CreateContainer(nameTooLong, disposition) };
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", nameTooLong.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", nameTooLong.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ const winrt::hstring invalidDot{ L"." };
+ try
+ {
+ [[maybe_unused]] auto container{ localSettings.CreateContainer(invalidDot, disposition) };
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", invalidDot.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", invalidDot.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ const winrt::hstring invalidDotDot{ L".." };
+ try
+ {
+ [[maybe_unused]] auto container{ localSettings.CreateContainer(invalidDotDot, disposition) };
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", invalidDotDot.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", invalidDotDot.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ }
+
+ TEST_METHOD(DeleteContainer_InvalidParameter)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(Publisher, Product) };
+ VERIFY_IS_NOT_NULL(applicationData);
+ auto localSettings{ applicationData.LocalSettings() };
+ VERIFY_IS_NOT_NULL(localSettings);
+
+ const winrt::hstring empty;
+ localSettings.DeleteContainer(empty);
+
+ const winrt::hstring invalidBackslash{ L"foo\\bar" };
+ try
+ {
+ localSettings.DeleteContainer(invalidBackslash);
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", invalidBackslash.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", invalidBackslash.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ constexpr size_t c_nameMaxLength{ 255 };
+ const std::wstring nameMaxLengthString(c_nameMaxLength, L'h');
+ const winrt::hstring nameMaxLength{ nameMaxLengthString.c_str() };
+ localSettings.DeleteContainer(nameMaxLength);
+
+ const std::wstring nameTooLongString(c_nameMaxLength + 1, L'k');
+ const winrt::hstring nameTooLong{ nameTooLongString.c_str() };
+ try
+ {
+ localSettings.DeleteContainer(nameTooLong);
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", nameTooLong.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", nameTooLong.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ const winrt::hstring invalidDot{ L"." };
+ try
+ {
+ localSettings.DeleteContainer(invalidDot);
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", invalidDot.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", invalidDot.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ const winrt::hstring invalidDotDot{ L".." };
+ try
+ {
+ localSettings.DeleteContainer(invalidDotDot);
+ VERIFY_FAIL(WEX::Common::String().Format(L"Success is not expected -- Publisher:%s Product:%s", invalidDotDot.c_str(), Product.c_str()));
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(E_INVALIDARG, e.code(), WEX::Common::String().Format(L"Publisher:%s Product:%s => 0x%X %s", invalidDotDot.c_str(), Product.c_str(), e.code(), e.message().c_str()));
+ }
+
+ }
+
+ TEST_METHOD(ContainerOperations)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(L"TestApplicationData_Contoso", L"SupermarketPointOfSale") };
+ VERIFY_IS_NOT_NULL(applicationData);
+
+ auto localSettings{ applicationData.LocalSettings() };
+ VERIFY_IS_NOT_NULL(localSettings);
+ VERIFY_ARE_EQUAL(L"", localSettings.Name());
+ VERIFY_ARE_EQUAL(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local, localSettings.Locality());
+
+ // Start clean
+ auto containers{ localSettings.Containers() };
+ if (containers.HasKey(L"Inventory"))
+ {
+ localSettings.DeleteContainer(L"Inventory");
+ }
+ containers = localSettings.Containers();
+ VERIFY_IS_FALSE(containers.HasKey(L"Inventory"));
+
+ // CreateContainer with Always creates a new container
+ auto inventory{ localSettings.CreateContainer(L"Inventory", winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always) };
+ VERIFY_IS_NOT_NULL(inventory);
+ VERIFY_ARE_EQUAL(L"Inventory", inventory.Name());
+ VERIFY_ARE_EQUAL(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local, inventory.Locality());
+
+ containers = localSettings.Containers();
+ VERIFY_ARE_EQUAL(1u, containers.Size());
+ VERIFY_IS_TRUE(containers.HasKey(L"Inventory"));
+ auto lookedUp{ containers.Lookup(L"Inventory") };
+ VERIFY_IS_NOT_NULL(lookedUp);
+ VERIFY_ARE_EQUAL(L"Inventory", lookedUp.Name());
+
+ // CreateContainer with Always on existing container returns it
+ auto inventoryAgain{ localSettings.CreateContainer(L"Inventory", winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always) };
+ VERIFY_IS_NOT_NULL(inventoryAgain);
+ VERIFY_ARE_EQUAL(L"Inventory", inventoryAgain.Name());
+ containers = localSettings.Containers();
+ VERIFY_ARE_EQUAL(1u, containers.Size());
+
+ // CreateContainer with Existing on existing container returns it
+ auto inventoryExisting{ localSettings.CreateContainer(L"Inventory", winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Existing) };
+ VERIFY_IS_NOT_NULL(inventoryExisting);
+ VERIFY_ARE_EQUAL(L"Inventory", inventoryExisting.Name());
+
+ // Multiple containers
+ auto pricing{ localSettings.CreateContainer(L"Pricing", winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always) };
+ containers = localSettings.Containers();
+ VERIFY_ARE_EQUAL(2u, containers.Size());
+ VERIFY_IS_TRUE(containers.HasKey(L"Inventory"));
+ VERIFY_IS_TRUE(containers.HasKey(L"Pricing"));
+
+ // Values in a child container
+ auto inventoryValues{ inventory.Values() };
+ VERIFY_ARE_EQUAL(0u, inventoryValues.Size());
+ inventoryValues.Insert(L"SKU", winrt::box_value(L"ABC-123"));
+ VERIFY_ARE_EQUAL(1u, inventoryValues.Size());
+ auto sku{ winrt::unbox_value(inventoryValues.Lookup(L"SKU")) };
+ VERIFY_ARE_EQUAL(L"ABC-123", sku);
+
+ // DeleteContainer removes the container and its values
+ localSettings.DeleteContainer(L"Inventory");
+ containers = localSettings.Containers();
+ VERIFY_ARE_EQUAL(1u, containers.Size());
+ VERIFY_IS_FALSE(containers.HasKey(L"Inventory"));
+ VERIFY_IS_TRUE(containers.HasKey(L"Pricing"));
+
+ // Cleanup
+ localSettings.DeleteContainer(L"Pricing");
+ containers = localSettings.Containers();
+ VERIFY_ARE_EQUAL(0u, containers.Size());
+ }
+
+ TEST_METHOD(Values_ScalarTypes)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(L"TestApplicationData_Contoso", L"SupermarketPointOfSale") };
+ auto localSettings{ applicationData.LocalSettings() };
+ auto values{ localSettings.Values() };
+ values.Clear();
+
+ // Empty (null)
+ values.Insert(L"Empty", nullptr);
+ VERIFY_IS_TRUE(values.HasKey(L"Empty"));
+ auto emptyVal{ values.Lookup(L"Empty") };
+ VERIFY_IS_NULL(emptyVal);
+
+ // String
+ values.Insert(L"String", winrt::box_value(L"Hello World"));
+ VERIFY_ARE_EQUAL(L"Hello World", winrt::unbox_value(values.Lookup(L"String")));
+
+ // UInt8
+ values.Insert(L"UInt8", winrt::box_value(static_cast(42)));
+ VERIFY_ARE_EQUAL(static_cast(42), winrt::unbox_value(values.Lookup(L"UInt8")));
+
+ // Int16
+ values.Insert(L"Int16", winrt::box_value(static_cast(-1234)));
+ VERIFY_ARE_EQUAL(static_cast(-1234), winrt::unbox_value(values.Lookup(L"Int16")));
+
+ // UInt16
+ values.Insert(L"UInt16", winrt::box_value(static_cast(65535)));
+ VERIFY_ARE_EQUAL(static_cast(65535), winrt::unbox_value(values.Lookup(L"UInt16")));
+
+ // Int32
+ values.Insert(L"Int32", winrt::box_value(static_cast(-100000)));
+ VERIFY_ARE_EQUAL(static_cast(-100000), winrt::unbox_value(values.Lookup(L"Int32")));
+
+ // UInt32
+ values.Insert(L"UInt32", winrt::box_value(static_cast(3000000000u)));
+ VERIFY_ARE_EQUAL(static_cast(3000000000u), winrt::unbox_value(values.Lookup(L"UInt32")));
+
+ // Int64
+ values.Insert(L"Int64", winrt::box_value(static_cast(-9876543210LL)));
+ VERIFY_ARE_EQUAL(static_cast(-9876543210LL), winrt::unbox_value(values.Lookup(L"Int64")));
+
+ // UInt64
+ values.Insert(L"UInt64", winrt::box_value(static_cast(18000000000000000000ULL)));
+ VERIFY_ARE_EQUAL(static_cast(18000000000000000000ULL), winrt::unbox_value(values.Lookup(L"UInt64")));
+
+ // Single
+ values.Insert(L"Single", winrt::box_value(3.14f));
+ VERIFY_ARE_EQUAL(3.14f, winrt::unbox_value(values.Lookup(L"Single")));
+
+ // Double
+ values.Insert(L"Double", winrt::box_value(2.718281828));
+ VERIFY_ARE_EQUAL(2.718281828, winrt::unbox_value(values.Lookup(L"Double")));
+
+ // Char16
+ values.Insert(L"Char16", winrt::box_value(u'Z'));
+ VERIFY_ARE_EQUAL(u'Z', winrt::unbox_value(values.Lookup(L"Char16")));
+
+ // Boolean true
+ values.Insert(L"BoolTrue", winrt::box_value(true));
+ VERIFY_IS_TRUE(winrt::unbox_value(values.Lookup(L"BoolTrue")));
+
+ // Boolean false
+ values.Insert(L"BoolFalse", winrt::box_value(false));
+ VERIFY_IS_FALSE(winrt::unbox_value(values.Lookup(L"BoolFalse")));
+
+ // DateTime
+ const winrt::Windows::Foundation::DateTime dateTime{ winrt::Windows::Foundation::TimeSpan{ 132800000000000000LL } };
+ values.Insert(L"DateTime", winrt::box_value(dateTime));
+ auto readDateTime{ winrt::unbox_value(values.Lookup(L"DateTime")) };
+ VERIFY_ARE_EQUAL(dateTime.time_since_epoch().count(), readDateTime.time_since_epoch().count());
+
+ // TimeSpan
+ const winrt::Windows::Foundation::TimeSpan timeSpan{ 36000000000LL }; // 1 hour in 100ns ticks
+ values.Insert(L"TimeSpan", winrt::box_value(timeSpan));
+ auto readTimeSpan{ winrt::unbox_value(values.Lookup(L"TimeSpan")) };
+ VERIFY_ARE_EQUAL(timeSpan.count(), readTimeSpan.count());
+
+ // Guid
+ const winrt::guid testGuid{ 0x01020304, 0x0506, 0x0708, { 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 } };
+ values.Insert(L"Guid", winrt::box_value(testGuid));
+ VERIFY_ARE_EQUAL(testGuid, winrt::unbox_value(values.Lookup(L"Guid")));
+
+ // Point
+ const winrt::Windows::Foundation::Point point{ 1.5f, 2.5f };
+ values.Insert(L"Point", winrt::box_value(point));
+ auto readPoint{ winrt::unbox_value(values.Lookup(L"Point")) };
+ VERIFY_ARE_EQUAL(point.X, readPoint.X);
+ VERIFY_ARE_EQUAL(point.Y, readPoint.Y);
+
+ // Size
+ const winrt::Windows::Foundation::Size size{ 100.0f, 200.0f };
+ values.Insert(L"Size", winrt::box_value(size));
+ auto readSize{ winrt::unbox_value(values.Lookup(L"Size")) };
+ VERIFY_ARE_EQUAL(size.Width, readSize.Width);
+ VERIFY_ARE_EQUAL(size.Height, readSize.Height);
+
+ // Rect
+ const winrt::Windows::Foundation::Rect rect{ 10.0f, 20.0f, 300.0f, 400.0f };
+ values.Insert(L"Rect", winrt::box_value(rect));
+ auto readRect{ winrt::unbox_value(values.Lookup(L"Rect")) };
+ VERIFY_ARE_EQUAL(rect.X, readRect.X);
+ VERIFY_ARE_EQUAL(rect.Y, readRect.Y);
+ VERIFY_ARE_EQUAL(rect.Width, readRect.Width);
+ VERIFY_ARE_EQUAL(rect.Height, readRect.Height);
+
+ // Overwrite a value
+ values.Insert(L"String", winrt::box_value(L"Updated"));
+ VERIFY_ARE_EQUAL(L"Updated", winrt::unbox_value(values.Lookup(L"String")));
+
+ // Verify count
+ VERIFY_ARE_EQUAL(20u, values.Size());
+
+ values.Clear();
+ }
+
+ TEST_METHOD(Values_ArrayTypes)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(L"TestApplicationData_Contoso", L"SupermarketPointOfSale") };
+ auto localSettings{ applicationData.LocalSettings() };
+ auto values{ localSettings.Values() };
+ values.Clear();
+
+ using PV = winrt::Windows::Foundation::PropertyValue;
+ namespace WF = winrt::Windows::Foundation;
+
+ // UInt8Array
+ {
+ // An initializer list like: { 1, 2, 3, 255 } is always an initializer_list unless told otherwise.
+ // winrt::com_array has a templated constructor that accepts iterators / initializer lists, so the compiler happily
+ // tries to copy int values into T, and then quite correctly warns you that: “I am truncating integers into smaller
+ // integer types. I hope you meant that.” Better to make the initializer list elements match the array element type.
+ std::array arrValues{ 1, 2, 3, 255 };
+ winrt::com_array arr(arrValues.begin(), arrValues.end());
+ values.Insert(L"UInt8Array", PV::CreateUInt8Array(arr));
+ auto pv{ values.Lookup(L"UInt8Array").as() };
+ winrt::com_array result;
+ pv.GetUInt8Array(result);
+ VERIFY_ARE_EQUAL(4u, result.size());
+ VERIFY_ARE_EQUAL(static_cast(1), result[0]);
+ VERIFY_ARE_EQUAL(static_cast(255), result[3]);
+ }
+
+ // Int16Array
+ {
+ // An initializer list like: { 1, 2, 3, 255 } is always an initializer_list unless told otherwise.
+ // winrt::com_array has a templated constructor that accepts iterators / initializer lists, so the compiler happily
+ // tries to copy int values into T, and then quite correctly warns you that: “I am truncating integers into smaller
+ // integer types. I hope you meant that.” Better to make the initializer list elements match the array element type.
+ std::array arrValues{ -32768, 0, 32767 };
+ winrt::com_array arr(arrValues.begin(), arrValues.end());
+ values.Insert(L"Int16Array", PV::CreateInt16Array(arr));
+ auto pv{ values.Lookup(L"Int16Array").as() };
+ winrt::com_array result;
+ pv.GetInt16Array(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(static_cast(-32768), result[0]);
+ VERIFY_ARE_EQUAL(static_cast(32767), result[2]);
+ }
+
+ // UInt16Array
+ {
+ // An initializer list like: { 1, 2, 3, 255 } is always an initializer_list unless told otherwise.
+ // winrt::com_array has a templated constructor that accepts iterators / initializer lists, so the compiler happily
+ // tries to copy int values into T, and then quite correctly warns you that: “I am truncating integers into smaller
+ // integer types. I hope you meant that.” Better to make the initializer list elements match the array element type.
+ std::array arrValues{ 0, 1000, 65535 };
+ winrt::com_array arr(arrValues.begin(), arrValues.end());
+ values.Insert(L"UInt16Array", PV::CreateUInt16Array(arr));
+ auto pv{ values.Lookup(L"UInt16Array").as() };
+ winrt::com_array result;
+ pv.GetUInt16Array(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(static_cast(0), result[0]);
+ VERIFY_ARE_EQUAL(static_cast(65535), result[2]);
+ }
+
+ // Int32Array
+ {
+ winrt::com_array arr{ -2147483647 - 1, 0, 2147483647 };
+ values.Insert(L"Int32Array", PV::CreateInt32Array(arr));
+ auto pv{ values.Lookup(L"Int32Array").as() };
+ winrt::com_array result;
+ pv.GetInt32Array(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(static_cast(-2147483647 - 1), result[0]);
+ VERIFY_ARE_EQUAL(static_cast(2147483647), result[2]);
+ }
+
+ // UInt32Array
+ {
+ winrt::com_array arr{ 0u, 1000000u, 4294967295u };
+ values.Insert(L"UInt32Array", PV::CreateUInt32Array(arr));
+ auto pv{ values.Lookup(L"UInt32Array").as() };
+ winrt::com_array result;
+ pv.GetUInt32Array(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(0u, result[0]);
+ VERIFY_ARE_EQUAL(4294967295u, result[2]);
+ }
+
+ // Int64Array
+ {
+ winrt::com_array arr{ -9223372036854775807LL - 1, 0LL, 9223372036854775807LL };
+ values.Insert(L"Int64Array", PV::CreateInt64Array(arr));
+ auto pv{ values.Lookup(L"Int64Array").as() };
+ winrt::com_array result;
+ pv.GetInt64Array(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(static_cast(-9223372036854775807LL - 1), result[0]);
+ VERIFY_ARE_EQUAL(static_cast(9223372036854775807LL), result[2]);
+ }
+
+ // UInt64Array
+ {
+ winrt::com_array arr{ 0ULL, 18446744073709551615ULL };
+ values.Insert(L"UInt64Array", PV::CreateUInt64Array(arr));
+ auto pv{ values.Lookup(L"UInt64Array").as() };
+ winrt::com_array result;
+ pv.GetUInt64Array(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(0ULL, result[0]);
+ VERIFY_ARE_EQUAL(18446744073709551615ULL, result[1]);
+ }
+
+ // SingleArray
+ {
+ winrt::com_array arr{ 1.1f, 2.2f, 3.3f };
+ values.Insert(L"SingleArray", PV::CreateSingleArray(arr));
+ auto pv{ values.Lookup(L"SingleArray").as() };
+ winrt::com_array result;
+ pv.GetSingleArray(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(1.1f, result[0]);
+ VERIFY_ARE_EQUAL(3.3f, result[2]);
+ }
+
+ // DoubleArray
+ {
+ winrt::com_array arr{ 1.11, 2.22 };
+ values.Insert(L"DoubleArray", PV::CreateDoubleArray(arr));
+ auto pv{ values.Lookup(L"DoubleArray").as() };
+ winrt::com_array result;
+ pv.GetDoubleArray(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(1.11, result[0]);
+ VERIFY_ARE_EQUAL(2.22, result[1]);
+ }
+
+ // Char16Array
+ {
+ winrt::com_array arr{ L'A', L'B', L'\x4e16' };
+ values.Insert(L"Char16Array", PV::CreateChar16Array(arr));
+ auto pv{ values.Lookup(L"Char16Array").as() };
+ winrt::com_array result;
+ pv.GetChar16Array(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(L'A', result[0]);
+ VERIFY_ARE_EQUAL(L'\x4e16', result[2]);
+ }
+
+ // BooleanArray
+ {
+ winrt::com_array arr{ true, false, true, false };
+ values.Insert(L"BooleanArray", PV::CreateBooleanArray(arr));
+ auto pv{ values.Lookup(L"BooleanArray").as() };
+ winrt::com_array result;
+ pv.GetBooleanArray(result);
+ VERIFY_ARE_EQUAL(4u, result.size());
+ VERIFY_IS_TRUE(result[0]);
+ VERIFY_IS_FALSE(result[1]);
+ VERIFY_IS_TRUE(result[2]);
+ VERIFY_IS_FALSE(result[3]);
+ }
+
+ // StringArray
+ {
+ winrt::com_array arr{ winrt::hstring{L"Alpha"}, winrt::hstring{L"Beta"}, winrt::hstring{L"Gamma"} };
+ values.Insert(L"StringArray", PV::CreateStringArray(arr));
+ auto pv{ values.Lookup(L"StringArray").as() };
+ winrt::com_array result;
+ pv.GetStringArray(result);
+ VERIFY_ARE_EQUAL(3u, result.size());
+ VERIFY_ARE_EQUAL(winrt::hstring{L"Alpha"}, result[0]);
+ VERIFY_ARE_EQUAL(winrt::hstring{L"Beta"}, result[1]);
+ VERIFY_ARE_EQUAL(winrt::hstring{L"Gamma"}, result[2]);
+ }
+
+ // DateTimeArray
+ {
+ WF::DateTime dt1{ WF::TimeSpan{ 100000000LL } };
+ WF::DateTime dt2{ WF::TimeSpan{ 200000000LL } };
+ winrt::com_array arr{ dt1, dt2 };
+ values.Insert(L"DateTimeArray", PV::CreateDateTimeArray(arr));
+ auto pv{ values.Lookup(L"DateTimeArray").as() };
+ winrt::com_array result;
+ pv.GetDateTimeArray(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(dt1.time_since_epoch().count(), result[0].time_since_epoch().count());
+ VERIFY_ARE_EQUAL(dt2.time_since_epoch().count(), result[1].time_since_epoch().count());
+ }
+
+ // TimeSpanArray
+ {
+ WF::TimeSpan ts1{ 10000000LL }; // 1 second
+ WF::TimeSpan ts2{ 600000000LL }; // 1 minute
+ winrt::com_array arr{ ts1, ts2 };
+ values.Insert(L"TimeSpanArray", PV::CreateTimeSpanArray(arr));
+ auto pv{ values.Lookup(L"TimeSpanArray").as() };
+ winrt::com_array result;
+ pv.GetTimeSpanArray(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(ts1.count(), result[0].count());
+ VERIFY_ARE_EQUAL(ts2.count(), result[1].count());
+ }
+
+ // GuidArray
+ {
+ winrt::guid g1{ 0x11111111, 0x2222, 0x3333, { 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB } };
+ winrt::guid g2{ 0xAAAAAAAA, 0xBBBB, 0xCCCC, { 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44 } };
+ winrt::com_array arr{ g1, g2 };
+ values.Insert(L"GuidArray", PV::CreateGuidArray(arr));
+ auto pv{ values.Lookup(L"GuidArray").as() };
+ winrt::com_array result;
+ pv.GetGuidArray(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(g1, result[0]);
+ VERIFY_ARE_EQUAL(g2, result[1]);
+ }
+
+ // PointArray
+ {
+ WF::Point p1{ 1.0f, 2.0f };
+ WF::Point p2{ 3.0f, 4.0f };
+ winrt::com_array arr{ p1, p2 };
+ values.Insert(L"PointArray", PV::CreatePointArray(arr));
+ auto pv{ values.Lookup(L"PointArray").as() };
+ winrt::com_array result;
+ pv.GetPointArray(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(p1.X, result[0].X);
+ VERIFY_ARE_EQUAL(p1.Y, result[0].Y);
+ VERIFY_ARE_EQUAL(p2.X, result[1].X);
+ VERIFY_ARE_EQUAL(p2.Y, result[1].Y);
+ }
+
+ // SizeArray
+ {
+ WF::Size s1{ 10.0f, 20.0f };
+ WF::Size s2{ 30.0f, 40.0f };
+ winrt::com_array arr{ s1, s2 };
+ values.Insert(L"SizeArray", PV::CreateSizeArray(arr));
+ auto pv{ values.Lookup(L"SizeArray").as() };
+ winrt::com_array result;
+ pv.GetSizeArray(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(s1.Width, result[0].Width);
+ VERIFY_ARE_EQUAL(s1.Height, result[0].Height);
+ VERIFY_ARE_EQUAL(s2.Width, result[1].Width);
+ VERIFY_ARE_EQUAL(s2.Height, result[1].Height);
+ }
+
+ // RectArray
+ {
+ WF::Rect r1{ 1.0f, 2.0f, 3.0f, 4.0f };
+ WF::Rect r2{ 5.0f, 6.0f, 7.0f, 8.0f };
+ winrt::com_array arr{ r1, r2 };
+ values.Insert(L"RectArray", PV::CreateRectArray(arr));
+ auto pv{ values.Lookup(L"RectArray").as() };
+ winrt::com_array result;
+ pv.GetRectArray(result);
+ VERIFY_ARE_EQUAL(2u, result.size());
+ VERIFY_ARE_EQUAL(r1.X, result[0].X);
+ VERIFY_ARE_EQUAL(r1.Y, result[0].Y);
+ VERIFY_ARE_EQUAL(r1.Width, result[0].Width);
+ VERIFY_ARE_EQUAL(r1.Height, result[0].Height);
+ VERIFY_ARE_EQUAL(r2.X, result[1].X);
+ VERIFY_ARE_EQUAL(r2.Y, result[1].Y);
+ VERIFY_ARE_EQUAL(r2.Width, result[1].Width);
+ VERIFY_ARE_EQUAL(r2.Height, result[1].Height);
+ }
+
+ values.Clear();
+ }
+
+ TEST_METHOD(Values_PropertySetOperations)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(L"TestApplicationData_Contoso", L"SupermarketPointOfSale") };
+ auto localSettings{ applicationData.LocalSettings() };
+ auto values{ localSettings.Values() };
+ values.Clear();
+
+ // Insert and Size
+ VERIFY_ARE_EQUAL(0u, values.Size());
+ auto inserted{ values.Insert(L"Key1", winrt::box_value(L"Value1")) };
+ VERIFY_IS_FALSE(inserted); // Insert returns true if key existed (replaced)
+ VERIFY_ARE_EQUAL(1u, values.Size());
+
+ values.Insert(L"Key2", winrt::box_value(static_cast(42)));
+ VERIFY_ARE_EQUAL(2u, values.Size());
+
+ // Replace existing key
+ inserted = values.Insert(L"Key1", winrt::box_value(L"Updated"));
+ VERIFY_IS_TRUE(inserted); // Key existed, so replaced
+ VERIFY_ARE_EQUAL(2u, values.Size());
+ VERIFY_ARE_EQUAL(L"Updated", winrt::unbox_value(values.Lookup(L"Key1")));
+
+ // HasKey
+ VERIFY_IS_TRUE(values.HasKey(L"Key1"));
+ VERIFY_IS_TRUE(values.HasKey(L"Key2"));
+ VERIFY_IS_FALSE(values.HasKey(L"NonExistent"));
+
+ // Lookup
+ auto val1{ values.Lookup(L"Key1") };
+ VERIFY_IS_NOT_NULL(val1);
+ auto val2{ values.Lookup(L"Key2") };
+ VERIFY_IS_NOT_NULL(val2);
+ VERIFY_ARE_EQUAL(static_cast(42), winrt::unbox_value(val2));
+
+ // Remove
+ values.Remove(L"Key2");
+ VERIFY_ARE_EQUAL(1u, values.Size());
+ VERIFY_IS_FALSE(values.HasKey(L"Key2"));
+ VERIFY_IS_TRUE(values.HasKey(L"Key1"));
+
+ // GetView
+ values.Insert(L"ViewKey", winrt::box_value(L"ViewValue"));
+ auto view{ values.GetView() };
+ VERIFY_ARE_EQUAL(2u, view.Size());
+ VERIFY_IS_TRUE(view.HasKey(L"Key1"));
+ VERIFY_IS_TRUE(view.HasKey(L"ViewKey"));
+
+ // First / iteration
+ auto iter{ values.First() };
+ VERIFY_IS_TRUE(iter.HasCurrent());
+ uint32_t iterCount{ 0 };
+ while (iter.HasCurrent())
+ {
+ auto kvp{ iter.Current() };
+ VERIFY_IS_TRUE(values.HasKey(kvp.Key()));
+ ++iterCount;
+ iter.MoveNext();
+ }
+ VERIFY_ARE_EQUAL(2u, iterCount);
+
+ // Clear
+ values.Clear();
+ VERIFY_ARE_EQUAL(0u, values.Size());
+ VERIFY_IS_FALSE(values.HasKey(L"Key1"));
+ VERIFY_IS_FALSE(values.HasKey(L"ViewKey"));
+ }
+
+ TEST_METHOD(Values_MapChangedEvent)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(L"TestApplicationData_Contoso", L"SupermarketPointOfSale") };
+ auto localSettings{ applicationData.LocalSettings() };
+ auto values{ localSettings.Values() };
+ values.Clear();
+
+ auto observableMap{ values.as>() };
+ VERIFY_IS_NOT_NULL(observableMap);
+
+ winrt::Windows::Foundation::Collections::CollectionChange lastChange{};
+ winrt::hstring lastKey;
+ int callCount{ 0 };
+
+ auto token{ observableMap.MapChanged([&](auto const&, auto const& args)
+ {
+ lastChange = args.CollectionChange();
+ lastKey = args.Key();
+ ++callCount;
+ }) };
+
+ // Insert fires ItemInserted
+ values.Insert(L"EventKey", winrt::box_value(L"EventValue"));
+ VERIFY_ARE_EQUAL(1, callCount);
+ VERIFY_ARE_EQUAL(winrt::Windows::Foundation::Collections::CollectionChange::ItemInserted, lastChange);
+ VERIFY_ARE_EQUAL(L"EventKey", lastKey);
+
+ // Replace fires ItemChanged
+ values.Insert(L"EventKey", winrt::box_value(L"UpdatedValue"));
+ VERIFY_ARE_EQUAL(2, callCount);
+ VERIFY_ARE_EQUAL(winrt::Windows::Foundation::Collections::CollectionChange::ItemChanged, lastChange);
+ VERIFY_ARE_EQUAL(L"EventKey", lastKey);
+
+ // Remove fires ItemRemoved
+ values.Remove(L"EventKey");
+ VERIFY_ARE_EQUAL(3, callCount);
+ VERIFY_ARE_EQUAL(winrt::Windows::Foundation::Collections::CollectionChange::ItemRemoved, lastChange);
+ VERIFY_ARE_EQUAL(L"EventKey", lastKey);
+
+ // Clear fires Reset
+ values.Insert(L"A", winrt::box_value(L"1"));
+ values.Insert(L"B", winrt::box_value(L"2"));
+ callCount = 0;
+ values.Clear();
+ VERIFY_ARE_EQUAL(1, callCount);
+ VERIFY_ARE_EQUAL(winrt::Windows::Foundation::Collections::CollectionChange::Reset, lastChange);
+
+ // Unregister event
+ observableMap.MapChanged(token);
+ values.Insert(L"NoEvent", winrt::box_value(L"NoEvent"));
+ VERIFY_ARE_EQUAL(1, callCount); // Should not have changed
+ values.Clear();
+ }
+
+ TEST_METHOD(Close_ThrowsAfterClose)
+ {
+ auto applicationData{ winrt::Microsoft::Windows::Storage::ApplicationData::GetForUnpackaged(L"TestApplicationData_Contoso", L"SupermarketPointOfSale") };
+ auto localSettings{ applicationData.LocalSettings() };
+
+ // Create a child container to test Close on
+ auto child{ localSettings.CreateContainer(L"CloseTest", winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always) };
+ VERIFY_IS_NOT_NULL(child);
+ VERIFY_ARE_EQUAL(L"CloseTest", child.Name());
+ VERIFY_ARE_EQUAL(winrt::Microsoft::Windows::Storage::ApplicationDataLocality::Local, child.Locality());
+
+ child.Close();
+
+ // After Close, all member accesses should throw RO_E_CLOSED
+ try
+ {
+ [[maybe_unused]] auto name{ child.Name() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ [[maybe_unused]] auto locality{ child.Locality() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ [[maybe_unused]] auto vals{ child.Values() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ [[maybe_unused]] auto containers{ child.Containers() };
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ child.CreateContainer(L"Sub", winrt::Microsoft::Windows::Storage::ApplicationDataCreateDisposition::Always);
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ try
+ {
+ child.DeleteContainer(L"Sub");
+ VERIFY_FAIL(L"Success is not expected");
+ }
+ catch (winrt::hresult_error& e)
+ {
+ VERIFY_ARE_EQUAL(RO_E_CLOSED, e.code(), WEX::Common::String().Format(L"0x%X %s", e.code(), e.message().c_str()));
+ }
+
+ // Cleanup
+ localSettings.DeleteContainer(L"CloseTest");
+ }
+ };
+}
diff --git a/test/ApplicationData/pch.h b/test/ApplicationData/pch.h
index 009cc61028..2837841ab5 100644
--- a/test/ApplicationData/pch.h
+++ b/test/ApplicationData/pch.h
@@ -11,6 +11,8 @@
#include
#include
+#include
+#include
#include
#include
@@ -25,6 +27,9 @@
#include
#include
+#include
+#include
+#include
#include
#include
diff --git a/test/inc/WindowsAppRuntime.Test.FileSystem.h b/test/inc/WindowsAppRuntime.Test.FileSystem.h
index b8b5235942..e07f08ff77 100644
--- a/test/inc/WindowsAppRuntime.Test.FileSystem.h
+++ b/test/inc/WindowsAppRuntime.Test.FileSystem.h
@@ -86,4 +86,77 @@ namespace Test::FileSystem
}
}
+namespace WEX::TestExecution
+{
+ // Teach TAEF how to format a std::filesystem::path
+ template <>
+ class VerifyOutputTraits
+ {
+ public:
+ static WEX::Common::NoThrowString ToString(std::filesystem::path const& value)
+ {
+ const auto s{ value.c_str() };
+ if (!s)
+ {
+ return WEX::Common::NoThrowString(L"nullptr");
+ }
+ else
+ {
+ return WEX::Common::NoThrowString().Format(L"\"%s\"", s);
+ }
+ }
+ };
+
+ // Teach TAEF how to compare a std::filesystem::path
+ template <>
+ class VerifyCompareTraits
+ {
+ public:
+ static bool AreEqual(std::filesystem::path const& expected, std::filesystem::path const& actual)
+ {
+ return Compare(expected, actual) == 0;
+ }
+
+ static bool AreSame(std::filesystem::path const& expected, std::filesystem::path const& actual)
+ {
+ return &expected == &actual;
+ }
+
+ static bool IsLessThan(std::filesystem::path const& expectedLess, std::filesystem::path const& expectedGreater)
+ {
+ return Compare(expectedLess, expectedGreater) < 0;
+ }
+
+ static bool IsGreaterThan(std::filesystem::path const& expectedGreater, std::filesystem::path const& expectedLess)
+ {
+ return Compare(expectedGreater, expectedLess) > 0;
+ }
+
+ static bool IsNull(std::filesystem::path const& object)
+ {
+ return object.c_str() == nullptr;
+ }
+ private:
+ static int Compare(std::filesystem::path const& left, std::filesystem::path const& right)
+ {
+ if (left == right)
+ {
+ return 0;
+ }
+ else if (left.c_str() == nullptr)
+ {
+ return -1;
+ }
+ else if (right.c_str() == nullptr)
+ {
+ return 1;
+ }
+ else
+ {
+ return CompareStringOrdinal(left .c_str(), -1, right.c_str(), -1, FALSE) - CSTR_EQUAL;
+ }
+ }
+ };
+}
+
#endif // __WINDOWSAPPRUNTIME_TEST_FILESYSTEM_H
diff --git a/test/inc/WindowsAppRuntime.Test.TAEF.cppwinrt.h b/test/inc/WindowsAppRuntime.Test.TAEF.cppwinrt.h
index 2662a1eb27..f9f9732799 100644
--- a/test/inc/WindowsAppRuntime.Test.TAEF.cppwinrt.h
+++ b/test/inc/WindowsAppRuntime.Test.TAEF.cppwinrt.h
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation and Contributors. All rights reserved.
+// Copyright (c) Microsoft Corporation and Contributors. All rights reserved.
// Licensed under the MIT License.
#ifndef __WINDOWSAPPRUNTIME_TEST_TAEF_CPPWINRT_H
@@ -77,4 +77,56 @@ namespace WEX::TestExecution
};
}
+namespace winrt
+{
+/// Define a winrt::hstring variant that compares as case-independent (L"ABC" == L"abc" == L"aBc")
+class hstring_nocase
+{
+public:
+ explicit hstring_nocase(winrt::hstring const& string) : m_string(string) { }
+
+ explicit hstring_nocase(PCWSTR string) : m_string(string) { }
+
+ ~hstring_nocase() = default;
+
+ bool empty() const { return m_string.empty(); }
+
+ PCWSTR c_str() const { return m_string.c_str(); }
+
+public:
+ static int compare(winrt::hstring_nocase const& left, winrt::hstring_nocase const& right)
+ {
+ return CompareStringOrdinal(left.c_str(), -1, right.c_str(), -1, TRUE) - CSTR_EQUAL;
+ }
+
+private:
+ winrt::hstring m_string;
+};
+}
+
+bool operator==(winrt::hstring_nocase const& left, winrt::hstring_nocase const& right)
+{
+ return winrt::hstring_nocase::compare(left, right) == 0;
+}
+bool operator!=(winrt::hstring_nocase const& left, winrt::hstring_nocase const& right)
+{
+ return winrt::hstring_nocase::compare(left, right) != 0;
+}
+bool operator<(winrt::hstring_nocase const& left, winrt::hstring_nocase const& right)
+{
+ return winrt::hstring_nocase::compare(left, right) < 0;
+}
+bool operator<=(winrt::hstring_nocase const& left, winrt::hstring_nocase const& right)
+{
+ return winrt::hstring_nocase::compare(left, right) <= 0;
+}
+bool operator>(winrt::hstring_nocase const& left, winrt::hstring_nocase const& right)
+{
+ return winrt::hstring_nocase::compare(left, right) > 0;
+}
+bool operator>=(winrt::hstring_nocase const& left, winrt::hstring_nocase const& right)
+{
+ return winrt::hstring_nocase::compare(left, right) >= 0;
+}
+
#endif // __WINDOWSAPPRUNTIME_TEST_TAEF_CPPWINRT_H