一、杂言
作为一个编程爱好者,同时,也作为一个宅。电脑几乎是每天都必须碰上几个小时的。对于Win10的桌面,自己的心中多多少少也希望能好看一点。市面上有很多桌面美化软件,但基本都不符合我的心意,有美化的没整理功能,有整理功能的没美化。秉承着对电脑的爱好之心,还是自己动手,丰衣足食吧(无奈脸)。
(然并卵,虽然市面上有这么多的桌面整理和美化软件,而能查到得的资料却少到可怜。也许国外比较多。)
二、正题
先来了解一下win10的桌面底层。
用按键精灵的抓抓或者是spy++观察一下桌面上的窗口。
在这幅图中,SysListView32和SHELLDLL_DefView是管理和显示桌面图标的。Progman是显示出来的总桌面,也是总程序,如果你向Progman窗口发送一个WM_CLOSE消息,Windows就会提醒你是否要关机。
我们先试着单独隐藏一下Progman窗口。
然后,整个窗口就黑了。这说明,Progman的确是总桌面。
我们再试着单独隐藏一下SHELLDLL_DefView窗口。
桌面的图标就不见了,这说明SHELLDLL_DefView是负责显示桌面图标的。
而关于SysListView32,这个窗口是控制桌面图标的排列顺序的,如果要加上桌面整理功能的话,可能要用得上它。
通过以上的演示,我们发现,在Windows中,桌面壁纸的显示和图标的显示是分开的。我们可以利用这一特性(win7和win8是没有的),来实现动态壁纸。因为我们只要将我们的程序插入到壁纸和图标之中,就可以想显示什么就显示什么了。
但是,这里要打一下茬,因为,Win10有一个多桌面的功能。但凡只要一按下这个按钮:
Windows的桌面底层就变成这个样子。
一看,SysListView32、SHELLDLL_DefView窗口和Progman窗口分 家 了。而且还多了WorkerW窗口出来,而且不止一个。当初我也很奇怪,怎么就变成这个样子了。后来翻翻资料。其实,多一个WorkerW窗口是为了在切换桌面时能平滑过渡,而不是一闪一亮式的切换创建的。只要你把下面那一个没有子窗口的WorkerW关闭了,你就能体验到什么叫生硬的桌面切换。
其实这个样子,还是能够利用win10桌面的特性来达到我们的目的。但多少会造成影响(后面会说解决方法)。这里多说一句,我也不知道为什么,WorkerW其实是有很多的,如下:
这是我写的一个小程序找出来的WokerW窗口的数量和句柄,我也不知道这么多有什么用。
三、编写代码
Progman是显示壁纸的,我们只要把自己的窗口放在它之前就得了,就是把自己的窗口设立为Progman的子窗口。要注意隐藏没有子窗口的WorkerW窗口,它负责平滑过渡,也就是说它在Progman的前面绘制,我们必须隐藏它,才能正常显示我们的窗口。
上代码(正论)
#include <iostream>
#include <windows.h>
#include <string>//与桌面相关的窗口句柄
HWND SysListView32 = nullptr;
HWND SHELLDLL_DefView = nullptr;
HWND WorkerW = nullptr;
HWND Program = nullptr;//报告错误
void Error(const char* _error,const char* _from)
{std::cout<<"Error:"<<_error<<" From:"<<_from<<std::endl;
}//EnumWindows的回调函数
inline BOOL CALLBACK EnumWindowsProc(_In_ HWND TopHandle, _In_ LPARAM topparamhandle)
{char str[256];GetClassName(TopHandle,str,256);std::string s1 = str;//检查窗口是否叫WorkerWif(s1 == "WorkerW"){//检查是否有子窗口SHELLDLL_DefViewHWND def = FindWindowEx(TopHandle,nullptr,"SHELLDLL_DefView",nullptr);if(nullptr != def){WorkerW = TopHandle;SHELLDLL_DefView = def;}else{ShowWindow(TopHandle,SW_HIDE);}}return true;
}//设置桌面的环境
//获取所有相关的句柄
bool SetDeskEnvironment()
{const char *ErrorF = "SetDeskEnvironment";//获取Program句柄Program = FindWindow("Progman","Program Manager");if(nullptr == Program){Error("Can't Get Program Hwnd",ErrorF);return false;}//发送信息,让Windows生成WorkerSendMessageTimeout(Program, 0x052c, 0 ,0, SMTO_NORMAL, 0x3e8,nullptr);EnumWindows(EnumWindowsProc,(LPARAM)nullptr);//获取SysListView32句柄SysListView32 = FindWindowEx(SHELLDLL_DefView,nullptr,"SysListView32","FolderView");if(nullptr == SysListView32){Error("Can't Get SysListView32 Hwnd",ErrorF);return false;}return true;
}//设置窗口为桌面背景
bool SetWallWindow(LPCSTR ClassName,LPCSTR TitleName)
{const char *ErrorF = "SetWallWindow";//寻找要植入桌面的窗口的句柄HWND hWall = FindWindow(ClassName,TitleName);if(nullptr == hWall){Error("Can't Get hWall Hwnd",ErrorF);return false;}if(nullptr == Program){Error("Not get Program Hwnd",ErrorF);return false;}//把窗口设立为Progman的子窗口HWND hPro = SetParent(hWall,Program);if(nullptr == hPro){Error("Set Parent Failed",ErrorF);return false;}return true;
}int main()
{std::cout<<"Program Start!"<<std::endl;SetDeskEnvironment();SetWallWindow(nullptr,"ACGLooking");std::cout<<"Press any key..."<<std::endl;getchar();return 0;
}
在这段代码中,我用EnumWindows遍历一边,找到合适的Worker窗口,全部隐藏。而后就把自己的窗口设置为Progman的子窗口,就得到了我们想要的功能。而后我们只需要用WPF或Qt写一个漂亮的动态壁纸,就万事大吉了(如果能这么轻松的解决问题就好了 悲)。
还记得我说的Progman和自己的两个子窗口分家的问题吗。它们分家的时机就是收受到一个message,一个微软就没有公开的message,连宏定义都没有。只要向Progman窗口发送这个消息,Progman就自动分家。相关代码如下:
SendMessageTimeout(Program, 0x052c, 0 ,0, SMTO_NORMAL, 0x3e8,nullptr);
下面展示一下效果图:
这种方案还有很多问题,比如只要一启动win10的多窗口,就会。。。
(突出了)
这个问题留到以后再解决把。
如果文章的内容有错误的,欢迎纠错。
同时,希望能有大神回答我的疑问。
欢迎大家一起来交流和学习。