{"id":96495,"date":"2017-06-30T07:00:00","date_gmt":"2017-06-30T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96495"},"modified":"2019-03-13T01:13:39","modified_gmt":"2019-03-13T08:13:39","slug":"20170630-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170630-00\/?p=96495","title":{"rendered":"Extracting pages from a PDF document and saving them as separate image files, C++\/CX edition with co_await"},"content":{"rendered":"<p><!-- backref: Extracting pages from a PDF document and saving them as separate image files, C++\/CX edition with explicit tasks -->Last time<\/a>, we converted the C++\/CX version of the <a HREF=\"https:\/\/github.com\/Microsoft\/Windows-universal-samples\/tree\/v1.0.11\/Samples\/PdfDocument\">PDF Document<\/a> sample program so that it saved the pages to disk as image files, using PPL tasks. Today we&#8217;ll do it again, but using the proposed <code>co_await<\/code> syntax. <\/p>\n<pre>\n<font COLOR=\"blue\">#include &lt;pplawait.h&gt;<\/font>\n\nvoid Scenario1_Render::ViewPage()\n{\n    ViewPageAsync();\n}\n\ntask&lt;void&gt; Scenario1_Render::ViewPageAsync()\n{\n    rootPage-&gt;NotifyUser(\"\", NotifyType::StatusMessage);\n\n    \/\/ If the text is not a valid number, then wcstoul returns 0,\n    \/\/ which is an invalid page number, so we don't need to\n    \/\/ special-case that possibility.\n    unsigned long pageNumber = wcstoul(PageNumberBox-&gt;Text-&gt;Data(),\n                                       nullptr, 10);\n\n    if ((pageNumber &lt; 1) || (pageNumber &gt; pdfDocument-&gt;PageCount))\n    {\n        rootPage-&gt;NotifyUser(\"Invalid page number.\",\n                             NotifyType::ErrorMessage);\n        return;\n    }\n\n    Output-&gt;Source = nullptr;\n    ProgressControl-&gt;Visibility =\n        Windows::UI::Xaml::Visibility::Visible;\n\n    \/\/ Convert from 1-based page number to 0-based page index.\n    unsigned long pageIndex = pageNumber - 1;\n\n    auto picker = ref new FileSavePicker();\n    picker-&gt;FileTypeChoices-&gt;Insert(\"PNG image\",\n        ref new Platform::Collections::Vector&lt;String^&gt;({ \".png\" }));\n    <font COLOR=\"blue\">auto outfile = co_await picker-&gt;PickSaveFileAsync();<\/font>\n    if (outfile)\n    {\n        auto page = pdfDocument-&gt;GetPage(pageIndex);\n\n        <font COLOR=\"blue\">auto transaction =\n            co_await outfile-&gt;OpenTransactedWriteAsync();<\/font>\n        auto options = ref new PdfPageRenderOptions();\n        options-&gt;DestinationHeight = (unsigned)(page-&gt;Size.Height * 2);\n        options-&gt;DestinationWidth = (unsigned)(page-&gt;Size.Width * 2);\n        <font COLOR=\"blue\">co_await page-&gt;RenderToStreamAsync(transaction-&gt;Stream, options);<\/font>\n        delete transaction;\n        delete page;\n    }\n    ProgressControl-&gt;Visibility =\n        Windows::UI::Xaml::Visibility::Collapsed;\n}\n\n\/\/ Plus appropriate changes to the Scenario1_Render.xaml.h\n\/\/ header file to match the new function, left to the reader.\n<\/pre>\n<p>We are using the proposed <code>co_await<\/code> language keyword which turns the function into a state machine, the same way that the <code>await<\/code> keyword does in C#. But instead of using C# <code>Task&lt;T&gt;<\/code> objects, the <code>co_await<\/code> keyword converts the function into whatever you specified in the traits type. In our case, we use the <code>pplawait.h<\/code> header file, which describes how to convert an <code>IAsyncAction<\/code> or <code>IAsyncOperation&lt;T&gt;<\/code> into a <code>Concurrency::<\/code><code>task&lt;T&gt;<\/code>. <\/p>\n<p>But wait! (await?) I&#8217;m not done yet. We&#8217;ll pick up the story next time. <\/p>\n<p><b>Bonus chatter<\/b>: Here&#8217;s the C++\/WinRT version. This is a theoretical exercise because the XAML compiler doesn&#8217;t support C++\/WinRT yet, but I&#8217;ll put it here for completeness. <\/p>\n<pre>\ntask&lt;void&gt; Scenario1_Render::ViewPageAsync()\n{\n    rootPage-&gt;NotifyUser(\"\", NotifyType::StatusMessage);\n\n    \/\/ If the text is not a valid number, then wcstoul returns 0,\n    \/\/ which is an invalid page number, so we don't need to\n    \/\/ special-case that possibility.\n    unsigned long pageNumber = wcstoul(PageNumberBox-&gt;Text-&gt;Data(),\n                                       nullptr, 10);\n\n    if ((pageNumber &lt; 1) || (pageNumber &gt; pdfDocument.PageCount()))\n    {\n        rootPage-&gt;NotifyUser(\"Invalid page number.\",\n                             NotifyType::ErrorMessage);\n        return;\n    }\n\n    Output-&gt;Source = nullptr;\n    ProgressControl-&gt;Visibility =\n        Windows::UI::Xaml::Visibility::Visible;\n\n    \/\/ Convert from 1-based page number to 0-based page index.\n    unsigned long pageIndex = pageNumber - 1;\n\n    <font COLOR=\"blue\">FileSavePicker picker;\n    picker.FileTypeChoices().Insert(\"PNG image\", { \".png\" })<\/font>;\n    auto outfile = co_await <font COLOR=\"blue\">picker.PickSaveFileAsync<\/font>();\n    if (outfile)\n    {\n        auto page = <font COLOR=\"blue\">pdfDocument.GetPage<\/font>(pageIndex);\n\n        auto transaction =\n            co_await <font COLOR=\"blue\">outfile.OpenTransactedWriteAsync<\/font>();\n        <font COLOR=\"blue\">PdfPageRenderOptions options<\/font>;\n        options.DestinationHeight((unsigned)(page-&gt;Size.Height * 2));\n        options.DestinationWidth((unsigned)(page-&gt;Size.Width * 2));\n        co_await <font COLOR=\"blue\">page.RenderToStreamAsync<\/font>(<font COLOR=\"blue\">transaction.Stream()<\/font>, options);\n        <font COLOR=\"blue\">transaction.Close();\n        page.Close();<\/font>\n    }\n    ProgressControl-&gt;Visibility =\n        Windows::UI::Xaml::Visibility::Collapsed;\n}\n<\/pre>\n<p>The changes are as follows: <\/p>\n<ul>\n<li>Method calls use dot notation rather than arrow notation. <\/li>\n<li>Fetching properties is done by calling a method (via dot)     with the name of the property, and no parameters. <\/li>\n<li>Setting properties is done by calling a method (via dot)     and passing the desired new value as the parameter. <\/li>\n<li>Closing an object is done by calling the <code>Close()<\/code>     method, as opposed to C++\/CX which overloaded the <code>delete<\/code>     operator. <\/li>\n<li>Constructing an object is done by merely declaring it.<\/li>\n<li>Wrapping a pointer is done by constructing an object around it.     (Though it is common to use assignment-style construction.)<\/li>\n<li>C++\/WinRT lets you pass an initializer list when an aggregate     is expected. <\/li>\n<li>We didn&#8217;t use it here (thanks to the magic of <code>co_await<\/code>)     but the parameter passed to task continuations     is an <code>adapter<\/code> if the underlying class     does not have a default constructor. <\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Everybody seems to be converging on C#.<\/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-96495","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Everybody seems to be converging on C#.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96495","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=96495"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96495\/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=96495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}