Visual Studio 2017 Visual C++ による MFC ActiveX コントロールの開発

#はじめに
ActiveX コントロールがあると、比較的簡単にデスクトップアプリケーションの画面を作成できます。普通は、既存の ActiveX コントロールを利用するのが普通ですが、MFC や ATL を使って独自の ActiveX コントロールを開発することもできます。

MFC ActiveX コントロールを開発するためには、C++ の知識の他、MFC ActiveX コントロールの動作原理を知っていたり、IDL (インタフェース記述言語) を知っていたりする必要があります。しかし、Visual Studio を使うと、これらについて詳しく知らなくても開発が可能です。

#プロジェクトの作成
「新しいプロジェクト」ダイアログで Visual C++ / MFC を選択し、MFC ActiveX コントロールを選んで OK ボタンをクリックします。

![VCProj_MFCAxControl.png](https://qiita-image-store.s3.amazonaws.com/0/62802/510499a3-2e9c-cd7a-46f7-b44dbac1082e.png)

Fig.1 新しいプロジェクト

コントロール名を変更するためのダイアログが表示されるので、わかりやすい名前に変更します。

![MFCAxControl1.png](https://qiita-image-store.s3.amazonaws.com/0/62802/305afaff-e4e1-4ea8-04d4-fa8474283702.png)

Fig.2 MFC ActiveX コントロール　コントロール名

コントロールの種々の設定を行うためのダイアログが開くので、ここでコントロールの特性を設定します。特に「次にもとづいてコントロールを作成」コンボボックスでは、全く独自のコントロールを作るのか、既存のコントロールに機能を追加したり、変更したコントロールを作るのかが決まるので注意が必要です。

既存のコントロールを土台にすれば、変更点だけコードを書けばいいのでコーディング量はかなり少なくなりますが、独自のコントロールの場合は、外観を独自に描画したり、独自のプロパティやメソッドを多く実装する必要があります。

![MFCAxControl2.png](https://qiita-image-store.s3.amazonaws.com/0/62802/0c6440aa-1660-97b4-4aa9-7e2940472b56.png)

Fig.3 MFC ActiveX コントロール　コントロールの設定

完了ボタンをクリックすると、設定にもとづいてソリューションに MFC ActiveX コントロールプロジェクトが追加されます。

#サンプル (既存のコントロールの拡張)
Fig.2 「MFC ActiveX コントロール　コントロールの設定」のところで、「次にもとづいてコントロールを作成」を "STATIC" にすると、STATIC コントロール (.NET では Label コントロールに相当) をベースにした ActiveX コントロールを作成できます。このサンプルでは、この STATIC コントロールをベースにした ActiveX コントロールを作成してみます。

こうして作成した STATIC ベースのコントロールですが、デフォルトでは何も表示されず領域だけが確保されます。

そのため、表示文字列プロパティを追加して文字列を表示できるようにします。

![TestMFCAxCtrl2.png](https://qiita-image-store.s3.amazonaws.com/0/62802/1c8e7dfe-fb64-c980-9362-9e3ff48bf3ef.png)


Fig.4 STATIC ベース ActiveX コントロールのテストプログラムの表示例

##プロパティの追加

ソリューションエクスプローラで STATIC ベース ActiveX コントロールのプロジェクトを選び、クラスビューを表示します。

![MFCAxCtrl2ClassView.png](https://qiita-image-store.s3.amazonaws.com/0/62802/0940daf9-01e8-12c7-bdc8-9e6947555d04.png)

Fig.5 STATIC ベース ActiveX コントロールのクラスビュー

このコントロールの名前を MFCAxCtrl2 とすると、MFCAxCtrl2Lib ノードを展開して _DMFCAxCtrl2 を選びコンテキストメニューで「追加 / プロパティの追加」を実行します。これにより、Fig.6 のようなダイアログが開きます。

STATIC コントロールには元々 Caption プロパティがありますが、それを継承します。具体的には、「実装型」を「ストック」にして、プロパティ名として Caption を選びます。「ストック」はベースとした STATIC コントロールの既存のプロパティを意味します。

Fig.6 のダイアログのように Caption プロパティを設定し、「次へ」をクリックします。

![MFCAxCtrl2_Property.png](https://qiita-image-store.s3.amazonaws.com/0/62802/59c5d9a0-2365-00bd-621a-44858ef49fa3.png)
Fig.6 Caption プロパティの IDL 属性（１）

必要なら追加の設定を行い、「完了」をクリックすると Caption プロパティがプロジェクトに追加されます。

![MFCAxCtrl2_Property2.png](https://qiita-image-store.s3.amazonaws.com/0/62802/2aa61f13-6f49-9570-6aca-df599e89711e.png)

Fig.7 Caption プロパティの IDL 属性（２）

Caption プロパティは .NET の Label コントロールの Text プロパティに相当しますが、これはストックプロパティ (STATIC が元々持っているプロパティ) なので特別なコードは必要ありません。

ストックプロパティでない場合は、当然、何らかのコードが必要になります。

以下にこの ActiveX コントロールのコードを示します。


###MFCAxCtrl2.idl

```
// MFCAxCtrl2.idl : ActiveX コントロール プロジェクトのタイプ ライブラリ ソースです。

// このファイルは、[!output PROJECT_NAME].ocx のリソース
// になるタイプ ライブラリ (MFCAxCtrl2.tlb) を生成するために MIDL コンパイラ ツールによって処理されます。これは次のリソースになります:
// MFCAxCtrl2.ocx。
#include <olectl.h>
#include <idispids.h>

[ uuid(7d5e477d-a629-49d7-8166-586924611436), version(1.0),
  control ]
library MFCAxCtrl2Lib
{
	importlib(STDOLE_TLB);

	//  CMFCAxCtrl2Ctrl のプライマリ ディスパッチ インターフェイス
	[ 
		uuid(a0a628e1-e4f5-410b-8bea-fde2433ddc03)
	]
	dispinterface _DMFCAxCtrl2
	{
		properties:
			[id(DISPID_CAPTION), bindable, requestedit] BSTR Caption;
	methods:

			[id(DISPID_ABOUTBOX)] void AboutBox();
	};

	//  CMFCAxCtrl2Ctrl のイベント ディスパッチ インターフェイス

	[ 
		uuid(68939367-a743-476b-81a9-5ce68c97ec94)
	]
	dispinterface _DMFCAxCtrl2Events
	{
		properties:
			//  イベント インターフェイスにプロパティがありません。

		methods:
	};

	//  CMFCAxCtrl2Ctrl のクラス情報
	[
		uuid(61be9e94-fa97-45a0-b180-5059dd888267)
	]
	coclass MFCAxCtrl2
	{
		[default] dispinterface _DMFCAxCtrl2;
		[default, source] dispinterface _DMFCAxCtrl2Events;
	};

};
```

###MFCAxCtrl2Ctrl.h

```C++
#pragma once

// MFCAxCtrl2Ctrl.h : CMFCAxCtrl2Ctrl ActiveX コントロール クラスの宣言。


// CMFCAxCtrl2Ctrl : 実装については MFCAxCtrl2Ctrl.cpp を参照してください。

class CMFCAxCtrl2Ctrl : public COleControl
{
	DECLARE_DYNCREATE(CMFCAxCtrl2Ctrl)

// コンストラクター
public:
	CMFCAxCtrl2Ctrl();

// オーバーライド
public:
	virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
	virtual void DoPropExchange(CPropExchange* pPX);
	virtual void OnResetState();

// 実装
protected:
	~CMFCAxCtrl2Ctrl();

	DECLARE_OLECREATE_EX(CMFCAxCtrl2Ctrl)    // クラス ファクトリ と guid
	DECLARE_OLETYPELIB(CMFCAxCtrl2Ctrl)      // GetTypeInfo
	DECLARE_PROPPAGEIDS(CMFCAxCtrl2Ctrl)     // プロパティ ページ ID
	DECLARE_OLECTLTYPE(CMFCAxCtrl2Ctrl)		// タイプ名とその他のステータス

	// サブクラス化されたコントロールのサポート
	BOOL IsSubclassedControl();
	LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam);

// メッセージ マップ
	DECLARE_MESSAGE_MAP()

// ディスパッチ マップ
	DECLARE_DISPATCH_MAP()

	afx_msg void AboutBox();

// イベント マップ
	DECLARE_EVENT_MAP()

// ディスパッチ と イベント ID
public:
	enum {
		dispidClear = 1L
	};
protected:

};
```

###MFCAxCtrl2Ctrl.cpp

```C++
// MFCAxCtrl2Ctrl.cpp : CMFCAxCtrl2Ctrl ActiveX コントロール クラスの実装。

#include "stdafx.h"
#include "MFCAxCtrl2.h"
#include "MFCAxCtrl2Ctrl.h"
#include "MFCAxCtrl2PropPage.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

IMPLEMENT_DYNCREATE(CMFCAxCtrl2Ctrl, COleControl)

// メッセージ マップ

BEGIN_MESSAGE_MAP(CMFCAxCtrl2Ctrl, COleControl)
	ON_MESSAGE(OCM_COMMAND, &CMFCAxCtrl2Ctrl::OnOcmCommand)
	ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()

// ディスパッチ マップ

BEGIN_DISPATCH_MAP(CMFCAxCtrl2Ctrl, COleControl)
	DISP_FUNCTION_ID(CMFCAxCtrl2Ctrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
	DISP_STOCKPROP_CAPTION()
	DISP_FUNCTION_ID(CMFCAxCtrl2Ctrl, "Clear", dispidClear, Clear, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()

// イベント マップ

BEGIN_EVENT_MAP(CMFCAxCtrl2Ctrl, COleControl)
END_EVENT_MAP()

// プロパティ ページ

// TODO: プロパティ ページを追加して、BEGIN 行の最後にあるカウントを増やしてください。
BEGIN_PROPPAGEIDS(CMFCAxCtrl2Ctrl, 1)
	PROPPAGEID(CMFCAxCtrl2PropPage::guid)
END_PROPPAGEIDS(CMFCAxCtrl2Ctrl)

// クラス ファクトリおよび GUID を初期化します。

IMPLEMENT_OLECREATE_EX(CMFCAxCtrl2Ctrl, "MFCACTIVEXCONTRO.MFCAxCtrl2Ctrl.1",
	0x61be9e94,0xfa97,0x45a0,0xb1,0x80,0x50,0x59,0xdd,0x88,0x82,0x67)

// タイプ ライブラリ ID およびバージョン

IMPLEMENT_OLETYPELIB(CMFCAxCtrl2Ctrl, _tlid, _wVerMajor, _wVerMinor)

// インターフェイス ID

const IID IID_DMFCAxCtrl2 = {0xa0a628e1,0xe4f5,0x410b,{0x8b,0xea,0xfd,0xe2,0x43,0x3d,0xdc,0x03}};
const IID IID_DMFCAxCtrl2Events = {0x68939367,0xa743,0x476b,{0x81,0xa9,0x5c,0xe6,0x8c,0x97,0xec,0x94}};

// コントロールの種類の情報

static const DWORD _dwMFCAxCtrl2OleMisc =
	OLEMISC_ACTIVATEWHENVISIBLE |
	OLEMISC_SETCLIENTSITEFIRST |
	OLEMISC_INSIDEOUT |
	OLEMISC_CANTLINKINSIDE |
	OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CMFCAxCtrl2Ctrl, IDS_MFCAXCTRL2, _dwMFCAxCtrl2OleMisc)

// CMFCAxCtrl2Ctrl::CMFCAxCtrl2CtrlFactory::UpdateRegistry -
// CMFCAxCtrl2Ctrl のシステム レジストリ エントリを追加または削除します

BOOL CMFCAxCtrl2Ctrl::CMFCAxCtrl2CtrlFactory::UpdateRegistry(BOOL bRegister)
{
	// TODO: コントロールが apartment モデルのスレッド処理の規則に従っていることを
	// 確認してください。詳細については MFC のテクニカル ノート 64 を参照してください。
	// アパートメント モデルのスレッド処理の規則に従わないコントロールの場合は、6 番目
	// のパラメーターを以下のように変更してください。
	// afxRegApartmentThreading を 0 に設定します。

	if (bRegister)
		return AfxOleRegisterControlClass(
			AfxGetInstanceHandle(),
			m_clsid,
			m_lpszProgID,
			IDS_MFCAXCTRL2,
			IDB_MFCAXCTRL2,
			afxRegApartmentThreading,
			_dwMFCAxCtrl2OleMisc,
			_tlid,
			_wVerMajor,
			_wVerMinor);
	else
		return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}

// CMFCAxCtrl2Ctrl::CMFCAxCtrl2Ctrl - コンストラクター

CMFCAxCtrl2Ctrl::CMFCAxCtrl2Ctrl()
{
	InitializeIIDs(&IID_DMFCAxCtrl2, &IID_DMFCAxCtrl2Events);
	// TODO: この位置にコントロールのインスタンス データの初期化処理を追加してください
}

// CMFCAxCtrl2Ctrl::~CMFCAxCtrl2Ctrl - デストラクタ―

CMFCAxCtrl2Ctrl::~CMFCAxCtrl2Ctrl()
{
	// TODO: この位置にコントロールのインスタンス データの後処理用のコードを追加してください
}

// CMFCAxCtrl2Ctrl::OnDraw - 描画関数

void CMFCAxCtrl2Ctrl::OnDraw(
			CDC* pdc, const CRect& rcBounds, const CRect& /* rcInvalid */)
{
	if (!pdc)
		return;

	DoSuperclassPaint(pdc, rcBounds);
}

// CMFCAxCtrl2Ctrl::DoPropExchange - 永続性のサポート

void CMFCAxCtrl2Ctrl::DoPropExchange(CPropExchange* pPX)
{
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);

	// TODO: 永続属性を持つ各カスタム プロパティ用の PX_ 関数を呼び出します。
}

// CMFCAxCtrl2Ctrl::OnResetState - コントロールを既定の状態にリセットします

void CMFCAxCtrl2Ctrl::OnResetState()
{
	COleControl::OnResetState();  // DoPropExchange を呼び出して既定値にリセット

	// TODO: この位置にコントロールの状態をリセットする処理を追加してください
}

// CMFCAxCtrl2Ctrl::AboutBox - "バージョン情報" ボックスをユーザーに表示します

void CMFCAxCtrl2Ctrl::AboutBox()
{
	CDialogEx dlgAbout(IDD_ABOUTBOX_MFCAXCTRL2);
	dlgAbout.DoModal();
}

// CMFCAxCtrl2Ctrl::PreCreateWindow - CreateWindowEx のパラメーターを変更します

BOOL CMFCAxCtrl2Ctrl::PreCreateWindow(CREATESTRUCT& cs)
{
	cs.lpszClass = _T("STATIC");
	BOOL bRet = COleControl::PreCreateWindow(cs);
	cs.hMenu = NULL;
	return bRet;
}

// CMFCAxCtrl2Ctrl::IsSubclassedControl - これはサブクラス コントロールです

BOOL CMFCAxCtrl2Ctrl::IsSubclassedControl()
{
	return TRUE;
}

// CMFCAxCtrl2Ctrl::OnOcmCommand - コマンド メッセージを処理します

LRESULT CMFCAxCtrl2Ctrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
{
	WORD wNotifyCode = HIWORD(wParam);

	// TODO: この位置にスイッチ ステートメントで wNotifyCode を処理するコードを追加してください

	return 0;
}
```

##テストプログラム
テストプログラムの外観は Fig.4 です。これは ダイアログベースの MFC アプリケーションです。

テストプロジェクトのリソースビューでダイアログを開いて、コンテキストメニューで「ActiveX コントロールの挿入」を実行します。ActiveX コントロール選択ダイアログが開くので、一覧から当該コントロールを探して OK ボタンをクリックします。


Fig.8 のように、当該 ActiveX コントロールがダイアログビューに挿入されます。

![TestMFCAxCtrl2Rc.png](https://qiita-image-store.s3.amazonaws.com/0/62802/53eebf31-3111-a857-1be3-2d462151487e.png)

Fig.8 テストプログラムのリソースビューのダイアログデザイン

テストプログラムのプロジェクトのクラスビューでダイアログ (ここでは CTestMFCAxCtrl2Dlg という名称になっている) を選択してコンテキストメニュー「追加」/「変数の追加」を実行して、ダイアログを開き ActiveX コントロール変数を追加します。(Fig.9)

(注意) 管理者モードで Visual Studio を起動しておかないと、ActiveX コントロールのビルド後のレジストリ登録が失敗します。その場合は、管理者モードで regsvr32 を実行して ActiveX コントロールのレジストリ登録を行います。

![TestMFCAxCtrl2_AddVariable.png](https://qiita-image-store.s3.amazonaws.com/0/62802/082dd9a8-38fb-57dd-d7c7-35fe11d29e8f.png)

Fig.9 ActiveX コントロール変数の追加

テストプログラムのソース TestMFCAxCtrl2Dlg.cpp を開き、OnInitDialog の最後の方に SetCaption() メソッドを追加します。m_label1 は ActiveX コントロール変数の名前です。

```C++
// CTestMFCAxCtrl2Dlg メッセージ ハンドラー

BOOL CTestMFCAxCtrl2Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// "バージョン情報..." メニューをシステム メニューに追加します。

	// IDM_ABOUTBOX は、システム コマンドの範囲内になければなりません。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
	//  Framework は、この設定を自動的に行います。
	SetIcon(m_hIcon, TRUE);			// 大きいアイコンの設定
	SetIcon(m_hIcon, FALSE);		// 小さいアイコンの設定

	// TODO: 初期化をここに追加します。
	m_label1.SetCaption(_T("MFC ActiveX コントロール"));
	return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}
```


#サンプル (オリジナル・コントロールの作成)
最初のサンプルは STATIC コントロールをベースにしましたが、Fig.3 「MFC ActiveX コントロール　コントロールの設定」で「次に基づいてコントロールを作成」で「＜なし＞」にすると、独自の MFC ActiveX コントロールを作成できます。

このサンプルで作成する ActiveX コントロール ですが、白またはグレーの矩形を描画するだけです。白かグレーかはメソッドで切り替えます。

Fig.10 は当該 ActiveX コントロールを埋め込んだ実行中のテストプログラムです。

![TestMFCAxCtrl3.png](https://qiita-image-store.s3.amazonaws.com/0/62802/ad18d532-2712-0b6d-47ac-1bb57cc66bfc.png)

Fig.10 サンプル（２）のテストプログラム

この独自の ActiveX コントロールの名前を MFCAxCtrl3 とします。Fig.11 に MFCAxCtrl3 のプロジェクトツリーを示します。

![MFCAxCtrl3_Proj.png](https://qiita-image-store.s3.amazonaws.com/0/62802/626b3f3e-9f61-d1b1-a791-ed8e2ea1ff7e.png)

Fig.11 MFCAxCtrl3 プロジェクトの内容

この ActiveX コントロールには、矩形の色を変更するためのメソッド FillInterior() を持ちます。このメソッドは矩形の色を 1 回呼び出すごとに(トグルで)白またはグレーに変更します。

メソッドを追加するため、ソリューションエクスプローラで当該プロジェクトを選択し、クラスビューを表示します。MFCAxCtrl3Lib ノードを選択して子ノードを展開します。

_DMFCAxCtrl3 を選択してコンテキストメニューを表示します。コンテキストメニューの「追加」/「メソッドの挿入」メニューを実行して「メソッド追加ウィザード」(Fig.13) を開きます。

![MFCAxCtrl3ClassView.png](https://qiita-image-store.s3.amazonaws.com/0/62802/fcfc8a20-520a-8a49-92e3-de4553d0a643.png)

Fig.12 MFCAxCtrl3 クラスビュー

「メソッド追加ウィザード」でメソッド FillInterior() を追加します。

![MFCAxCtrl3_Method.png](https://qiita-image-store.s3.amazonaws.com/0/62802/4a834427-d8c7-92e5-b50a-dea075664423.png)

Fig.13 MFCAxCtrl3 クラスビューでメソッド追加ウィザード

MFCAxCtrl3.idl と MFCAxCtrl3.cpp に当該メソッドが追加されます。


###MFCAxCtrl3.idl

```
// MFCAxCtrl3.idl : ActiveX コントロール プロジェクトのタイプ ライブラリ ソースです。

// このファイルは、[!output PROJECT_NAME].ocx のリソース
// になるタイプ ライブラリ (MFCAxCtrl3.tlb) を生成するために MIDL コンパイラ ツールによって処理されます。これは次のリソースになります:
// MFCAxCtrl3.ocx。

#include <olectl.h>
#include <idispids.h>

[ uuid(01d31794-a0e2-4cbd-95bd-a108ba5fd496), version(1.0),
  control ]
library MFCAxCtrl3Lib
{
	importlib(STDOLE_TLB);

	//  CMFCAxCtrl3Ctrl のプライマリ ディスパッチ インターフェイス
	[ 
		uuid(7e6f9eaa-759e-4a92-9446-a2f36759511c)
	]
	dispinterface _DMFCAxCtrl3
	{
		properties:

	methods:

			[id(DISPID_ABOUTBOX)] void AboutBox();
			[id(1)] void FillInterior();
	};

	//  CMFCAxCtrl3Ctrl のイベント ディスパッチ インターフェイス

	[ 
		uuid(50ff435e-c3a1-4027-b332-d32abf144e5f)
	]
	dispinterface _DMFCAxCtrl3Events
	{
		properties:
			//  イベント インターフェイスにプロパティがありません。

		methods:
	};

	//  CMFCAxCtrl3Ctrl のクラス情報
	[
		uuid(7ba1d222-d68f-44cd-9896-167af300ccfc)
	]
	coclass MFCAxCtrl3
	{
		[default] dispinterface _DMFCAxCtrl3;
		[default, source] dispinterface _DMFCAxCtrl3Events;
	};

};
```

###MFCAxCtrl3Ctrl.h
m_filled は false なら矩形は白、true ならグレーにする BOOL 型変数です。これは クラスビューで MFCAxCtrl3Ctrl を選択して「追加」/「変数の追加」コンテキストメニューを実行して表示されるダイアログで挿入できます。

```C++
#pragma once

// MFCAxCtrl3Ctrl.h : CMFCAxCtrl3Ctrl ActiveX コントロール クラスの宣言。


// CMFCAxCtrl3Ctrl : 実装については MFCAxCtrl3Ctrl.cpp を参照してください。

class CMFCAxCtrl3Ctrl : public COleControl
{
	DECLARE_DYNCREATE(CMFCAxCtrl3Ctrl)

// コンストラクター
public:
	CMFCAxCtrl3Ctrl();

// オーバーライド
public:
	virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);
	virtual void DoPropExchange(CPropExchange* pPX);
	virtual void OnResetState();

// 実装
protected:
	~CMFCAxCtrl3Ctrl();

	DECLARE_OLECREATE_EX(CMFCAxCtrl3Ctrl)    // クラス ファクトリ と guid
	DECLARE_OLETYPELIB(CMFCAxCtrl3Ctrl)      // GetTypeInfo
	DECLARE_PROPPAGEIDS(CMFCAxCtrl3Ctrl)     // プロパティ ページ ID
	DECLARE_OLECTLTYPE(CMFCAxCtrl3Ctrl)		// タイプ名とその他のステータス

// メッセージ マップ
	DECLARE_MESSAGE_MAP()

// ディスパッチ マップ
	DECLARE_DISPATCH_MAP()

	afx_msg void AboutBox();

// イベント マップ
	DECLARE_EVENT_MAP()

// ディスパッチ と イベント ID
public:
	enum {
		dispidFillInteria = 1L
	};
protected:
	void FillInterior();
public:
	bool m_filled;
};
```

###MFCAxCtrl3Ctrl.cpp

このファイルはコントロールの実装コードです。OnDraw ハンドラで // TODO: 以下にコードを追加しています。また、最後の方に、FillInterior メソッドを実装しています。

```C++
// MFCAxCtrl3Ctrl.cpp : CMFCAxCtrl3Ctrl ActiveX コントロール クラスの実装。

#include "stdafx.h"
#include "MFCAxCtrl3.h"
#include "MFCAxCtrl3Ctrl.h"
#include "MFCAxCtrl3PropPage.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

IMPLEMENT_DYNCREATE(CMFCAxCtrl3Ctrl, COleControl)

// メッセージ マップ

BEGIN_MESSAGE_MAP(CMFCAxCtrl3Ctrl, COleControl)
	ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()

// ディスパッチ マップ

BEGIN_DISPATCH_MAP(CMFCAxCtrl3Ctrl, COleControl)
	DISP_FUNCTION_ID(CMFCAxCtrl3Ctrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION_ID(CMFCAxCtrl3Ctrl, "FillInteria", dispidFillInteria, FillInteria, VT_EMPTY, VTS_NONE)
	DISP_STOCKPROP_FORECOLOR()
END_DISPATCH_MAP()

// イベント マップ

BEGIN_EVENT_MAP(CMFCAxCtrl3Ctrl, COleControl)
END_EVENT_MAP()

// プロパティ ページ

// TODO: プロパティ ページを追加して、BEGIN 行の最後にあるカウントを増やしてください。
BEGIN_PROPPAGEIDS(CMFCAxCtrl3Ctrl, 1)
	PROPPAGEID(CMFCAxCtrl3PropPage::guid)
END_PROPPAGEIDS(CMFCAxCtrl3Ctrl)

// クラス ファクトリおよび GUID を初期化します。

IMPLEMENT_OLECREATE_EX(CMFCAxCtrl3Ctrl, "MFCACTIVEXCONTRO.MFCAxCtrl3Ctrl.1",
	0x7ba1d222,0xd68f,0x44cd,0x98,0x96,0x16,0x7a,0xf3,0x00,0xcc,0xfc)

// タイプ ライブラリ ID およびバージョン

IMPLEMENT_OLETYPELIB(CMFCAxCtrl3Ctrl, _tlid, _wVerMajor, _wVerMinor)

// インターフェイス ID

const IID IID_DMFCAxCtrl3 = {0x7e6f9eaa,0x759e,0x4a92,{0x94,0x46,0xa2,0xf3,0x67,0x59,0x51,0x1c}};
const IID IID_DMFCAxCtrl3Events = {0x50ff435e,0xc3a1,0x4027,{0xb3,0x32,0xd3,0x2a,0xbf,0x14,0x4e,0x5f}};

// コントロールの種類の情報

static const DWORD _dwMFCAxCtrl3OleMisc =
	OLEMISC_ACTIVATEWHENVISIBLE |
	OLEMISC_SETCLIENTSITEFIRST |
	OLEMISC_INSIDEOUT |
	OLEMISC_CANTLINKINSIDE |
	OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CMFCAxCtrl3Ctrl, IDS_MFCAXCTRL3, _dwMFCAxCtrl3OleMisc)

// CMFCAxCtrl3Ctrl::CMFCAxCtrl3CtrlFactory::UpdateRegistry -
// CMFCAxCtrl3Ctrl のシステム レジストリ エントリを追加または削除します

BOOL CMFCAxCtrl3Ctrl::CMFCAxCtrl3CtrlFactory::UpdateRegistry(BOOL bRegister)
{
	// TODO: コントロールが apartment モデルのスレッド処理の規則に従っていることを
	// 確認してください。詳細については MFC のテクニカル ノート 64 を参照してください。
	// アパートメント モデルのスレッド処理の規則に従わないコントロールの場合は、6 番目
	// のパラメーターを以下のように変更してください。
	// afxRegApartmentThreading を 0 に設定します。

	if (bRegister)
		return AfxOleRegisterControlClass(
			AfxGetInstanceHandle(),
			m_clsid,
			m_lpszProgID,
			IDS_MFCAXCTRL3,
			IDB_MFCAXCTRL3,
			afxRegApartmentThreading,
			_dwMFCAxCtrl3OleMisc,
			_tlid,
			_wVerMajor,
			_wVerMinor);
	else
		return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}


// CMFCAxCtrl3Ctrl::CMFCAxCtrl3Ctrl - コンストラクター
//  m_filled = false で初期化される。
CMFCAxCtrl3Ctrl::CMFCAxCtrl3Ctrl()
	: m_filled(false)
{
	InitializeIIDs(&IID_DMFCAxCtrl3, &IID_DMFCAxCtrl3Events);
	// TODO: この位置にコントロールのインスタンス データの初期化処理を追加してください
}

// CMFCAxCtrl3Ctrl::~CMFCAxCtrl3Ctrl - デストラクタ―

CMFCAxCtrl3Ctrl::~CMFCAxCtrl3Ctrl()
{
	// TODO: この位置にコントロールのインスタンス データの後処理用のコードを追加してください
}

// CMFCAxCtrl3Ctrl::OnDraw - 描画関数

void CMFCAxCtrl3Ctrl::OnDraw(
			CDC* pdc, const CRect& rcBounds, const CRect& /* rcInvalid */)
{
	if (!pdc)
		return;

	// TODO: 以下のコードを描画用のコードに置き換えてください
	if (m_filled)
	{
		pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(GRAY_BRUSH)));

	}
	else
	{
		pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
	}
}

// CMFCAxCtrl3Ctrl::DoPropExchange - 永続性のサポート

void CMFCAxCtrl3Ctrl::DoPropExchange(CPropExchange* pPX)
{
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);

	// TODO: 永続属性を持つ各カスタム プロパティ用の PX_ 関数を呼び出します。
}

// CMFCAxCtrl3Ctrl::OnResetState - コントロールを既定の状態にリセットします

void CMFCAxCtrl3Ctrl::OnResetState()
{
	COleControl::OnResetState();  // DoPropExchange を呼び出して既定値にリセット

	// TODO: この位置にコントロールの状態をリセットする処理を追加してください
}

// CMFCAxCtrl3Ctrl::AboutBox - "バージョン情報" ボックスをユーザーに表示します

void CMFCAxCtrl3Ctrl::AboutBox()
{
	CDialogEx dlgAbout(IDD_ABOUTBOX_MFCAXCTRL3);
	dlgAbout.DoModal();
}

// CMFCAxCtrl3Ctrl メッセージ ハンドラー

void CMFCAxCtrl3Ctrl::FillInterior()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	// TODO: ここにディスパッチ ハンドラー コードを追加してください。
	m_filled = !m_filled;
	Invalidate();
	UpdateWindow();
}
```

#テストプログラム
テストプログラムの外観は、Fig.10 のとおりです。また、テストプログラムのプロジェクトは、「サンプル(既存のコントロールの拡張)」とほぼ同様です。

動作としては Change ボタンをクリックしたとき、メソッド FillInterior() を呼び出します。以下にコードを示します。

###TestMFCAxCtrl3Dlg.h

```C++
// TestMFCAxCtrl3Dlg.h : ヘッダー ファイル
//

#pragma once
#include "afxwin.h"
#include "mfcaxctrl3ctrl1.h"


// CTestMFCAxCtrl3Dlg ダイアログ
class CTestMFCAxCtrl3Dlg : public CDialog
{
// コンストラクション
public:
	CTestMFCAxCtrl3Dlg(CWnd* pParent = NULL);	// 標準コンストラクター

// ダイアログ データ
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_TESTMFCAXCTRL3_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV サポート


// 実装
protected:
	HICON m_hIcon;

	// 生成された、メッセージ割り当て関数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CButton m_button1;
	afx_msg void OnClickedButton1();
	CMfcaxctrl3ctrl1 m_axctrl3;
};
```

###TestMFCAxCtrl3Dlg.cpp
変更箇所は最後の方の Change ボタンのハンドラの追加のみです。

```C++
// TestMFCAxCtrl3Dlg.cpp : 実装ファイル
//

#include "stdafx.h"
#include "TestMFCAxCtrl3.h"
#include "TestMFCAxCtrl3Dlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// アプリケーションのバージョン情報に使われる CAboutDlg ダイアログ

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// ダイアログ データ
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート

// 実装
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()

// CTestMFCAxCtrl3Dlg ダイアログ

CTestMFCAxCtrl3Dlg::CTestMFCAxCtrl3Dlg(CWnd* pParent /*=NULL*/)
	: CDialog(IDD_TESTMFCAXCTRL3_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CTestMFCAxCtrl3Dlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_BUTTON1, m_button1);
	DDX_Control(pDX, IDC_MFCAXCTRL3CTRL1, m_axctrl3);
}

BEGIN_MESSAGE_MAP(CTestMFCAxCtrl3Dlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CTestMFCAxCtrl3Dlg::OnClickedButton1)
END_MESSAGE_MAP()

// CTestMFCAxCtrl3Dlg メッセージ ハンドラー

BOOL CTestMFCAxCtrl3Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// "バージョン情報..." メニューをシステム メニューに追加します。

	// IDM_ABOUTBOX は、システム コマンドの範囲内になければなりません。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
	//  Framework は、この設定を自動的に行います。
	SetIcon(m_hIcon, TRUE);			// 大きいアイコンの設定
	SetIcon(m_hIcon, FALSE);		// 小さいアイコンの設定

	// TODO: 初期化をここに追加します。

	return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}

void CTestMFCAxCtrl3Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// ダイアログに最小化ボタンを追加する場合、アイコンを描画するための
//  下のコードが必要です。ドキュメント/ビュー モデルを使う MFC アプリケーションの場合、
//  これは、Framework によって自動的に設定されます。

void CTestMFCAxCtrl3Dlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 描画のデバイス コンテキスト

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// クライアントの四角形領域内の中央
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// アイコンの描画
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// ユーザーが最小化したウィンドウをドラッグしているときに表示するカーソルを取得するために、
//  システムがこの関数を呼び出します。
HCURSOR CTestMFCAxCtrl3Dlg::OnQueryDragIcon()
{
	return static_cast(m_hIcon);
}

//
//  Change ボタンがクリックされたとき
//  =================================
void CTestMFCAxCtrl3Dlg::OnClickedButton1()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	m_axctrl3.FillInteria();
}
```

-