2009年2月19日木曜日

.netアセンブリをCOMサーバとして登録する場合の注意

GUIDがあるんだから、同じ``名前空間.クラス名''のクラスでも問題ないと思ったら問題大有り。生成されるレジストリエントリは

[HKEY_CLASSES_ROOT\MyEffect]
@="WMPWPFTest.MyEffect"

[HKEY_CLASSES_ROOT\MyEffect\CLSID]
@="{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}"

[HKEY_CLASSES_ROOT\CLSID\{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}]
@="WMPWPFTest.MyEffect"

[HKEY_CLASSES_ROOT\CLSID\{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}\InprocServer32]
@="mscoree.dll"
"ThreadingModel"="Both"
"Class"="WMPWPFTest.MyEffect"
"Assembly"="WMPWPFTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="file:///C:/Users/Ruke/Documents/Visual Studio 2008/Projects/WMPWPFTest/bin/Release/WMPWPFTest.dll"

[HKEY_CLASSES_ROOT\CLSID\{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}\InprocServer32\1.0.0.0]
"Class"="WMPWPFTest.MyEffect"
"Assembly"="WMPWPFTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="file:///C:/Users/Ruke/Documents/Visual Studio 2008/Projects/WMPWPFTest/bin/Release/WMPWPFTest.dll"

と、実際に登録されるのはmscoree.dllだ。こいつが.netアセンブリをロードし、マーシャリングをする仕組みになっているっぽい。そこで、たとえGUIDがちがくとも、同じ``名前空間.クラス名''のクラスを複数登録すると、一つのCOMクライアントからそれらを同時に参照する場合に問題が生じる。多分、厳密な名前とやらを付与しても変わらない(未実験)。

そういうわけで、前のエントリで公開したWMPの視覚エフェクトのスケルトンをいじる時は名前空間やクラス名を変えて、その上でクラスのGUIDも変えるというステップを踏みましょう。

C#+WPFによるWMP VisualizerのスケルトンにIWMPEffects2を実装した

これで、他の視覚エフェクトと行儀良く共存できるようになった。
ダウンロードはこちらの WMPWPFTest.zip です。

C#とWPFによるWMPの視覚エフェクトのサンプル


サンプルコードはここのWMPWPFTEST.zipです。

今週はきわめて調子が悪いので、直ぐにというわけにはいかないけれど、これで、マーカ5つくらいを各周波数帯域に対応させてミクさんを音楽に同期して放出できる。周波数帯域の区切り方は対数で行うべきだろうな。

このコードでは、HwndSourceで作ったウィンドウをWMPの子ウィンドウにし、そのRootVisualを自作のユーザコントロールにしている。しかし、IWMPEffectsではこのウィンドウを破棄するタイミングがない。あくまでオンフレーム描画を念頭においた設計になっている。このために、一度この視覚エフェクトを選んでしまうと、他の視覚エフェクトを選んでもそのまま子ウィンドウが残ってしまう。

この問題は、IWMPEffects2では解決するようだ。CreateメソッドやDestroyメソッドが存在する。これもそのうち試してみよう。

C#+WPFによるWindows mediaplayerの視覚エフェクトのスケルトンができた


ようやっと上手くいった。HDCからウィンドウのHWNDをとり、そこからHwndSourceを作ってRootVisualを設定っていうAdam Nahanさんの方針はうまく行かなかったのは何とも謎。結局HwndSourceをコンストラクタから作ってHDCから取得したHWNDを親ウィンドウに、RootVisualを自作のUserControlにするという方針で上手く動いた。

スケルトンのソースはここのWMPWPFTEST.zipです。

それからCOMインタフェースを定義する方法をまとめておく。
1.IDLの中のinterfaceをlibraryで囲んで、midl /newtlb foo.idlのファイル名、でfoo.tlbができる。次にtlbimp foo.tlbでfoo.dllができる。
2.foo.dllをReflectorで開いて必要な定義をcsファイルにコピーし、HDCなど実質void *である型が_RemotableHandleになっている所をIntPtrに直す。