{"id":110934,"date":"2025-03-05T07:00:00","date_gmt":"2025-03-05T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110934"},"modified":"2025-03-05T11:35:29","modified_gmt":"2025-03-05T19:35:29","slug":"20250305-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250305-00\/?p=110934","title":{"rendered":"I tried to subscribe to a C++\/WinRT event, but my callback was never called"},"content":{"rendered":"<p>A customer wanted to subscribe to an event, but they reported that the callback never occurred. They were kind enough to provide a minimal example.<\/p>\n<pre>#ifndef UNICODE\r\n#define UNICODE\r\n#endif \r\n#include &lt;windows.h&gt;\r\n#include &lt;winrt\/Windows.UI.ViewManagement.h&gt;\r\n#include &lt;iostream&gt;\r\n\r\nwinrt::Windows::UI::ViewManagement::UISettings uiSettings;\r\n\r\nwinrt::fire_and_forget OnTextScaleFactorChangedAsync(\r\n    [[maybe_unused]] const winrt::UISettings&amp; sender,\r\n    [[maybe_unused]] const winrt::IInspectable&amp; args)\r\n{\r\n    std::cout &lt;&gt; \"New scale factor: \"\r\n              &lt;&lt; uiSettings.TextScaleFactor()\r\n              &lt;&lt; std::endl;\r\n}\r\n\r\nint main()\r\n{\r\n    winrt::init_apartment();\r\n\r\n    uiSettings.TextScaleFactorChanged(\r\n        winrt::auto_revoke,\r\n        OnTextScaleFactorChangedAsync);\r\n    \r\n    Sleep(INFINITE);\r\n\r\n    return 0;\r\n}\r\n<\/pre>\n<p>There are multiple things wrong here.<\/p>\n<p>The first is that the <code>uiSettings<\/code> object is being created as a global object, and global objects are constructed <i>before<\/i> the <code>main<\/code> function begins. This means that we try to create the <code>uiSettings<\/code> before COM is initialized, which is not a great idea.<\/p>\n<p>(But you get away with it because C++\/WinRT has special code that detects that you&#8217;re trying to create Windows Runtime objects without initializing COM, so it secretly initializes COM for you. I do not recommend that you rely on this feature.)<\/p>\n<p>The other problem is the way the event handler is registered:<\/p>\n<pre>    uiSettings.TextScaleFactorChanged(\r\n        <span style=\"border: solid 1px currentcolor;\">winrt::auto_revoke<\/span>,\r\n        OnTextScaleFactorChangedAsync);\r\n<\/pre>\n<p>The <code>auto_revoke<\/code> parameter selects the overload that returns a revoker object (in this case, a <code>Text\u00adScale\u00adFactor\u00adChanged_<wbr \/>revoker<\/code>), which is an RAII object that revokes the event handler upon destruction. Therefore, this code registers an event handler, and since the revoker was never saved anywhere, it destructed immediately, thereby unregistering the handler.<\/p>\n<p>Now, the <code>auto_revoke<\/code> version of event registration is marked as <code>[[nodiscard]]<\/code> which is a recommendation to the compiler that it produce a diagnostic if the value is not saved anywhere.<\/p>\n<p>We asked the customer to turn on warnings, and how about that, they got a warning that the return value of <code>Text\u00adScale\u00adFactor\u00adChanged(<wbr \/>winrt::<wbr \/>auto_revoke, ...)<\/code> was discarded. Once they saved the revoker into a variable, they started receiving the events.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Check how you registered your event handler.<\/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-110934","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Check how you registered your event handler.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110934","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=110934"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110934\/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=110934"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110934"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110934"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}