C++/WinRT version of SetFileTime

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.’.

That is, you don’t change/save this property from UWP.

Leave a Reply