Using Surface Dial in my apps.
It’s easy in UWP environment. But it’s not easy in Desktop environment.
WRL technology is required to use Surface Dial in desktop applications (e.g : Games) developed in C/C++.
But WRL became a legendary technology. MS dislike old technology like WRL.
I spent a all day for getting ‘ABI::Windows::Storage::Streams::IRandomAccessStreamReference’ object.
Finally, It was possible to check the RuntimeClass name using C++/CX code.
クソMS。
Anyway I succeeded.
Ok.I will explain how to do it.
First, Let’s see the sample code in github.
https://github.com/Microsoft/Windows-classic-samples/tree/master/Samples/RadialController
The code is not complicated. The important thing here is how to change the menu item icon.
Looking at the code, …
HRESULT DeviceListener::AddMenuItemFromKnownIcon(_In_ HSTRING itemName, _In_ RadialControllerMenuKnownIcon icon) { // Get menu items ComPtr<Collections::IVector<RadialControllerMenuItem*>> menuItems; RETURN_IF_FAILED(_menu->get_Items(&menuItems)); // Create item ComPtr<IRadialControllerMenuItem> menuItem; RETURN_IF_FAILED(_menuItemStatics->CreateFromKnownIcon(itemName, icon, &menuItem)); // Set Callback RETURN_IF_FAILED(menuItem->add_Invoked( Callback<ITypedEventHandler<RadialControllerMenuItem*, IInspectable*>>(this, &DeviceListener::OnItemInvoked).Get(), &_menuItem2Token)); // Add item to menu RETURN_IF_FAILED(menuItems->Append(menuItem.Get())); // Log new item wchar_t message[2000]; swprintf_s(message, 2000, L"Added %s to menu\n", WindowsGetStringRawBuffer(itemName, nullptr)); WriteDebugStringW(DEBUG_OUTPUT_TYPE_DEBUG_CONSOLE,message); return S_OK; }
If you want to change the icon, add another such function.
HRESULT DeviceListener::AddMenuItemFromUnknownIcon(_In_ HSTRING itemName, _In_ ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStreamReference> icon) { // Get menu items ComPtr<Collections::IVector<RadialControllerMenuItem*>> menuItems; RETURN_IF_FAILED(_menu->get_Items(&menuItems)); // Create item ComPtr<IRadialControllerMenuItem> menuItem; HRESULT hr = _menuItemStatics->CreateFromIcon(itemName, icon.Get(), &menuItem); RETURN_IF_FAILED(hr); // Set Callback RETURN_IF_FAILED(menuItem->add_Invoked( Callback<ITypedEventHandler<RadialControllerMenuItem*, IInspectable*>>(this, &DeviceListener::OnItemInvoked).Get(), &_menuItem2Token)); // Add item to menu RETURN_IF_FAILED(menuItems->Append(menuItem.Get())); // Log new item wchar_t message[2000]; swprintf_s(message, 2000, L"Added %s to menu\n", WindowsGetStringRawBuffer(itemName, nullptr)); WriteDebugStringW(DEBUG_OUTPUT_TYPE_DEBUG_CONSOLE, message); return S_OK; }
The question is how to get ComPtr icon.
In C++/CX, you can write this code.
Windows::ApplicationModel::Package^ package = Windows::ApplicationModel::Package::Current; Windows::Storage::StorageFolder^ folder = package->InstalledLocation; String^ path = folder->Path; create_task(folder->GetFileAsync(L"miku_icon.png")).then([this](Windows::Storage::StorageFile^ file) { Windows::Storage::Streams::RandomAccessStreamReference^ streamRef = Windows::Storage::Streams::RandomAccessStreamReference::CreateFromFile(file); });
Then, in WRL, how should it be done?
Object of ABI::Windows::Storage::StorageFile and Object of ABI::Windows::Storage::Streams::RandomAccessStreamReference are required.
But it is impossible to call class static method directly in WRL.
You need to get the object with the suffix “Statics” and call the method through it.
Object of ABI::Windows::Storage::IStorageFileStatics required for calling static method of ABI::Windows::Storage::StorageFile.
And Object of ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics required for calling static method of ABI::Windows::Storage::Streams::RandomAccessStreamReference.
Get a object of ABI::Windows::Storage::IStorageFileStatics as follows.
HRESULT hr; ComPtr<IActivationFactory> pFactory; hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_Storage_StorageFile).Get(), &pFactory); if (S_OK != hr) __debugbreak(); ComPtr<ABI::Windows::Storage::IStorageFileStatics> pStorageFileStatics; hr = pFactory.As(&pStorageFileStatics); if (S_OK != hr) __debugbreak();
Get a object of ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics as follows.
ComPtr<IActivationFactory> pStreamRefFactory; const WCHAR* runtime_class_name = L"Windows.Storage.Streams.RandomAccessStreamReference"; hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(runtime_class_name).Get(), &pStreamRefFactory); if (S_OK != hr) __debugbreak(); ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics> pStreamRefStatic; hr = pStreamRefFactory.As(&pStreamRefStatic); if (S_OK != hr) __debugbreak();
I can not find Class Runtime Name of ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics in header files.
Finally, I found out the Class Runtime Name using the following C++/CX code.
auto streamRef = Windows::Storage::Streams::RandomAccessStreamReference::CreateFromFile(nullptr); auto uri = ref new Windows::Foundation::Uri(ref new String(L"ms-appx:///") + L"Images/Icon_Etc.png"); auto streamRef = Windows::Storage::Streams::RandomAccessStreamReference::CreateFromUri(uri); IInspectable* pObj = reinterpret_cast<IInspectable*>(streamRef); HSTRING str; HRESULT hr = pObj->GetRuntimeClassName(&str); // str -> RuntimeClass Name!!!
In this way, it is possible to find Class Runtime Name in WRL using C ++ / CX code.
Below is the complete source code.
void DeviceListener::AddExtMenu() { // Icon's filename for adding const WCHAR* ICON_FILE_NAME = L"\\icon_miku.png"; const WCHAR* MENU_NAME = L"Miku"; HRESULT hr; ComPtr<IActivationFactory> pFactory; hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_Storage_StorageFile).Get(), &pFactory); if (S_OK != hr) __debugbreak(); ComPtr<ABI::Windows::Storage::IStorageFileStatics> pStorageFileStatics; hr = pFactory.As(&pStorageFileStatics); if (S_OK != hr) __debugbreak(); // Open the file. WCHAR wchFullPath[_MAX_PATH] = {}; GetCurrentDirectory(_MAX_PATH, wchFullPath); wcscat_s(wchFullPath, ICON_FILE_NAME); HString path; path.Set(wchFullPath); ComPtr<ABI::Windows::Foundation::__FIAsyncOperation_1_Windows__CStorage__CStorageFile_t> async; pStorageFileStatics->GetFileFromPathAsync(path.Get(), &async); typedef IAsyncOperationCompletedHandler<ABI::Windows::Storage::StorageFile*> HandlerType; auto handler = Microsoft::WRL::Callback<HandlerType>([this, MENU_NAME](IAsyncOperation<ABI::Windows::Storage::StorageFile*>* async, AsyncStatus status) { HRESULT hr = S_FALSE; ComPtr<ABI::Windows::Storage::IStorageFile> file; switch (status) { case Started: break; case Completed: case Error: hr = async->GetResults(&file); break; case Canceled: break; } if (file.Get()) { ComPtr<IActivationFactory> pStreamRefFactory; const WCHAR* runtime_class_name = L"Windows.Storage.Streams.RandomAccessStreamReference"; hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(runtime_class_name).Get(), &pStreamRefFactory); if (S_OK != hr) __debugbreak(); ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics> pStreamRefStatic; hr = pStreamRefFactory.As(&pStreamRefStatic); if (S_OK != hr) __debugbreak(); ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStreamReference> streamRef; hr = pStreamRefStatic->CreateFromFile(file.Get(), &streamRef); AddMenuItemFromUnknownIcon(HStringReference(MENU_NAME).Get(), streamRef); } return hr; }); async->put_Completed(handler.Get()); }
It was very hard to find document of WRL.
I hope that this posting help developers using Surface Dial with desktop applications using C ++.
May the force with C++ Developers
There is a desktop C++ sample here: https://github.com/Microsoft/Windows-classic-samples/tree/master/Samples/RadialController/cpp
좋아요좋아요
Of course I have looked the source code. That code has no code to change the icon. This post is a way to change the icon.
By the way, are you Raymond Chen really? I like your book-Old new thing.
좋아요좋아요
Thank you so much – I was so frustrated about the exact same thing.
Thanks!
-espen
좋아요좋아요
I am happy to help you.
좋아요좋아요
정말 도움되는 정보입니다! 감사합니다! 포스팅해주셔서~
좋아요좋아요