From a Windows app, how can I check whether there is an app installed that implements a particular URI scheme?, part 2

Raymond Chen

Last time, we looked at detecting packaged apps which support a particular URI scheme. Unpackaged apps do not have AppIds (in the Windows Store sense), so some of the operations don’t work.

Function Packaged apps Unpackaged apps
Launcher.Find­Uri­Scheme­Handlers­Async Yes No
Launcher.Query­Uri­Support­Async(uri) Yes Yes
Launcher.Query­Uri­Support­Async(uri, pfn) Yes N/A
Launcher.Launch­Uri­Async Yes Yes

If you think about it, the reasons for the above entries are obvious:

  • Launcher.Find­Uri­Scheme­Handlers­Async returns a collection of AppInfo objects, and AppInfo objects describe packaged apps. So it has no way to report an unpackaged app.
  • Launcher.Query­Uri­Support­Async(uri) just tells you whether the URI can be launched or not. If it can be launched by an unpackaged app, then it will report Available, just like the case where it can be launched by a packaged app. It doesn’t tell you which app will launch it, so it doesn’t run into the problem of trying to describe something that it has no way to describe.
  • Launcher.Query­Uri­Support­Async(uri, pfn) takes a package family name, and unpackaged apps don’t have a package family name, so it’s not even possible to specify the unpackaged app you are querying for.
  • Launcher.Launch­Uri­Async tries to launch the URI and tells you whether it succeeded. It doesn’t tell you anything about the app that ultimately handled the URI, so unpackaged apps don’t cause any problems.

But what if you want to ask about unpackaged apps, too?

The SH­Assoc­Enum­Handlers­For­Protocol­By­Application function gives you the apps (both packaged and unpackaged) which can launch a particular URI scheme.

Today’s smart pointer library will be (rolls dice)¹ WRL.

Microsoft::WRL::ComPtr<IEnumAssocHandlers> e;
auto hr = SHAssocEnumHandlersForProtocolByApplication(L"http", IID_PPV_ARGS(&e));
if (SUCCEEDED(hr)) {
    Microsoft::WRL::ComPtr<IAssocHandler> handler;
    while (e->Next(1, &handler, nullptr) == S_OK) {
        PWSTR name;
        if (SUCCEEDED(handler->GetUIName(&name))) {
            printf("UI Name: %ls\n", name);
            CoTaskMemFree(name);
        }
    }
}

You can even ask pass the URI to a specific handler by calling the Invoke method:

HRESULT InvokeHandlerOnURI(IAssocHandler* handler, PCWSTR uri)
{
    Microsoft::WRL::ComPtr<IShellItem> item;
    RETURN_IF_FAILED(SHCreateItemFromParsingName(
        L"http://msn.com/", nullptr, IID_PPV_ARGS(&item)));
    Microsoft::WRL::ComPtr<IDataObject> dto;
    RETURN_IF_FAILED(item->BindToHandler(nullptr,
        BHID_DataObject, IID_PPV_ARGS(&dto)));
    RETURN_IF_FAILED(handler->Invoke(dto.Get()));
    return S_OK;
}

¹ Dirty secret: The dice are loaded.

5 comments

Discussion is closed. Login to edit/delete existing comments.

  • Paulo Pinto 0

    It is interesting that WRL keeps being used by plenty of developers, despite of its deprecation, most likely because C++/WinRT hardly adds any value to the Visual Studio developer experience.

    • Me Gusta 0

      Is WRL deprecated? I know that it was superseded by C++/WinRT, but I don’t remember seeing any mention of it being deprecated.
      Isn’t it also only superseded on the Windows Runtime side of things? Since the interfaces used in this post are classic COM interfaces then WRL is just as good an option.

      • Paulo Pinto 0

        It is, check its documentation, it is quite hard to miss the big pink square,

        https://learn.microsoft.com/en-us/cpp/cppcx/wrl/windows-runtime-cpp-template-library-wrl?view=msvc-170

        While it says superseded, and not deprecated, it is really the same, as it is clear where development budget is being spent.

        However since, having the one true library was never a WinDev in regards to COM, not only you can use MFC, ATL, WRL, C++/CX, C++/WinRT, there is WIL as well,

        https://github.com/microsoft/wil

        With exception of MFC, they all share one common thread, bad developer experience for COM authoring and lack of Visual Studio tooling for IDL files, maybe that is where budget could be spent, instead of replacing C++ frameworks for COM every couple of years.

        • Me Gusta 3

          Then no, you are incorrect on this matter.
          Supersede and deprecate have completely different meanings. That big pink box states that it has been superseded and then provides reasons for why C++/WinRT should be used. It does not provide any kind of disapproval of WRL though. There is a big difference between “you shouldn’t use this” and “you would find using that to be a much better experience”. When Microsoft deprecate things, they don’t just have it in the documentation in one little pink box that states Note, they make it really obvious. For example:

          NOT_BUILD_WINDOWS_DEPRECATE BOOL GetVersionExA(
            [in, out] LPOSVERSIONINFOA lpVersionInformation
          );

          Advocating for better development tooling is one thing. Using this post in the way you did was completely off of the mark.

    • George Tokmaji 0

      It’s classic COM, WRL is just as good an option as C++/WinRT is (or _com_ptr, if you’re willing to deal with some of its gotchas). C++/WinRT gives you more helpers to make life easier – I consider

      winrt::capture(&SHCreateItemFromParsingName, L"http://msn.com/", nullptr);

      + exception handling to be more readable than

      Microsoft::WRL::ComPtr item;
      THROW_EXCEPTION_IF_FAILED(SHCreateItemFromParsingName(L"http://msn.com/", nullptr, IID_PPV_ARGS(&item)));

      – but that doesn’t make it a game changer.

      On the other hand, try consuming Windows Runtime classes with WRL… (And if you already might be using the Windows Runtime, why not use C++/WinRT in the frist place instead of WRL and C++/WinRT?)

Feedback usabilla icon