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