【IE限定】COMからJavaScriptを呼び出す

[前記事](https://qiita.com/sh0x01/items/b5a2bc7da61e743a25c0)の続きです
COM<-->JavaScript のやりとりができれば、いろいろなことができそうです。

# 前提
[【IE限定】JavaScriptからCOMを呼び出す](https://qiita.com/sh0x01/items/b5a2bc7da61e743a25c0)の続きです。

この記事は、上の記事を読んでいることを前提に書いています。
上の記事を読んでいない人は、この記事を読む前に、上の記事をお読みください。
（でないと、この記事で書いていることがサッパリわからないかもしれません。。）

# 実装
## COM側の実装
### IDLファイルを記述

IDLファイルには、以下を追加します。

* JavaScript関数を設定するプロパティ
* JavaScript関数を呼び出すメソッド

```c++:IDLファイル
// ATLProject1.idl : ATLProject1 の IDL ソース
//

// このファイルは、タイプ ライブラリ ([!output SAFE_IDL_NAME].tlb) およびマーシャリング コードを
// タイプ ライブラリ (ATLProject1.tlb) とマーシャリング コードを生成します。

import "oaidl.idl";
import "ocidl.idl";

[
	object,
	uuid(d0b8ec50-7953-4607-86c6-6f4b2499db6f),
	dual,
	nonextensible,
	pointer_default(unique)
]
interface IATLSimpleObject : IDispatch
{
	// ★★★★★★★★★★ ここから ★★★★★★★★★★
	[propput, id(1)] HRESULT JsFunc([in] VARIANT newVal);
	[id(2)] HRESULT CallJsFunc();
	// ★★★★★★★★★★ ここまで ★★★★★★★★★★
};
[
	uuid(8cb35385-6c0f-4d8c-aef3-864ff7ec2143),
	version(1.0),
]
library ATLProject1Lib
{
	importlib("stdole2.tlb");
	[
		uuid(da1a207a-5427-49b2-b2fb-08b9f5fef902)
	]
	coclass ATLSimpleObject
	{
		[default] interface IATLSimpleObject;
	};
};

import "shobjidl.idl";
```

### ヘッダーファイルを記述
ヘッダーファイルにもプロパティ、メソッドを追加します。

```c++:ヘッダーファイル
// ATLSimpleObject.h : CATLSimpleObject の宣言

#pragma once
#include "resource.h"       // メイン シンボル



#include "ATLProject1_i.h"



#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "DCOM の完全サポートを含んでいない Windows Mobile プラットフォームのような Windows CE プラットフォームでは、単一スレッド COM オブジェクトは正しくサポートされていません。ATL が単一スレッド COM オブジェクトの作成をサポートすること、およびその単一スレッド COM オブジェクトの実装の使用を許可することを強制するには、_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA を定義してください。ご使用の rgs ファイルのスレッド モデルは 'Free' に設定されており、DCOM Windows CE 以外のプラットフォームでサポートされる唯一のスレッド モデルと設定されていました。"
#endif

using namespace ATL;


// CATLSimpleObject

class ATL_NO_VTABLE CATLSimpleObject :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CATLSimpleObject, &CLSID_ATLSimpleObject>,
	public IDispatchImpl<IATLSimpleObject, &IID_IATLSimpleObject, &LIBID_ATLProject1Lib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
	CATLSimpleObject()
	{
	}

DECLARE_REGISTRY_RESOURCEID(106)


BEGIN_COM_MAP(CATLSimpleObject)
	COM_INTERFACE_ENTRY(IATLSimpleObject)
	COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}
// ★★★★★★★★★★ ここから ★★★★★★★★★★
private:
	CComPtr<IDispatch> mJsFunc;

public:
	
	STDMETHOD(put_JsFunc)(VARIANT newVal);
	STDMETHOD(CallJsFunc)();
};
// ★★★★★★★★★★ ここまで ★★★★★★★★★★

OBJECT_ENTRY_AUTO(__uuidof(ATLSimpleObject), CATLSimpleObject)
```

### 実装ファイルを記述
実装ファイルにもプロパティ、メソッドを追加します。

```c++:実装ファイル
// ATLSimpleObject.cpp : CATLSimpleObject の実装

#include "pch.h"
#include "ATLSimpleObject.h"


// CATLSimpleObject


// ★★★★★★★★★★ ここから ★★★★★★★★★★
STDMETHODIMP CATLSimpleObject::CallJsFunc()
{
    if (mJsFunc)
    {
        DISPPARAMS params = { 0, 0, 0, 0 };
        mJsFunc->Invoke(0,
            IID_NULL,
            LOCALE_USER_DEFAULT,
            DISPATCH_METHOD,
            &params, NULL, NULL, NULL);
    }
    return S_OK;
}

STDMETHODIMP CATLSimpleObject::put_JsFunc(VARIANT newVal)
{
    if (newVal.vt == VT_DISPATCH)
    {
        mJsFunc = newVal.pdispVal;
    }
    return S_OK;
}
// ★★★★★★★★★★ ここまで ★★★★★★★★★★
```

## JavaScript側の実装

JavaScript側の実装です。
COM側にJavaScript関数を設定し、呼び出します。

```html:HTMLファイル
<!doctype html>

<html>

<head>
  <meta charset="utf-8">
  <title>ActiveX</title>
</head>
<body>
  <object id="activeXObj" classid="clsid:da1a207a-5427-49b2-b2fb-08b9f5fef902"></object>
  <script>
    var activeXObj = document.getElementById('activeXObj');
    activeXObj.JsFunc = jsFunc;
    activeXObj.CallJsFunc();

    function jsFunc() {
      alert('Call js func');
    }
  </script>
</body>
</html>
```