Visual Studio 2017 Visual C++ による VC++/CLI GUI の開発

#はじめに
VC++/CLI は .NET CLR 上で動作するものですが、GUI も開発できます。しかし、VC++ で GUI を作る人はほとんどいなかったのか、すでに**オワコン状態**です。以前は確かもっと厚くサポートされていたと思いますが、現在ではオマケのような感じです。

さらに、Visual Studio 2017 での開発においても、何かと問題があるように見えます。例えば、

- 一部のコントロールがちゃんと動作しない。(少なくとも C# や VB.NET と動作が異なる)
- ウィンドウプロシージャを手動で書いてやる必要がある。
- フォームのデザイン画面を最初に開いたとき必ずエラーになる。。
- プロジェクトのプロパティを手動で修正する必要がある。

ということで、複雑な GUI を作るのにはお勧めできませんが、処理結果をコンソール画面でなくウィンドウに表示したいという程度なら使えそうです。

#プロジェクトの作成
Visual C++ の「新しいプロジェクト」には 「Windows Forms アプリケーション」のような選択肢はありません。次のように「空のプロジェクト」を選択します。

![VCProj_Empty.png](https://qiita-image-store.s3.amazonaws.com/0/62802/7ed2aa74-a8f4-d4fa-ac86-c9fb92dd87cb.png)
Fig.1 「新しいプロジェクト」ダイアログ

空のプロジェクトが作成されたら、そのプロジェクトを選択し、コンテキストメニューの「追加」-「新しい項目」から Fig.2 のように 「UI」-「Windows フォーム」を選んで OK ボタンをクリックします。

![GUI_MyForm.png](https://qiita-image-store.s3.amazonaws.com/0/62802/293412e7-496a-db1c-cea1-03496efc6749.png)
Fig.2 「新しい項目の追加」ダイアログ

デフォルトでは MyForm.cpp と MyForm.h というファイルがプロジェクトに追加されます。このうち、MyForm.h が GUI に関連するので、これを開いてみると Fig.3 のようなエラー画面が表示されてしまいます。

![GUI_DesignError.png](https://qiita-image-store.s3.amazonaws.com/0/62802/71ad7bf4-a808-9c68-b628-bfb214f28795.png)
Fig.3 フォーム表示のエラー画面

これは一度、ビルドを行えば解消されますが、Fig.4 のようなエラーが表示されます。これは「空のプロジェクト」にフォームを追加しただけなので、main 関数が未定義になっているためです。

![GUI_EntryPointError.png](https://qiita-image-store.s3.amazonaws.com/0/62802/e490df87-f392-cd8b-2489-2db4bcdc7962.png)
Fig.4 ビルドエラー

MyForm.cpp をコード表示して、次のように main 関数を追加します。あるいは Main.cpp のようなソースを追加してそこに記述しても構いません。ここで、名前空間は各自のプロジェクトに合わせて修正が必要です。


###Code.1 MyForm.cpp の修正例

```C++
#include <Windows.h>
#include "MyForm.h"

using namespace GUI2;

int main()
{
	MSG msg;

	Console::WriteLine(L"Starting ..");

	// フォームを構築して表示する。
	MyForm ^myForm = gcnew MyForm();
	myForm->Show();
	Console::WriteLine(L"MyForm has shown ..");

	// ウィンドウメッセージをディスパッチ
	while (GetMessage(&msg, 0, 0, 0))
		DispatchMessage(&msg);

	Console::WriteLine(L"MyForm destroyed.");
	return 0;
}
```

ビルドしてみると、Fig.5 のようなリンカーエラーが大量に出ます。これは、プロジェクトのプロパティでリンカー設定を修正すると解消できます。

![GUI_LinkError.png](https://qiita-image-store.s3.amazonaws.com/0/62802/25e86c41-286c-6f94-c7b4-81693de9146d.png)
Fig.5 リンカーのエラー

Fig.6 のようにリンカーの入力に Windows のシステム DLL をリンクするように指示します。

![GUI_LinkProp.png](https://qiita-image-store.s3.amazonaws.com/0/62802/6f6a4874-9069-7444-3c8e-32f94e33c0a1.png)
Fig.6 プロジェクトのプロパティの設定

これで実行してみると、Fig.7 のような画面が表示されるはずです。

![GUI_MyFormShown.png](https://qiita-image-store.s3.amazonaws.com/0/62802/b3d18118-9b52-2c23-94af-54fbf64abeec.png)
Fig.7 フォームの表示


#サンプル
Fig.7 の画面でフォームの閉じるボタン (x) をクリックしてもアプリケーションは終了しません。

アプリケーションを終了させるには、PostQuitMessage API 関数を実行すればいいので、FormClosed ハンドラでこの関数を呼び出すようにします。

変更が必要なのはヘッダーファイルだけです。FormClosed イベントハンドラを追加して、そこで PostQuitMessage を呼び出しています。(Code.2 の下の方)


###Code.2 MyForm.h

```C++
#pragma once

namespace GUI2 {

	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Collections;
	using namespace System::Windows::Forms;
	using namespace System::Data;
	using namespace System::Drawing;

	/// <summary>
	/// MyForm の概要
	/// </summary>
	public ref class MyForm : public System::Windows::Forms::Form
	{
	public:
		MyForm(void)
		{
			InitializeComponent();
			//
			//TODO: ここにコンストラクター コードを追加します
			//
		}

	protected:
		/// <summary>
		/// 使用中のリソースをすべてクリーンアップします。
		/// </summary>
		~MyForm()
		{
			if (components)
			{
				delete components;
			}
		}
	private: System::Windows::Forms::Button^  button1;

	private:
		/// <summary>
		/// 必要なデザイナー変数です。
		/// </summary>
		System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
		/// <summary>
		/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
		/// コード エディターで変更しないでください。
		/// </summary>
		void InitializeComponent(void)
		{
			this->components = gcnew System::ComponentModel::Container();
			this->Size = System::Drawing::Size(300,300);
			this->Text = L"MyForm";
			this->Padding = System::Windows::Forms::Padding(0);
			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
			this->FormClosed += gcnew System::Windows::Forms::FormClosedEventHandler(this, &MyForm::MyForm_FormClosed);
		}
#pragma endregion
	// FormClosed
	private: System::Void MyForm_FormClosed(System::Object^  sender, System::Windows::Forms::FormClosedEventArgs^  e) {
		PostQuitMessage(0);
	}
	};
}
```

-