## 一、前言说明
之前已经实现了通过ffmpeg桌面采集,也支持指定应用程序的标题来采集,但是需要用户手动输入程序窗口标题,这个就很不友好了,有几个用户知道如何才是完整的正确的标题呢,所以近期特意单独写了个类,专门用来获取当前环境中打开的应用程序标题,以便供用户直接下拉选择,obs推流工具也是这样处理的,通过向著名的软件参考和学习,不断的改进代码。在windows中,通过EnumWindows函数可以枚举所有窗口,然后在回调函数EnumWindowsProc中调用GetWindowText读取标题,发现读取出来的非常多,一大堆,大部分其实是不需要的,所以先要过滤掉不可见窗体的,也就是已经最小化的,这种窗体是无法采集的,obs也是无法采集最小化的窗体,原因未知,可能是系统的特性,不允许采集最小化窗口。后面还发现还有一些系统层的窗口也读取到了,需要用GetWindowLong函数来排除工具窗口和系统窗口,自此基本干净了,那还有没有完善的空间呢,有的,因为还有些应用程序的标题可能重复,那如何区分呢,所以还需要获取程序的完整路径及程序名称,也就是xxx.exe,下拉框中要做出对应的区分,比如obs的格式就是 xxx ,这样就一目了然,知道当前窗口属于哪个程序。
其实Qt6.6版本开始已经提供了类QWindowCapture::capturableWindows(),用来枚举所有窗口,估计后期还需要不断的完善,因为列举出来的窗口没有过滤,一堆都是工具窗口和系统窗口,要么无法采集,要么没有意义,不知道有没有谁去提点建议。好处是这个类跨平台,在其他系统都能完美的枚举窗口,可以学习其中的源码来参考。
下面是从Qt6.6中抠出来的代码
```cpp
static QString windowTitle(HWND hwnd) {
// QTBUG-114890
// TODO: investigate the case when hwnd is inner and belows to another thread.
// It might causes deadlocks in specific cases.
auto titleLength = ::GetWindowTextLengthW(hwnd);
std::wstring buffer(titleLength + 1, L'\0');
titleLength = ::GetWindowTextW(hwnd, buffer.data(), titleLength + 1);
buffer.resize(titleLength);
return QString::fromStdWString(buffer);
}
QList QWinCapturableWindows::windows() const
{
QList result;
auto windowHandler = [](HWND hwnd, LPARAM lParam) {
if (!canCaptureWindow(hwnd))
return TRUE; // Ignore window and continue enumerating
auto& windows = *reinterpret_cast(lParam);
auto windowData = std::make_unique();
windowData->id = reinterpret_cast(hwnd);
windowData->description = windowTitle(hwnd);
windows.push_back(windowData.release()->create());
return TRUE;
};
::EnumWindows(windowHandler, reinterpret_cast(&result));
return result;
}
```
## 二、效果图
## 三、相关代码
```cpp
#include "applicationwindow.h"
#include "qdebug.h"
#ifdef Q_OS_WIN
#include "windows.h"
#include "psapi.h"
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
//检查窗口是否可见或者最小化
if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) {
return TRUE;
}
//检查窗口是否有标题
int length = GetWindowTextLength(hwnd);
if (length == 0) {
return TRUE;
}
//排除工具窗口和系统窗口
LONG style = GetWindowLong(hwnd, GWL_STYLE);
LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
if (!(style & WS_OVERLAPPEDWINDOW) || (exStyle & WS_EX_TOOLWINDOW)) {
return TRUE;
}
//排除被其他窗口拥有的窗口
if (GetWindow(hwnd, GW_OWNER) != NULL) {
return TRUE;
}
//获取进程ID和名称
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (hProcess == NULL) {
return TRUE;
}
wchar_t exePath;
GetModuleFileNameEx(hProcess, NULL, exePath, MAX_PATH);
CloseHandle(hProcess);
QString name = QString::fromWCharArray(exePath);
name = name.split("\\").last();
//过滤不需要的/可以自行增加其他的
static QStringList names = QStringList() addItem(text, title);
return TRUE;
}
#endif
void ApplicationWindow::getApplicationWindow(QComboBox *cbox)<