トップ回答者
DWMを使った不定形ウィンドウを作成するには?

質問
-
現在、レイヤードウィンドウを使いD3Dでアニメーションするキャラクターを
デスクトップマスコットとしてデスクトップ上で表示させているのですが、
バックバッファからBitBitコピーしてレイヤードウィンドウで
表示させているため表示が遅くなるのでDWMでこれを代替出来ればと
思い情報を集めているところです。
DwmEnableBlurBehindWindowなどの関数を使うことにより
ガラス効果を得ることはできるのですが
透過部分の完全透明化、クリック透過が出来ずに困っています。
DWMでこれが可能なのかすら分からない状況なので
方法や詳しいサイトなどありましたらご教授おねがいします。
当方使える言語がVC++のみで英語はちんぷんかんぷんなので
考慮していただけると助かります。
回答
-
WPFはDWMに内包されているのかなと思ってたのですが別物なのでしょうか?
あえて書けば,
DWM は,デスクトップ上の ウィンドウ(・フレーム) を ビジュアル(・ツリー)化 するためのカラクリで
WPF は,ウィンドウの クライアント領域内 を ビジュアル(・ツリー)化 するためのカラクリです。
なので,
(皮 と 中身 という感じではあるけれど,それは)
いわゆる内包ではないですね。
WPF には Window クラス が出てきますが,
いわゆるWindowとWPFを繋ぐヘルパークラスで
WPF自体は,そのクライアント領域を扱うものになります。
枠が無いサンプルなどは枠が透明になっているのではなく,
クライアント領域だけにしているだけです。
たぶん,
DWM も WPF のどちらも
求めているもの/想定していたものと違うんじゃないかと思います。
稍丼 / yayadon- 回答としてマーク SHUN 2009年12月20日 21:32
-
DWM が接頭語につく関数では、完全な透明を実現する新たな機能はなかったと思っています。(私が思っているだけで、間違っている可能性あり)
Layered Window と Direct3D の関連で遅いという話は、多分、下記の投稿で言われているような内容だろうと推測します。
http://social.msdn.microsoft.com/Forums/ja-JP/wpfja/thread/e4ffb2ab-35c5-4d4c-9862-917874d10752#c8bd292e-1de5-4e98-8d56-54d94c2c1398
現状はどうなったのか、WPF を介さない場合にも有効なのかは申し訳ありませんが、存じません。
なお、Region を作る手法であっても、一旦ビットマップに落とさざる得なく、変換コストがある点は同じです。
逆に Layered Window と異なって、Region での切り抜きは半透明ができないなどの弊害があります。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。- 回答としてマーク SHUN 2009年12月20日 21:34
すべての返信
-
OS が XP なもので、DwmEnableBlurBehindWindow 関数等に関しては無知ですが・・・。
Direct3D で作成した画像をビットマップ等に変換でき、アニメーション抜きの、独自の形をしたウィンドウでいいなら SetWindowRgn 関数で実現出来ます。
ウィンドウ形状を変える2
http://www.japan-iss.co.jp/?p=433
アニメーションを行うのであれば、全てのフレーム分の HRGN を作成し、SetWindowRgn と Invalidate を繰り返す事になるでしょうか・・・ ( Windows が想定していない無茶な処理かもしれませんが ) 。
ただし、WM_NCHITTEST ハンドラ等を実装して、閉じるボタンの位置等を指定しないと終了出来なくなりますが・・・。
追記
よく見ればアニメーションについても触れてありますね、このページは。- 編集済み ミッヒー 2009年12月5日 2:58 ウィンドウ名セージ名誤記修正
-
DWM が接頭語につく関数では、完全な透明を実現する新たな機能はなかったと思っています。(私が思っているだけで、間違っている可能性あり)
Layered Window と Direct3D の関連で遅いという話は、多分、下記の投稿で言われているような内容だろうと推測します。
http://social.msdn.microsoft.com/Forums/ja-JP/wpfja/thread/e4ffb2ab-35c5-4d4c-9862-917874d10752#c8bd292e-1de5-4e98-8d56-54d94c2c1398
現状はどうなったのか、WPF を介さない場合にも有効なのかは申し訳ありませんが、存じません。
なお、Region を作る手法であっても、一旦ビットマップに落とさざる得なく、変換コストがある点は同じです。
逆に Layered Window と異なって、Region での切り抜きは半透明ができないなどの弊害があります。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。- 回答としてマーク SHUN 2009年12月20日 21:34
-
他の部分に関しては既にレスがついているようなのでちょっとだけ。
御本人の年齢等が分からないのでもし英語を学習する前の年齢ならご勘弁いただきたいのですが、
この手の技術文章の最新版は基本的に英語ですし、皆が使うと言うわけではないようなライブラリに関しても
説明文書が英語である事は、珍しく有りません。
なので英語はちんぷんかんぷんと言わず、辞書を片手にでも読むことをお勧めします。
私もけして英語が得意と言うわけでは有りませんが、辞書を片手に読むようにしています。
また、技術文章なので難しい文法が使われていたりはしません。
中学生程度の知識とあとは辞書と根気があれば、内容を把握するくらいはできると思いますよ。
翻訳する必要は有りません、内容が分かれば良いのですから。
日本語に翻訳しようとするとえらい目にあいますが、
内容を読み取るだけと割り切れば何とかなると思います。
解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。 -
ミッヒーさん、Azuleanさん、PATIOさんレスありがとうございます。
D3D画面をビットマップ化したリージョンやレイヤードウィンドウはすでにやってはみたのですが
書かれている通りビットマップ化にかかるコストが大きすぎて動作環境が限定されてしまうので
どうせ限定されるならとDWMによる描写に走ってみた次第です。
WPFはDWMに内包されているのかなと思ってたのですが別物なのでしょうか?
私的にはWPFの2D・3Dの描写が1元化されることを利用したレイヤードウィンドウを作ろうと
思っていたので、XAMLを使わない(Xファイル形式の3Dデータを使った)
WPFプログラミングの手法が見つかれば十分発展可能なのですが見つからずに右往左往しています。
エキサイト翻訳を助けに海外サイトも見てはいるのですが有用な情報かを判断するするだけでも
4・5日かかってしまうので日本語の関連情報サイトがあればと探し回っていたところ
WPFの描写の仕組みの詳細を知ることが出来たので、これをなぞる形で組み立ててみたいと思います。
自分自身の無知も相まってまだまだDWM・WPFの情報が十分でないので
来週一杯様子を見させていただき追加情報がない場合、回答マークをつけてレスを閉めさせて頂きます。 -
WPFはDWMに内包されているのかなと思ってたのですが別物なのでしょうか?
あえて書けば,
DWM は,デスクトップ上の ウィンドウ(・フレーム) を ビジュアル(・ツリー)化 するためのカラクリで
WPF は,ウィンドウの クライアント領域内 を ビジュアル(・ツリー)化 するためのカラクリです。
なので,
(皮 と 中身 という感じではあるけれど,それは)
いわゆる内包ではないですね。
WPF には Window クラス が出てきますが,
いわゆるWindowとWPFを繋ぐヘルパークラスで
WPF自体は,そのクライアント領域を扱うものになります。
枠が無いサンプルなどは枠が透明になっているのではなく,
クライアント領域だけにしているだけです。
たぶん,
DWM も WPF のどちらも
求めているもの/想定していたものと違うんじゃないかと思います。
稍丼 / yayadon- 回答としてマーク SHUN 2009年12月20日 21:32
-
# Windows 7 / Vista SP2 & WDDM 1.1 前提になってしまいますが...
なんか偶然にも,MSDNマガジンの今月号(2009年12月号)の記事
Layered Windows with Direct2D
http://msdn.microsoft.com/en-us/magazine/ee819134.aspx
に比較が載ってますね。
最初に GDI の問題点,次に WPF の問題点 を挙げた後に,
Direct2D の話に持ってきて,
いろいろやり方があるけれど,なんたらかんたらで,
要するに,
WIC を使ったビットマップ操作 か
Direct3D の テクスチャ に Direct2D を使ったもので
描画するのがパフォーマンスが良いんじゃないか
のような記事になってます。
記事の途中で,(逆な書き方で出てきますが)
ID2D1HwndRenderTarger でウィンドウに直接書いている場合は,
ハードウェア・アクセラレーション が 自動的に 効くようなことも書いてあります。
-----
しばらくすれば,日本語訳が出るんじゃないかと思います。
Direct2D の日本語情報は少ないですが
検索すれば見つかります。
# Kenny Kerr氏の最後のMSDNマガジンの記事のようです。
# というか,MSDNマガジン自体が終わるのかしらん。
稍丼 / yayadon- 編集済み yayadon 2009年12月17日 18:33 追記
-
検索していたらたまたま発見しましたので、参考までに私がとあるソフトで使用した方法を書かせていただきます。
大変長くなってしまい申し訳ありませんが、面倒でなければお目をお通しください。私のソフトでは、そちらで検討されているとおり、DwmEnableBlurBehindWindowを使用してガラス効果を設定しています。
このとき、ガラス効果を設定するリージョンを渡しますが、
このリージョンを「クライアント領域外」を差すように作成してあげると、
クライアント領域にはガラス効果が行われず、完全透明になります。
空のリージョンを渡すとエラーとなり透明になりませんので、
(-1, -1) - (0, 0) など、1x1 pixel のクライアント領域外を指定してください。
その後、DwmExtendFrameIntoClientArea を使用して実際にクライアント領域へガラス効果を拡張し、
ここで初めてクライアント領域が完全透明にできるようになります。ここまでは実際にやられている方も多いようですが、みなさん透明部分にマウスが反応してしまうことを
回避することがなかなかできないようです。
私の最初の発想としては、マウス関連のグローバルフックを行い、
マウスメッセージをリダイレクトすることでした。
――が、これもなかなか難しく、いろいろと制限が出てきてしまいます。
その後、研究を続けた結果、充分に実用的な手法を作成できましたので、紹介させていただきます。使用するのは、SetWindowsHookEx の WH_MOUSE_LL です。
グローバルフックですが、このフックは特殊で、DLL 内になくてもグローバルフックが可能、かつ、他のプロセスに DLL を注入しません。
このフックを行う際には、優先度の高い専用のワーカースレッドを作成し、SetWindowsHookEx をその中で行ってください。
これは、WH_MOUSE_LL のフックプロシージャが、SetWindowsHookEx を行ったスレッドへの何らかのメッセージ送信により呼び出されるためで、
普通にメインスレッドなどから作成すると、ブロック処理をした際など、フックの処理までブロックされてしまいます。
このフックは OS 側の設定により、一定時間でタイムアウトしてスキップされるので、まったく操作不能になることはありませんが、
マウスの反応が大変鈍くなるので、なるべく処理が止まることのないよう、専用のスレッドを作成した方が無難です。
フックプロシージャが呼ばれたら、まず自分の疑似レイヤードウィンドウの矩形範囲内にあるか判定し、
範囲外で有れば関係ないので次のフックチェインを呼び出します。範囲内であれば、ウィンドウに表示している内容のα値を参照し、自前で HitTest を行います。
私の場合には、排他制御の影響で反応が鈍くなるのをなるべく避けるために、HitTest 専用の Direct2D バックバッファを作成し、そちらの内容を参照しています。
Direct2D バッファの内容を参照するにはいくつかの方法が考えられますが、実験の結果良いと思われた手段で、現在私のソフトでは、
ID2D1GdiInteropRenderTarget を取得し、GetDC を行った後、1x1pixel の DIBSection に 該当座標を BitBlt で転送し、内容を参照しています。HitTest の結果が False のとき、裏にあるウィンドウにメッセージが行くようにしなければなりませんが、
SendMessage や PostMessage を使用してリダイレクトするのはなかなか難しく、研究の結果、
WS_EX_TRANSPARENT を指定したウィンドウがマウスに反応しなくなることを使用することにしました。
トップレベルウィンドウでは、このスタイルは単独ではなにも効果を及ぼしませんので、
WS_EX_LAYERED を指定してウィンドウを作成しておきます。
それだけでは動作しませんので、作成後一度 SetLayeredWindowAttributes を行っておきます。
この設定で作成すると本来の (GDI 経由での) 描画の結果が表示されるのではないかと心配されるかと思いますが、
Direct2D の HwndRenderTarget や、Direct3D を使用した場合、そちらの方が優先的に表示されるので、問題ないようです。
ただし、OS 側が行う HitTest は、ここでの設定の影響を受けますので、
第2引数のカラーキーに、ARGB(0, 0, 0, 0) 以外のα値が 0 のカラーキーを指定しておきます。
第4引数は、LWA_COLORKEY を指定してください。それから、ウィンドウクラスの背景ブラシには、BLACK_BRUSH を指定しておきます。
こうしておくことで、何らかのタイミングで GDI 経由の描画結果が表示されようとしたとしても、
α値が 0 の色で塗りつぶされているので、何も表示されません。
SetLayeredWindowAttributes のカラーキーを 0 にしていると、カラーキーとマッチしてまるっきりマウスに反応しなくなりますので注意してください。この下準備をした後、自前の HitTest で結果が False のときには、SetWindowLongPtr で、WS_EX_TRANSPARENT を追加してから
次のフックチェインを呼ぶと、マウスに反応しない状態で続く処理が行われますので、
裏にあるウィンドウにメッセージが送信されるようになります。
自前の HitTest が True のときには、SetWindowLongPtr で、WS_EX_TRANSPARENT を削除してから次のフックチェインを呼ぶことで、
自分のウィンドウにメッセージが来るようにすることができます。この実装で、私のソフトウェアでは、Direct2D の HwndRenderTarget を使用して描画周りをすべてハードウェア処理させたまま、
レイヤードウィンドウとほぼ同等の振る舞いをさせることに成功しました。
難点としては、専用スレッドを生成しているものの、状況によってはコンテキスト切り替えが滞り、マウスの反応が鈍くなってしまうこと、
あとはデバッガでのトレース作業が困難なくらいでしょうか。
デバッガでスレッドが停止しますので、ブレーク中にはマウスの動きが飛び飛びになってしまいます。以上、Direct2D で話を進めてきましたが、Direct3D でもまったく同様の手法で実現できるようです。
(Direct2D は Direct3D を簡単に使用するためのユーザモードラッパーライブラリなので、当然ですが)
この情報でうまく実現することができましたら幸いです。 -
検索したら出てきた古いQAですが、Windows8.1で状況が変わっているようなのでリプライしておきます。
現在、MSDNマガジン上でKenny Kerr氏が「Windows と C++」の連載を再開されていますが、最近の連載ではDWMの合成APIを連続して取り扱っています。特に「Windows 合成エンジンを使った高パフォーマンスのレイヤード ウィンドウ」の記事は本QAのために書かれたような記事です。他の連載もDWMとDX2D/3D関連なので、一連の記事の一読をお勧めします。