记录学习利用 QWidget 类创建桌面壁纸程序
学习一短时间Qt后想做一个桌面壁纸的程序,由于没有学过 WinAPI 编程只会些Qt,所以取桌面的句柄和设置桌面的背景图只能先从网上寻找。功夫不负有心人还是找到比较满意的WinAPI的代码。然后根据这些代码学习了以下。下面是从网上寻找到的代码,我根据这些代码和在网上查资料做了些注释。毕竟基础不牢,感觉有很多的错误。
#include <windows.h> /** 声明一个句柄类的 静态变量 */ static HWND g_workerw = 0; /** 实现一个让 EnumWindows()使用的,获取我们要将程序设置到桌面位置的句柄回调函数 */ static BOOL CALLBACK EnumWndCallback(HWND tophandle, LPARAM topparamhandle) { // 该函数的作用:由于我们要用的 窗口 是没有窗口名但我们知道我们所需要的窗口的类名是:"WorkerW", // 我们还知道我们所需要的窗口的子窗口的 子窗口的类名是:"SHELLDLL_DefView" // 我们还知道我们所需要的窗口的子窗口的窗口风格是 可见的窗口风格 // 所以我们先寻找一个窗口的窗口风格是可见的窗口风格,再检测该窗口的子窗口有没有类名是 "SHELLDLL_DefView"的窗口, // 然后再检测该窗口的父窗口有没有类名是 "WorkerW" 的窗口,如果都吻合,那这个窗口就是我们所需要的窗口的子窗口, // 那变量 tophandle 存储的句柄所对应的窗口的 父窗口就是我们需要的窗口 // 注意:topparamhandle形参之所以没有使用,是因为 EnumWindows函数并没有向 // 存储获取我们要想窗口的句柄的变量或者是变量指针,而是传递了 0,0是无法存储数据的 // 所以通过 EnumWndCallback() 函数获取的窗口句柄 只能通过 全局变量进行传递了 // GetWindowLong() 函数可获取指定窗口的有关信息, // tophandle 目标窗口句柄也就是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄 // GWL_STYLE 的作用是获取传入窗口句柄的窗口的窗口风格 long wflags = GetWindowLong(tophandle, GWL_STYLE); // 判断获取到的窗口风格是否是 可见的窗口风格 // 判断方法是 获取的窗口风格 和 可见的窗口风格 WS_VISIBLE 进行 与(&) 操作 // 如果 获取的窗口风格的二进制中的第 29 位是 1, // 那 进行 与(&) 操作 后的值 就是 WS_VISIBLE 所对应的值也就是不为 0 的值 // 如果 获取的窗口风格的二进制中的第 29 位是 0, // 那 进行 与(&) 操作 后的值 就是 0 // WS_VISIBLE(十六进制是 10000000, 二进制是 1 0000 0000 0000 0000 0000 0000 000 0) // 然后根据 ! 运算符进行反转 if (!(wflags & WS_VISIBLE)) { // 判断 获取的 窗口风格是否不是 可见的窗口风格 // 如果 不是可见的窗口风格,就返回 TRUE, 就让 EnumWindows() 函数继续枚举新的窗口 return TRUE; }; // tophandle 是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄 // 利用 FindWindowEx() 寻找父窗口的句柄是变量 tophandle 存储的句柄的子窗口的类是 "SHELLDLL_DefView" 的窗口的句柄 // 我们寻找的窗口的类名是 "SHELLDLL_Defview" HWND p = FindWindowEx(tophandle, 0, L"SHELLDLL_DefView", 0); // 判断 是否获取了句柄 if (p != 0) { // Gets the WorkerW Window after the current one // 利用 FindWindowEx() 寻找子窗口的句柄是变量 tophandle 存储的句柄的父窗口的类是 "SHELLDLL_DefView" 的窗口的句柄 // 我们寻找的窗口的类名是 "WorkerW" ,类名是 "WorkerW" 才是我们要用的窗口 g_workerw = FindWindowEx(0, tophandle, L"WorkerW", 0); // 判断 g_workerw 中是否存储了我们想要的窗口枚举值 if (0 != g_workerw) { // 传给 EnumWindows() 函数 FALSE // 停止 EnumWindows() 函数的枚举 return FALSE; } } // 传给 EnumWindows() 函数 TRUE // 让 EnumWindows() 函数继续枚举 return TRUE; } Widget::Widget(QWidget *parent) : QWidget(parent) { /** 利用 FindWindow() 函数获取一个 类是 "Progman" 窗口标题是"Program Manager"的窗口句柄 */ HWND hwndPrgam = FindWindow(L"Progman", L"Program Manager"); // 利用 SendMessageTimeout() 函数将指定的消息 0x052C, 发送 hwndPrgam 存储的枚举值的窗口 // SMTO_NORMAL:调用线程等待函数返回时,不被阻止处理其他请求。 1000 为超时周期指定以毫秒为单位的持续时间 SendMessageTimeout(hwndPrgam, 0x052C, 0,0,SMTO_NORMAL, 1000,0); // 上面的两行代码,根据查询的知识做了些注释但还是不清楚作用是什么,将上面的两行代码注释掉 // 也可以实现 将程序设置到桌面壁纸上 // 利用 EnumWindows() 枚举屏幕上的所有的顶层窗口,并将枚举的窗口传入 EnumWndCallback() 函数中 // 也就是,不停的传窗口句柄给 EnumWndCallback() 函数直到找到需要的 窗口的句柄为止 // 或枚举完所有的窗口也没有找到我们需要的窗口 // EnumWindows() 函数第二个形参传入的是 0,意味着无法通过EnumWindows() 函数第二个形参获取 // 我们想要的窗口句柄,只能通过 全局变量获取 EnumWndCallback() 函数中找到的我们需要的窗口句柄 EnumWindows(EnumWndCallback, 0); // 判断是否获取了窗口句柄 if (g_workerw == 0) { // 如果没有获取到句柄就结束该程序 abort(); } else { // 如果 获取了窗口句柄 // 将该程序设置到桌面的壁纸层中 SetParent((HWND)this->winId(), g_workerw); }
我用上面找到的代码,然后结合我自己学习的Qt的知识,可以实现将程序放到桌面壁纸上但是当关闭程序时,桌面壁纸还是程序上的图片,我用 Spy++ 查看,程序的句柄
已经没有了,所有我到 Windows10 个性化中将壁纸从新设置以下,发现桌面壁纸不在是程序上的图片了,所有我考虑是否可以用代码设置系统的壁纸。所以我在网上寻找到了下面的代码,并根据网上的知识做了些注释。
// 通过 setting 获取 Windows10系统的 "HKEY_CURRENT_USER\\Control Panel\\Desktop" 的注册表 QSettings setting("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat); // 获取 注册表地址上名称是 "WallPaper" 的值,也就是 Windows10系统的 // 当前桌面壁纸的路径 QString src = setting.value("WallPaper",true).toString(); // 将桌面壁纸的路径转换成宽字节,很重要,否则显示不了图片 TCHAR *ptch = (TCHAR *)src.toStdWString().c_str(); // 显示桌面壁纸 SPI_SETDESKWALLPAPER 表示要设置桌面墙纸,bmpfile 是要设置的图片的路径 // SPIF_UPDATEINIFILE:把新的系统参数的设置内容写入用户配置文件 SystemParametersInfo(SPI_SETDESKWALLPAPER, 0,ptch, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE );
使用上面的代码就可以实现从系统的注册表取出桌面壁纸的图片路径并又设置了一次桌面壁纸。
结合上面的两端代码,设置壁纸的核心就算搞定了,其他的可以用Qt 来写了。
为了验证自己的代码是否成功又添加了一些 Qt的代码,写成了下面写的Qt窗口类,毕竟只是验证一下,下面的代码还需要进行很多的完善。
头文件:
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QPainter> // Qt 的画家类 #include <QPixmap> // Qt 的 Pixmap 类 #include <windows.h> // Windows.h是一个最重要的头文件,它包含了其他Windows头文件 #include <QSettings> // QSettings类提供持久的独立于平台的应用程序设置。 class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); public slots: /** 接收传过来的图片路径的槽函数 */ void gengGaiTuPian(QString path); /** 接收传过来的图片路径的槽函数 */ void guanBiChuangKou(); private: /** 图片路径 */ QString tuPianLuJiang = ""; protected: /** 重写绘图事件 */ void paintEvent(QPaintEvent *event); }; #endif // WIDGET_H
源文件:
#include "widget.h" /** 声明一个句柄类的 静态变量 */ static HWND g_workerw = 0; /** 实现一个让 EnumWindows()使用的,获取我们要将程序设置到桌面位置的句柄回调函数 */ static BOOL CALLBACK EnumWndCallback(HWND tophandle, LPARAM topparamhandle) { // 该函数的作用:由于我们要用的 窗口 是没有窗口名但我们知道我们所需要的窗口的类名是:"WorkerW", // 我们还知道我们所需要的窗口的子窗口的 子窗口的类名是:"SHELLDLL_DefView" // 我们还知道我们所需要的窗口的子窗口的窗口风格是 可见的窗口风格 // 所以我们先寻找一个窗口的窗口风格是可见的窗口风格,再检测该窗口的子窗口有没有类名是 "SHELLDLL_DefView"的窗口, // 然后再检测该窗口的父窗口有没有类名是 "WorkerW" 的窗口,如果都吻合,那这个窗口就是我们所需要的窗口的子窗口, // 那变量 tophandle 存储的句柄所对应的窗口的 父窗口就是我们需要的窗口 // 注意:topparamhandle形参之所以没有使用,是因为 EnumWindows函数并没有向 // 存储获取我们要想窗口的句柄的变量或者是变量指针,而是传递了 0,0是无法存储数据的 // 所以通过 EnumWndCallback() 函数获取的窗口句柄 只能通过 全局变量进行传递了 // GetWindowLong() 函数可获取指定窗口的有关信息, // tophandle 目标窗口句柄也就是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄 // GWL_STYLE 的作用是获取传入窗口句柄的窗口的窗口风格 long wflags = GetWindowLong(tophandle, GWL_STYLE); // 判断获取到的窗口风格是否是 可见的窗口风格 // 判断方法是 获取的窗口风格 和 可见的窗口风格 WS_VISIBLE 进行 与(&) 操作 // 如果 获取的窗口风格的二进制中的第 29 位是 1, // 那 进行 与(&) 操作 后的值 就是 WS_VISIBLE 所对应的值也就是不为 0 的值 // 如果 获取的窗口风格的二进制中的第 29 位是 0, // 那 进行 与(&) 操作 后的值 就是 0 // WS_VISIBLE(十六进制是 10000000, 二进制是 1 0000 0000 0000 0000 0000 0000 000 0) // 然后根据 ! 运算符进行反转 if (!(wflags & WS_VISIBLE)) { // 判断 获取的 窗口风格是否不是 可见的窗口风格 // 如果 不是可见的窗口风格,就返回 TRUE, 就让 EnumWindows() 函数继续枚举新的窗口 return TRUE; }; // tophandle 是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄 // 利用 FindWindowEx() 寻找父窗口的句柄是变量 tophandle 存储的句柄的子窗口的类是 "SHELLDLL_DefView" 的窗口的句柄 // 我们寻找的窗口的类名是 "SHELLDLL_Defview" HWND p = FindWindowEx(tophandle, 0, L"SHELLDLL_DefView", 0); // 判断 是否获取了句柄 if (p != 0) { // Gets the WorkerW Window after the current one // 利用 FindWindowEx() 寻找子窗口的句柄是变量 tophandle 存储的句柄的父窗口的类是 "SHELLDLL_DefView" 的窗口的句柄 // 我们寻找的窗口的类名是 "WorkerW" ,类名是 "WorkerW" 才是我们要用的窗口 g_workerw = FindWindowEx(0, tophandle, L"WorkerW", 0); // 判断 g_workerw 中是否存储了我们想要的窗口枚举值 if (0 != g_workerw) { // 传给 EnumWindows() 函数 FALSE // 停止 EnumWindows() 函数的枚举 return FALSE; } } // 传给 EnumWindows() 函数 TRUE // 让 EnumWindows() 函数继续枚举 return TRUE; } Widget::Widget(QWidget *parent) : QWidget(parent) { /** 利用 FindWindow() 函数获取一个 类是 "Progman" 窗口标题是"Program Manager"的窗口句柄 */ HWND hwndPrgam = FindWindow(L"Progman", L"Program Manager"); // 利用 SendMessageTimeout() 函数将指定的消息 0x052C, 发送 hwndPrgam 存储的枚举值的窗口 // SMTO_NORMAL:调用线程等待函数返回时,不被阻止处理其他请求。 1000 为超时周期指定以毫秒为单位的持续时间 SendMessageTimeout(hwndPrgam, 0x052C, 0,0,SMTO_NORMAL, 1000,0); // 上面的两行代码,根据查询的知识做了些注释但还是不清楚作用是什么,将上面的两行代码注释掉 // 也可以实现 将程序设置到桌面壁纸上 // 利用 EnumWindows() 枚举屏幕上的所有的顶层窗口,并将枚举的窗口传入 EnumWndCallback() 函数中 // 也就是,不停的传窗口句柄给 EnumWndCallback() 函数直到找到需要的 窗口的句柄为止 // 或枚举完所有的窗口也没有找到我们需要的窗口 // EnumWindows() 函数第二个形参传入的是 0,意味着无法通过EnumWindows() 函数第二个形参获取 // 我们想要的窗口句柄,只能通过 全局变量获取 EnumWndCallback() 函数中找到的我们需要的窗口句柄 EnumWindows(EnumWndCallback, 0); // 判断是否获取了窗口句柄 if (g_workerw == 0) { // 如果没有获取到句柄就结束该程序 abort(); } else { // 如果 获取了窗口句柄 // 将该程序设置到桌面的壁纸层中 SetParent((HWND)this->winId(), g_workerw); } // 给窗口去掉边框 this-> setWindowFlags(Qt::FramelessWindowHint | windowFlags() ); // 设置窗口全屏显示 this->showFullScreen(); // 为 tuPianLuJiang 赋值 tuPianLuJiang = "://BeiJing.jpg"; } Widget::~Widget() { } void Widget::gengGaiTuPian(QString path) { // 判断 path 是否不为空 if (!path.isEmpty()) { tuPianLuJiang = path; // 通过 update() 函数调用 绘图事件 this->update(); } } void Widget::guanBiChuangKou() { // 隐藏该窗口 this->hide(); // 通过 setting 获取 Windows系统的 "HKEY_CURRENT_USER\\Control Panel\\Desktop" 的注册表 QSettings setting("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat); // 获取 注册表地址上名称是 "WallPaper" 的值,也就是 Windows10系统的 // 当前桌面壁纸的路径 QString src = setting.value("WallPaper",true).toString(); // 将桌面壁纸的路径转换成宽字节,很重要,否则显示不了图片 TCHAR *ptch = (TCHAR *)src.toStdWString().c_str(); // 显示桌面壁纸 SPI_SETDESKWALLPAPER 表示要设置桌面墙纸,bmpfile 是要设置的图片的路径 // SPIF_UPDATEINIFILE:把新的系统参数的设置内容写入用户配置文件 SystemParametersInfo(SPI_SETDESKWALLPAPER, 0,ptch, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE ); // delete 该类 // 使用完该类后,delete 该类,句柄才会消失,当是主窗口时不用 delete delete this; } void Widget::paintEvent(QPaintEvent *event) { // 实例化 画家的对象 // 需要先添加 #include <QPainter> 头文件 // this 是设置在哪里画画是指定绘图的设备不是设置父类 QPainter painter(this); // 实例化一个 QPixmap,并传入本地的一张图片 QPixmap pixmap(tuPianLuJiang); // 将图片 通过画家类的实例绘制的 窗口上 painter.drawPixmap(0, 0, this->width(), this->height(), pixmap); }
由于对编程的爱好,学习了一段时间的编程,虽然学习的时间不短,但基础实在是太弱了。第一次写博客,这篇博客算是自己学习其他知识的一点总结。完善这个程序后再学习其他的Qt 知识。
赞 (0)