Wallpapper Engineみたいなのを作りたい その1

初めましてらいちです。
一昨年ぐらいに流行ったアプリ
Wallpaper Engine
あれの仕組みが気になっていろいろ探りながら自作できないかと。

##Wallpaper Engineとは
いわゆる壁紙の拡張
普通壁紙は静止画が常識ですが
このアプリを使えば動画等を壁紙にしたりできる
約400円の有料ソフト
私はまだ買っていませんが()

でこいつがどんな仕組みで動いてるか気になったけどそもそもソフト買ったわけでもないから検証もできない
でもなんとなく想像はできる

__用は壁紙を何かしらで上書きしてやればいいんだ__

まぁ結局いろいろやってみないとわからん

#Windowsでの描画の基礎
WindowsではGDI(Graphics Device Interface)というグラフィックス描画機能を提供している。
(Direct CompositionとかいうのもWindows 8辺りから追加されたけど割愛)
で、このGDIを使うためにはデバイスコンテキストのハンドルを取得する必要があると
>デバイスコンテキストとは
>Windowsが描画するための情報を管理しているメモリ上のデーターベースで
>ハンドルはそのデーターベースを指すポインタのことである
でこのデバイスコンテキストはウィンドウハンドルから取得できるので壁紙を描画しているウィンドウのハンドルを取得してしまえばいいんじゃないかと。

#DesktopWindow
探してみたらあった
[GetDesktopWindow](https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getdesktopwindow)
こいつで取得して試しに描画してみよう

```cpp:desktop_draw.cpp
//必要なライブラリを読み込むVC++コンパイラへの命令
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")

#include <iostream>
#include <functional>
#include <Windows.h>

int main() {
//デスクトップウィンドウの取得
 HWND deks = GetDesktopWindow();
 //デスクトップウィンドウからデバイスコンテキストの取得
 HDC desk_hdc = GetDC(desk);
 
 //描画用ペン&ブラシの用意
 HGDIOBJ old_pen = SelectObject(desk_hdc, GetStockObject(BLACK_PEN)); //黒のペン(周りの描画用の色)
 HGDIOBJ old_brush = SelectObject(desk_hdc, GetStockObject(BLACK_BRUSH));//黒のブラシ(塗りつぶしの色)
 
 //四角の描画

 Rectangle(desk_hdc, 100, 100, 500, 500);
 
 //元のペン&ブラシに戻す
 SelectObject(desk_hdc, old_pen);
 SelectObject(desk_hdc, old_brush);

 //デバイスコンテキストを取得したら必ず終わりにリリースする
 ReleaseDC(desk, desk_hdc);
}
```

こんな感じですね
最初の`#pragma comment(lib, "ライブラリ名")`はライブラリ使用の宣言みたいなもので
`user32.lib`と`gdi32.lib`を使用します
`user32.lib`はウインドウ操作やメッセージ、入力処理とかのライブラリで
`gdi32.lib`はGDIを利用するためのライブラリ
`.lib`はあまり聞かないかもだけど用は`.dll`の中の関数を使うよってこと
`user32.dll`や`gdi32.dll`なら聞いたことあるでしょ
(C#書く人はきっと`[DllImport("user32.dll")]`とか書いたことあるはず)
でそのdll関数へのリンクに必要なファイル

でコンパイルして実行!!
![2019-03-18_131856.png](https://qiita-image-store.s3.amazonaws.com/0/379631/2ffadace-dde8-a9ef-96da-8bc7ac76ec56.png)
おおお黒い四角が描けた!!これで行ける!!!
(ちなみに1度しか描画してないから再描画が起こると消えるよ)

ってちょっと待って?
![2019-03-18_132143.png](https://qiita-image-store.s3.amazonaws.com/0/379631/f370b661-e171-bb8a-e4cd-c88f68fe2e8d.png)
なんでChromeさんの上に描画されるん!!
ほんとは下に描画されてほしいのに
で調べてみたらGetDesktopWindowで取得されるウィンドウはどうやら本当にデスクトップみたいで?こいつに描画すると最前面に描画されるみたい(描画順序によるのかも?)
![2019-03-18_132803.png](https://qiita-image-store.s3.amazonaws.com/0/379631/b77d89e4-8616-17dc-623d-39d106cb3757.png)
Spy++で見たところのこのすべてのウィンドウの親ウィンドウなんだとか?
じゃあどうすりゃいいねん!!

#壁紙ウィンドウ
で壁紙を描画してるウィンドウが見つからない
で、ふと思い出した、Spy++にはD&DでWindowを探す機能がある!!
![2019-03-18_133710.png](https://qiita-image-store.s3.amazonaws.com/0/379631/4abf2256-66d4-8644-8ab5-649c2973bea0.png)
Folder View ???
ってかなんか階層になってるなぁ、、、
まぁ物は試し。

```cpp:folder_view_draw.cpp
//必要なライブラリを読み込むVC++コンパイラへの命令
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")

#include <iostream>
#include <functional>
#include <Windows.h>

int main() {
//デスクトップウィンドウの取得
 HWND fv = reinterpret_cast<HWND>(0x00010234);//ウィンドウハンドルを直接記述
 //デスクトップウィンドウからデバイスコンテキストの取得
 HDC fv_hdc = GetDC(fv);
 
 //描画用ペン&ブラシの用意
 HGDIOBJ old_pen = SelectObject(fv_hdc, GetStockObject(BLACK_PEN)); //黒のペン(周りの描画用の色)
 HGDIOBJ old_brush = SelectObject(fv_hdc, GetStockObject(BLACK_BRUSH));//黒のブラシ(塗りつぶしの色)
 
 //四角の描画

 Rectangle(fv_hdc, 100, 100, 500, 500);
 
 //元のペン&ブラシに戻す
 SelectObject(fv_hdc, old_pen);
 SelectObject(fv_hdc, old_brush);

 //デバイスコンテキストを取得したら必ず終わりにリリースする
 ReleaseDC(fv, fv_hdc);
}
```

前のとほぼ一緒ですが
ウィンドウハンドルはSpy++が示してた値を直接入力しました(自分の環境で試す場合はご自身でお調べください。エクスプローラーが起動する度に代わります)
では実行
...
何も表示されない...
試しに色を変えてみよう
`BLACK_BRUSH`を`WHITE_BRUSH`に
![2019-03-18_135423.png](https://qiita-image-store.s3.amazonaws.com/0/379631/c4e45718-fe87-68b5-2a65-cd0988b45be6.png)
おお！ちゃんと下に描画されるじゃん!!
で、Folder Viewってことはだよ?
もしかするとデスクトップのフォルダーとかを描画してるウィンドウなのかな?
と思ってフォルダーを動かしたりしたらドンピシャ!
再描画が走って四角は消えた!ついでに描画範囲にフォルダーがあったらそいつも上書きされた
なるほどね?でもそのフォルダーとかが消えたら壁紙じゃないな
じゃあこのウィンドウの一番親ウィンドウの`WorkerW`ウィンドウ?こいつに描画してみよう
コードはウィンドウハンドルを書き換えただけなので割愛
で、まぁ実際描画されないんですね
はい。
でよく見るとWorkerWウィンドウ二つあるじゃないですか
もう片方のWorkerWウィンドウに描画してみたところ描画されました。
黒も描画されました
しかもちゃんと壁紙の上に描画されていて
壁紙を変更したとき消えました
はい。
さらに謎ですね
でそっちのウィンドウに送られるメッセージを見てみたところ
![2019-03-18_142402.png](https://qiita-image-store.s3.amazonaws.com/0/379631/23bb458b-cd96-43ba-6d7a-1df7d7dd7d2b.png)
デスクトップをアクティブにした場合と壁紙を変えた場合にメッセージが送られてます
`WM_WINDOWPOSCHANGING`はまぁ最前面に移動させようとしてるメッセージですね
壁紙なので無視するメッセージでしょう
`WM_WINDOWPOSCHANGED`も同様
[WM_WINDOWPOSCHANGING ](https://docs.microsoft.com/ja-jp/windows/desktop/winmsg/wm-windowposchanging)
[WM_WINDOWPOSCHANGED](https://docs.microsoft.com/ja-jp/windows/desktop/winmsg/wm-windowposchanging)
`WM_ACTIVETEAPP`はアクティブ化されようとしてるときと非アクティブ化されようとしているときに送られるメッセージですね
[WM_ACTIVETEAPP](https://docs.microsoft.com/ja-jp/windows/desktop/winmsg/wm-activateapp)

まぁこれらは全ウィンドウ共通で送られるはずです。
で、次に気になるメッセージが。
`message:0xC1BD [登録された:"ForwardMessage"]`
これが壁紙を変えたときに送られているんですね
壁紙を変えろ!って
ちなみにSpy++で表示されるウィンドウの順序は実際のウィンドウの順序と一致しているのでまぁ下のWorkerWが壁紙描画
その上のWorkerWがフォルダーの描画といったところでしょうか
つまりそのWorkerWに描画してあげればいい感じですかね

では次回からいろいろ試行錯誤していきましょう

[Wallpapper Engineみたいなのを作りたい その2](https://qiita.com/Raitichan82/items/cdcea85ddfe7d618ef94)