【原创译文】如何编码实现Windows炫酷桌面壁纸软件-开源的桌面动态壁纸软件的实现原理


2021-07-02更新:

已找到实现动态桌面及任务栏透明的开源项目/开源成品软件,该软件还随手实现了任务栏毛玻璃和透明效果(在该软件的设置界面可自行设置)。

软件成品:Win10系统-微软商店-搜索"Lively Wallpaper"-安装。

该软件的开源项目地址:GitHub地址Gitee地址

我安装后研究了下,使用的正是本文中的方法。该壁纸使用 CEF内核(即谷歌浏览器的轻量级内核)播放音视频,CPU占用较高。如下图,是使用 spyxx 软件查看到的该动态桌面软件的窗口句柄。

源码面前,了无秘密。

如果你对动态桌面感兴趣,或者觉得利用桌面可以做出更多更有意义的东西,可以研究该开源项目的源代码。


原文:https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

翻译:https://blog.csdn.net/qilei2010

译注:原文发布于2014年,讲解了如何在桌面图标层和壁纸层之间绘制图形和自定义窗口,适用于Windows 7/8/10,开启Areo效果后的Windows7不适用,但有其它方法。本文中,作者详细讲述了自己探索和找到此解决方案的过程。因为本文讲的是原理,所以一旦掌握,就能在桌面壁纸之上,桌面图标之下插入自定义窗口,然后Everythings is possible!可以通过在窗口中调用浏览器/Chrome内核,再结合GIF播放动图、HTML5播放视频、Three.js控制3D模型等,就能轻松实现超级炫酷的动态壁纸,还能鼠标点击交互。而这一切的起点就在本文的原理之中。

译文:


在Windows8+系统的桌面图标下绘制图形和窗口

简介

你可能听说过 DreamScene,这是Vista的一个特色功能,它可以将.dream格式的视频列表作为电脑桌面背景。还有一个叫 rainmeter 的工具软件,它能让你在电脑桌面内的 不同层(最顶层、顶层、底层)上放置 widgets/gadgets/或者其它任何东西。还有winamp,我发现它是第一个在桌面图标层下面用 DirectX 做图形渲染的软件。

这些工具有一个共同点,就是不支持win8,或者说至少“在桌面图标下绘制(图形和窗口)”这部分功能不支持win8。

更正:本文所述方法适用于win10。

工作原理(在XP、Vista、7中)

下图是windows的树状展示的所有窗口【译者注:原文此处无图,我已补上】。这棵树包含了电脑桌面上所有显示的隐藏的窗口。有个工具Spy++,(Visual Studio -> Tools -> Spy++),可以查看所有的窗口列表。这个工具是 Visual Studio 的一部分。【译者注:可以百度下载Spy++,一个小软件】

这棵树的最后一个叶子节点就是 Program Manager 。这个窗口代表整个Shell。在XP、Vista以及关掉Aero的win7中,Program Manager(Progman)包含一个窗口:SysListView32,它就是桌面图标所在的窗口。所以,如果你把Program Manager设置为 父窗口,你就能把自己创建的窗口放在这些桌面图标的下面。

原文更正:在(原文)评论里你可以找到如何应对 开启Aero的win7的办法,见这里

这里有个非常棒的文章,讲解怎么样在 桌面图标的上层和下层 绘制窗口。

但是我一直没有找到适用win8的方法。

Win8遇到的问题

win7和8很像,xp的办法适用win7,前提是把7的Areo效果关掉。但是对于8,你关不掉aero,所以只能用其它办法。

下图是window8窗口树形图。【译者注:win10和win8一样,Progman不再是SHELLDLL_DefView的父窗口】

SysListView32 已经从 Program Manager中分离出来了。这还不是最头疼的。

最头疼的桌面背景。现在它和桌面上的图标融合了。所以我们只能把包括图标在内的所有东西重绘一遍,或者只能在包括桌面背景在内的所有东西的下层绘制窗口。这样的话,你就不可能把自己创建的窗口放在桌面图标和桌面壁纸之间了。我试过所有办法。

一种方案

灵感来自 personalization dialog 。当你手动设置壁纸时,你不会看到壁纸突然就切换了,而是系统使用了一个平滑的消退动画效果。我认为,这个动画只有可能是系统在桌面图标层的下面使用某种方法绘制出的窗口形成的,不然粗暴的切换壁纸很慢也很丑。

故我用Spy++,打开personalization dialog,然后切换壁纸。它出现了,当我切换壁纸时,一个新的 WorkerW 窗口在持有桌面图标(即SysListView32)的 WorkerW 实例和 Desktop Manager 之间被创建了。

我找到这个新创建的 WorkerW 的窗口句柄,放入我的测试程序,它确实能在桌面图标的下面被绘制,并且是直接在壁纸的上面。

但还有个问题。当我关闭 personalization dialog, 那个新 WorkerW 就找不到了。

我必须找到触发 这个 WorkerW 窗口创建过程的方法。

在Spy++中,这个窗口是 Program Manager 的相邻节点,也是它的子节点,看起来像是 Program Manager 创建了它。我用Spy++打开对Program Manager 的消息监控,找到了我想要的。

就在我点击切换电脑桌面壁纸时,Program Manager 收到大量消息。第一条消息是一个用户自定义,也没有文档描述的消息。XXX。

我扩充了下测试程序,将这条用户自定义的消息(0x052C)发送给 Program Manager。然后它就出现了我希望见到的。Program Manager 在收到该消息后,就创建了那个 WorkerW 窗口。

一切准备就绪后,我写了个Demo,来说明如何在桌面的图标下层绘制窗口,还有如何将一个窗口放在桌面图标的下层。

代码

(译注:原文桌面的代码使用的是 C#,下面均为C#代码)

获取Program Manager句柄

首先,找到 Progman (即Program Manager)窗口句柄。用 C#的 FindWindow()实现。

// C#
// Fetch the Progman window
IntPtr progman = W32.FindWindow("Progman", null);

向Program Manager发送消息

为了触发 WorkerW 窗口的创建,要给Program Manager发送消息。该消息没有文档定义,所以也没有 Windows API 名字,只有数字 0x052C 。使用Windows API方法 SendMessageTimeout 实现。

// C#
IntPtr result = IntPtr.Zero;

// Send 0x052C to Progman. This message directs Progman to spawn a 
// WorkerW behind the desktop icons. If it is already there, nothing 
// happens.
W32.SendMessageTimeout(progman, 
                       0x052C, 
                       new IntPtr(0), 
                       IntPtr.Zero, 
                       W32.SendMessageTimeoutFlags.SMTO_NORMAL, 
                       1000, 
                       out result);

获取新创建窗口的句柄

我们要获取刚创建的 WorkerW 窗口的句柄,但是Windows系统里面有很多 WorkerW 窗口,我们只能逐个顺序遍历了。可以使用 EnumWindows 函数遍历。

该函数为每一个顶级窗口提供了 EnumWindowProc 方法。从那个方法开始,我们可以检查当前遍历的窗口是否包含名为“SHELLDLL_DefView”子窗口,若有这个 SHELLDLL_DefView ,就表明当前窗口持有桌面图标。然后我们得到该窗口的下一个兄弟窗口。

// C#
// Spy++ output
// .....
// 0x00010190 "" WorkerW
//   ...
//   0x000100EE "" SHELLDLL_DefView
//     0x000100F0 "FolderView" SysListView32
// 0x00100B8A "" WorkerW       <-- This is the WorkerW instance we are after!
// 0x000100EC "Program Manager" Progman

IntPtr workerw = IntPtr.Zero;

// We enumerate all Windows, until we find one, that has the SHELLDLL_DefView 
// as a child. 
// If we found that window, we take its next sibling and assign it to workerw.
W32.EnumWindows(new W32.EnumWindowsProc((tophandle, topparamhandle) =>
{
    IntPtr p = W32.FindWindowEx(tophandle, 
                                IntPtr.Zero, 
                                "SHELLDLL_DefView", 
                                IntPtr.Zero);

    if (p != IntPtr.Zero)
    {
        // Gets the WorkerW Window after the current one.
        workerw = W32.FindWindowEx(IntPtr.Zero, 
                                   tophandle, 
                                   "WorkerW", 
                                   IntPtr.Zero);
    }

    return true;
}), IntPtr.Zero);

Demo1:在桌面图标和壁纸中间层绘图

有 workerw 窗口在手,有趣的事情才刚开始。第一个Demo就是使用 C# 的 System.Drawing 类画点东西。

【译注:此段关于多显示器,略】

注意:在此层绘制的一切将一直保留,除非你重绘、使之失效或者重置壁纸。

// C#
// Get the Device Context of the WorkerW
IntPtr dc = W32.GetDCEx(workerw, IntPtr.Zero, (W32.DeviceContextValues)0x403);
if (dc != IntPtr.Zero)
{
    // Create a Graphics instance from the Device Context
    using (Graphics g = Graphics.FromHdc(dc))
    {

        // Use the Graphics instance to draw a white rectangle in the upper 
        // left corner. In case you have more than one monitor think of the 
        // drawing area as a rectangle that spans across all monitors, and 
        // the 0,0 coordinate being in the upper left corner.
        g.FillRectangle(new SolidBrush(Color.White), 0, 0, 500, 500);

    }
    // make sure to release the device context after use.
    W32.ReleaseDC(workerw, dc);
}

Demo2:在桌面图标下放置Windows Form窗口

此Demo展示如何在图标下放置普通的Windows Form窗口。其实,只要将Windows Form的父窗口设置为我们的 WorkerW 窗口就行了。可以使用 Windows API 函数 SetParent 来实现。

注意:只有 form 已经被创建后,SetParent 函数才能使用。 form.Load 事件适合它。

为了减少代码,我没有用"Project->Add Windows Form..."对话框和设计器,直接创建了 Form。

// C#
Form form = new Form();
form.Text = "Test Window";

form.Load += new EventHandler((s, e) =>
{
    // Move the form right next to the in demo 1 drawn rectangle
    form.Width = 500;
    form.Height = 500;
    form.Left = 500;
    form.Top = 0;

    // Add a randomly moving button to the form
    Button button = new Button() { Text = "Catch Me" };
    form.Controls.Add(button);
    Random rnd = new Random();
    System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
    timer.Interval = 100;
    timer.Tick += new EventHandler((sender, eventArgs) =>
    {
        button.Left = rnd.Next(0, form.Width - button.Width);
        button.Top = rnd.Next(0, form.Height - button.Height);
    });
    timer.Start();

    // This line makes the form a child of the WorkerW window, 
    // thus putting it behind the desktop icons and out of reach 
    // for any user input. The form will just be rendered, no 
    // keyboard or mouse input will reach it. You would have to use 
    // WH_KEYBOARD_LL and WH_MOUSE_LL hooks to capture mouse and 
    // keyboard input and redirect it to the windows form manually, 
    // but that's another story, to be told at a later time.
    W32.SetParent(form.Handle, workerw);
});

// Start the Application Loop for the Form.
Application.Run(form);

你可能注意到 没有办法和这个 Form 交互,因为它的父窗口被设置为 WokerW 窗口。电脑桌面在被设计的时候,就被设计为没有可交互的子元素,所以所有的 鼠标移动、键盘输入或者其他的事件都不可能送达Form。

也有一种方案。你可以订阅 WH_KEYBOARD_LL 和 WH_MOUSE_LL 低级事件,就像 键盘记录工具和鼠标捕获软件做的那样。通过这些事件,你可以接收鼠标移动、点击和键盘按下事件,无论它们在哪里发生的。你可能必须把这些消息自己发送给你的 form 然后自己做点击区域判定。

结论

一个适用一切的命令。

// C#
W32.SendMessageTimeout(W32.FindWindow("Progman", null), 
                       0x052C, 
                       new IntPtr(0), 
                       IntPtr.Zero, 
                       W32.SendMessageTimeoutFlags.SMTO_NORMAL, 
                       1000, 
                       out result);


原文完,其它同主题文章一一列举如下:

1. CSDN:python 动态壁纸

用Pywin32实现的可直接运行的单文件源代码,简洁可用,亲测Windows10下有效,代码可直接粘贴使用。强烈推荐。

首先使用pip安装 pillow,pywin32,pyglet 三个库,再在 此处 下载个 GIF格式的壁纸,与.py文件放在同文件夹内,运行.py即可。

安装库命令: pip install pillow -i https://pypi.tuna.tsinghua.edu.cn/simple,其它库类似,替换库名称即可。

2. 知乎:如何实现一个 windows 桌面动态壁纸

   该文创作于2018年,比我翻译的2014年的原文较新,该文也参考了原文文章,但是知乎文章的内容却和原文矛盾,孰对孰错,未细研究。知乎该文有代码成品的 Github 链接,可下载研究参考。实现语言未研究。

3. 知乎:Python怎样实现Wallpaper Engine?

   该回答写于 2021年1月,参考了 原文。回答简单清晰。

4. CSDN:Windows编程之--桌面壁纸实现深入探索

   层层递进,分析透彻,最详细的一篇文章!

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值