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); }
<!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, );