はじめに
この記事ではWallpaperEngineのようにWindows端末の壁紙を動かすための実装方法について書きます。
やり方は、壁紙とアイコンの間にあるウィンドウのデバイスコンテキストを取得して書き換える、というものです。
Win32APIを使用するので、DLLのインポートの記述が必要です。
参考にしたもの
Draw Behind Desktop Icons in Windows 8+
Natsunekoさんのgit
最小コードの内容
// 1 「壁紙変えたよ」メッセージを発行する
var progman = User32.FindWindow("Progman", null);
User32.SendMessageTimeout(progman, 0x52C, new IntPtr(0), IntPtr.Zero, 0x0, 1000, out var result);
// 2 Workerwウィンドウのハンドルを取得する
handle_workerw = IntPtr.Zero;
User32.EnumWindows((hwnd, lParam) =>
{
IntPtr shell = User32.FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null);
if (shell != IntPtr.Zero) handle_workerw = User32.FindWindowEx(IntPtr.Zero, hwnd, "WorkerW", null);
return true;
}, IntPtr.Zero);
// 3 WorkerWのDCを取得する
dc_workerw = User32.GetDCEx(handle_workerw, IntPtr.Zero, 0x403);
// 4 壁紙にしたい描写をしたDCを用意する
DoSomething();
// 5 WorkerWのDCに第6引数の内容をコピペ
GDI32.BitBlt(dc_workerw, 0, 0, w, h, dc_form, 0, 0, 0x00CC0020);
// 6 デバイスコンテキストを開放する。
User32.ReleaseDC(handle_workerw, dc_workerw);
用語解説
ウィンドウハンドル
ウィンドウに割り振られた番号のこと。これを使うことでアプリケーションのウィンドウを操作できるよ。
DC(デバイスコンテキスト)
ウィンドウごとに存在してる、描写をするやつ。BitBlt関数とかで書き換えることが出来るよ。
1の解説
Gerald Degeneve氏の「Draw Behind Desktop Icons in Windows 8+」からコードを使用しました。
Windows8以降、壁紙とアイコンの描写は同じWindowで行われています。このままではアイコンを塗りつぶしてしか描写することは出来ません。しかしシステムが壁紙を変えたとき送られる、「0x52C」というメッセージがプログラムマネージャー(progman)に送信されると、この壁紙とアイコンの描写に割り込むウィンドウ「WorkerW」が作成されます。これはGerald Degeneve氏の記事で紹介されているものです。このウィンドウは再起動するまで存在します。
2の解説
メッセージにより作成されたWorkerWのウィンドウハンドルを取得します。WorkerWさんはたくさんいらっしゃるので、Shellの近くにあるものを取得しています。
3の解説
とくにないです
4の解説
描写したい内容を描写したデバイスコンテキストを用意します。僕は画面と同じ大きさの、透明のフォームを作成し、それのデバイスコンテキストを使いました。フォームにピクチャボックスを作成してそこの画像をいじってます。それらの設定はこんな感じにするとうまくいきます。
//上のX削除
form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
//透明にするよ
form.Opacity = 0D;
form.Location = new Point(0, 0);
form.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
form.pictureBox1.Location = new Point(0, 0);
form.pictureBox1.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
5の解説
ここが描写処理です。第6引数の描写内容が第1引数にコピペされます。
5を無限ループにすると、用意したデバイスコンテキストが書き換わったときに壁紙も書き換わるようになります。
6の解説
開放しないとまずいかもしれないっぽい?温室育ちなのでよくわかんないです
最後に
https://github.com/utasimaru/ProgrammableWallpaper
https://github.com/utasimaru/DllTest
描写処理だけを記述したDLLだけで動かせる、壁紙のソフトをちょびちょび書いてます。DrawClassというクラスの、引数なし返り値なしの関数「DrawStart」と引数なしでbitmap型を返り値にする「GetDrawBitmap」を読み込んで、適宜呼び出してくれます。興味があったら見てみてください。