Chapter.11

ツールバー(3)

2005/1/7
 ・インターフェースが継承可能であることが判明したのでサンプルソースを修正。

1.より完全な実装を目指して

現時点でインプレースアクティベーションが可能となっており、ツールバーも表示可能です。 最大の欠点は、インプレースアクティベーション前の表示サイズとインプレースアクティベーション中の表示サイズに大きな差があります。 今まで、動作させることを優先していたため、こういった座標計算を行っていませんでした。 ツールバーを表示させるにあたり、クライアント領域の使用可能なサイズが、流動的に変化するようになっています。 そのため、この章では座標計算を完全にしたいと思います。それにあわせてOLEサーバの移動を処理します。

インプレースアクティブ化時、OLEサーバはIOleInPlaceUIWindow.SetBorderSpaceによって、ツールバーを配置する領域を要求します。 クライアントは要求に対して、メインウィンドウから、その領域を割り当てるのですが、その時クライアント座標の論理原点が要求された領域の左辺と上辺の分だけシフトします。 下の図を見てください。

この図では、左辺の領域しか確保していませんが、インプレースアクティブ状態の切り替えによって、クライアント座標の論理原点が移動しているのが理解できるかと思います。 なお、この原点を移動しないこともクライアントには許されています。また、原点を移動しない場合、ツールバーを表示しないOLEサーバがあってもおかしくはないのです。

下の二つの図を見比べて下さい。

最初の図は、インプレースアクティブ化された状態で、2番目の図は、インプレースアクティブ化が解除された状態です。 最初の図で斜線で表示されているオブジェクトの枠と、2番目の図で表示されているビットマップのサイズには差があることが解ります。 この2番目の図で表示されているビットマップのサイズとは、何回か登場しているオブジェクトのエクステントのことです。 それに対して最初の図のオブジェクトの枠は、インプレースウィンドウの矩形(や、単純に座標)と呼ばれています。 これら2つの値は異なっており、クライアントは、オブジェクトを正しく表示するために、その両方を適切に管理する必要があります。


2.MIDLのinput_sync属性

OLEサーバの座標を適切に扱うように修正したサンプルソースです。
VB.NETサンプルソース:chap11.vb.lzh(35KB未満 2005/1/7)
このサンプルからはメイクファイルによってビルドを行います。 nmakeユーティリティの使用方法も併せて学習していただければ幸いです。

サンプルでは、座標計算を行う部分が、随処に出てきています。 HIMETRIC単位とPIXEL単位を変換する関数は、既に作成しているので、それほど苦労することは無いと思います。 むしろ、問題となるのは、input_sync属性を持つメソッドによるデッドロック制御を戻さない再入を防止する部分でしょう。 下にinput_sync属性を持つメソッドをまとめてみました。
メソッド 解説
IOleWindow.GetWindow このメソッドを呼び出した側は、この後、ダイアログボックス等のメッセージループを伴う処理を行うことが予想される。そのため、このメソッドにinput_sync属性が設定されていると考えられる。.NET Frameworkで使用する場合、IWin32WindowインターフェースのHandleプロパティは、アクセスした時点でウィンドウを作成することがあるので、注意しなければならない。最も簡単な回避方法は、このメソッドが呼び出される前に、ウィンドウが作成されていることを保障することである。
IOleInPlaceUIWindow.GetBorder IOleInPlaceUIWindow.GetBorderIOleInPlaceUIWindow.RequestBorderSpace、および、IOleInPlaceUIWindow.SetBorderSpaceは、まとまって呼び出される可能性が高く、最初の呼び出し以降に、ウィンドウのサイズ等が変更されることを防止するために、input_sync属性が設定されていると考えられる。これらのメソッドのうち、IOleInPlaceUIWindow.SetBorderSpaceは、実際にウィンドウの操作を必要とするため、特に注意が必要である。その他のメソッドも、情報を取得するため、IOleObject.GetExtentメソッド等を呼び出そうとしてしまう可能性があるため、注意しなければならない。
IOleInPlaceUIWindow.RequestBorderSpace
IOleInPlaceUIWindow.SetBorderSpace
IOleInPlaceActiveObject.OnFrameWindowActivate 詳細は不明。MSDNライブラリによれば、フォーカスの管理を適切にするためと記述してある。とりあえず、気をつけてコーディングしなければならない。
IOleInPlaceActiveObject.OnDocWindowActivate
IOleInPlaceActiveObject.ResizeBorder このメソッドで、問題が発生することはない。call_as属性によって、リモート側にinput_sync属性が付与されているが、このメソッドをリモート呼び出しで使用することはありえない。
IOleInPlaceFrame.SetMenu ユーザーのキー入力によって、このメソッドが呼び出された場合、SetMenu関数の呼び出しによって、フォーカスの状態が異常になる可能性が考えられる。
IOleInPlaceFrame.SetStatusText このメソッドの呼び出しに対して、SetWindowText関数を使用することがある。それの同一スレッド内のウィンドウに対する使用は、再入的な呼び出しとなるが、別スレッドのウィンドウに対して使用すると、完全にブロッキング操作となる。ローカルサーバから呼び出されていた場合、デッドロックに陥る可能性が高いと考えられる。
IOleInPlaceObject.SetObjectRects このメソッドが、インプロセスサーバから呼び出される場合は、特に問題とならない。問題点はサンプルソースで、このメソッドを使用している個所に記述してあるため、そちらを参照のこと。
ちなみに、解説に書いてあるinput_sync属性を持つ理由と言うのは私の推測です。 公式の解説は、MSDNライブラリの「Call Synchronization」に記述されてあります。 ただし、この内容は、それぞれに独自の内容ではなく、一般的な解説が記述してあるだけです。 そのため、私なりの解釈を入れることにしました。この記述が正しいかは、不明です。

.NET Frameworkを使用すると、思わぬところでRPC呼び出しが発生しているらしく、Platform SDKのサンプルコードを単純に移植できないことがあります。 OLEインターフェースのメソッドを呼び出してハングアップした場合、上の表を参考にしてみて下さい。 上の表で記述したメソッド内部で、メッセージループとRPC呼び出しを行ってはなりません。 この問題を解決するためには、PostMessage関数、または、タイマーを使用する必要があります。


3.まとめ

最初、座標計算について語ってた割には、座標計算の解説が少なかった気がします。 座標計算は、考えれば誰でも出来るので、説明がめんどくさかったは不要と考えました。 それよりも、知らないとハマってしまう、RPCの方に文章の大半を割きました。 .NET Frameworkの内部では、RPCが多用されていることが理解できるかと思います。 例によって、ツールバー以外の話が多いのですが、ツールバーについてはこれで終わりにしたいと思います。


前へ 次へ
OLE on .NET Frameworkへ
総合トップへ