测试所用系统版本
OS 名称:Microsoft Windows 10 家庭版;版本号:10.0.14393 暂缺 Build 14393
OS 名称:Microsoft Windows 7 企业版;版本号: 6.1.7600 暂缺 Build 7600(已启用Aero)
本章原理在以上Windows系统版本均已测试成功
原理阐述以及实践操作
Windows顾名思义即窗口的意思,我们平常所接触的桌面、任务栏实际上也是窗口,就和我们所使用的应用软件窗口一样,而且对于开启Aero的Win7和Win10,桌面图标所在的窗口层和桌面背景(壁纸)所在的窗口层不是同一个。通过使用VS自带的工具spy对桌面窗口进行剖析,获取窗口信息,获得下图:
图中窗口类SysListView32(红色箭头所指)即为桌面图标的窗口,可见,正常情况下该窗口是父窗口Progman的子窗口。通过下列代码可以激发窗口发生一个有趣的变化。
//获取Progman的窗口句柄
HWND windowHandle = FindWindow(L"Progman", NULL);
//向Progman发送特殊消息
SendMessageTimeout(windowHandle, 0x052c, 0 ,0, SMTO_NORMAL, 0x3e8,(PDWORD_PTR)&result);
在执行了上述代码后,重新使用spy获取桌面窗口的信息,得到下图:
可以轻松的发现,多出了两个WorkerW窗口(蓝线所标),而原来的SysListView32窗口不再以Progman为父窗口,而是以第一个WorkerW窗口为父窗口,这种设计的原因可能是微软为了防止壁纸更换时出现画面撕裂所采取,而动态桌面的原理便是利用这种设计。所谓的动态桌面就是把自己的应用程序嵌入桌面背景的窗口层,比如:把视频用作动态桌面,实际上就是写一个播放视频的程序,然后把该程序的窗口嵌入到桌面背景的窗口。要想实现这种嵌入,需要获取程序的窗口句柄,然后获取桌面背景的窗口句柄,最后通过SetParent函数把程序窗口作为桌面背景窗口的子窗口即可,由于上图中的第二个WorkerW与Progman都可作为桌面背景窗口(桌面背景窗口可以不只一个),对此,下面将分别介绍针对这两个窗口具体操作实现(至于为什么不以第一个WorkerW为父窗口或者以发送特殊消息前的Progman为父窗口?因为SysListView32与你的程序窗口以同一个窗口为父窗口会导致桌面图标与程序处于同一窗口层,就好像一个窗口下的两个控件,不好看啊):
1、程序窗口将Progman作为父窗口
HWND workerw=NULL; //第二个WorkerW窗口句柄
inline BOOL CALLBACK EnumWindowsProc(HWND handle,LPARAM lparam)
{
//获取第一个WorkerW窗口
HWND defview = FindWindowEx(handle, 0, L"SHELLDLL_DefView", NULL);
if (defview != NULL) //找到第一个WorkerW窗口
{
//获取第二个WorkerW窗口的窗口句柄
workerw = FindWindowEx(0, handle, L"WorkerW", 0);
}
return true;
}
//参数myAppHwnd为你开发的窗口程序的窗口句柄
void SetDesktop(HWND myAppHwnd)
{
int result;
HWND windowHandle = FindWindow(L"Progman", NULL);
SendMessageTimeout(windowHandle, 0x052c, 0 ,0, SMTO_NORMAL, 0x3e8,(PDWORD_PTR)&result);
//枚举窗口
EnumWindows(EnumWindowsProc,(LPARAM)NULL);
//隐藏第二个WorkerW窗口,当以Progman为父窗口时需要对其进行隐藏,
//不然程序窗口会被第二个WorkerW覆盖
ShowWindow(workerw,SW_HIDE);
SetParent(myAppHwnd,windowHandle);
}
2、程序窗口将第二个WorkerW作为父窗口
代码与上面的代码基本一致,只需要将ShowWindow(workerw,SW_HIDE)删除,并将SetParent(myAppHwnd,windowHandle)的第二个参数更改为workerw即可。
注意:
1、WorkerW其实不只有两个,本文中的第一个与第二个WorkerW窗口指的是向Progman发送特殊消息后产生的两个窗口,这两个窗口在Z序上邻近,正因为如此,所以我们需要锁定第一个WorkerW,再找到第二个WorkerW;
2、程序窗口嵌入到桌面背景,作为桌面背景后,程序窗口将无法响应你的键盘和鼠标操作,OS不会对其进行聚焦,需要另外进行处理;
3、理论上,这种方法对于其它程序窗口也适用,比如自己开发一个浏览器,你可以把浏览器变为桌面背景;
4、细心的同学会发现,SysListView32桌面图标的父窗口其实为SHELLDLL_DefView,SHELLDLL_DefView的父窗口才是Progman或第一个WorkerW,这里为了方便叙述,省略掉了。
测试所用系统版本
OS 名称:Microsoft Windows 10 家庭版;版本号:10.0.14393 暂缺 Build 14393
OS 名称:Microsoft Windows 7 企业版;版本号: 6.1.7600 暂缺 Build 7600(已启用Aero)
本章原理在以上Windows系统版本均已测试成功
原理阐述以及实践操作
Windows顾名思义即窗口的意思,我们平常所接触的桌面、任务栏实际上也是窗口,就和我们所使用的应用软件窗口一样,而且对于开启Aero的Win7和Win10,桌面图标所在的窗口层和桌面背景(壁纸)所在的窗口层不是同一个。通过使用VS自带的工具spy对桌面窗口进行剖析,获取窗口信息,获得下图:
图中窗口类SysListView32(红色箭头所指)即为桌面图标的窗口,可见,正常情况下该窗口是父窗口Progman的子窗口。通过下列代码可以激发窗口发生一个有趣的变化。
//获取Progman的窗口句柄
HWND windowHandle = FindWindow(L"Progman", NULL);
//向Progman发送特殊消息
SendMessageTimeout(windowHandle, 0x052c, 0 ,0, SMTO_NORMAL, 0x3e8,(PDWORD_PTR)&result);
在执行了上述代码后,重新使用spy获取桌面窗口的信息,得到下图:
可以轻松的发现,多出了两个WorkerW窗口(蓝线所标),而原来的SysListView32窗口不再以Progman为父窗口,而是以第一个WorkerW窗口为父窗口,这种设计的原因可能是微软为了防止壁纸更换时出现画面撕裂所采取,而动态桌面的原理便是利用这种设计。所谓的动态桌面就是把自己的应用程序嵌入桌面背景的窗口层,比如:把视频用作动态桌面,实际上就是写一个播放视频的程序,然后把该程序的窗口嵌入到桌面背景的窗口。要想实现这种嵌入,需要获取程序的窗口句柄,然后获取桌面背景的窗口句柄,最后通过SetParent函数把程序窗口作为桌面背景窗口的子窗口即可,由于上图中的第二个WorkerW与Progman都可作为桌面背景窗口(桌面背景窗口可以不只一个),对此,下面将分别介绍针对这两个窗口具体操作实现(至于为什么不以第一个WorkerW为父窗口或者以发送特殊消息前的Progman为父窗口?因为SysListView32与你的程序窗口以同一个窗口为父窗口会导致桌面图标与程序处于同一窗口层,就好像一个窗口下的两个控件,不好看啊):
1、程序窗口将Progman作为父窗口
HWND workerw=NULL; //第二个WorkerW窗口句柄
inline BOOL CALLBACK EnumWindowsProc(HWND handle,LPARAM lparam)
{
//获取第一个WorkerW窗口
HWND defview = FindWindowEx(handle, 0, L"SHELLDLL_DefView", NULL);
if (defview != NULL) //找到第一个WorkerW窗口
{
//获取第二个WorkerW窗口的窗口句柄
workerw = FindWindowEx(0, handle, L"WorkerW", 0);
}
return true;
}
//参数myAppHwnd为你开发的窗口程序的窗口句柄
void SetDesktop(HWND myAppHwnd)
{
int result;
HWND windowHandle = FindWindow(L"Progman", NULL);
SendMessageTimeout(windowHandle, 0x052c, 0 ,0, SMTO_NORMAL, 0x3e8,(PDWORD_PTR)&result);
//枚举窗口
EnumWindows(EnumWindowsProc,(LPARAM)NULL);
//隐藏第二个WorkerW窗口,当以Progman为父窗口时需要对其进行隐藏,
//不然程序窗口会被第二个WorkerW覆盖
ShowWindow(workerw,SW_HIDE);
SetParent(myAppHwnd,windowHandle);
}
2、程序窗口将第二个WorkerW作为父窗口
代码与上面的代码基本一致,只需要将ShowWindow(workerw,SW_HIDE)删除,并将SetParent(myAppHwnd,windowHandle)的第二个参数更改为workerw即可。
注意:
1、WorkerW其实不只有两个,本文中的第一个与第二个WorkerW窗口指的是向Progman发送特殊消息后产生的两个窗口,这两个窗口在Z序上邻近,正因为如此,所以我们需要锁定第一个WorkerW,再找到第二个WorkerW;
2、程序窗口嵌入到桌面背景,作为桌面背景后,程序窗口将无法响应你的键盘和鼠标操作,OS不会对其进行聚焦,需要另外进行处理;
3、理论上,这种方法对于其它程序窗口也适用,比如自己开发一个浏览器,你可以把浏览器变为桌面背景;
4、细心的同学会发现,SysListView32桌面图标的父窗口其实为SHELLDLL_DefView,SHELLDLL_DefView的父窗口才是Progman或第一个WorkerW,这里为了方便叙述,省略掉了。