实现思路
众所周知qt中,要将一歌窗口嵌入另一个窗口只需要调用setParent()函数就可以了。但是setparent()参数必须是qwidget对象的指针,很遗憾的是桌面并不是一个qwidget对象,所以并不能这么做。
在查阅了一些资料之后,发现实现类似的效果都是通过winapi实现的,所以我记录下来的也只是在windows平台上的实现方法。
在winapi中提供了一个SetParent()的函数,功能与qt的setParent函数如出一辙,它的原型是这样的。
HWND SetParent(HWND hWndChild,HWND hWndNewParent);
它有两个参数,hWndChild 是子窗口的句柄,hWndNewParent 是父窗口的句柄。qwidget对象提供了函数获取窗口句柄,所以我们只需要找到桌面的窗口句柄就可以实现这个功能了。
寻找桌面窗口句柄
然而我并不熟悉winApi,在网上我找到了这么一段代码
BOOL enumUserWindowsCB(HWND hwnd,LPARAM lParam) { long wflags = GetWindowLong(hwnd, GWL_STYLE); if(!(wflags & WS_VISIBLE)) return TRUE; HWND sndWnd; if( !(sndWnd=FindWindowEx(hwnd, NULL, L"SHELLDLL_DefView", NULL)) ) return TRUE; HWND targetWnd; if( !(targetWnd=FindWindowEx(sndWnd, NULL, L"SysListView32", L"FolderView")) ) return TRUE; HWND* resultHwnd = (HWND*)lParam; *resultHwnd = targetWnd; return FALSE; } HWND findDesktopIconWnd() { HWND resultHwnd = NULL; EnumWindows((WNDENUMPROC)enumUserWindowsCB, (LPARAM)&resultHwnd); return resultHwnd; }
将代码复制到qtcreator中,确实是可以实现我想要的功能的,本着求知的精神,我仔细看了一下实现的原理。
首先这里主要使用了三个winApi的中的函数
- EnumWindows( );
函数原型
BOOL EnumWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam );
功能:遍历屏幕上所有的顶层窗口,并调用回调函数
参数:
- 第一个参数的是回调函数的指针,回调函数原型 BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam);
- 第二个参数是用户定义的值,用于传入回调函数中进行操作
- GetWindowLong
函数原型
Long GetWindowLong(HWND hWnd,int nlndex);
功能:获取窗口的一些参数,参数类型由函数的第二个参数决定
参数:
- 窗口句柄
- 参数类型
- FindWindowEx()
函数原型
HWND FindWindowEx(HWND hwndParent,HWND hwndChildAfter,LPCTSTR lpszClass,LPCTSTR lpszWindow);
功能:查找指定窗口句柄
参数:
- 父窗口的句柄,如果该参数为空,则查找屏幕上的所有顶层窗口
- 指定一个子窗口句柄,从这个窗口开始查找。可以为null
- 窗口的类名
- 窗口名称(窗口标题名称)
那么这个三个函数的功能搞清楚了,原理也基本明了了。首先是遍历屏幕的所有的顶层窗口,然后再每一个窗口下寻找桌面的图标层,直到找到之后返回。不过这里需要注意的是在回调函数中,不仅判断了窗口的可见状态,还窗口中查找了两层子窗口。之前一直搞不太懂这两层子窗口的类名是怎么确定的,在网上搜索了一番也无果。后来我尝试用spy++直接查找桌面,然后得到了这样的结果。
一下让我豁然开朗,spy++真是个好东西。
使用spy++可以直接看到最开始的顶层窗口的类名和窗口标题,也就是说可以不必遍历所有顶层窗口,而是直接使用标题跟类名查找就可以找到图表层的句柄了。我尝试了一下确实是可以的,但是查阅资料后看到有人说之所以不直接查找是因为在有的windows系统里图标层并不在这个窗口下(我使用的win10),所以使用遍历所有窗口的方式更稳妥一点。
示例
#include "mainwindow.h" #include <QApplication> #include <windows.h> #include <qlabel.h> BOOL enumUserWindowsCB(HWND hwnd,LPARAM lParam) { long wflags = GetWindowLong(hwnd, GWL_STYLE); if(!(wflags & WS_VISIBLE)) return TRUE; HWND sndWnd; if( !(sndWnd=FindWindowEx(hwnd, NULL, L"SHELLDLL_DefView", NULL)) ) return TRUE; HWND targetWnd; if( !(targetWnd=FindWindowEx(sndWnd, NULL, L"SysListView32", L"FolderView")) ) return TRUE; HWND* resultHwnd = (HWND*)lParam; *resultHwnd = targetWnd; return FALSE; } HWND findDesktopIconWnd() { HWND resultHwnd = NULL; EnumWindows((WNDENUMPROC)enumUserWindowsCB, (LPARAM)&resultHwnd); return resultHwnd; } int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); QLabel label; label.setText("hell desk"); label.setMinimumSize(QSize(100,100)); HWND desktopHwnd = findDesktopIconWnd(); if(desktopHwnd) SetParent((HWND)label.winId(), desktopHwnd); label.show(); return a.exec(); }
文章评论
点击内嵌窗口后就消失了,感觉是藏到桌面后面了
@broin 如果跟我一样用的是qt,那应该是qt的BUG,再某些情况下窗口不会绘制,如果对窗口设置了透明背景+无边框,那么基本上100%可以复现这个bug
@大脸猫 确实是这样 之后有空研究下源码