キノコの自省録

テクノロジーとコンテンツの融合を目指して

Visual C++でWPFっぽく その1

Visual C++では、WPFが使えません。
標準で用意されているVisual C++のコントロールは、
見た目も地味で、種類も少ないです。
それが嫌なら、描画関数とイベントを駆使して、自分で作るか、
C++/CLIWPFを利用するしかありません。


そんなGUIが貧弱になりがちなVisual C++で、
WPFっぽいウィンドウを作っちゃおう!というお話をします。
しかもこの方法だと、.NET2003あたりの古いVisual C++(ver7.1)でもWPFっぽいことができてしまいます。


何を使うかを簡単に言うと、
CDHtmlDialogと、Silverlight(JavaScriptベース)です。
実はもうこれでほとんどの説明は終わりなのですが……;


CDHtmlDialogはCHtmlViewでもいけると思いますが、
CDHtmlDialogはDynamic HTMLを扱えるようになっているので、楽です。


CDHtmlDialogクラスのインスタンスがHTMLを読み込み、
HTMLに関連付けられたJavaScriptを実行してSilverlightを制御する、といった感じです。
C++部分に処理の中核(Model)を担当させ、Silverlightは表示(View)に特化します。
ということで、C++ → Silverlightへ通信する方法が必要となります。


さて、その方法ですが、JavaScriptを仲介して行います。
Visual C++JavaScriptSilverlight
という感じに、Visual C++からJavaScriptの関数をコールし、
その呼び出された関数がSilverlightの描画を書き換える処理を行う、
といった方法をとります。


C++からJavaScriptをコールする方法についてですが、
CDHtmlDialogのメンバ変数、m_spHtmlDocを用いて行うことができます。
その方法について、ざっと処理概要だけ書きます。


C++側のコードサンプル(CDHtmlDialogの派生クラスに記述)



//ページのScriptを取得
CComPtr<IDispatch> spDisp;
HRESULT hr = m_spHtmlDoc->get_Script(&spDisp);

//JavaScript関数「helloworld」を取得
CComBSTR funcname(L"helloworld");
DISPID dispid = -1;
hr = spDisp->GetIDsOfNames(IID_NULL, &funcname, 1, LOCALE_SYSTEM_DEFAULT, &dispid);

//helloworldを実行
DISPPARAMS dispParam = {0, 0, 0, 0}; // JavaScript関数の引数
EXCEPINFO eInfo; // 例外情報
UINT argErr = -1; // 引数エラー
hr = spDisp->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dispParam,
&retValue, &eInfo, &argErr);

現在のページにロードされている「helloworld」という関数を取得して、
実行させる、というだけのコードです。


後は、JavaScript側で、関数helloworldを用意するだけです。

JavaScript側のコードサンプル



function helloworld() {
alert("helloworld");
}

上記の例ではSilverlightに関するコードは記述していませんが、
JavaScript関数内で、Silverlightを制御するコードを普通に記述すれば、
ちゃんとSilverlightも動作してくれます。


JavaScript関数にVARIANT型引数を渡す場合は、
DISPPARAMSのcArgsに引数の個数を設定し、rgvargに引数のポインタを渡します。
数値や文字列はこれで渡せますが、Array型などの名前つきオブジェクトを渡すのは、かなり面倒くさいです。
出来なくはないですが、複雑です。


とりあえず、C++Silverlightはこれで制御できます。
SilverlightC++は、若干工夫が必要になります。
結局JavaScriptを仲介させるのですが、C++JavaScriptより面倒です。
さっき面倒だと書いた、「名前付きオブジェクトを引数で渡す」方法と、若干似ています。


長くなったので続きは次回。