.log

miscellaneous memorandum

C#でデスクトップに貼り付くWindowを作る

September 9, 2012

いやー、なんか積年の疑問が解消するっていうのはうれしいものですね。 (積年ってほどじゃないですけど)

以前、デスクトップの壁紙を変更するツールを作っていたのですが、 元々は壁紙全体を書き換えるんじゃなくて、RainmeterとかのようにデスクトップにくっついているWindowで実現したかったんですね。そうすると、何らか操作も受け付けますし、他にもいろんなものを表示できるかなぁとか考えてたのです。 となると調べることと言えば、Windowを最背面に配置して、アクティブになったとしても前面に来なくするにはどうしたらいいかですよね。ただ、標準では最前面=TopMostは出来ても、最背面=BottomMostっていうのはないんですね。 で、調べると大抵、

  • FindWindowでProgram Managerのハンドルを取得
  • SetParentでFormの親をProgram Managerに指定 というやり方が紹介されています。まあ、SetParentで別プロセスの子ウインドウにするのはトラブルが起きる可能性があるかと思いますが、まあ簡単にできるのでいいですよね。

ところがです。 うちの環境では上手くいかないんです。 単純に、FindWindowとSetParentの宣言、FormのLoad時にProgram Managerを探してSetParent、だけのコードで試すわけですが、ちゃんとハンドルも取れてるし、SetParentも効いている、だけど肝心のFormが見えなくなるのです。見えなくなるだけで、一応ちゃんと機能はしてるっぽいのです。 64bitだから?とかで色々調べまくった結果、どうもそうじゃない。そういえば、ノートPCは32bitだったなと思って、全く同じコードで試したらこれまたでないんですよ。

ここまでが前回のお話し。もう調べまくっても何も分からなかったので、結局壁紙自体を書き換えることで対応しました。で、自作の壁紙ツールで壁紙が変わるところを見ながら、ふつふつとまた「デスクトップにWindowを貼り付ける」に再チャレンジしたくなり、また調べておりました。

が、やっぱりWindowが見えないのです。 やっぱり引っかかるのは、32bit/64bitの違いか、なんかVisual C#の問題か、ぐらいしか思いつかず色々調べていたら、ビルド時のx86とかx64, Any CPUがどう違うのかとか、その辺が理解できました。多分この辺の脱線で、1,2時間使ってます。 まあ、袋小路に入ったら落ち着いてスタートに戻るのがよい、というのが定石でしょうから、とりあえずもう一回、新しくFindWindowやらSetParentだけのプロジェクトにして、64bitと32bitで試してみることにしました。

64bitはやっぱり見えなかったのですが、なんと32bitでは見えたのです!! でないと思って動かしたものですから、マジで二度見しましたよ。 プログラムとかやってると、こういうなにかきっかけが見つかる瞬間って、なんか人生の重荷が全部消えるぐらいうれしいですよね。 前はだめだったのに今大丈夫なのはなぜか、何が違うのかめちゃくちゃ考えましたね。

で、気がついたのが、Windows Aero。

普段、全然意識せずにAero有効で使っていたのであんまり気にしてなかったのですが、よく見るとノートPCの方のタイトルバーが透けてない……Aeroが無効になっている。 で、64bitの方もAero無効にするとですね……これが表示されるようになったんですねー、これはきっと空も飛べるはずですよ。 原因はAeroだったんですね。さあもうこれで解決目前です、

  • Windows7 のデスクトップウィンドウ http://www.orangemaker.sakura.ne.jp/labo/memo/sdk-mfc/win7Desktop.html (非常に端的なわかりやすい解説、ありがとうございます) これですよ、どんぴしゃですよ。 Aeroが有効になると、デスクトップウインドウを扱う親ウインドウが変わるんですねぇ。そりゃあProgram Managerにぶら下げても見えなくなるわけです。 あとはここで紹介されているコードを参考に、SHELLDLL_DevViewがぶら下がっているWorkerWかProgmanを探して、SetParentでそこの子ウインドウにしてやれば良いということがわかりました。

EnumWindowでウインドウハンドルを総当たりして、WorkerWかProgmanを見つけたら、そこにSHELLDLL_DefViewがぶら下がっていたらそのWorkerWかProgmanのハンドルでSetParentする、と言うコードを書いたら、見事AeroでもFormが表示されました。

もうしばらくは、何か全てをやりきったかのような晴れ晴れしさと、脱力感が非常に心地よかったですね。

あとはもう、ウインドウの枠を非表示にしてもマウスドラッグで移動できるようにしたり、サイズを変更できるようにしたり、いろんなコントロールを貼り付けて表示を試してみたりしてました。 次に気になるのは、Aero状態のデスクトップの子にしたFormの透過ですね。なんか透過の部分はデスクトップ側で管理しているようで、勝手に黒が透過されます。Form側で透過色を設定したり、Opacityを変えるとおかしくなります。 しかも、透過される黒も、コマンドボタンの文字は透過されるんですが、ラベルの背景に指定した黒は透過されない、とかなんかパターンがあります。まあ、この辺は今のところどうでもいいので、デスクトップに貼り付くFormを使って壁紙を実現してみようと思います。

追記

そういえば、デスクトップの壁紙を変更するツールここで悩んでいた、Program Manager周りの動きって、今回のと関係あるのではないかな……?