{"id":112005,"date":"2026-01-22T07:00:00","date_gmt":"2026-01-22T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112005"},"modified":"2026-01-22T08:50:56","modified_gmt":"2026-01-22T16:50:56","slug":"20260122-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260122-00\/?p=112005","title":{"rendered":"A simple helper function for attaching a progress handler to a Windows Runtime IAsync&shy;Action&shy;With&shy;Progress or IAsync&shy;Operation&shy;With&shy;Progress"},"content":{"rendered":"<p>The Windows Runtime has interfaces <code>IAsync\u00adAction<\/code> and <code>IAsync\u00adOperation&lt;T&gt;<\/code> which represent asynchronous activity: The function starts the work and returns immediately, and then it calls you back when the work completes. Most language projections allow you to treat these as coroutines, so you can <code>await<\/code> or <code>co_await<\/code> them in order to suspend execution until the completion occurs.<\/p>\n<p>There are also progress versions of these interfaces: <code>IAsync\u00adAction\u00adWith\u00adProgress&lt;P&gt;<\/code> and <code>IAsync\u00adOperation\u00adWith\u00adProgress&lt;T, P&gt;<\/code>. In addition to having a completion callback, you can also register a callback which will be called to inform you of the progress of the operation.<\/p>\n<p>The usual usage pattern is<\/p>\n<pre>\/\/ C++\/WinRT\r\nauto operation = DoSomethingAsync();\r\noperation.Progress([](auto&amp;&amp; op, auto&amp;&amp; p) { \u27e6 ... \u27e7 });\r\nauto result = co_await operation;\r\n\r\n\/\/ C++\/CX\r\nIAsyncOperation&lt;R^, P&gt;^ operation = DoSomethingAsync();\r\noperation-&gt;Progress = ref new AsyncOperationProgressHandler&lt;R^, P&gt;(\r\n    [](auto op, P p) { \u27e6 ... \u27e7 });\r\nR^ result = co_await operation;\r\n\r\n\/\/ C#\r\nvar operation = DoSomethingAsync();\r\noperation.Progress += (op, p) =&gt; { \u27e6 ... \u27e7 };\r\nvar result = await operation;\r\n\r\n\/\/ JavaScript\r\nvar result = await DoSomethingAsync()\r\n                .then(null, null, p =&gt; { \u27e6 ... \u27e7 });\r\n<\/pre>\n<p>The JavaScript version is not too bad: You can attach the progress to the Promise and then await the whole thing. However, the other languages are fairly cumbersome because you have to declare an extra variable to hold the operation, so that you can attach the progress handler to it, and then await it. And in the C++ cases, having an explicitly named variable means that it is no longer a temporary, so instead of destructing at the end of the statement, it destructs when the variable destructs, which could be much later.<\/p>\n<p>Here&#8217;s my attempt to bring the ergonomics of JavaScript to C++ and C#.<\/p>\n<pre>\/\/ C++\/WinRT\r\ntemplate&lt;typename Async, typename Handler&gt;\r\nAsync const&amp; with_progress(Async&amp;&amp; async, Handler&amp;&amp; handler)\r\n{\r\n    async.Progress(std::forward&lt;Handler&gt;(handler));\r\n    return std::forward&lt;Async&gt;(async);\r\n}\r\n\r\n\/\/ C++\/CX\r\ntemplate&lt;typename Async, typename Handler&gt;\r\nAsync with_progress(Async async, Handler handler)\r\n{\r\n    async-&gt;Progress = handler;\r\n    return async;\r\n}\r\n\r\n\/\/ C#\r\nstatic Async WithProgress&lt;Async, Handler&gt;(this Async async, Handler handler)\r\n{\r\n    async.Progress += handler;\r\n    return async;\r\n}\r\n<\/pre>\n<p>These functions don&#8217;t do much, but they save you the trouble of having to name your operations.<\/p>\n<pre>\/\/ C++\/WinRT\r\nauto result = co_await with_progress(DoSomethingAsync(),\r\n            [](auto&amp;&amp; op, auto&amp;&amp; p) { \u27e6 ... \u27e7 });\r\n\r\n\/\/ C++\/CX\r\nR^ result = co_await with_progress(DoSomethingAsync(),\r\n            ref new AsyncOperationProgressHandler&lt;R^, P&gt;(\r\n            [](auto op, P p) { \u27e6 ... \u27e7 });\r\n\r\n\/\/ C#\r\nvar result = await DoSomethingAsync()\r\n                .WithProgress((op, p) =&gt; { \u27e6 ... \u27e7 });\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>It doesn&#8217;t do much, but it saves typing.<\/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-112005","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It doesn&#8217;t do much, but it saves typing.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112005","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=112005"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112005\/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=112005"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112005"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112005"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}