{"id":110312,"date":"2024-09-25T07:00:00","date_gmt":"2024-09-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110312"},"modified":"2024-09-25T10:19:29","modified_gmt":"2024-09-25T17:19:29","slug":"20240925-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240925-00\/?p=110312","title":{"rendered":"Another example of the Windows Runtime interop pattern: Using the UserConsentVerifier from a Win32 program"},"content":{"rendered":"<p>The <code>User\u00adConsent\u00adVerifier<\/code> Windows Runtime class lets you display one of those authentication prompts to confirm the identity of the user. <a title=\"The UserConsentVerifier confirms that the user is there, but it doesn't protect any data\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240924-00\/?p=110308\"> We discussed last time that this is a consent verifier, not a data protector<\/a>.<\/p>\n<p>The class was originally designed for UWP apps, but you can also use it from Win32 apps by using the <code>IUser\u00adConsent\u00adVerifier\u00adInterop<\/code> interface. This follows <a title=\"How do I show the sharing pane from a Win32 desktop application?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170315-00\/?p=95735\"> the normal pattern for interop interfaces that correspond to static methods<\/a>:\u00b9<\/p>\n<ul>\n<li>Obtain the interface from the activation factory.<\/li>\n<li>The methods have the same names as the corresponding names of the static Windows Runtime methods, except that they also say <code>For\u00adWindow<\/code>.<\/li>\n<li>The methods take the same parameters as the static Windows Runtime methods, except that there is an extra <code>HWND<\/code> parameter, and the output is returned in the form of a <code>REFIID<\/code>\/<code>void**<\/code>.<\/li>\n<\/ul>\n<p>Let&#8217;s add a consent verifier to our <a title=\"The scratch program\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20030723-00\/?p=43073\"> scratch program<\/a>.<\/p>\n<pre>#include &lt;winrt\/Windows.Foundation.h&gt;              \/\/ for IAsyncOperation\r\n#include &lt;winrt\/Windows.Security.Credentials.UI.h&gt; \/\/ for UserConsentVerifier\r\n#include &lt;wrl\/wrappers\/corewrappers.h&gt;             \/\/ for HStringReference\r\n#include &lt;UserConsentVerifierInterop.h&gt;            \/\/ for IUserConsentVerifierInterop\r\n\r\nnamespace winrt\r\n{\r\n    using namespace winrt::Windows::Foundation;\r\n    using namespace winrt::Windows::Security::Credentials::UI;\r\n}\r\n\r\nnamespace WRL = Microsoft::WRL;\r\n\r\nwinrt::fire_and_forget RequestConsent(HWND hwnd)\r\n{\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">auto consentResult = co_await wil::capture_interop&lt; <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    winrt::IAsyncOperation&lt;                         <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        winrt::UserConsentVerificationResult&gt;,      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    winrt::UserConsentVerifier&gt;(                    <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    &amp;::IUserConsentVerifierInterop::                <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        RequestVerificationForWindowAsync,          <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        hwnd,                                       <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        WRL::Wrappers::HStringReference(            <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">            L\"Just checking that it's you.\").Get());<\/span>\r\n\r\n    PCWSTR message;\r\n    switch (consentResult)\r\n    {\r\n    case winrt::UserConsentVerificationResult::Verified:\r\n        message = L\"User verified.\";\r\n        break;\r\n    case winrt::UserConsentVerificationResult::DeviceBusy:\r\n        message = L\"Authentication device is busy.\";\r\n        break;\r\n    case winrt::UserConsentVerificationResult::DeviceNotPresent:\r\n        message = L\"No authentication device found.\";\r\n        break;\r\n    case winrt::UserConsentVerificationResult::DisabledByPolicy:\r\n        message = L\"Authentication device verification is disabled by policy.\";\r\n        break;\r\n    case winrt::UserConsentVerificationResult::NotConfiguredForUser:\r\n        message = L\"Please go to Account Settings to set up PIN \"\r\n                  L\"or other advanced authentication.\";\r\n        break;\r\n    case winrt::UserConsentVerificationResult::RetriesExhausted:\r\n        message = L\"There have been too many failed attempts. \"\r\n                  L\"Device authentication canceled.\";\r\n        break;\r\n    case winrt::UserConsentVerificationResult::Canceled:\r\n        message = L\"Device authentication canceled.\";\r\n        break;\r\n    default:\r\n        message = L\"Authentication device is currently unavailable.\";\r\n        break;\r\n    }\r\n\r\n    SetWindowText(hwnd, message);\r\n}\r\n\r\nvoid OnChar(HWND hwnd, TCHAR ch, int cRepeat)\r\n{\r\n    if (ch == VK_SPACE)\r\n    {\r\n        RequestConsent(hwnd);\r\n    }\r\n}\r\n\r\n        HANDLE_MSG(hwnd, WM_CHAR, OnChar);\r\n<\/pre>\n<p>When you hit the space bar, we go into action. We take advantage of <a title=\"Using Windows Runtime interop methods from C++\/WinRT: Some helper functions\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210806-00\/?p=105529\"> the <code>capture_<wbr \/>interop<\/code> helper function we added to the Windows Implementation Library some time ago<\/a> as part of our <a title=\"Using Windows Runtime interop methods from C++\/WinRT: Some helper functions\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210806-00\/?p=105529\"> initial exploration of C++\/WinRT interop pattern<\/a>.<\/p>\n<p>Taking that helper apart, the first thing that happens is that we call <code>get_<wbr \/>activation_<wbr \/>factory<\/code>, asking for the <code>IUser\u00adConsent\u00adVerifier\u00adInterop<\/code> interface of the <code>User\u00adConsent\u00adVerifier<\/code> factory.<\/p>\n<pre>auto interop = winrt::get_activation_factory&lt;\r\n        winrt::UserConsentVerifier&gt;,\r\n        ::IUserConsentVerifierInterop&gt;();\r\n<\/pre>\n<p>At the ABI, this would be<\/p>\n<pre>::IUserConsentVerifierInterop* interop;\r\n\r\nhr = RoGetActivationFactory(\r\n  HStringReference(\r\n    RuntimeClass_Windows_Security_Credentials_UI_UserConsentVerifier)\r\n  .Get(),\r\n  IID_PPV_ARGS(&amp;interop));\r\n\r\nTHROW_IF_FAILED(hr);\r\n<\/pre>\n<p>Next, we call <code>IUser\u00adConsent\u00adVerifier\u00adInterop::<wbr \/>Request\u00adVerification\u00adFor\u00adWindow\u00adAsync<\/code>, which is the <code>HWND<\/code> equivalent of <code>User\u00adConsent\u00adVerifier::<wbr \/>Request\u00adVerification\u00adAsync<\/code>. We ask for the result in the form of a <code>winrt::<wbr \/>IAsyncOperation&lt;<wbr \/>User\u00adConsent\u00adVerification\u00adResult&gt;<\/code> so we can <code>co_await<\/code> it. The <code>capture_<wbr \/>interop<\/code> function does it by calling <code>winrt::<wbr \/>capture<\/code>, which is a C++\/WinRT helper function that calls a method and returns the produced object.<\/p>\n<pre>winrt::capture&lt;\r\n    winrt::IAsyncOperation&lt;\r\n        winrt::UserConsentVerificationResult&gt;&gt;(\r\n    interop,\r\n    &amp;::IUserConsentVerifierInterop::\r\n        RequestVerificationForWindowAsync,\r\n    hwnd,\r\n    WRL::Wrappers::HStringReference(\r\n        L\"Just checking that it's you.\").Get());\r\n<\/pre>\n<p>This is shorthand for<\/p>\n<pre>winrt::IAsyncOperation&lt;winrt::UserConsentVerificationResult&gt; result;\r\nwinrt::check_hresult(\r\n    interop-&gt;RequestVerificationForWindowAsync(\r\n        hwnd,\r\n        WRL::Wrappers::HStringReference(\r\n            L\"Just checking that it's you.\").Get(),\r\n        winrt::guid_of&lt;decltype(result)&gt;(),\r\n        winrt::put_abi(result())));\r\n<\/pre>\n<p>The ABI equivalent is<\/p>\n<pre>ABI::Windows::Foundation::IAsyncOperation&lt;\r\n    ABI::Windows::Security::Credentials::UI::UserConsentVerificationResult&gt;*\r\n        result;\r\n\r\nhr = interop-&gt;RequestVerificationForWindowAsync(\r\n    hwnd,\r\n    WRL::Wrappers::HStringReference(\r\n        L\"Just checking that it's you.\").Get(),\r\n    IID_PPV_ARGS(&amp;result));\r\n<\/pre>\n<p>Once we get our asynchronous operation, we await it and show the result in our title bar.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Following the standard pattern.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-110312","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Following the standard pattern.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110312","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=110312"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110312\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=110312"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110312"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110312"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}