SetFileTime
is simple and does not deserve a blog post: you have a file handle, you have date/time, you set it.
StorageFile File { ... };
FILETIME DateCreatedFileTime = ...;
winrt::file_handle File { CreateFile(File.Path().c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr) };
winrt::check_bool(SetFileTime(File.get(), &DateCreatedFileTime, &DateCreatedFileTime, &DateCreatedFileTime));
C++/WinRT and UWP make the journey much more exciting.
There is StorageItemContentProperties
Class which provides access to the content-related properties of an item (like a file or folder). This includes file times which are exposed as named properties, like “System.DateModified”. StorageItemContentProperties.SavePropertiesAsync
Method is to save properties associated with the item.
SavePropertiesAsync
is asynchronous so you have to deal with this too, but it is not to worry since C++/WinRT does not let you down with this at least.
One another thing is you need a Windows::Foundation::DateTime
value for the time which is something you might be not have good understanding for, as opposed to old fashioned FILETIME'
s definition of “a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)”.
In C++/WinRT Windows::Foundation::DateTime
is directly mapped to std::chrono::time_point
and there is also winrt::clock
as well, and conversion helpers which are easy to use but you have to be aware of them.
Moving on? With Windows::Foundation::DateTime
value on hands you need to put it into a key-value collection of properties to save. However, since values, in general, might be of different types you need to convert the time to variant IInspectable
. That is, to “box” it. Luckily, C++/WinRT has it all for you already:
C++/WinRT provides the winrt::box_value function, which takes a scalar value and returns the value boxed into an IInspectable. For unboxing an IInspectable back into a scalar value, there are the winrt::unbox_value and winrt::unbox_value_or functions.
Now we reached a hard one. SavePropertiesAsync
signature is this:
...SavePropertiesAsync(param::async_iterable<Windows::Foundation::Collections::IKeyValuePair<hstring, Windows::Foundation::IInspectable>> const& propertiesToSave) const
{
...
}
So IInspectable
there is the boxed value. This needs to be taken into something convertible to Windows::Foundation::Collections::IKeyValuePair
which is already a bit scary. But the truth is the real bastard is param::async_iterable
.
I have to admit MSDN documentation for C++/WinRT is awesome. There are so many things already mentioned there, including, for example, this article: Standard C++ data types and C++/WinRT, which is relevant and helpful. C++/WinRT is awesome too.
So you might think you could just have a std::map and it will be converted to Windows::Foundation::Collections::IKeyValuePair
automagically and then it would be picked up by param::async_iterable
as an argument? No.
[...] error C2664: 'winrt::Windows::Foundation::IAsyncAction winrt::impl::consume_Windows_Storage_FileProperties_IStorageItemExtraProperties::SavePropertiesAsync(void) const': cannot convert argument 1 from 'std::map,std::allocator>>' to 'const winrt::param::async_iterable> &' [...] Reason: cannot convert from 'std::map,std::allocator>>' to 'const winrt::param::async_iterable>' [...] No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
The thing is, and it’s mentioned here, for example, that async_iterable
need to acquire ownership of the collection when it takes it as an argument in a thin and lightweight way. So you have got to std::move
it there before the code starts building.
What do we got? Thing do build and call UWP API correctly. Everything is important here:
StorageFile File { ... };
FILETIME DateCreatedFileTime = ...;
std::map<winrt::hstring, winrt::Windows::Foundation::IInspectable> PropertyMap;
PropertyMap.emplace(L"System.DateModified", winrt::box_value(winrt::clock::from_file_time(DateCreatedFileTime)));
File.Properties().SavePropertiesAsync(std::move(PropertyMap)).get();
Now the problem is that runtime behavior is this: E_INVALIDARG
0x80070057 : ‘Cannot write to the specified property (System.DateModified). The property is read-only.’.