MFCでもWinRTその1.　メッセージダイアログとAsync/co_wait

#1. 今回やること
・MFCアプリからWinRTのIAsyncAction()とMessageDialogを呼び出すことをやります。
・非同期関数を使用できて、ダイアログだけでもWinRTを利用できれば、なんとモダンな！って感じがしませんか？
・UWPのコントロールを表示させるわけではないので、ダイアログベースで作成します。
・[デスクトップ アプリで Windows ランタイム API を呼び出す](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-enhance)、[デスクトップ アプリでサポートされていない Windows ランタイム API](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-supported-api)に書かれているような内容をやります。


以下のことをやっていきます。
①重複するマクロの削除と、必要なヘッダの読み込み
②プロパティーの変更（C++17への変更と/awaitの追加）
③コードの追加 

#2. プロジェクトの作成
・VS2019を立ち上げて、「新しいプロジェクトの作成」→「MFCアプリ」で次へ
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/2b755812-785b-d1a0-e9de-f2aaa9ac777e.png)

・プロジェクト名は「MFCWinRTDlg」としました。そして作成（「_」や「-」がプロジェクト名にあるとエラーが出るので注意）
・アプリケーションの種類は「ダイアログベース」にして完了。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/1f0cba26-8680-a213-23a5-be475cf3fc95.png)

・確認のため何もせず実行すると、以下のダイアログベースのアプリが立ち上がります。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/0d480d57-2bcd-d8b1-203d-5465832ad47b.png)

<br>

#3. 重複するマクロの削除と、必要なヘッダの読み込み
・ソリューションエクスプローラーから「pch.h」を読み込んで、「GetCurrentTime」と「TRY」を#undef。
・必要なヘッダを読み込みます。必要なヘッダは以下の4種類
　①winrt/base.h：これは必ず必要
　②winrt/Windows.Foundation.h：IAsyncに必要
　③winrt/Windows.UI.Popups.h：ダイアログの表示に必要
　④winrt/Windows.System.Threading.Core.h：スレッドプールも使用するので必要
・というわけで「pch.h」は以下のように変更しました。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/0cbd67f6-c788-c1ca-190a-643e4dc9d2df.png)


```CPP:pch.h（変更後）

#ifndef PCH_H
#define PCH_H

// プリコンパイルするヘッダーをここに追加します
#include "framework.h"

//名前がバッティングするのでundef
#undef GetCurrentTime
#undef TRY

//必要なヘッダの読み込み
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Popups.h>
#include <winrt/Windows.System.Threading.Core.h>

#endif //PCH_H

```

#4. プロパティの変更
・「プロジェクト」→「MFCWinRTDlgのプロパティ」より以下の変更を行います
・「全般」からC++言語標準を「ISO C++17標準」へ変更
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/a53dd602-55c4-36d6-161f-a791df4ae660.png)

・「C/C++」→「コマンドライン」へ/awaitの追加
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/72a7ef15-f5c1-5224-e0f2-42dccd9a2f60.png)


#5. コードの追加
・ここまで来たら、後はコードを追加してメッセージダイアログを表示させるだけです。
・リソースビューでボタンを2個追加します。
・右クリックからまずは「Button1」のイベントハンドラを追加します。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/0a686cb4-9860-c46f-aeaa-7234cc515659.png)

・MFCWinRTDlgDlg.hを開き、「afx_msg void OnBnClickedButton1()」の後に「winrt::Windows::Foundation::IAsyncAction AsyncButton1()」を追加します。winrt/Windows.Foundation.hを読み込むだけで非同期関数は使えるので追加するだけです。
・そして、cppファイルに定義を追加します。
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/d1ff8630-1500-f6d1-b9b7-b4e0e44b8e43.png)

・[IInitializeWithWindow を使用するクラス](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-supported-api#classes-that-use-iinitializewithwindow)を参考に「MFCWinRTDlgDlg.cpp」を以下のように変更しました。
・[IInitializeWithWindow interface](https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-iinitializewithwindow)にも書かれていますが、各種pickerやメッセージダイアログはIInitializeWithWindowで初期化すれば使えます。なので、以下のように、宣言して、初期化して、表示しています。

```CPP:MFCWinRTDlgDlg.cpp（変更後）

//ここまで略
void CMFCWinRTDlgDlg::OnBnClickedButton1()
{
	AsyncButton1();
}

winrt::Windows::Foundation::IAsyncAction CMFCWinRTDlgDlg::AsyncButton1()
{
	auto msgdlg = winrt::Windows::UI::Popups::MessageDialog(L"Hello MFC WinRT");
	msgdlg.as<IInitializeWithWindow>()->Initialize(m_hWnd);
	msgdlg.Content(L"Hello MFC WinRT");
	co_await msgdlg.ShowAsync();
}

```

・ビルドすると以下のようにメッセージダイアログが表示されます。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/f67a5f6d-0d30-71ed-8e7b-d60aba7b4c96.png)
<br>
<br>
<br>
・Button2では5秒後にメッセージダイアログを表示させるようにしてみます。[以前](https://qiita.com/lilac0011/items/54da15cc506b59e893d8)やったようにスレッドプールを使用して。

```CPP:MFCWinRTDlgDlg.cpp（変更後）

//ここまで略
void CMFCWinRTDlgDlg::OnBnClickedButton2()
{
	AsyncButton2();
}

winrt::Windows::Foundation::IAsyncAction CMFCWinRTDlgDlg::AsyncButton2()
{
	co_await winrt::Windows::System::Threading::ThreadPool::RunAsync([&](winrt::Windows::Foundation::IAsyncAction const& workItem)
		{
			Sleep(5000);
		});

	auto msgdlg = winrt::Windows::UI::Popups::MessageDialog(L"Hello MFC WinRT wait 5S");
	msgdlg.as<IInitializeWithWindow>()->Initialize(m_hWnd);
	msgdlg.Content(L"Hello MFC WinRT wait 5S");
	co_await msgdlg.ShowAsync();
}

```

・どうです。かなり簡単にMFCからスレッドプールを使用した非同期関数が利用できるでしょ？
・PPL等でも利用可能ですが、お手軽に利用可能な手段が増えるのはとてもいいものです。


#6. まとめ
・今回はMFCアプリからWinRTの非同期関数とメッセージダイアログを使用してみました。
・簡単にUWP風味が味わえるでしょう？

[github](https://github.com/yoshiyoshi-git/MFCWinRTDlg)はここに

やっぱり変更して[WinRT APIの使用（OCR）](https://qiita.com/lilac0011/items/4f1a6d4c689430d16330)をやります。

[目次](https://qiita.com/lilac0011/items/f53f7401ea7da31e3a0b)へ