わびさびサンプルソース

WindowsやHTML5などのプログラムのサンプルコードやフリーソフトを提供します。

JavaScriptからC++を呼び出す

IEコントロールのJavaScriptからC++を呼び出すには、IAxWinHostWindowのSetExternalDispatch()メソッドでイベントシンクを登録します。 イベントシンクを登録すると、JavaScriptのexternal.関数名()の呼び出しがあると、GetIDsOfNames()メソッドが関数名を引数にして渡されてきます。 ここでその関数名に対して、適当なIDを返すと、今度は、先ほど割り当てたIDでInvoke()メソッドが呼び出されます。Invoke()メソッドでは、関数に対する引数なども取得する事ができます。 Invoke()メソッドにC++側でやりたい好きな処理を実装します。

#include <tchar.h>
#include <iostream>
#include <string>
#include <shlobj.h>
#include <comdef.h>
#include <comutil.h>
#pragma warning(push)
#pragma warning(disable:4996)
#include <atlbase.h>
#pragma warning(pop)
#include <ExDispid.h>



// Atl.dllの関数ポインタ定義
typedef BOOL(WINAPI *ATLAXWININIT)();
typedef HRESULT(WINAPI *ATLAXGETCONTROL)(HWND, IUnknown **);

// I/F解放用マクロ
#define SAFE_RELEASE( p1 ) if ( p1 ) { p1->Release(); p1 = NULL; }



/*
	externalに対するディスパッチクラスの登録
*/
HRESULT SetExternalDispatch
(
	  HWND hWnd
	, IDispatch* pDispatch
)
{
	HRESULT hResult = E_FAIL;

	// IAxWinHostWindow
	CComPtr<IAxWinHostWindow> pWinHostWindow;

	// WM_ATLGETHOSTメッセージのIDを取得
	UINT ID_WM_ATLGETHOST = ::RegisterWindowMessageW( L"WM_ATLGETHOST" );

	// IUnknown
	CComPtr<IUnknown> pUnknown = (IUnknown*)SendMessage( hWnd, ID_WM_ATLGETHOST, 0, 0 );
	if ( NULL != pUnknown ) {

		// IAxWinHostWindowの取得
		hResult = pUnknown->QueryInterface( __uuidof(IAxWinHostWindow), (void**)&pWinHostWindow);
		if ( SUCCEEDED( hResult ) ) {

			// SetExternalDispatchの呼び出し
			hResult = pWinHostWindow->SetExternalDispatch( pDispatch );
		}
	}
	return( hResult );
}



/*
	イベントをディスパッチさせる為にIDispatchから派生させたクラス
*/
class EventSink_CL : public IDispatch
{
	ULONG m_qRefCounter;

public:
	// コンストラクタ
	EventSink_CL()
		: m_qRefCounter( 1 )
	{
	}

	// デストラクタ
	virtual ~EventSink_CL()
	{
	}


	// AddRef
	virtual STDMETHODIMP_(ULONG) AddRef(void)
    {
		// 参照カウンタ+1
		return( ++m_qRefCounter );
    }

	// Release
	virtual STDMETHODIMP_(ULONG) Release(void)
	{
		// 参照カウンタ-1
		if ( 0 == --m_qRefCounter ) {

			// 参照カウンタが0になったら自身を破棄する
			delete this;
		}
		// 参照カウンタを返す
		return( m_qRefCounter );
    }

	// QueryInterface
	virtual STDMETHODIMP QueryInterface( REFIID riid, void **ppvOut )
	{
		HRESULT hResult = E_NOINTERFACE;

		// IID_IUnknownもしくはIID_IDispatchのI/Fは保有している
		if( IsEqualIID( riid, IID_IUnknown ) || IsEqualIID( riid, IID_IDispatch ) ) {

			// 参照カウンタ+1
			this->AddRef();

			// I/Fを返す。
			*ppvOut = reinterpret_cast<IDispatch*>( this );

			// 正常終了
			hResult = S_OK;

		} else {

			// 指定されたI/Fは保有していない。
			*ppvOut = NULL;
        }

		// 戻り値を返す
		return( hResult );
	}

	// GetTypeInfoCount
	virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount
	(
		/* [out] */	__RPC__out UINT *pctinfo
	)
	{
		return E_NOTIMPL;
	}

	// GetTypeInfo
	virtual HRESULT STDMETHODCALLTYPE GetTypeInfo
	(
		/* [in] */	UINT iTInfo,
		/* [in] */	LCID lcid,
		/* [out] */	__RPC__deref_out_opt ITypeInfo **ppTInfo
	)
	{
		return E_NOTIMPL;
	}

	// GetIDsOfNames
	virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames
	(
		/* [in] */				__RPC__in REFIID riid,
		/* [size_is][in] */		__RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
		/* [range][in] */		UINT cNames,
		/* [in] */				LCID lcid,
		/* [size_is][out] */	__RPC__out_ecount_full(cNames) DISPID *rgDispId
	)
	{
		// external.TestFunc1
		if (0 == ::wcscmp(L"TestFunc1", (const wchar_t*)rgszNames[ 0 ])) {

			// IDを返す
			*rgDispId = 1000000;
			return S_OK;
		}

		// external.TestFunc2
		if (0 == ::wcscmp(L"TestFunc2", (const wchar_t*)rgszNames[ 0 ])) {

			// IDを返す
			*rgDispId = 1000001;
			return S_OK;
		}

		// external.TestFunc3
		if (0 == ::wcscmp(L"TestFunc3", (const wchar_t*)rgszNames[ 0 ])) {

			// IDを返す
			*rgDispId = 1000002;
			return S_OK;
		}
		return E_NOTIMPL;
	}

	// Invoke
	virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke
	(
		/* [in] */		DISPID dispIdMember,
		/* [in] */		REFIID riid,
		/* [in] */		LCID lcid,
		/* [in] */		WORD wFlags,
		/* [out][in] */	DISPPARAMS *pDispParams,
		/* [out] */		VARIANT *pVarResult,
		/* [out] */		EXCEPINFO *pExcepInfo,
		/* [out] */		UINT *puArgErr
	)
    {
        /*
			イベントを受信するとここが呼び出されます
				dispidMemberにイベントの種別が入ります。
				パラメータはpDispParamsに入ります。
		*/


		/*
			C++関数のIDの処理
		*/
		if ( 1000000 <= dispIdMember ) {

			switch( dispIdMember ) {
			case 1000000:
				std::wcout << L"TestFunc1関数(";
				break;

			case 1000001:
				std::wcout << L"TestFunc2関数(";
				break;

			case 1000002:
				std::wcout << L"TestFunc3関数(";
				break;
			}


			/*
				C++関数への引数の数だけループ
			*/
			int nCount = pDispParams->cArgs;
			if ( 0 < nCount ) {

				// 引数は配列に対して逆向きに格納されている
				for ( int nI = nCount - 1; 0 <= nI; nI-- ) {

					/*
						引数はVARIANT型で取得できる
					*/
					VARIANTARG* pValiantArg = &pDispParams->rgvarg[ nI ];
					switch( pValiantArg->vt ) {
					case VT_NULL:
						{
							std::wcout << L"null";
						}
						break;

					case VT_BOOL:
						{
							std::wcout << ( -1 == pValiantArg->boolVal )? L"true" : L"false";
						}
						break;

					case VT_I4:
						{
							std::wcout << pValiantArg->intVal;
						}
						break;

					case VT_UI4:
						{
							std::wcout << pValiantArg->uintVal;
						}
						break;

					case VT_R8:
						{
							std::wcout << pValiantArg->dblVal;
						}
						break;

					case VT_BSTR:
						{
							std::wcout << pValiantArg->bstrVal;
						}
						break;

					default:
						{
							std::wcout << L"Unknown( " << pValiantArg->vt << L" )";
						}
						break;
					}
					std::wcout << L", ";
				}
			}
			std::wcout << L" );" << std::endl;
		}

		// 正常終了
		return S_OK;
	}
};



/*
	JavaScriptからC++を呼び出す
*/
int _tmain
(
	int argc
	, _TCHAR* argv[]
)
{
	// ロケール変更(wcoutでユニコードを出力する為)
	std::wcout.imbue(std::locale("", std::locale::ctype));

	// COMを初期化する
	CoInitialize(NULL);


	/*
		Atl.dllのロード
	*/
	HMODULE hAtl = NULL;
	std::wstring strWindowClassName;
	{
		struct {
			TCHAR* pszDllName;				// DLLの名前
			TCHAR* pszAtlAxWindowClassName;	// ウインドウクラスの名前

		} taDllInfo[] = {
			 { L"atl110.dll", L"AtlAxWin110" }
			,{ L"atl100.dll", L"AtlAxWin100" }
			,{ L"atl90.dll" , L"AtlAxWin90"  }
			,{ L"atl80.dll" , L"AtlAxWin80"  }
			,{ L"atl71.dll" , L"AtlAxWin71"  }
			,{ L"atl.dll"   , L"AtlAxWin"    }
		};
		for (int nI = 0; nI < _countof(taDllInfo); nI++) {

			// DLLをロードしてみる
			hAtl = LoadLibrary(taDllInfo[nI].pszDllName);
			if (NULL != hAtl) {

				// ロードできたので、ウインドウクラス名を取得
				strWindowClassName = taDllInfo[nI].pszAtlAxWindowClassName;
				break;
			}
		}
	}

	if (NULL == hAtl) {

		std::wcout << L"atl.dllのロードに失敗しました。" << std::endl;
	}
	else {

		std::wcout << L"atl.dllのロードに成功しました。" << std::endl;

		// Atl.dllの関数ポインタ取得
		ATLAXWININIT    dll_AtlAxWinInit = (ATLAXWININIT)GetProcAddress(hAtl, "AtlAxWinInit");
		ATLAXGETCONTROL dll_AtlAxGetControl = (ATLAXGETCONTROL)GetProcAddress(hAtl, "AtlAxGetControl");

		// Atlの初期化
		dll_AtlAxWinInit();

		// ウインドウ矩形
		RECT tRect = { 100, 100, 1000, 900 };


		/*
			ブラウザコントロールの生成
		*/
		HWND hWnd = CreateWindowEx(
			0
			, strWindowClassName.c_str()
			, L"Shell.Explorer.2"
			, WS_OVERLAPPEDWINDOW
			| WS_TABSTOP
			| WS_VISIBLE
			, tRect.left
			, tRect.top
			, tRect.right - tRect.left
			, tRect.bottom - tRect.top
			, NULL	// 親ウインドウハンドル
			, 0
			, (HINSTANCE)::GetModuleHandle(NULL)
			, 0
		);

		if (NULL == hWnd) {

			std::wcout << L"ブラウザコントロールの生成失敗" << std::endl;
		}
		else {

			std::wcout << L"ブラウザコントロールの生成成功" << std::endl;

			CComPtr<IUnknown> pUnknown;

			// ATLコントロールの取得
			if (S_OK != dll_AtlAxGetControl(hWnd, (IUnknown**)&pUnknown)) {

				std::wcout << L"IUnknownの取得失敗" << std::endl;
			}
			else {

				std::wcout << L"IUnknownの取得成功" << std::endl;

				CComPtr<IWebBrowser2> pIWebBrowser2;

				// IWebBrowser2の取得
				pUnknown->QueryInterface(IID_IWebBrowser2, (VOID**)&pIWebBrowser2);
				if (NULL == pIWebBrowser2) {

					std::wcout << L"IWebBrowser2の取得失敗" << std::endl;
				}
				else {

					std::wcout << L"IWebBrowser2の取得成功" << std::endl;

					IDispatch*                 pDisp = NULL;
					IConnectionPointContainer* pCPC = NULL;
					IConnectionPoint*          pCP = NULL;
					DWORD dwCookie = 0L;

					// EventSink_CLの生成
					EventSink_CL *opSink = new EventSink_CL();
					if ( NULL != opSink ) {

						// IDispatchの取得
						if ( S_OK != opSink->QueryInterface( IID_IDispatch, (void**)&pDisp ) ) {
							goto err;
						}

						// externalディスパッチの登録
						if ( S_OK != SetExternalDispatch( hWnd, pDisp ) ) {
							std::wcout << L"SetExternalDispatch失敗" << std::endl;
						}

						// 接続ポイントコンテナの取得
						if ( S_OK != pIWebBrowser2->QueryInterface( IID_IConnectionPointContainer
							, reinterpret_cast<LPVOID*>( &pCPC ) ) ) {
							goto err;
						}

						// 接続ポイント取得
						if ( S_OK != pCPC->FindConnectionPoint( DIID_DWebBrowserEvents2, &pCP ) ) {
							goto err;
						}

						// イベントシンクの接続
						if ( S_OK != pCP->Advise( reinterpret_cast<IUnknown*>( pDisp ), &dwCookie ) ) {
							goto err;
						}
					}


					/*
						ホームページを開く
					*/
					{
						TCHAR waFilePath[ MAX_PATH ];

						// 実行ファイルのPATH名を得る
						::GetModuleFileNameW( NULL, waFilePath, _countof( waFilePath ) );

						// ファイル名をHtmlファイルへ変更
						TCHAR* pFileNameTop = ::wcsrchr( waFilePath, L'¥¥' ) + 1;
						::wcscpy( pFileNameTop, L"Test.html" );

						// URL
//						CComVariant	oNaviGateUrl(L"http://www.yahoo.co.jp");
						CComVariant	oNaviGateUrl( waFilePath );

						CComVariant oNoUse;

						pIWebBrowser2->Navigate2(&oNaviGateUrl, &oNoUse, &oNoUse, &oNoUse, &oNoUse);
					}

					// 時間待ち(テスト用)
					::MessageBox(NULL, L"OKを押下するとブラウザを閉じます。", L"ブラウザコントロールの生成", MB_OK);
err:
					// externalディスパッチの解放
					if ( S_OK != SetExternalDispatch( hWnd, NULL ) ) {
						std::wcout << L"SetExternalDispatch失敗" << std::endl;
					}

					// イベントシンクの接続解除
					if ( NULL != pCP && 0 != dwCookie ) {
						pCP->Unadvise( dwCookie );
					}

					// I/Fの破棄
					SAFE_RELEASE( pCPC );
					SAFE_RELEASE( pCP );
					SAFE_RELEASE( pDisp );

					// EventSink_CLの解放
					opSink->Release();
				}
			}

			// ウインドウの破棄
			::DestroyWindow( hWnd );
		}

		// Atl.dllの破棄
		FreeLibrary(hAtl);
	}

	// COMの使用終了
	CoUninitialize();

	// 正常終了
	return(0);
}



JavaScript側

<!doctype html>
<html>
<head>
<meta charset="UTF-8"> 
<meta http-equiv="content-language" content="ja"> 
<title>JavaScriptからC++の関数を呼び出す。</title>
<style>
</style>
<script>
</script>
</head>
<body>
JavaScriptからC++の関数を呼び出す。
<br/>
<input type="button" onclick="external.TestFunc1();" value="TestFunc1を呼び出す"/>
<br/>
<input type="button" onclick="external.TestFunc2( 1, 1.23, 'abc' );" value="TestFunc2を呼び出す"/>
<br/>
<input type="button" onclick="external.TestFunc3( null, true, false );" value="TestFunc3を呼び出す"/>
<br/>
</body>
</html>

実行結果

atl.dllのロードに成功しました。
ブラウザコントロールの生成成功
IUnknownの取得成功
IWebBrowser2の取得成功
TestFunc1関数( );
TestFunc2関数(1, 1.23, abc,  );
TestFunc3関数(null, 1, 0,  );
TestFunc2関数(1, 1.23, abc,  );
TestFunc1関数( );
TestFunc2関数(1, 1.23, abc,  );
TestFunc3関数(null, 1, 0,  );
TestFunc2関数(1, 1.23, abc,  );
TestFunc1関数( );
TestFunc3関数(null, 1, 0,  );






わびさびサンプルソース

WindowsやHTML5などのプログラムのサンプルコードやフリーソフトを提供します。