Extracting pages from a PDF document and saving them as separate image files, C++/CX edition with co_await


Last time, we converted the C++/CX version of the PDF Document sample program so that it saved the pages to disk as image files, using PPL tasks. Today we'll do it again, but using the proposed co_await syntax.

#include <pplawait.h>

void Scenario1_Render::ViewPage()
{
    ViewPageAsync();
}

task<void> Scenario1_Render::ViewPageAsync()
{
    rootPage->NotifyUser("", NotifyType::StatusMessage);

    // If the text is not a valid number, then wcstoul returns 0,
    // which is an invalid page number, so we don't need to
    // special-case that possibility.
    unsigned long pageNumber = wcstoul(PageNumberBox->Text->Data(),
                                       nullptr, 10);

    if ((pageNumber < 1) || (pageNumber > pdfDocument->PageCount))
    {
        rootPage->NotifyUser("Invalid page number.",
                             NotifyType::ErrorMessage);
        return;
    }

    Output->Source = nullptr;
    ProgressControl->Visibility =
        Windows::UI::Xaml::Visibility::Visible;

    // Convert from 1-based page number to 0-based page index.
    unsigned long pageIndex = pageNumber - 1;

    auto picker = ref new FileSavePicker();
    picker->FileTypeChoices->Insert("PNG image",
        ref new Platform::Collections::Vector<String^>({ ".png" }));
    auto outfile = co_await picker->PickSaveFileAsync();
    if (outfile)
    {
        auto page = pdfDocument->GetPage(pageIndex);

        auto transaction =
            co_await outfile->OpenTransactedWriteAsync();
        auto options = ref new PdfPageRenderOptions();
        options->DestinationHeight = (unsigned)(page->Size.Height * 2);
        options->DestinationWidth = (unsigned)(page->Size.Width * 2);
        co_await page->RenderToStreamAsync(transaction->Stream, options);
        delete transaction;
        delete page;
    }
    ProgressControl->Visibility =
        Windows::UI::Xaml::Visibility::Collapsed;
}

// Plus appropriate changes to the Scenario1_Render.xaml.h
// header file to match the new function, left to the reader.

We are using the proposed co_await language keyword which turns the function into a state machine, the same way that the await keyword does in C#. But instead of using C# Task<T> objects, the co_await keyword converts the function into whatever you specified in the traits type. In our case, we use the pplawait.h header file, which describes how to convert an IAsyncAction or IAsyncOperation<T> into a Concurrency::task<T>.

But wait! (await?) I'm not done yet. We'll pick up the story next time.

Bonus chatter: Here's the C++/WinRT version. This is a theoretical exercise because the XAML compiler doesn't support C++/WinRT yet, but I'll put it here for completeness.

task<void> Scenario1_Render::ViewPageAsync()
{
    rootPage->NotifyUser("", NotifyType::StatusMessage);

    // If the text is not a valid number, then wcstoul returns 0,
    // which is an invalid page number, so we don't need to
    // special-case that possibility.
    unsigned long pageNumber = wcstoul(PageNumberBox->Text->Data(),
                                       nullptr, 10);

    if ((pageNumber < 1) || (pageNumber > pdfDocument.PageCount()))
    {
        rootPage->NotifyUser("Invalid page number.",
                             NotifyType::ErrorMessage);
        return;
    }

    Output->Source = nullptr;
    ProgressControl->Visibility =
        Windows::UI::Xaml::Visibility::Visible;

    // Convert from 1-based page number to 0-based page index.
    unsigned long pageIndex = pageNumber - 1;

    FileSavePicker picker;
    picker.FileTypeChoices().Insert("PNG image", { ".png" });
    auto outfile = co_await picker.PickSaveFileAsync();
    if (outfile)
    {
        auto page = pdfDocument.GetPage(pageIndex);

        auto transaction =
            co_await outfile.OpenTransactedWriteAsync();
        PdfPageRenderOptions options;
        options.DestinationHeight((unsigned)(page->Size.Height * 2));
        options.DestinationWidth((unsigned)(page->Size.Width * 2));
        co_await page.RenderToStreamAsync(transaction.Stream(), options);
        transaction.Close();
        page.Close();
    }
    ProgressControl->Visibility =
        Windows::UI::Xaml::Visibility::Collapsed;
}

The changes are as follows:

  • Method calls use dot notation rather than arrow notation.
  • Fetching properties is done by calling a method (via dot) with the name of the property, and no parameters.
  • Setting properties is done by calling a method (via dot) and passing the desired new value as the parameter.
  • Closing an object is done by calling the Close() method, as opposed to C++/CX which overloaded the delete operator.
  • Constructing an object is done by merely declaring it.
  • Wrapping a pointer is done by constructing an object around it. (Though it is common to use assignment-style construction.)
  • C++/WinRT lets you pass an initializer list when an aggregate is expected.
  • We didn't use it here (thanks to the magic of co_await) but the parameter passed to task continuations is an adapter if the underlying class does not have a default constructor.
Comments (4)
  1. Darran Rowe says:

    Everybody seems to be converging on C#. Yeah, because C++/CX is just terrible and C++/WinRT is looking like a serious case of too little too late.

  2. Erkin Alp Güney says:

    Can you translate for we ISO C++ programmers?

    1. Darran Rowe says:

      He already has, look at the C++/WinRT version.
      They call this a standard C++ projection, and it is a header only library which wraps the runtime. But as mentioned in the post, the XAML compiler doesn't support it yet, so if you wanted to use the C++/WinRT library to target the UWP, then you would have to use the XAML classes manually.

  3. Pietro Gagliardi (andlabs) says:

    Here's something I do wonder, if it isn't too late: judging from MSDN, the file picker wraps around IFileDialog somehow, and judging from Googling C#'s async/await, I'd have to return to the outermost message pump somehow. So is there a way to open a IFileDialog in a non-code-modal way?

Comments are closed.

Skip to main content